aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2013-02-05 11:43:28 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2013-02-11 15:33:12 -0500
commitd25be546a86fcb2ea919fb909a42b79aa40b816c (patch)
tree5a2a62efa453f8c85299063cc83a1bd819f0778a
parent9a99af494bd7141d567d00b5ef94b141821e158c (diff)
NFSv4.1: Don't lose locks when a server reboots during delegation return
If the server reboots while we are converting a delegation into OPEN/LOCK stateids as part of a delegation return, the current code will simply exit with an error. This causes us to lose both delegation state and locking state (i.e. locking atomicity). Deal with this by exposing the delegation stateid during delegation return, so that we can recover the delegation, and then resume open/lock recovery. Note that not having to hold the nfs_inode->rwsem across the calls to nfs_delegation_claim_opens() also fixes a deadlock against the NFSv4.1 reboot recovery code. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/delegation.c147
-rw-r--r--fs/nfs/delegation.h1
-rw-r--r--fs/nfs/nfs4proc.c3
3 files changed, 108 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
100again: 103again:
@@ -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
184static struct nfs_delegation * 191static struct nfs_delegation *
192nfs_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);
203out:
204 return ret;
205}
206
207static struct nfs_delegation *
208nfs_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
218static void
219nfs_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
230static struct nfs_delegation *
185nfs_detach_delegation_locked(struct nfs_inode *nfsi, 231nfs_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;
202nomatch:
203 return NULL;
204} 250}
205 251
206static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, 252static 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
264static struct nfs_delegation *
265nfs_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 */
295static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 357static 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 */
368void nfs_inode_return_delegation_noreclaim(struct inode *inode) 437void 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 */
391int nfs4_inode_return_delegation(struct inode *inode) 456int 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)
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index bbc6a4dba0d8..d54d4fca6793 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -29,6 +29,7 @@ enum {
29 NFS_DELEGATION_NEED_RECLAIM = 0, 29 NFS_DELEGATION_NEED_RECLAIM = 0,
30 NFS_DELEGATION_RETURN, 30 NFS_DELEGATION_RETURN,
31 NFS_DELEGATION_REFERENCED, 31 NFS_DELEGATION_REFERENCED,
32 NFS_DELEGATION_RETURNING,
32}; 33};
33 34
34int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); 35int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 92584c1ea725..a1e89e270ad8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -896,6 +896,8 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
896 return 0; 896 return 0;
897 if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags)) 897 if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
898 return 0; 898 return 0;
899 if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
900 return 0;
899 nfs_mark_delegation_referenced(delegation); 901 nfs_mark_delegation_referenced(delegation);
900 return 1; 902 return 1;
901} 903}
@@ -973,6 +975,7 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
973 975
974 spin_lock(&deleg_cur->lock); 976 spin_lock(&deleg_cur->lock);
975 if (nfsi->delegation != deleg_cur || 977 if (nfsi->delegation != deleg_cur ||
978 test_bit(NFS_DELEGATION_RETURNING, &deleg_cur->flags) ||
976 (deleg_cur->type & fmode) != fmode) 979 (deleg_cur->type & fmode) != fmode)
977 goto no_delegation_unlock; 980 goto no_delegation_unlock;
978 981