Blame view

fs/nfs/delegation.c 29 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /*
   * linux/fs/nfs/delegation.c
   *
   * Copyright (C) 2004 Trond Myklebust
   *
   * NFS file delegation management
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <linux/completion.h>
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
10
  #include <linux/kthread.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  #include <linux/module.h>
  #include <linux/sched.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
15
16
17
18
  #include <linux/spinlock.h>
  
  #include <linux/nfs4.h>
  #include <linux/nfs_fs.h>
  #include <linux/nfs_xdr.h>
4ce79717c   Trond Myklebust   [PATCH] NFS: Head...
19
  #include "nfs4_fs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include "delegation.h"
24c8dbbb5   David Howells   NFS: Generalise t...
21
  #include "internal.h"
ca8acf8d8   Trond Myklebust   NFSv4: Add tracep...
22
  #include "nfs4trace.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23

905f8d16e   Trond Myklebust   NFSv4: Don't call...
24
25
  static void nfs_free_delegation(struct nfs_delegation *delegation)
  {
e00b8a240   Trond Myklebust   NFS: Fix an NFS c...
26
27
28
29
  	if (delegation->cred) {
  		put_rpccred(delegation->cred);
  		delegation->cred = NULL;
  	}
26f04dde6   Lai Jiangshan   nfs,rcu: convert ...
30
  	kfree_rcu(delegation, rcu);
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
31
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
32
33
34
35
36
  /**
   * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
   * @delegation: delegation to process
   *
   */
b7391f44f   Trond Myklebust   NFSv4: Return unr...
37
38
39
40
  void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
  {
  	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
  }
aa05c87f2   Trond Myklebust   NFSv4: nfs4_copy_...
41
42
43
44
45
46
47
48
49
50
  static bool
  nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
  		fmode_t flags)
  {
  	if (delegation != NULL && (delegation->type & flags) == flags &&
  	    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
  	    !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
  		return true;
  	return false;
  }
15bb3afe9   Peng Tao   nfs4: add nfs4_ch...
51
52
  static int
  nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
b7391f44f   Trond Myklebust   NFSv4: Return unr...
53
54
55
56
57
58
59
  {
  	struct nfs_delegation *delegation;
  	int ret = 0;
  
  	flags &= FMODE_READ|FMODE_WRITE;
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
aa05c87f2   Trond Myklebust   NFSv4: nfs4_copy_...
60
  	if (nfs4_is_valid_delegation(delegation, flags)) {
15bb3afe9   Peng Tao   nfs4: add nfs4_ch...
61
62
  		if (mark)
  			nfs_mark_delegation_referenced(delegation);
b7391f44f   Trond Myklebust   NFSv4: Return unr...
63
64
65
66
67
  		ret = 1;
  	}
  	rcu_read_unlock();
  	return ret;
  }
15bb3afe9   Peng Tao   nfs4: add nfs4_ch...
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  /**
   * nfs_have_delegation - check if inode has a delegation, mark it
   * NFS_DELEGATION_REFERENCED if there is one.
   * @inode: inode to check
   * @flags: delegation types to check for
   *
   * Returns one if inode has the indicated delegation, otherwise zero.
   */
  int nfs4_have_delegation(struct inode *inode, fmode_t flags)
  {
  	return nfs4_do_check_delegation(inode, flags, true);
  }
  
  /*
   * nfs4_check_delegation - check if inode has a delegation, do not mark
   * NFS_DELEGATION_REFERENCED if it has one.
   */
  int nfs4_check_delegation(struct inode *inode, fmode_t flags)
  {
  	return nfs4_do_check_delegation(inode, flags, false);
  }
b7391f44f   Trond Myklebust   NFSv4: Return unr...
89

db4f2e637   Trond Myklebust   NFSv4: Clean up d...
90
  static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
888e694c1   Trond Myklebust   NFSv4: Recover lo...
91
92
93
  {
  	struct inode *inode = state->inode;
  	struct file_lock *fl;
bd61e0a9c   Jeff Layton   locks: convert po...
94
95
  	struct file_lock_context *flctx = inode->i_flctx;
  	struct list_head *list;
d5122201a   Trond Myklebust   NFSv4: Move error...
96
  	int status = 0;
888e694c1   Trond Myklebust   NFSv4: Recover lo...
97

bd61e0a9c   Jeff Layton   locks: convert po...
98
  	if (flctx == NULL)
65b62a29f   Trond Myklebust   NFSv4: Ensure del...
99
  		goto out;
314d7cc05   Jeff Layton   nfs: remove unnec...
100

bd61e0a9c   Jeff Layton   locks: convert po...
101
  	list = &flctx->flc_posix;
6109c8503   Jeff Layton   locks: add a dedi...
102
  	spin_lock(&flctx->flc_lock);
bd61e0a9c   Jeff Layton   locks: convert po...
103
104
  restart:
  	list_for_each_entry(fl, list, fl_list) {
cd3758e37   Trond Myklebust   NFS: Replace file...
105
  		if (nfs_file_open_context(fl->fl_file) != ctx)
888e694c1   Trond Myklebust   NFSv4: Recover lo...
106
  			continue;
6109c8503   Jeff Layton   locks: add a dedi...
107
  		spin_unlock(&flctx->flc_lock);
db4f2e637   Trond Myklebust   NFSv4: Clean up d...
108
  		status = nfs4_lock_delegation_recall(fl, state, stateid);
d5122201a   Trond Myklebust   NFSv4: Move error...
109
  		if (status < 0)
3f09df70e   Trond Myklebust   NFS: Ensure we al...
110
  			goto out;
6109c8503   Jeff Layton   locks: add a dedi...
111
  		spin_lock(&flctx->flc_lock);
888e694c1   Trond Myklebust   NFSv4: Recover lo...
112
  	}
bd61e0a9c   Jeff Layton   locks: convert po...
113
114
115
  	if (list == &flctx->flc_posix) {
  		list = &flctx->flc_flock;
  		goto restart;
888e694c1   Trond Myklebust   NFSv4: Recover lo...
116
  	}
6109c8503   Jeff Layton   locks: add a dedi...
117
  	spin_unlock(&flctx->flc_lock);
3f09df70e   Trond Myklebust   NFS: Ensure we al...
118
  out:
888e694c1   Trond Myklebust   NFSv4: Recover lo...
119
120
  	return status;
  }
24311f884   Trond Myklebust   NFSv4: Recovery o...
121
122
  static int nfs_delegation_claim_opens(struct inode *inode,
  		const nfs4_stateid *stateid, fmode_t type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_open_context *ctx;
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
126
  	struct nfs4_state_owner *sp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  	struct nfs4_state *state;
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
128
  	unsigned int seq;
888e694c1   Trond Myklebust   NFSv4: Recover lo...
129
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
135
136
137
138
  
  again:
  	spin_lock(&inode->i_lock);
  	list_for_each_entry(ctx, &nfsi->open_files, list) {
  		state = ctx->state;
  		if (state == NULL)
  			continue;
  		if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
  			continue;
f8ebf7a8c   Trond Myklebust   NFS: Don't try to...
139
140
  		if (!nfs4_valid_open_stateid(state))
  			continue;
f597c5379   Trond Myklebust   NFSv4: Add helper...
141
  		if (!nfs4_stateid_match(&state->stateid, stateid))
901630278   Trond Myklebust   NFSv4: Support re...
142
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
  		get_nfs_open_context(ctx);
  		spin_unlock(&inode->i_lock);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
145
  		sp = state->owner;
65b62a29f   Trond Myklebust   NFSv4: Ensure del...
146
147
  		/* Block nfs4_proc_unlck */
  		mutex_lock(&sp->so_delegreturn_mutex);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
148
  		seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
24311f884   Trond Myklebust   NFSv4: Recovery o...
149
  		err = nfs4_open_delegation_recall(ctx, state, stateid, type);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
150
  		if (!err)
db4f2e637   Trond Myklebust   NFSv4: Clean up d...
151
  			err = nfs_delegation_claim_locks(ctx, state, stateid);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
152
153
  		if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
  			err = -EAGAIN;
65b62a29f   Trond Myklebust   NFSv4: Ensure del...
154
  		mutex_unlock(&sp->so_delegreturn_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  		put_nfs_open_context(ctx);
888e694c1   Trond Myklebust   NFSv4: Recover lo...
156
  		if (err != 0)
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
157
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
  		goto again;
  	}
  	spin_unlock(&inode->i_lock);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
161
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
163
164
165
166
167
168
  /**
   * nfs_inode_reclaim_delegation - process a delegation reclaim request
   * @inode: inode to process
   * @cred: credential to use for request
   * @res: new delegation state from server
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
   */
d3978bb32   Chuck Lever   NFS: Move cl_dele...
170
171
  void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
  				  struct nfs_openres *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  {
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
173
174
  	struct nfs_delegation *delegation;
  	struct rpc_cred *oldcred = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175

8f649c376   Trond Myklebust   NFSv4: Fix the lo...
176
177
178
179
180
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
  	if (delegation != NULL) {
  		spin_lock(&delegation->lock);
  		if (delegation->inode != NULL) {
f597c5379   Trond Myklebust   NFSv4: Add helper...
181
  			nfs4_stateid_copy(&delegation->stateid, &res->delegation);
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
182
  			delegation->type = res->delegation_type;
7d160a6c4   Trond Myklebust   NFSv4: Express de...
183
  			delegation->pagemod_limit = res->pagemod_limit;
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
184
185
186
187
  			oldcred = delegation->cred;
  			delegation->cred = get_rpccred(cred);
  			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
  				  &delegation->flags);
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
188
  			spin_unlock(&delegation->lock);
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
189
  			rcu_read_unlock();
7c0af9ffb   Trond Myklebust   NFSv4: Don't call...
190
  			put_rpccred(oldcred);
ca8acf8d8   Trond Myklebust   NFSv4: Add tracep...
191
  			trace_nfs4_reclaim_delegation(inode, res->delegation_type);
b1a318de9   Trond Myklebust   NFSv4: Fix a race...
192
  			return;
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
193
  		}
b1a318de9   Trond Myklebust   NFSv4: Fix a race...
194
195
  		/* We appear to have raced with a delegation return. */
  		spin_unlock(&delegation->lock);
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
196
  	}
b1a318de9   Trond Myklebust   NFSv4: Fix a race...
197
198
  	rcu_read_unlock();
  	nfs_inode_set_delegation(inode, cred, res);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  }
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
200
201
202
  static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
  {
  	int res = 0;
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
203
204
205
206
207
  	if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
  		res = nfs4_proc_delegreturn(inode,
  				delegation->cred,
  				&delegation->stateid,
  				issync);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
208
209
210
  	nfs_free_delegation(delegation);
  	return res;
  }
86e894899   Trond Myklebust   NFSv4: Fix up the...
211
212
213
214
215
216
217
218
219
220
  static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
  {
  	struct inode *inode = NULL;
  
  	spin_lock(&delegation->lock);
  	if (delegation->inode != NULL)
  		inode = igrab(delegation->inode);
  	spin_unlock(&delegation->lock);
  	return inode;
  }
dda4b2256   Chuck Lever   NFS: Introduce nf...
221
  static struct nfs_delegation *
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
  {
  	struct nfs_delegation *ret = NULL;
  	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
  
  	if (delegation == NULL)
  		goto out;
  	spin_lock(&delegation->lock);
  	if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
  		ret = delegation;
  	spin_unlock(&delegation->lock);
  out:
  	return ret;
  }
  
  static struct nfs_delegation *
  nfs_start_delegation_return(struct nfs_inode *nfsi)
  {
  	struct nfs_delegation *delegation;
  
  	rcu_read_lock();
  	delegation = nfs_start_delegation_return_locked(nfsi);
  	rcu_read_unlock();
  	return delegation;
  }
  
  static void
  nfs_abort_delegation_return(struct nfs_delegation *delegation,
  		struct nfs_client *clp)
  {
  
  	spin_lock(&delegation->lock);
  	clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
  	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
  	spin_unlock(&delegation->lock);
  	set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
  }
  
  static struct nfs_delegation *
dda4b2256   Chuck Lever   NFS: Introduce nf...
261
  nfs_detach_delegation_locked(struct nfs_inode *nfsi,
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
262
263
  		struct nfs_delegation *delegation,
  		struct nfs_client *clp)
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
264
  {
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
265
  	struct nfs_delegation *deleg_cur =
17d2c0a0c   David Howells   NFS: Fix RCU issu...
266
  		rcu_dereference_protected(nfsi->delegation,
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
267
  				lockdep_is_held(&clp->cl_lock));
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
268

d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
269
270
  	if (deleg_cur == NULL || delegation != deleg_cur)
  		return NULL;
dda4b2256   Chuck Lever   NFS: Introduce nf...
271

343104308   Trond Myklebust   NFSv4: Fix up ano...
272
  	spin_lock(&delegation->lock);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
273
  	set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
274
  	list_del_rcu(&delegation->super_list);
86e894899   Trond Myklebust   NFSv4: Fix up the...
275
  	delegation->inode = NULL;
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
276
  	rcu_assign_pointer(nfsi->delegation, NULL);
343104308   Trond Myklebust   NFSv4: Fix up ano...
277
  	spin_unlock(&delegation->lock);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
278
  	return delegation;
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
279
  }
dda4b2256   Chuck Lever   NFS: Introduce nf...
280
  static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
281
282
  		struct nfs_delegation *delegation,
  		struct nfs_server *server)
dda4b2256   Chuck Lever   NFS: Introduce nf...
283
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
284
  	struct nfs_client *clp = server->nfs_client;
dda4b2256   Chuck Lever   NFS: Introduce nf...
285
286
  
  	spin_lock(&clp->cl_lock);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
287
  	delegation = nfs_detach_delegation_locked(nfsi, delegation, clp);
dda4b2256   Chuck Lever   NFS: Introduce nf...
288
289
290
  	spin_unlock(&clp->cl_lock);
  	return delegation;
  }
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
291
292
293
294
295
296
297
298
299
300
301
302
  static struct nfs_delegation *
  nfs_inode_detach_delegation(struct inode *inode)
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_server *server = NFS_SERVER(inode);
  	struct nfs_delegation *delegation;
  
  	delegation = nfs_start_delegation_return(nfsi);
  	if (delegation == NULL)
  		return NULL;
  	return nfs_detach_delegation(nfsi, delegation, server);
  }
cf6726e2e   Trond Myklebust   NFSv4: Deal with ...
303
304
305
306
307
308
309
310
311
312
  static void
  nfs_update_inplace_delegation(struct nfs_delegation *delegation,
  		const struct nfs_delegation *update)
  {
  	if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) {
  		delegation->stateid.seqid = update->stateid.seqid;
  		smp_wmb();
  		delegation->type = update->type;
  	}
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
313
314
315
316
317
318
319
  /**
   * nfs_inode_set_delegation - set up a delegation on an inode
   * @inode: inode to which delegation applies
   * @cred: cred to use for subsequent delegation processing
   * @res: new delegation state from server
   *
   * Returns zero on success, or a negative errno value.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
321
322
   */
  int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
323
324
  	struct nfs_server *server = NFS_SERVER(inode);
  	struct nfs_client *clp = server->nfs_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
  	struct nfs_inode *nfsi = NFS_I(inode);
17d2c0a0c   David Howells   NFS: Fix RCU issu...
326
  	struct nfs_delegation *delegation, *old_delegation;
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
327
  	struct nfs_delegation *freeme = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  	int status = 0;
8535b2be5   Trond Myklebust   NFSv4: Don't use ...
329
  	delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  	if (delegation == NULL)
  		return -ENOMEM;
f597c5379   Trond Myklebust   NFSv4: Add helper...
332
  	nfs4_stateid_copy(&delegation->stateid, &res->delegation);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  	delegation->type = res->delegation_type;
7d160a6c4   Trond Myklebust   NFSv4: Express de...
334
  	delegation->pagemod_limit = res->pagemod_limit;
a9a4a87a5   Trond Myklebust   NFS: Use the inod...
335
  	delegation->change_attr = inode->i_version;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
  	delegation->cred = get_rpccred(cred);
  	delegation->inode = inode;
b7391f44f   Trond Myklebust   NFSv4: Return unr...
338
  	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
343104308   Trond Myklebust   NFSv4: Fix up ano...
339
  	spin_lock_init(&delegation->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
  
  	spin_lock(&clp->cl_lock);
17d2c0a0c   David Howells   NFS: Fix RCU issu...
342
  	old_delegation = rcu_dereference_protected(nfsi->delegation,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
343
  					lockdep_is_held(&clp->cl_lock));
17d2c0a0c   David Howells   NFS: Fix RCU issu...
344
  	if (old_delegation != NULL) {
cf6726e2e   Trond Myklebust   NFSv4: Deal with ...
345
346
347
348
349
  		/* Is this an update of the existing delegation? */
  		if (nfs4_stateid_match_other(&old_delegation->stateid,
  					&delegation->stateid)) {
  			nfs_update_inplace_delegation(old_delegation,
  					delegation);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
350
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  		}
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
352
353
354
  		/*
  		 * Deal with broken servers that hand out two
  		 * delegations for the same file.
17280175c   Trond Myklebust   NFS: Fix a number...
355
356
  		 * Allow for upgrades to a WRITE delegation, but
  		 * nothing else.
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
357
358
359
360
  		 */
  		dfprintk(FILE, "%s: server %s handed out "
  				"a duplicate delegation!
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
361
  				__func__, clp->cl_hostname);
17280175c   Trond Myklebust   NFS: Fix a number...
362
363
  		if (delegation->type == old_delegation->type ||
  		    !(delegation->type & FMODE_WRITE)) {
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
364
365
366
367
  			freeme = delegation;
  			delegation = NULL;
  			goto out;
  		}
ade04647d   Trond Myklebust   NFSv4: Ensure we ...
368
369
370
371
  		if (test_and_set_bit(NFS_DELEGATION_RETURNING,
  					&old_delegation->flags))
  			goto out;
  		freeme = nfs_detach_delegation_locked(nfsi,
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
372
373
374
  				old_delegation, clp);
  		if (freeme == NULL)
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	}
38942ba20   Trond Myklebust   NFSv4: Append del...
376
  	list_add_tail_rcu(&delegation->super_list, &server->delegations);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
377
378
  	rcu_assign_pointer(nfsi->delegation, delegation);
  	delegation = NULL;
412c77cee   Trond Myklebust   NFSv4: Defer inod...
379

ca8acf8d8   Trond Myklebust   NFSv4: Add tracep...
380
  	trace_nfs4_set_delegation(inode, res->delegation_type);
412c77cee   Trond Myklebust   NFSv4: Defer inod...
381

57bfa8917   Trond Myklebust   NFSv4: Deal more ...
382
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  	spin_unlock(&clp->cl_lock);
603c83da1   Trond Myklebust   NFSv4: Fix an rpc...
384
385
  	if (delegation != NULL)
  		nfs_free_delegation(delegation);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
386
387
  	if (freeme != NULL)
  		nfs_do_return_delegation(inode, freeme, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  	return status;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
392
  /*
   * Basic procedure for returning a delegation to the server
   */
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
393
  static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
  {
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
395
  	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  	struct nfs_inode *nfsi = NFS_I(inode);
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
397
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398

d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
399
400
401
  	if (delegation == NULL)
  		return 0;
  	do {
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
402
403
  		if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
  			break;
24311f884   Trond Myklebust   NFSv4: Recovery o...
404
405
  		err = nfs_delegation_claim_opens(inode, &delegation->stateid,
  				delegation->type);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
406
407
408
409
410
411
412
413
414
415
416
417
418
  		if (!issync || err != -EAGAIN)
  			break;
  		/*
  		 * Guard against state recovery
  		 */
  		err = nfs4_wait_clnt_recover(clp);
  	} while (err == 0);
  
  	if (err) {
  		nfs_abort_delegation_return(delegation, clp);
  		goto out;
  	}
  	if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
419
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420

d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
421
422
423
  	err = nfs_do_return_delegation(inode, delegation, issync);
  out:
  	return err;
901630278   Trond Myklebust   NFSv4: Support re...
424
  }
b757144fd   Trond Myklebust   NFSv4: Be less ag...
425
426
427
  static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
  {
  	bool ret = false;
ec3ca4e57   Trond Myklebust   NFSv4: Ensure we ...
428
429
  	if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
  		goto out;
b757144fd   Trond Myklebust   NFSv4: Be less ag...
430
431
432
433
434
435
436
437
438
439
440
  	if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
  		ret = true;
  	if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
  		struct inode *inode;
  
  		spin_lock(&delegation->lock);
  		inode = delegation->inode;
  		if (inode && list_empty(&NFS_I(inode)->open_files))
  			ret = true;
  		spin_unlock(&delegation->lock);
  	}
ec3ca4e57   Trond Myklebust   NFSv4: Ensure we ...
441
  out:
b757144fd   Trond Myklebust   NFSv4: Be less ag...
442
443
  	return ret;
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
444
445
446
447
  /**
   * nfs_client_return_marked_delegations - return previously marked delegations
   * @clp: nfs_client to process
   *
dc327ed4c   Trond Myklebust   NFSv4: nfs_client...
448
449
450
451
   * Note that this function is designed to be called by the state
   * manager thread. For this reason, it cannot flush the dirty data,
   * since that could deadlock in case of a state recovery error.
   *
d3978bb32   Chuck Lever   NFS: Move cl_dele...
452
   * Returns zero on success, or a negative errno value.
515d86117   Trond Myklebust   NFSv4: Clean up t...
453
   */
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
454
  int nfs_client_return_marked_delegations(struct nfs_client *clp)
515d86117   Trond Myklebust   NFSv4: Clean up t...
455
456
  {
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
457
  	struct nfs_server *server;
515d86117   Trond Myklebust   NFSv4: Clean up t...
458
  	struct inode *inode;
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
459
  	int err = 0;
515d86117   Trond Myklebust   NFSv4: Clean up t...
460
461
462
  
  restart:
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
463
464
465
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
  		list_for_each_entry_rcu(delegation, &server->delegations,
  								super_list) {
b757144fd   Trond Myklebust   NFSv4: Be less ag...
466
  			if (!nfs_delegation_need_return(delegation))
d3978bb32   Chuck Lever   NFS: Move cl_dele...
467
  				continue;
9f0f8e12c   Trond Myklebust   NFSv4: Pin the su...
468
  			if (!nfs_sb_active(server->super))
d3978bb32   Chuck Lever   NFS: Move cl_dele...
469
  				continue;
9f0f8e12c   Trond Myklebust   NFSv4: Pin the su...
470
471
472
473
474
475
  			inode = nfs_delegation_grab_inode(delegation);
  			if (inode == NULL) {
  				rcu_read_unlock();
  				nfs_sb_deactive(server->super);
  				goto restart;
  			}
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
476
  			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
d3978bb32   Chuck Lever   NFS: Move cl_dele...
477
  			rcu_read_unlock();
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
478
  			err = nfs_end_delegation_return(inode, delegation, 0);
d3978bb32   Chuck Lever   NFS: Move cl_dele...
479
  			iput(inode);
9f0f8e12c   Trond Myklebust   NFSv4: Pin the su...
480
  			nfs_sb_deactive(server->super);
d3978bb32   Chuck Lever   NFS: Move cl_dele...
481
482
483
484
  			if (!err)
  				goto restart;
  			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
  			return err;
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
485
  		}
515d86117   Trond Myklebust   NFSv4: Clean up t...
486
487
  	}
  	rcu_read_unlock();
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
488
  	return 0;
515d86117   Trond Myklebust   NFSv4: Clean up t...
489
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
490
491
492
493
494
495
  /**
   * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
   * @inode: inode to process
   *
   * Does not protect against delegation reclaims, therefore really only safe
   * to be called from nfs4_clear_inode().
e6f810759   Trond Myklebust   NFS: Add an async...
496
497
498
   */
  void nfs_inode_return_delegation_noreclaim(struct inode *inode)
  {
e6f810759   Trond Myklebust   NFS: Add an async...
499
  	struct nfs_delegation *delegation;
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
500
501
  	delegation = nfs_inode_detach_delegation(inode);
  	if (delegation != NULL)
5fcdfacc0   Trond Myklebust   NFSv4: Return del...
502
  		nfs_do_return_delegation(inode, delegation, 1);
e6f810759   Trond Myklebust   NFS: Add an async...
503
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
504
505
506
507
  /**
   * nfs_inode_return_delegation - synchronously return a delegation
   * @inode: inode to process
   *
c57d1bc5e   Trond Myklebust   NFS: nfs_inode_re...
508
509
510
511
   * This routine will always flush any dirty data to disk on the
   * assumption that if we need to return the delegation, then
   * we should stop caching.
   *
d3978bb32   Chuck Lever   NFS: Move cl_dele...
512
513
   * Returns zero on success, or a negative errno value.
   */
57ec14c55   Bryan Schumaker   NFS: Create a ret...
514
  int nfs4_inode_return_delegation(struct inode *inode)
901630278   Trond Myklebust   NFSv4: Support re...
515
  {
901630278   Trond Myklebust   NFSv4: Support re...
516
517
518
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_delegation *delegation;
  	int err = 0;
c57d1bc5e   Trond Myklebust   NFS: nfs_inode_re...
519
  	nfs_wb_all(inode);
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
520
521
522
  	delegation = nfs_start_delegation_return(nfsi);
  	if (delegation != NULL)
  		err = nfs_end_delegation_return(inode, delegation, 1);
901630278   Trond Myklebust   NFSv4: Support re...
523
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
  }
b757144fd   Trond Myklebust   NFSv4: Be less ag...
525
526
527
528
529
530
  static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
  		struct nfs_delegation *delegation)
  {
  	set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
  	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
  }
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
531
532
  static void nfs_mark_return_delegation(struct nfs_server *server,
  		struct nfs_delegation *delegation)
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
533
534
  {
  	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
535
  	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
536
  }
5c31e2368   Trond Myklebust   NFSv4: Fix nfs_se...
537
538
539
540
541
542
543
544
545
546
547
  static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
  {
  	struct nfs_delegation *delegation;
  	bool ret = false;
  
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
  		nfs_mark_return_delegation(server, delegation);
  		ret = true;
  	}
  	return ret;
  }
b02ba0b66   Trond Myklebust   NFSv4: Clean up n...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
  {
  	struct nfs_server *server;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		nfs_server_mark_return_all_delegations(server);
  	rcu_read_unlock();
  }
  
  static void nfs_delegation_run_state_manager(struct nfs_client *clp)
  {
  	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
  		nfs4_schedule_state_manager(clp);
  }
  
  /**
   * nfs_expire_all_delegations
   * @clp: client to process
   *
   */
  void nfs_expire_all_delegations(struct nfs_client *clp)
  {
  	nfs_client_mark_return_all_delegations(clp);
  	nfs_delegation_run_state_manager(clp);
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
574
575
576
577
  /**
   * nfs_super_return_all_delegations - return delegations for one superblock
   * @sb: sb to process
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
   */
eeebf9167   Bryan Schumaker   NFS: Use nfs4_des...
579
  void nfs_server_return_all_delegations(struct nfs_server *server)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
581
  	struct nfs_client *clp = server->nfs_client;
5c31e2368   Trond Myklebust   NFSv4: Fix nfs_se...
582
  	bool need_wait;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
584
585
  
  	if (clp == NULL)
  		return;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
586

8383e4602   Trond Myklebust   NFSv4: Use RCU to...
587
  	rcu_read_lock();
5c31e2368   Trond Myklebust   NFSv4: Fix nfs_se...
588
  	need_wait = nfs_server_mark_return_all_delegations(server);
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
589
  	rcu_read_unlock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
590

5c31e2368   Trond Myklebust   NFSv4: Fix nfs_se...
591
  	if (need_wait) {
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
592
  		nfs4_schedule_state_manager(clp);
5c31e2368   Trond Myklebust   NFSv4: Fix nfs_se...
593
594
  		nfs4_wait_clnt_recover(clp);
  	}
515d86117   Trond Myklebust   NFSv4: Clean up t...
595
  }
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
596
  static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
597
  						 fmode_t flags)
515d86117   Trond Myklebust   NFSv4: Clean up t...
598
599
  {
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
600
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
c79571a50   Alexandros Batsakis   nfs4: V2 return/e...
601
602
603
  		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
  			continue;
  		if (delegation->type & flags)
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
604
  			nfs_mark_return_if_closed_delegation(server, delegation);
707fb4b32   Trond Myklebust   NFSv4: Clean up N...
605
  	}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
606
  }
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
607
  static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
608
609
610
611
612
613
  							fmode_t flags)
  {
  	struct nfs_server *server;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
614
  		nfs_mark_return_unused_delegation_types(server, flags);
515d86117   Trond Myklebust   NFSv4: Clean up t...
615
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
  }
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
617
618
619
620
  static void nfs_mark_delegation_revoked(struct nfs_server *server,
  		struct nfs_delegation *delegation)
  {
  	set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
059b43e97   Trond Myklebust   NFSv4: Ensure we ...
621
  	delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
622
623
624
625
626
  	nfs_mark_return_delegation(server, delegation);
  }
  
  static bool nfs_revoke_delegation(struct inode *inode,
  		const nfs4_stateid *stateid)
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
627
628
  {
  	struct nfs_delegation *delegation;
7f0488314   Trond Myklebust   NFS: Always call ...
629
  	nfs4_stateid tmp;
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
630
  	bool ret = false;
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
631
632
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
633
634
  	if (delegation == NULL)
  		goto out;
7f0488314   Trond Myklebust   NFS: Always call ...
635
636
637
638
  	if (stateid == NULL) {
  		nfs4_stateid_copy(&tmp, &delegation->stateid);
  		stateid = &tmp;
  	} else if (!nfs4_stateid_match(stateid, &delegation->stateid))
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
639
640
641
642
  		goto out;
  	nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
  	ret = true;
  out:
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
643
  	rcu_read_unlock();
7f0488314   Trond Myklebust   NFS: Always call ...
644
645
  	if (ret)
  		nfs_inode_find_state_and_recover(inode, stateid);
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
646
  	return ret;
869f9dfa4   Trond Myklebust   NFSv4: Fix races ...
647
  }
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
648
649
  void nfs_remove_bad_delegation(struct inode *inode,
  		const nfs4_stateid *stateid)
a1d0b5eeb   Trond Myklebust   NFS: Properly han...
650
651
  {
  	struct nfs_delegation *delegation;
41020b671   Trond Myklebust   NFSv4.x: Allow ca...
652
653
  	if (!nfs_revoke_delegation(inode, stateid))
  		return;
d25be546a   Trond Myklebust   NFSv4.1: Don't lo...
654
  	delegation = nfs_inode_detach_delegation(inode);
7f0488314   Trond Myklebust   NFS: Always call ...
655
  	if (delegation)
a1d0b5eeb   Trond Myklebust   NFS: Properly han...
656
  		nfs_free_delegation(delegation);
a1d0b5eeb   Trond Myklebust   NFS: Properly han...
657
  }
9cb819683   Andy Adamson   NFSv4.1 handle DS...
658
  EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
a1d0b5eeb   Trond Myklebust   NFS: Properly han...
659

d3978bb32   Chuck Lever   NFS: Move cl_dele...
660
  /**
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
661
   * nfs_expire_unused_delegation_types
d3978bb32   Chuck Lever   NFS: Move cl_dele...
662
663
664
665
   * @clp: client to process
   * @flags: delegation types to expire
   *
   */
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
666
  void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
667
  {
826e00130   Trond Myklebust   NFSv4: Fix CB_REC...
668
  	nfs_client_mark_return_unused_delegation_types(clp, flags);
b0d3ded1a   Trond Myklebust   NFSv4: Clean up n...
669
  	nfs_delegation_run_state_manager(clp);
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
670
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
671
  static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
b7391f44f   Trond Myklebust   NFSv4: Return unr...
672
673
  {
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
674
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
b7391f44f   Trond Myklebust   NFSv4: Return unr...
675
676
  		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
  			continue;
b757144fd   Trond Myklebust   NFSv4: Be less ag...
677
  		nfs_mark_return_if_closed_delegation(server, delegation);
b7391f44f   Trond Myklebust   NFSv4: Return unr...
678
  	}
b7391f44f   Trond Myklebust   NFSv4: Return unr...
679
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
680
681
682
683
684
  /**
   * nfs_expire_unreferenced_delegations - Eliminate unused delegations
   * @clp: nfs_client to process
   *
   */
b7391f44f   Trond Myklebust   NFSv4: Return unr...
685
686
  void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
687
688
689
690
691
692
  	struct nfs_server *server;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		nfs_mark_return_unreferenced_delegations(server);
  	rcu_read_unlock();
b7391f44f   Trond Myklebust   NFSv4: Return unr...
693
694
  	nfs_delegation_run_state_manager(clp);
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
695
696
697
  /**
   * nfs_async_inode_return_delegation - asynchronously return a delegation
   * @inode: inode to process
8e663f0e5   Trond Myklebust   NFSv4.1: Fix matc...
698
   * @stateid: state ID information
d3978bb32   Chuck Lever   NFS: Move cl_dele...
699
700
   *
   * Returns zero on success, or a negative errno value.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
   */
d3978bb32   Chuck Lever   NFS: Move cl_dele...
702
703
  int nfs_async_inode_return_delegation(struct inode *inode,
  				      const nfs4_stateid *stateid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  {
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
705
706
  	struct nfs_server *server = NFS_SERVER(inode);
  	struct nfs_client *clp = server->nfs_client;
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
707
  	struct nfs_delegation *delegation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708

6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
709
710
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
755a48a7a   Trond Myklebust   NFS: Fix a delega...
711
712
  	if (delegation == NULL)
  		goto out_enoent;
4816fdada   Trond Myklebust   NFSv4: Don't use ...
713
714
  	if (stateid != NULL &&
  	    !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
755a48a7a   Trond Myklebust   NFS: Fix a delega...
715
  		goto out_enoent;
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
716
  	nfs_mark_return_delegation(server, delegation);
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
717
  	rcu_read_unlock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
718

6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
719
720
  	nfs_delegation_run_state_manager(clp);
  	return 0;
755a48a7a   Trond Myklebust   NFS: Fix a delega...
721
722
723
  out_enoent:
  	rcu_read_unlock();
  	return -ENOENT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
725
726
727
  static struct inode *
  nfs_delegation_find_inode_server(struct nfs_server *server,
  				 const struct nfs_fh *fhandle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
  {
  	struct nfs_delegation *delegation;
  	struct inode *res = NULL;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
731
732
  
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
86e894899   Trond Myklebust   NFSv4: Fix up the...
733
734
735
  		spin_lock(&delegation->lock);
  		if (delegation->inode != NULL &&
  		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  			res = igrab(delegation->inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  		}
86e894899   Trond Myklebust   NFSv4: Fix up the...
738
739
740
  		spin_unlock(&delegation->lock);
  		if (res != NULL)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
  	}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
  	return res;
  }
  
  /**
   * nfs_delegation_find_inode - retrieve the inode associated with a delegation
   * @clp: client state handle
   * @fhandle: filehandle from a delegation recall
   *
   * Returns pointer to inode matching "fhandle," or NULL if a matching inode
   * cannot be found.
   */
  struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
  					const struct nfs_fh *fhandle)
  {
  	struct nfs_server *server;
  	struct inode *res = NULL;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
  		res = nfs_delegation_find_inode_server(server, fhandle);
  		if (res != NULL)
  			break;
  	}
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
765
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
767
  	return res;
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
768
769
770
  static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
  {
  	struct nfs_delegation *delegation;
45870d690   Trond Myklebust   NFSv4.1: Test del...
771
772
773
774
775
776
777
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
  		/*
  		 * If the delegation may have been admin revoked, then we
  		 * cannot reclaim it.
  		 */
  		if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
  			continue;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
778
  		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
45870d690   Trond Myklebust   NFSv4.1: Test del...
779
  	}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
780
781
782
783
784
785
  }
  
  /**
   * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
   * @clp: nfs_client to process
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
   */
adfa6f980   David Howells   NFS: Rename struc...
787
  void nfs_delegation_mark_reclaim(struct nfs_client *clp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
789
  	struct nfs_server *server;
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
790
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
791
792
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		nfs_delegation_mark_reclaim_server(server);
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
793
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
794
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
795
796
797
798
  /**
   * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
   * @clp: nfs_client to process
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
   */
adfa6f980   David Howells   NFS: Rename struc...
800
  void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  {
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
802
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
803
  	struct nfs_server *server;
86e894899   Trond Myklebust   NFSv4: Fix up the...
804
  	struct inode *inode;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
805

8383e4602   Trond Myklebust   NFSv4: Use RCU to...
806
807
  restart:
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
808
809
810
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
  		list_for_each_entry_rcu(delegation, &server->delegations,
  								super_list) {
ec3ca4e57   Trond Myklebust   NFSv4: Ensure we ...
811
812
813
  			if (test_bit(NFS_DELEGATION_RETURNING,
  						&delegation->flags))
  				continue;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
814
815
816
  			if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
  						&delegation->flags) == 0)
  				continue;
9f0f8e12c   Trond Myklebust   NFSv4: Pin the su...
817
  			if (!nfs_sb_active(server->super))
d3978bb32   Chuck Lever   NFS: Move cl_dele...
818
  				continue;
9f0f8e12c   Trond Myklebust   NFSv4: Pin the su...
819
820
821
822
823
824
  			inode = nfs_delegation_grab_inode(delegation);
  			if (inode == NULL) {
  				rcu_read_unlock();
  				nfs_sb_deactive(server->super);
  				goto restart;
  			}
b04b22f4c   Trond Myklebust   NFSv4: Ensure tha...
825
  			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
d3978bb32   Chuck Lever   NFS: Move cl_dele...
826
  			rcu_read_unlock();
b04b22f4c   Trond Myklebust   NFSv4: Ensure tha...
827
828
829
830
831
832
  			if (delegation != NULL) {
  				delegation = nfs_detach_delegation(NFS_I(inode),
  					delegation, server);
  				if (delegation != NULL)
  					nfs_free_delegation(delegation);
  			}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
833
  			iput(inode);
9f0f8e12c   Trond Myklebust   NFSv4: Pin the su...
834
  			nfs_sb_deactive(server->super);
d3978bb32   Chuck Lever   NFS: Move cl_dele...
835
836
  			goto restart;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  	}
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
838
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
  }
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
840

bb3d1a3b2   Trond Myklebust   NFSv4.1: Deal wit...
841
842
843
844
845
846
  static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
  {
  	return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
  				BIT(NFS4CLNT_LEASE_EXPIRED) |
  				BIT(NFS4CLNT_SESSION_RESET))) != 0;
  }
45870d690   Trond Myklebust   NFSv4.1: Test del...
847
848
849
  static void nfs_mark_test_expired_delegation(struct nfs_server *server,
  	    struct nfs_delegation *delegation)
  {
059b43e97   Trond Myklebust   NFSv4: Ensure we ...
850
851
  	if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
  		return;
45870d690   Trond Myklebust   NFSv4.1: Test del...
852
853
854
855
  	clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
  	set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
  	set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
  }
bb3d1a3b2   Trond Myklebust   NFSv4.1: Deal wit...
856
857
858
859
860
861
862
863
864
865
866
867
  static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
  		struct inode *inode)
  {
  	struct nfs_delegation *delegation;
  
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
  	if (delegation)
  		nfs_mark_test_expired_delegation(server, delegation);
  	rcu_read_unlock();
  
  }
45870d690   Trond Myklebust   NFSv4.1: Test del...
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
  static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
  {
  	struct nfs_delegation *delegation;
  
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
  		nfs_mark_test_expired_delegation(server, delegation);
  }
  
  /**
   * nfs_mark_test_expired_all_delegations - mark all delegations for testing
   * @clp: nfs_client to process
   *
   * Iterates through all the delegations associated with this server and
   * marks them as needing to be checked for validity.
   */
  void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
  {
  	struct nfs_server *server;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		nfs_delegation_mark_test_expired_server(server);
  	rcu_read_unlock();
  }
  
  /**
   * nfs_reap_expired_delegations - reap expired delegations
   * @clp: nfs_client to process
   *
   * Iterates through all the delegations associated with this server and
   * checks if they have may have been revoked. This function is usually
   * expected to be called in cases where the server may have lost its
   * lease.
   */
  void nfs_reap_expired_delegations(struct nfs_client *clp)
  {
  	const struct nfs4_minor_version_ops *ops = clp->cl_mvops;
  	struct nfs_delegation *delegation;
  	struct nfs_server *server;
  	struct inode *inode;
  	struct rpc_cred *cred;
  	nfs4_stateid stateid;
  
  restart:
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
  		list_for_each_entry_rcu(delegation, &server->delegations,
  								super_list) {
  			if (test_bit(NFS_DELEGATION_RETURNING,
  						&delegation->flags))
  				continue;
  			if (test_bit(NFS_DELEGATION_TEST_EXPIRED,
  						&delegation->flags) == 0)
  				continue;
  			if (!nfs_sb_active(server->super))
  				continue;
  			inode = nfs_delegation_grab_inode(delegation);
  			if (inode == NULL) {
  				rcu_read_unlock();
  				nfs_sb_deactive(server->super);
  				goto restart;
  			}
  			cred = get_rpccred_rcu(delegation->cred);
  			nfs4_stateid_copy(&stateid, &delegation->stateid);
  			clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
  			rcu_read_unlock();
  			if (cred != NULL &&
  			    ops->test_and_free_expired(server, &stateid, cred) < 0) {
  				nfs_revoke_delegation(inode, &stateid);
  				nfs_inode_find_state_and_recover(inode, &stateid);
  			}
  			put_rpccred(cred);
bb3d1a3b2   Trond Myklebust   NFSv4.1: Deal wit...
940
941
942
943
944
945
  			if (nfs4_server_rebooted(clp)) {
  				nfs_inode_mark_test_expired_delegation(server,inode);
  				iput(inode);
  				nfs_sb_deactive(server->super);
  				return;
  			}
45870d690   Trond Myklebust   NFSv4.1: Test del...
946
947
948
949
950
951
952
  			iput(inode);
  			nfs_sb_deactive(server->super);
  			goto restart;
  		}
  	}
  	rcu_read_unlock();
  }
6c2d8f8d3   Trond Myklebust   NFSv4: nfs_inode_...
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
  void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
  		const nfs4_stateid *stateid)
  {
  	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
  	struct nfs_delegation *delegation;
  	bool found = false;
  
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
  	if (delegation &&
  	    nfs4_stateid_match_other(&delegation->stateid, stateid)) {
  		nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
  		found = true;
  	}
  	rcu_read_unlock();
  	if (found)
  		nfs4_schedule_state_manager(clp);
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
  /**
   * nfs_delegations_present - check for existence of delegations
   * @clp: client state handle
   *
   * Returns one if there are any nfs_delegation structures attached
   * to this nfs_client.
   */
  int nfs_delegations_present(struct nfs_client *clp)
  {
  	struct nfs_server *server;
  	int ret = 0;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		if (!list_empty(&server->delegations)) {
  			ret = 1;
  			break;
  		}
  	rcu_read_unlock();
  	return ret;
  }
  
  /**
   * nfs4_copy_delegation_stateid - Copy inode's state ID information
d3978bb32   Chuck Lever   NFS: Move cl_dele...
995
   * @inode: inode to check
0032a7a74   Trond Myklebust   NFS: Don't copy r...
996
   * @flags: delegation type requirement
abf4e13cc   Trond Myklebust   NFSv4: Use the ri...
997
998
   * @dst: stateid data structure to fill in
   * @cred: optional argument to retrieve credential
d3978bb32   Chuck Lever   NFS: Move cl_dele...
999
   *
0032a7a74   Trond Myklebust   NFS: Don't copy r...
1000
1001
   * Returns "true" and fills in "dst->data" * if inode had a delegation,
   * otherwise "false" is returned.
d3978bb32   Chuck Lever   NFS: Move cl_dele...
1002
   */
abf4e13cc   Trond Myklebust   NFSv4: Use the ri...
1003
1004
  bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
  		nfs4_stateid *dst, struct rpc_cred **cred)
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
1005
  {
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
1006
1007
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_delegation *delegation;
0032a7a74   Trond Myklebust   NFS: Don't copy r...
1008
  	bool ret;
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
1009

0032a7a74   Trond Myklebust   NFS: Don't copy r...
1010
  	flags &= FMODE_READ|FMODE_WRITE;
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
1011
1012
  	rcu_read_lock();
  	delegation = rcu_dereference(nfsi->delegation);
aa05c87f2   Trond Myklebust   NFSv4: nfs4_copy_...
1013
  	ret = nfs4_is_valid_delegation(delegation, flags);
0032a7a74   Trond Myklebust   NFS: Don't copy r...
1014
  	if (ret) {
f597c5379   Trond Myklebust   NFSv4: Add helper...
1015
  		nfs4_stateid_copy(dst, &delegation->stateid);
0032a7a74   Trond Myklebust   NFS: Don't copy r...
1016
  		nfs_mark_delegation_referenced(delegation);
abf4e13cc   Trond Myklebust   NFSv4: Use the ri...
1017
1018
  		if (cred)
  			*cred = get_rpccred(delegation->cred);
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
1019
  	}
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
1020
1021
  	rcu_read_unlock();
  	return ret;
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
1022
  }
5445b1fbd   Trond Myklebust   NFSv4: Respect th...
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  
  /**
   * nfs4_delegation_flush_on_close - Check if we must flush file on close
   * @inode: inode to check
   *
   * This function checks the number of outstanding writes to the file
   * against the delegation 'space_limit' field to see if
   * the spec requires us to flush the file on close.
   */
  bool nfs4_delegation_flush_on_close(const struct inode *inode)
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_delegation *delegation;
  	bool ret = true;
  
  	rcu_read_lock();
  	delegation = rcu_dereference(nfsi->delegation);
  	if (delegation == NULL || !(delegation->type & FMODE_WRITE))
  		goto out;
a6b6d5b85   Trond Myklebust   NFS: Use an atomi...
1042
  	if (atomic_long_read(&nfsi->nrequests) < delegation->pagemod_limit)
5445b1fbd   Trond Myklebust   NFSv4: Respect th...
1043
1044
1045
1046
1047
  		ret = false;
  out:
  	rcu_read_unlock();
  	return ret;
  }