Blame view

fs/nfs/delegation.c 18.1 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"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

905f8d16e   Trond Myklebust   NFSv4: Don't call...
23
24
  static void nfs_free_delegation(struct nfs_delegation *delegation)
  {
e00b8a240   Trond Myklebust   NFS: Fix an NFS c...
25
26
27
28
  	if (delegation->cred) {
  		put_rpccred(delegation->cred);
  		delegation->cred = NULL;
  	}
26f04dde6   Lai Jiangshan   nfs,rcu: convert ...
29
  	kfree_rcu(delegation, rcu);
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
30
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
31
32
33
34
35
  /**
   * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
   * @delegation: delegation to process
   *
   */
b7391f44f   Trond Myklebust   NFSv4: Return unr...
36
37
38
39
  void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
  {
  	set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
40
41
42
43
44
45
46
  /**
   * nfs_have_delegation - check if inode has a delegation
   * @inode: inode to check
   * @flags: delegation types to check for
   *
   * Returns one if inode has the indicated delegation, otherwise zero.
   */
bd7bf9d54   Trond Myklebust   NFSv4: Convert de...
47
  int nfs_have_delegation(struct inode *inode, fmode_t flags)
b7391f44f   Trond Myklebust   NFSv4: Return unr...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  {
  	struct nfs_delegation *delegation;
  	int ret = 0;
  
  	flags &= FMODE_READ|FMODE_WRITE;
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
  	if (delegation != NULL && (delegation->type & flags) == flags) {
  		nfs_mark_delegation_referenced(delegation);
  		ret = 1;
  	}
  	rcu_read_unlock();
  	return ret;
  }
888e694c1   Trond Myklebust   NFSv4: Recover lo...
62
63
64
65
  static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
  {
  	struct inode *inode = state->inode;
  	struct file_lock *fl;
d5122201a   Trond Myklebust   NFSv4: Move error...
66
  	int status = 0;
888e694c1   Trond Myklebust   NFSv4: Recover lo...
67

3f09df70e   Trond Myklebust   NFS: Ensure we al...
68
69
  	if (inode->i_flock == NULL)
  		goto out;
b89f43213   Arnd Bergmann   fs/locks.c: prepa...
70
71
  	/* Protect inode->i_flock using the file locks lock */
  	lock_flocks();
90dc7d279   Harvey Harrison   nfs: fix sparse w...
72
  	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
888e694c1   Trond Myklebust   NFSv4: Recover lo...
73
74
  		if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
  			continue;
cd3758e37   Trond Myklebust   NFS: Replace file...
75
  		if (nfs_file_open_context(fl->fl_file) != ctx)
888e694c1   Trond Myklebust   NFSv4: Recover lo...
76
  			continue;
b89f43213   Arnd Bergmann   fs/locks.c: prepa...
77
  		unlock_flocks();
888e694c1   Trond Myklebust   NFSv4: Recover lo...
78
  		status = nfs4_lock_delegation_recall(state, fl);
d5122201a   Trond Myklebust   NFSv4: Move error...
79
  		if (status < 0)
3f09df70e   Trond Myklebust   NFS: Ensure we al...
80
  			goto out;
b89f43213   Arnd Bergmann   fs/locks.c: prepa...
81
  		lock_flocks();
888e694c1   Trond Myklebust   NFSv4: Recover lo...
82
  	}
b89f43213   Arnd Bergmann   fs/locks.c: prepa...
83
  	unlock_flocks();
3f09df70e   Trond Myklebust   NFS: Ensure we al...
84
  out:
888e694c1   Trond Myklebust   NFSv4: Recover lo...
85
86
  	return status;
  }
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
87
  static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
  {
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_open_context *ctx;
  	struct nfs4_state *state;
888e694c1   Trond Myklebust   NFSv4: Recover lo...
92
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
95
96
97
98
99
100
101
  
  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;
901630278   Trond Myklebust   NFSv4: Support re...
102
103
  		if (memcmp(state->stateid.data, stateid->data, sizeof(state->stateid.data)) != 0)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
  		get_nfs_open_context(ctx);
  		spin_unlock(&inode->i_lock);
13437e12f   Trond Myklebust   NFSv4: Support re...
106
  		err = nfs4_open_delegation_recall(ctx, state, stateid);
888e694c1   Trond Myklebust   NFSv4: Recover lo...
107
108
  		if (err >= 0)
  			err = nfs_delegation_claim_locks(ctx, state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
  		put_nfs_open_context(ctx);
888e694c1   Trond Myklebust   NFSv4: Recover lo...
110
  		if (err != 0)
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
111
  			return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
  		goto again;
  	}
  	spin_unlock(&inode->i_lock);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
115
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
117
118
119
120
121
122
  /**
   * 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
123
   */
d3978bb32   Chuck Lever   NFS: Move cl_dele...
124
125
  void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
  				  struct nfs_openres *res)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  {
8f649c376   Trond Myklebust   NFSv4: Fix the lo...
127
128
  	struct nfs_delegation *delegation;
  	struct rpc_cred *oldcred = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129

8f649c376   Trond Myklebust   NFSv4: Fix the lo...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
  	if (delegation != NULL) {
  		spin_lock(&delegation->lock);
  		if (delegation->inode != NULL) {
  			memcpy(delegation->stateid.data, res->delegation.data,
  			       sizeof(delegation->stateid.data));
  			delegation->type = res->delegation_type;
  			delegation->maxsize = res->maxsize;
  			oldcred = delegation->cred;
  			delegation->cred = get_rpccred(cred);
  			clear_bit(NFS_DELEGATION_NEED_RECLAIM,
  				  &delegation->flags);
  			NFS_I(inode)->delegation_state = delegation->type;
  			spin_unlock(&delegation->lock);
  			put_rpccred(oldcred);
  			rcu_read_unlock();
  		} else {
  			/* We appear to have raced with a delegation return. */
  			spin_unlock(&delegation->lock);
  			rcu_read_unlock();
  			nfs_inode_set_delegation(inode, cred, res);
  		}
  	} else {
  		rcu_read_unlock();
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  }
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
157
158
159
160
161
162
163
164
  static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
  {
  	int res = 0;
  
  	res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
  	nfs_free_delegation(delegation);
  	return res;
  }
86e894899   Trond Myklebust   NFSv4: Fix up the...
165
166
167
168
169
170
171
172
173
174
  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...
175
176
  static struct nfs_delegation *
  nfs_detach_delegation_locked(struct nfs_inode *nfsi,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
177
  			     struct nfs_server *server)
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
178
  {
17d2c0a0c   David Howells   NFS: Fix RCU issu...
179
180
  	struct nfs_delegation *delegation =
  		rcu_dereference_protected(nfsi->delegation,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
181
  				lockdep_is_held(&server->nfs_client->cl_lock));
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
182
183
184
  
  	if (delegation == NULL)
  		goto nomatch;
dda4b2256   Chuck Lever   NFS: Introduce nf...
185

343104308   Trond Myklebust   NFSv4: Fix up ano...
186
  	spin_lock(&delegation->lock);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
187
  	list_del_rcu(&delegation->super_list);
86e894899   Trond Myklebust   NFSv4: Fix up the...
188
  	delegation->inode = NULL;
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
189
190
  	nfsi->delegation_state = 0;
  	rcu_assign_pointer(nfsi->delegation, NULL);
343104308   Trond Myklebust   NFSv4: Fix up ano...
191
  	spin_unlock(&delegation->lock);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
192
193
194
195
  	return delegation;
  nomatch:
  	return NULL;
  }
dda4b2256   Chuck Lever   NFS: Introduce nf...
196
  static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
197
  						    struct nfs_server *server)
dda4b2256   Chuck Lever   NFS: Introduce nf...
198
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
199
  	struct nfs_client *clp = server->nfs_client;
dda4b2256   Chuck Lever   NFS: Introduce nf...
200
201
202
  	struct nfs_delegation *delegation;
  
  	spin_lock(&clp->cl_lock);
d3978bb32   Chuck Lever   NFS: Move cl_dele...
203
  	delegation = nfs_detach_delegation_locked(nfsi, server);
dda4b2256   Chuck Lever   NFS: Introduce nf...
204
205
206
  	spin_unlock(&clp->cl_lock);
  	return delegation;
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
207
208
209
210
211
212
213
  /**
   * 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
214
215
216
   */
  int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
217
218
  	struct nfs_server *server = NFS_SERVER(inode);
  	struct nfs_client *clp = server->nfs_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
  	struct nfs_inode *nfsi = NFS_I(inode);
17d2c0a0c   David Howells   NFS: Fix RCU issu...
220
  	struct nfs_delegation *delegation, *old_delegation;
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
221
  	struct nfs_delegation *freeme = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  	int status = 0;
8535b2be5   Trond Myklebust   NFSv4: Don't use ...
223
  	delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
228
229
  	if (delegation == NULL)
  		return -ENOMEM;
  	memcpy(delegation->stateid.data, res->delegation.data,
  			sizeof(delegation->stateid.data));
  	delegation->type = res->delegation_type;
  	delegation->maxsize = res->maxsize;
a9a4a87a5   Trond Myklebust   NFS: Use the inod...
230
  	delegation->change_attr = inode->i_version;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231
232
  	delegation->cred = get_rpccred(cred);
  	delegation->inode = inode;
b7391f44f   Trond Myklebust   NFSv4: Return unr...
233
  	delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
343104308   Trond Myklebust   NFSv4: Fix up ano...
234
  	spin_lock_init(&delegation->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
  
  	spin_lock(&clp->cl_lock);
17d2c0a0c   David Howells   NFS: Fix RCU issu...
237
  	old_delegation = rcu_dereference_protected(nfsi->delegation,
d3978bb32   Chuck Lever   NFS: Move cl_dele...
238
  					lockdep_is_held(&clp->cl_lock));
17d2c0a0c   David Howells   NFS: Fix RCU issu...
239
240
241
242
  	if (old_delegation != NULL) {
  		if (memcmp(&delegation->stateid, &old_delegation->stateid,
  					sizeof(old_delegation->stateid)) == 0 &&
  				delegation->type == old_delegation->type) {
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
243
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  		}
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
245
246
247
248
249
250
251
  		/*
  		 * Deal with broken servers that hand out two
  		 * delegations for the same file.
  		 */
  		dfprintk(FILE, "%s: server %s handed out "
  				"a duplicate delegation!
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
252
  				__func__, clp->cl_hostname);
17d2c0a0c   David Howells   NFS: Fix RCU issu...
253
  		if (delegation->type <= old_delegation->type) {
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
254
255
256
257
  			freeme = delegation;
  			delegation = NULL;
  			goto out;
  		}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
258
  		freeme = nfs_detach_delegation_locked(nfsi, server);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  	}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
260
  	list_add_rcu(&delegation->super_list, &server->delegations);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
261
262
263
  	nfsi->delegation_state = delegation->type;
  	rcu_assign_pointer(nfsi->delegation, delegation);
  	delegation = NULL;
412c77cee   Trond Myklebust   NFSv4: Defer inod...
264
265
266
267
268
  
  	/* Ensure we revalidate the attributes and page cache! */
  	spin_lock(&inode->i_lock);
  	nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
  	spin_unlock(&inode->i_lock);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
269
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
  	spin_unlock(&clp->cl_lock);
603c83da1   Trond Myklebust   NFSv4: Fix an rpc...
271
272
  	if (delegation != NULL)
  		nfs_free_delegation(delegation);
57bfa8917   Trond Myklebust   NFSv4: Deal more ...
273
274
  	if (freeme != NULL)
  		nfs_do_return_delegation(inode, freeme, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
  	return status;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
  /*
   * Basic procedure for returning a delegation to the server
   */
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
280
  static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  	struct nfs_inode *nfsi = NFS_I(inode);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
283
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284

3f09df70e   Trond Myklebust   NFS: Ensure we al...
285
286
287
288
  	/*
  	 * Guard against new delegated open/lock/unlock calls and against
  	 * state recovery
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  	down_write(&nfsi->rwsem);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
290
  	err = nfs_delegation_claim_opens(inode, &delegation->stateid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  	up_write(&nfsi->rwsem);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
292
293
  	if (err)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294

d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
295
296
297
  	err = nfs_do_return_delegation(inode, delegation, issync);
  out:
  	return err;
901630278   Trond Myklebust   NFSv4: Support re...
298
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
299
300
301
302
303
  /**
   * nfs_client_return_marked_delegations - return previously marked delegations
   * @clp: nfs_client to process
   *
   * Returns zero on success, or a negative errno value.
515d86117   Trond Myklebust   NFSv4: Clean up t...
304
   */
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
305
  int nfs_client_return_marked_delegations(struct nfs_client *clp)
515d86117   Trond Myklebust   NFSv4: Clean up t...
306
307
  {
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
308
  	struct nfs_server *server;
515d86117   Trond Myklebust   NFSv4: Clean up t...
309
  	struct inode *inode;
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
310
  	int err = 0;
515d86117   Trond Myklebust   NFSv4: Clean up t...
311
312
313
  
  restart:
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
  		list_for_each_entry_rcu(delegation, &server->delegations,
  								super_list) {
  			if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
  							&delegation->flags))
  				continue;
  			inode = nfs_delegation_grab_inode(delegation);
  			if (inode == NULL)
  				continue;
  			delegation = nfs_detach_delegation(NFS_I(inode),
  								server);
  			rcu_read_unlock();
  
  			if (delegation != NULL) {
  				filemap_flush(inode->i_mapping);
  				err = __nfs_inode_return_delegation(inode,
  								delegation, 0);
  			}
  			iput(inode);
  			if (!err)
  				goto restart;
  			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
  			return err;
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
337
  		}
515d86117   Trond Myklebust   NFSv4: Clean up t...
338
339
  	}
  	rcu_read_unlock();
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
340
  	return 0;
515d86117   Trond Myklebust   NFSv4: Clean up t...
341
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
342
343
344
345
346
347
  /**
   * 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...
348
349
350
   */
  void nfs_inode_return_delegation_noreclaim(struct inode *inode)
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
351
  	struct nfs_server *server = NFS_SERVER(inode);
e6f810759   Trond Myklebust   NFS: Add an async...
352
353
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_delegation *delegation;
17d2c0a0c   David Howells   NFS: Fix RCU issu...
354
  	if (rcu_access_pointer(nfsi->delegation) != NULL) {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
355
  		delegation = nfs_detach_delegation(nfsi, server);
e6f810759   Trond Myklebust   NFS: Add an async...
356
357
358
359
  		if (delegation != NULL)
  			nfs_do_return_delegation(inode, delegation, 0);
  	}
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
360
361
362
363
364
365
  /**
   * nfs_inode_return_delegation - synchronously return a delegation
   * @inode: inode to process
   *
   * Returns zero on success, or a negative errno value.
   */
901630278   Trond Myklebust   NFSv4: Support re...
366
367
  int nfs_inode_return_delegation(struct inode *inode)
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
368
  	struct nfs_server *server = NFS_SERVER(inode);
901630278   Trond Myklebust   NFSv4: Support re...
369
370
371
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_delegation *delegation;
  	int err = 0;
17d2c0a0c   David Howells   NFS: Fix RCU issu...
372
  	if (rcu_access_pointer(nfsi->delegation) != NULL) {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
373
  		delegation = nfs_detach_delegation(nfsi, server);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
374
  		if (delegation != NULL) {
1b924e5f8   Trond Myklebust   NFS: Clean up the...
375
  			nfs_wb_all(inode);
d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
376
377
  			err = __nfs_inode_return_delegation(inode, delegation, 1);
  		}
901630278   Trond Myklebust   NFSv4: Support re...
378
379
  	}
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  }
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
381
382
  static void nfs_mark_return_delegation(struct nfs_server *server,
  		struct nfs_delegation *delegation)
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
383
384
  {
  	set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
385
  	set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
386
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
387
388
389
390
  /**
   * nfs_super_return_all_delegations - return delegations for one superblock
   * @sb: sb to process
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
   */
515d86117   Trond Myklebust   NFSv4: Clean up t...
392
  void nfs_super_return_all_delegations(struct super_block *sb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
394
395
  	struct nfs_server *server = NFS_SB(sb);
  	struct nfs_client *clp = server->nfs_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  	struct nfs_delegation *delegation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
  
  	if (clp == NULL)
  		return;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
400

8383e4602   Trond Myklebust   NFSv4: Use RCU to...
401
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
402
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
86e894899   Trond Myklebust   NFSv4: Fix up the...
403
  		spin_lock(&delegation->lock);
d3978bb32   Chuck Lever   NFS: Move cl_dele...
404
  		set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
86e894899   Trond Myklebust   NFSv4: Fix up the...
405
  		spin_unlock(&delegation->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  	}
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
407
  	rcu_read_unlock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
408

d18cc1fda   Trond Myklebust   NFSv4: Fix a pote...
409
410
  	if (nfs_client_return_marked_delegations(clp) != 0)
  		nfs4_schedule_state_manager(clp);
515d86117   Trond Myklebust   NFSv4: Clean up t...
411
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
412
413
  static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
  						 fmode_t flags)
515d86117   Trond Myklebust   NFSv4: Clean up t...
414
415
  {
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
416
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
c79571a50   Alexandros Batsakis   nfs4: V2 return/e...
417
418
419
  		if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
  			continue;
  		if (delegation->type & flags)
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
420
  			nfs_mark_return_delegation(server, delegation);
707fb4b32   Trond Myklebust   NFSv4: Clean up N...
421
  	}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
422
423
424
425
426
427
428
429
430
431
  }
  
  static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
  							fmode_t flags)
  {
  	struct nfs_server *server;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		nfs_mark_return_all_delegation_types(server, flags);
515d86117   Trond Myklebust   NFSv4: Clean up t...
432
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
  }
c79571a50   Alexandros Batsakis   nfs4: V2 return/e...
434
435
436
437
  static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
  {
  	nfs_client_mark_return_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
  }
b0d3ded1a   Trond Myklebust   NFSv4: Clean up n...
438
  static void nfs_delegation_run_state_manager(struct nfs_client *clp)
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
439
  {
b0d3ded1a   Trond Myklebust   NFSv4: Clean up n...
440
441
  	if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
  		nfs4_schedule_state_manager(clp);
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
442
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
443
444
445
446
447
448
  /**
   * nfs_expire_all_delegation_types
   * @clp: client to process
   * @flags: delegation types to expire
   *
   */
31f096077   Alexandros Batsakis   nfs41: V2 initial...
449
  void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
450
  {
c79571a50   Alexandros Batsakis   nfs4: V2 return/e...
451
  	nfs_client_mark_return_all_delegation_types(clp, flags);
b0d3ded1a   Trond Myklebust   NFSv4: Clean up n...
452
  	nfs_delegation_run_state_manager(clp);
58d9714a4   Trond Myklebust   NFSv4: Send RENEW...
453
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
454
455
456
457
458
  /**
   * nfs_expire_all_delegations
   * @clp: client to process
   *
   */
c79571a50   Alexandros Batsakis   nfs4: V2 return/e...
459
460
461
462
  void nfs_expire_all_delegations(struct nfs_client *clp)
  {
  	nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
463
464
465
466
  /**
   * nfs_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
   * @clp: client to process
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
   */
adfa6f980   David Howells   NFS: Rename struc...
468
  void nfs_handle_cb_pathdown(struct nfs_client *clp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
469
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
471
  	if (clp == NULL)
  		return;
707fb4b32   Trond Myklebust   NFSv4: Clean up N...
472
  	nfs_client_mark_return_all_delegations(clp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
474
  static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
b7391f44f   Trond Myklebust   NFSv4: Return unr...
475
476
  {
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
477
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
b7391f44f   Trond Myklebust   NFSv4: Return unr...
478
479
  		if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
  			continue;
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
480
  		nfs_mark_return_delegation(server, delegation);
b7391f44f   Trond Myklebust   NFSv4: Return unr...
481
  	}
b7391f44f   Trond Myklebust   NFSv4: Return unr...
482
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
483
484
485
486
487
  /**
   * nfs_expire_unreferenced_delegations - Eliminate unused delegations
   * @clp: nfs_client to process
   *
   */
b7391f44f   Trond Myklebust   NFSv4: Return unr...
488
489
  void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
490
491
492
493
494
495
  	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...
496
497
  	nfs_delegation_run_state_manager(clp);
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
498
499
500
501
502
503
  /**
   * nfs_async_inode_return_delegation - asynchronously return a delegation
   * @inode: inode to process
   * @stateid: state ID information from CB_RECALL arguments
   *
   * Returns zero on success, or a negative errno value.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
   */
d3978bb32   Chuck Lever   NFS: Move cl_dele...
505
506
  int nfs_async_inode_return_delegation(struct inode *inode,
  				      const nfs4_stateid *stateid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  {
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
508
509
  	struct nfs_server *server = NFS_SERVER(inode);
  	struct nfs_client *clp = server->nfs_client;
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
510
  	struct nfs_delegation *delegation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511

6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
512
513
  	rcu_read_lock();
  	delegation = rcu_dereference(NFS_I(inode)->delegation);
2597641de   Alexandros Batsakis   nfs41: v2 fix cb_...
514

e047a10c1   Trond Myklebust   NFSv41: Fix nfs_a...
515
  	if (!clp->cl_mvops->validate_stateid(delegation, stateid)) {
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
516
517
518
  		rcu_read_unlock();
  		return -ENOENT;
  	}
ed1e6211a   Trond Myklebust   NFSv4: Don't use ...
519
  	nfs_mark_return_delegation(server, delegation);
6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
520
  	rcu_read_unlock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
521

6411bd4a4   Trond Myklebust   NFSv4: Clean up t...
522
523
  	nfs_delegation_run_state_manager(clp);
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
525
526
527
  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
528
529
530
  {
  	struct nfs_delegation *delegation;
  	struct inode *res = NULL;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
531
532
  
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
86e894899   Trond Myklebust   NFSv4: Fix up the...
533
534
535
  		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
536
  			res = igrab(delegation->inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
  		}
86e894899   Trond Myklebust   NFSv4: Fix up the...
538
539
540
  		spin_unlock(&delegation->lock);
  		if (res != NULL)
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541
  	}
d3978bb32   Chuck Lever   NFS: Move cl_dele...
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  	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...
565
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
567
  	return res;
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
568
569
570
571
572
573
574
575
576
577
578
579
  static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
  {
  	struct nfs_delegation *delegation;
  
  	list_for_each_entry_rcu(delegation, &server->delegations, super_list)
  		set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
  }
  
  /**
   * 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
580
   */
adfa6f980   David Howells   NFS: Rename struc...
581
  void nfs_delegation_mark_reclaim(struct nfs_client *clp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  {
d3978bb32   Chuck Lever   NFS: Move cl_dele...
583
  	struct nfs_server *server;
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
584
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
585
586
  	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
  		nfs_delegation_mark_reclaim_server(server);
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
587
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
  }
d3978bb32   Chuck Lever   NFS: Move cl_dele...
589
590
591
592
  /**
   * 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
593
   */
adfa6f980   David Howells   NFS: Rename struc...
594
  void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  {
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
596
  	struct nfs_delegation *delegation;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
597
  	struct nfs_server *server;
86e894899   Trond Myklebust   NFSv4: Fix up the...
598
  	struct inode *inode;
d3978bb32   Chuck Lever   NFS: Move cl_dele...
599

8383e4602   Trond Myklebust   NFSv4: Use RCU to...
600
601
  restart:
  	rcu_read_lock();
d3978bb32   Chuck Lever   NFS: Move cl_dele...
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
  	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_NEED_RECLAIM,
  						&delegation->flags) == 0)
  				continue;
  			inode = nfs_delegation_grab_inode(delegation);
  			if (inode == NULL)
  				continue;
  			delegation = nfs_detach_delegation(NFS_I(inode),
  								server);
  			rcu_read_unlock();
  
  			if (delegation != NULL)
  				nfs_free_delegation(delegation);
  			iput(inode);
  			goto restart;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
620
  	}
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
621
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
  }
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
623

d3978bb32   Chuck Lever   NFS: Move cl_dele...
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  /**
   * 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
   * @dst: stateid data structure to fill in
   * @inode: inode to check
   *
   * Returns one and fills in "dst->data" * if inode had a delegation,
   * otherwise zero is returned.
   */
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
654
655
  int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
  {
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
656
657
  	struct nfs_inode *nfsi = NFS_I(inode);
  	struct nfs_delegation *delegation;
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
658
  	int ret = 0;
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
659

8383e4602   Trond Myklebust   NFSv4: Use RCU to...
660
661
  	rcu_read_lock();
  	delegation = rcu_dereference(nfsi->delegation);
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
662
663
  	if (delegation != NULL) {
  		memcpy(dst->data, delegation->stateid.data, sizeof(dst->data));
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
664
  		ret = 1;
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
665
  	}
8383e4602   Trond Myklebust   NFSv4: Use RCU to...
666
667
  	rcu_read_unlock();
  	return ret;
3e4f6290c   Trond Myklebust   NFSv4: Send the d...
668
  }