Skip to content

Commit

Permalink
NFSv4: Fix a potential state manager deadlock when returning delegations
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Dec 3, 2009
1 parent d327cf7 commit d18cc1f
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 15 deletions.
42 changes: 28 additions & 14 deletions fs/nfs/delegation.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
return status;
}

static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *ctx;
Expand All @@ -116,10 +116,11 @@ static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *
err = nfs_delegation_claim_locks(ctx, state);
put_nfs_open_context(ctx);
if (err != 0)
return;
return err;
goto again;
}
spin_unlock(&inode->i_lock);
return 0;
}

/*
Expand Down Expand Up @@ -261,30 +262,34 @@ static void nfs_msync_inode(struct inode *inode)
/*
* Basic procedure for returning a delegation to the server
*/
static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
{
struct nfs_inode *nfsi = NFS_I(inode);
int err;

nfs_msync_inode(inode);
/*
* Guard against new delegated open/lock/unlock calls and against
* state recovery
*/
down_write(&nfsi->rwsem);
nfs_delegation_claim_opens(inode, &delegation->stateid);
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
up_write(&nfsi->rwsem);
nfs_msync_inode(inode);
if (err)
goto out;

return nfs_do_return_delegation(inode, delegation, 1);
err = nfs_do_return_delegation(inode, delegation, issync);
out:
return err;
}

/*
* Return all delegations that have been marked for return
*/
void nfs_client_return_marked_delegations(struct nfs_client *clp)
int nfs_client_return_marked_delegations(struct nfs_client *clp)
{
struct nfs_delegation *delegation;
struct inode *inode;
int err = 0;

restart:
rcu_read_lock();
Expand All @@ -298,12 +303,18 @@ void nfs_client_return_marked_delegations(struct nfs_client *clp)
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
spin_unlock(&clp->cl_lock);
rcu_read_unlock();
if (delegation != NULL)
__nfs_inode_return_delegation(inode, delegation);
if (delegation != NULL) {
filemap_flush(inode->i_mapping);
err = __nfs_inode_return_delegation(inode, delegation, 0);
}
iput(inode);
goto restart;
if (!err)
goto restart;
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
return err;
}
rcu_read_unlock();
return 0;
}

/*
Expand Down Expand Up @@ -338,8 +349,10 @@ int nfs_inode_return_delegation(struct inode *inode)
spin_lock(&clp->cl_lock);
delegation = nfs_detach_delegation_locked(nfsi, NULL);
spin_unlock(&clp->cl_lock);
if (delegation != NULL)
err = __nfs_inode_return_delegation(inode, delegation);
if (delegation != NULL) {
nfs_msync_inode(inode);
err = __nfs_inode_return_delegation(inode, delegation, 1);
}
}
return err;
}
Expand Down Expand Up @@ -368,7 +381,8 @@ void nfs_super_return_all_delegations(struct super_block *sb)
spin_unlock(&delegation->lock);
}
rcu_read_unlock();
nfs_client_return_marked_delegations(clp);
if (nfs_client_return_marked_delegations(clp) != 0)
nfs4_schedule_state_manager(clp);
}

static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/delegation.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void nfs_super_return_all_delegations(struct super_block *sb);
void nfs_expire_all_delegations(struct nfs_client *clp);
void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
void nfs_handle_cb_pathdown(struct nfs_client *clp);
void nfs_client_return_marked_delegations(struct nfs_client *clp);
int nfs_client_return_marked_delegations(struct nfs_client *clp);

void nfs_delegation_mark_reclaim(struct nfs_client *clp);
void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
Expand Down

0 comments on commit d18cc1f

Please sign in to comment.