forked from luck/tmp_suning_uos_patched
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>
This commit is contained in:
parent
d327cf7449
commit
d18cc1fda2
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user