aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/delegation.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2009-12-03 08:10:17 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2009-12-03 08:10:17 -0500
commitd18cc1fda25295416a2855d44c2936db01df9eec (patch)
tree962ec82ffff2fb49691779039c76976be7d964f0 /fs/nfs/delegation.c
parentd327cf7449e6fd5cbac784c641770e9366faa386 (diff)
NFSv4: Fix a potential state manager deadlock when returning delegations
The nfsv4 state manager could potentially deadlock inside __nfs_inode_return_delegation() if the server reboots, so that the calls to nfs_msync_inode() end up waiting on state recovery to complete. Also ensure that if a server reboot or network partition causes us to have to stop returning delegations, that NFS4CLNT_DELEGRETURN is set so that the state manager can resume any outstanding delegation returns after it has dealt with the state recovery situation. Finally, ensure that the state manager doesn't wait for the DELEGRETURN call to complete. It doesn't need to, and that too can cause a deadlock. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/delegation.c')
-rw-r--r--fs/nfs/delegation.c42
1 files changed, 28 insertions, 14 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 6dd48a4405b4..eeecd69c130c 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -92,7 +92,7 @@ out:
92 return status; 92 return status;
93} 93}
94 94
95static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) 95static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
96{ 96{
97 struct nfs_inode *nfsi = NFS_I(inode); 97 struct nfs_inode *nfsi = NFS_I(inode);
98 struct nfs_open_context *ctx; 98 struct nfs_open_context *ctx;
@@ -116,10 +116,11 @@ again:
116 err = nfs_delegation_claim_locks(ctx, state); 116 err = nfs_delegation_claim_locks(ctx, state);
117 put_nfs_open_context(ctx); 117 put_nfs_open_context(ctx);
118 if (err != 0) 118 if (err != 0)
119 return; 119 return err;
120 goto again; 120 goto again;
121 } 121 }
122 spin_unlock(&inode->i_lock); 122 spin_unlock(&inode->i_lock);
123 return 0;
123} 124}
124 125
125/* 126/*
@@ -261,30 +262,34 @@ static void nfs_msync_inode(struct inode *inode)
261/* 262/*
262 * Basic procedure for returning a delegation to the server 263 * Basic procedure for returning a delegation to the server
263 */ 264 */
264static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) 265static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
265{ 266{
266 struct nfs_inode *nfsi = NFS_I(inode); 267 struct nfs_inode *nfsi = NFS_I(inode);
268 int err;
267 269
268 nfs_msync_inode(inode);
269 /* 270 /*
270 * Guard against new delegated open/lock/unlock calls and against 271 * Guard against new delegated open/lock/unlock calls and against
271 * state recovery 272 * state recovery
272 */ 273 */
273 down_write(&nfsi->rwsem); 274 down_write(&nfsi->rwsem);
274 nfs_delegation_claim_opens(inode, &delegation->stateid); 275 err = nfs_delegation_claim_opens(inode, &delegation->stateid);
275 up_write(&nfsi->rwsem); 276 up_write(&nfsi->rwsem);
276 nfs_msync_inode(inode); 277 if (err)
278 goto out;
277 279
278 return nfs_do_return_delegation(inode, delegation, 1); 280 err = nfs_do_return_delegation(inode, delegation, issync);
281out:
282 return err;
279} 283}
280 284
281/* 285/*
282 * Return all delegations that have been marked for return 286 * Return all delegations that have been marked for return
283 */ 287 */
284void nfs_client_return_marked_delegations(struct nfs_client *clp) 288int nfs_client_return_marked_delegations(struct nfs_client *clp)
285{ 289{
286 struct nfs_delegation *delegation; 290 struct nfs_delegation *delegation;
287 struct inode *inode; 291 struct inode *inode;
292 int err = 0;
288 293
289restart: 294restart:
290 rcu_read_lock(); 295 rcu_read_lock();
@@ -298,12 +303,18 @@ restart:
298 delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); 303 delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
299 spin_unlock(&clp->cl_lock); 304 spin_unlock(&clp->cl_lock);
300 rcu_read_unlock(); 305 rcu_read_unlock();
301 if (delegation != NULL) 306 if (delegation != NULL) {
302 __nfs_inode_return_delegation(inode, delegation); 307 filemap_flush(inode->i_mapping);
308 err = __nfs_inode_return_delegation(inode, delegation, 0);
309 }
303 iput(inode); 310 iput(inode);
304 goto restart; 311 if (!err)
312 goto restart;
313 set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
314 return err;
305 } 315 }
306 rcu_read_unlock(); 316 rcu_read_unlock();
317 return 0;
307} 318}
308 319
309/* 320/*
@@ -338,8 +349,10 @@ int nfs_inode_return_delegation(struct inode *inode)
338 spin_lock(&clp->cl_lock); 349 spin_lock(&clp->cl_lock);
339 delegation = nfs_detach_delegation_locked(nfsi, NULL); 350 delegation = nfs_detach_delegation_locked(nfsi, NULL);
340 spin_unlock(&clp->cl_lock); 351 spin_unlock(&clp->cl_lock);
341 if (delegation != NULL) 352 if (delegation != NULL) {
342 err = __nfs_inode_return_delegation(inode, delegation); 353 nfs_msync_inode(inode);
354 err = __nfs_inode_return_delegation(inode, delegation, 1);
355 }
343 } 356 }
344 return err; 357 return err;
345} 358}
@@ -368,7 +381,8 @@ void nfs_super_return_all_delegations(struct super_block *sb)
368 spin_unlock(&delegation->lock); 381 spin_unlock(&delegation->lock);
369 } 382 }
370 rcu_read_unlock(); 383 rcu_read_unlock();
371 nfs_client_return_marked_delegations(clp); 384 if (nfs_client_return_marked_delegations(clp) != 0)
385 nfs4_schedule_state_manager(clp);
372} 386}
373 387
374static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) 388static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)