diff options
Diffstat (limited to 'fs/nfs/delegation.c')
-rw-r--r-- | fs/nfs/delegation.c | 147 |
1 files changed, 104 insertions, 43 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 81c5eec3cf38..2542cdaa1116 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -55,7 +55,8 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags) | |||
55 | flags &= FMODE_READ|FMODE_WRITE; | 55 | flags &= FMODE_READ|FMODE_WRITE; |
56 | rcu_read_lock(); | 56 | rcu_read_lock(); |
57 | delegation = rcu_dereference(NFS_I(inode)->delegation); | 57 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
58 | if (delegation != NULL && (delegation->type & flags) == flags) { | 58 | if (delegation != NULL && (delegation->type & flags) == flags && |
59 | !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { | ||
59 | nfs_mark_delegation_referenced(delegation); | 60 | nfs_mark_delegation_referenced(delegation); |
60 | ret = 1; | 61 | ret = 1; |
61 | } | 62 | } |
@@ -94,7 +95,9 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s | |||
94 | { | 95 | { |
95 | struct nfs_inode *nfsi = NFS_I(inode); | 96 | struct nfs_inode *nfsi = NFS_I(inode); |
96 | struct nfs_open_context *ctx; | 97 | struct nfs_open_context *ctx; |
98 | struct nfs4_state_owner *sp; | ||
97 | struct nfs4_state *state; | 99 | struct nfs4_state *state; |
100 | unsigned int seq; | ||
98 | int err; | 101 | int err; |
99 | 102 | ||
100 | again: | 103 | again: |
@@ -109,9 +112,13 @@ again: | |||
109 | continue; | 112 | continue; |
110 | get_nfs_open_context(ctx); | 113 | get_nfs_open_context(ctx); |
111 | spin_unlock(&inode->i_lock); | 114 | spin_unlock(&inode->i_lock); |
115 | sp = state->owner; | ||
116 | seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); | ||
112 | err = nfs4_open_delegation_recall(ctx, state, stateid); | 117 | err = nfs4_open_delegation_recall(ctx, state, stateid); |
113 | if (err >= 0) | 118 | if (!err) |
114 | err = nfs_delegation_claim_locks(ctx, state); | 119 | err = nfs_delegation_claim_locks(ctx, state); |
120 | if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) | ||
121 | err = -EAGAIN; | ||
115 | put_nfs_open_context(ctx); | 122 | put_nfs_open_context(ctx); |
116 | if (err != 0) | 123 | if (err != 0) |
117 | return err; | 124 | return err; |
@@ -182,39 +189,91 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation | |||
182 | } | 189 | } |
183 | 190 | ||
184 | static struct nfs_delegation * | 191 | static struct nfs_delegation * |
192 | nfs_start_delegation_return_locked(struct nfs_inode *nfsi) | ||
193 | { | ||
194 | struct nfs_delegation *ret = NULL; | ||
195 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | ||
196 | |||
197 | if (delegation == NULL) | ||
198 | goto out; | ||
199 | spin_lock(&delegation->lock); | ||
200 | if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) | ||
201 | ret = delegation; | ||
202 | spin_unlock(&delegation->lock); | ||
203 | out: | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | static struct nfs_delegation * | ||
208 | nfs_start_delegation_return(struct nfs_inode *nfsi) | ||
209 | { | ||
210 | struct nfs_delegation *delegation; | ||
211 | |||
212 | rcu_read_lock(); | ||
213 | delegation = nfs_start_delegation_return_locked(nfsi); | ||
214 | rcu_read_unlock(); | ||
215 | return delegation; | ||
216 | } | ||
217 | |||
218 | static void | ||
219 | nfs_abort_delegation_return(struct nfs_delegation *delegation, | ||
220 | struct nfs_client *clp) | ||
221 | { | ||
222 | |||
223 | spin_lock(&delegation->lock); | ||
224 | clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | ||
225 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | ||
226 | spin_unlock(&delegation->lock); | ||
227 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
228 | } | ||
229 | |||
230 | static struct nfs_delegation * | ||
185 | nfs_detach_delegation_locked(struct nfs_inode *nfsi, | 231 | nfs_detach_delegation_locked(struct nfs_inode *nfsi, |
186 | struct nfs_server *server) | 232 | struct nfs_delegation *delegation, |
233 | struct nfs_client *clp) | ||
187 | { | 234 | { |
188 | struct nfs_delegation *delegation = | 235 | struct nfs_delegation *deleg_cur = |
189 | rcu_dereference_protected(nfsi->delegation, | 236 | rcu_dereference_protected(nfsi->delegation, |
190 | lockdep_is_held(&server->nfs_client->cl_lock)); | 237 | lockdep_is_held(&clp->cl_lock)); |
191 | 238 | ||
192 | if (delegation == NULL) | 239 | if (deleg_cur == NULL || delegation != deleg_cur) |
193 | goto nomatch; | 240 | return NULL; |
194 | 241 | ||
195 | spin_lock(&delegation->lock); | 242 | spin_lock(&delegation->lock); |
243 | set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | ||
196 | list_del_rcu(&delegation->super_list); | 244 | list_del_rcu(&delegation->super_list); |
197 | delegation->inode = NULL; | 245 | delegation->inode = NULL; |
198 | nfsi->delegation_state = 0; | 246 | nfsi->delegation_state = 0; |
199 | rcu_assign_pointer(nfsi->delegation, NULL); | 247 | rcu_assign_pointer(nfsi->delegation, NULL); |
200 | spin_unlock(&delegation->lock); | 248 | spin_unlock(&delegation->lock); |
201 | return delegation; | 249 | return delegation; |
202 | nomatch: | ||
203 | return NULL; | ||
204 | } | 250 | } |
205 | 251 | ||
206 | static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, | 252 | static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, |
207 | struct nfs_server *server) | 253 | struct nfs_delegation *delegation, |
254 | struct nfs_server *server) | ||
208 | { | 255 | { |
209 | struct nfs_client *clp = server->nfs_client; | 256 | struct nfs_client *clp = server->nfs_client; |
210 | struct nfs_delegation *delegation; | ||
211 | 257 | ||
212 | spin_lock(&clp->cl_lock); | 258 | spin_lock(&clp->cl_lock); |
213 | delegation = nfs_detach_delegation_locked(nfsi, server); | 259 | delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); |
214 | spin_unlock(&clp->cl_lock); | 260 | spin_unlock(&clp->cl_lock); |
215 | return delegation; | 261 | return delegation; |
216 | } | 262 | } |
217 | 263 | ||
264 | static struct nfs_delegation * | ||
265 | nfs_inode_detach_delegation(struct inode *inode) | ||
266 | { | ||
267 | struct nfs_inode *nfsi = NFS_I(inode); | ||
268 | struct nfs_server *server = NFS_SERVER(inode); | ||
269 | struct nfs_delegation *delegation; | ||
270 | |||
271 | delegation = nfs_start_delegation_return(nfsi); | ||
272 | if (delegation == NULL) | ||
273 | return NULL; | ||
274 | return nfs_detach_delegation(nfsi, delegation, server); | ||
275 | } | ||
276 | |||
218 | /** | 277 | /** |
219 | * nfs_inode_set_delegation - set up a delegation on an inode | 278 | * nfs_inode_set_delegation - set up a delegation on an inode |
220 | * @inode: inode to which delegation applies | 279 | * @inode: inode to which delegation applies |
@@ -268,7 +327,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
268 | delegation = NULL; | 327 | delegation = NULL; |
269 | goto out; | 328 | goto out; |
270 | } | 329 | } |
271 | freeme = nfs_detach_delegation_locked(nfsi, server); | 330 | freeme = nfs_detach_delegation_locked(nfsi, |
331 | old_delegation, clp); | ||
332 | if (freeme == NULL) | ||
333 | goto out; | ||
272 | } | 334 | } |
273 | list_add_rcu(&delegation->super_list, &server->delegations); | 335 | list_add_rcu(&delegation->super_list, &server->delegations); |
274 | nfsi->delegation_state = delegation->type; | 336 | nfsi->delegation_state = delegation->type; |
@@ -292,19 +354,29 @@ out: | |||
292 | /* | 354 | /* |
293 | * Basic procedure for returning a delegation to the server | 355 | * Basic procedure for returning a delegation to the server |
294 | */ | 356 | */ |
295 | static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | 357 | static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) |
296 | { | 358 | { |
359 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | ||
297 | struct nfs_inode *nfsi = NFS_I(inode); | 360 | struct nfs_inode *nfsi = NFS_I(inode); |
298 | int err; | 361 | int err; |
299 | 362 | ||
300 | /* | 363 | if (delegation == NULL) |
301 | * Guard against new delegated open/lock/unlock calls and against | 364 | return 0; |
302 | * state recovery | 365 | do { |
303 | */ | 366 | err = nfs_delegation_claim_opens(inode, &delegation->stateid); |
304 | down_write(&nfsi->rwsem); | 367 | if (!issync || err != -EAGAIN) |
305 | err = nfs_delegation_claim_opens(inode, &delegation->stateid); | 368 | break; |
306 | up_write(&nfsi->rwsem); | 369 | /* |
307 | if (err) | 370 | * Guard against state recovery |
371 | */ | ||
372 | err = nfs4_wait_clnt_recover(clp); | ||
373 | } while (err == 0); | ||
374 | |||
375 | if (err) { | ||
376 | nfs_abort_delegation_return(delegation, clp); | ||
377 | goto out; | ||
378 | } | ||
379 | if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode))) | ||
308 | goto out; | 380 | goto out; |
309 | 381 | ||
310 | err = nfs_do_return_delegation(inode, delegation, issync); | 382 | err = nfs_do_return_delegation(inode, delegation, issync); |
@@ -340,13 +412,10 @@ restart: | |||
340 | inode = nfs_delegation_grab_inode(delegation); | 412 | inode = nfs_delegation_grab_inode(delegation); |
341 | if (inode == NULL) | 413 | if (inode == NULL) |
342 | continue; | 414 | continue; |
343 | delegation = nfs_detach_delegation(NFS_I(inode), | 415 | delegation = nfs_start_delegation_return_locked(NFS_I(inode)); |
344 | server); | ||
345 | rcu_read_unlock(); | 416 | rcu_read_unlock(); |
346 | 417 | ||
347 | if (delegation != NULL) | 418 | err = nfs_end_delegation_return(inode, delegation, 0); |
348 | err = __nfs_inode_return_delegation(inode, | ||
349 | delegation, 0); | ||
350 | iput(inode); | 419 | iput(inode); |
351 | if (!err) | 420 | if (!err) |
352 | goto restart; | 421 | goto restart; |
@@ -367,15 +436,11 @@ restart: | |||
367 | */ | 436 | */ |
368 | void nfs_inode_return_delegation_noreclaim(struct inode *inode) | 437 | void nfs_inode_return_delegation_noreclaim(struct inode *inode) |
369 | { | 438 | { |
370 | struct nfs_server *server = NFS_SERVER(inode); | ||
371 | struct nfs_inode *nfsi = NFS_I(inode); | ||
372 | struct nfs_delegation *delegation; | 439 | struct nfs_delegation *delegation; |
373 | 440 | ||
374 | if (rcu_access_pointer(nfsi->delegation) != NULL) { | 441 | delegation = nfs_inode_detach_delegation(inode); |
375 | delegation = nfs_detach_delegation(nfsi, server); | 442 | if (delegation != NULL) |
376 | if (delegation != NULL) | 443 | nfs_do_return_delegation(inode, delegation, 0); |
377 | nfs_do_return_delegation(inode, delegation, 0); | ||
378 | } | ||
379 | } | 444 | } |
380 | 445 | ||
381 | /** | 446 | /** |
@@ -390,18 +455,14 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) | |||
390 | */ | 455 | */ |
391 | int nfs4_inode_return_delegation(struct inode *inode) | 456 | int nfs4_inode_return_delegation(struct inode *inode) |
392 | { | 457 | { |
393 | struct nfs_server *server = NFS_SERVER(inode); | ||
394 | struct nfs_inode *nfsi = NFS_I(inode); | 458 | struct nfs_inode *nfsi = NFS_I(inode); |
395 | struct nfs_delegation *delegation; | 459 | struct nfs_delegation *delegation; |
396 | int err = 0; | 460 | int err = 0; |
397 | 461 | ||
398 | nfs_wb_all(inode); | 462 | nfs_wb_all(inode); |
399 | if (rcu_access_pointer(nfsi->delegation) != NULL) { | 463 | delegation = nfs_start_delegation_return(nfsi); |
400 | delegation = nfs_detach_delegation(nfsi, server); | 464 | if (delegation != NULL) |
401 | if (delegation != NULL) { | 465 | err = nfs_end_delegation_return(inode, delegation, 1); |
402 | err = __nfs_inode_return_delegation(inode, delegation, 1); | ||
403 | } | ||
404 | } | ||
405 | return err; | 466 | return err; |
406 | } | 467 | } |
407 | 468 | ||
@@ -471,7 +532,7 @@ void nfs_remove_bad_delegation(struct inode *inode) | |||
471 | { | 532 | { |
472 | struct nfs_delegation *delegation; | 533 | struct nfs_delegation *delegation; |
473 | 534 | ||
474 | delegation = nfs_detach_delegation(NFS_I(inode), NFS_SERVER(inode)); | 535 | delegation = nfs_inode_detach_delegation(inode); |
475 | if (delegation) { | 536 | if (delegation) { |
476 | nfs_inode_find_state_and_recover(inode, &delegation->stateid); | 537 | nfs_inode_find_state_and_recover(inode, &delegation->stateid); |
477 | nfs_free_delegation(delegation); | 538 | nfs_free_delegation(delegation); |
@@ -649,7 +710,7 @@ restart: | |||
649 | if (inode == NULL) | 710 | if (inode == NULL) |
650 | continue; | 711 | continue; |
651 | delegation = nfs_detach_delegation(NFS_I(inode), | 712 | delegation = nfs_detach_delegation(NFS_I(inode), |
652 | server); | 713 | delegation, server); |
653 | rcu_read_unlock(); | 714 | rcu_read_unlock(); |
654 | 715 | ||
655 | if (delegation != NULL) | 716 | if (delegation != NULL) |