NFSv4: Fix a deadlock when recovering state on a sillyrenamed file
If the file is sillyrenamed, and slated for delete on close, it is
possible for a server reboot to triggeer an open reclaim, with can again
race with the application call to close(). When that happens, the call
to put_nfs_open_context() can trigger a synchronous delegreturn call
which deadlocks because it is not marked as privileged.
Instead, ensure that the call to nfs4_inode_return_delegation_on_close()
catches the delegreturn, and schedules it asynchronously.
Reported-by: Li Lingfeng <lilingfeng3@huawei.com>
Fixes: adb4b42d19
("Return the delegation when deleting sillyrenamed files")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
This commit is contained in:
parent
5bbd6e863b
commit
8f8df955f0
3 changed files with 41 additions and 0 deletions
|
@ -780,6 +780,43 @@ int nfs4_inode_return_delegation(struct inode *inode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs4_inode_set_return_delegation_on_close - asynchronously return a delegation
|
||||
* @inode: inode to process
|
||||
*
|
||||
* This routine is called to request that the delegation be returned as soon
|
||||
* as the file is closed. If the file is already closed, the delegation is
|
||||
* immediately returned.
|
||||
*/
|
||||
void nfs4_inode_set_return_delegation_on_close(struct inode *inode)
|
||||
{
|
||||
struct nfs_delegation *delegation;
|
||||
struct nfs_delegation *ret = NULL;
|
||||
|
||||
if (!inode)
|
||||
return;
|
||||
rcu_read_lock();
|
||||
delegation = nfs4_get_valid_delegation(inode);
|
||||
if (!delegation)
|
||||
goto out;
|
||||
spin_lock(&delegation->lock);
|
||||
if (!delegation->inode)
|
||||
goto out_unlock;
|
||||
if (list_empty(&NFS_I(inode)->open_files) &&
|
||||
!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
|
||||
/* Refcount matched in nfs_end_delegation_return() */
|
||||
ret = nfs_get_delegation(delegation);
|
||||
} else
|
||||
set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
|
||||
out_unlock:
|
||||
spin_unlock(&delegation->lock);
|
||||
if (ret)
|
||||
nfs_clear_verifier_delegated(inode);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
nfs_end_delegation_return(inode, ret, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs4_inode_return_delegation_on_close - asynchronously return a delegation
|
||||
* @inode: inode to process
|
||||
|
|
|
@ -49,6 +49,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
|
|||
unsigned long pagemod_limit, u32 deleg_type);
|
||||
int nfs4_inode_return_delegation(struct inode *inode);
|
||||
void nfs4_inode_return_delegation_on_close(struct inode *inode);
|
||||
void nfs4_inode_set_return_delegation_on_close(struct inode *inode);
|
||||
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
|
||||
void nfs_inode_evict_delegation(struct inode *inode);
|
||||
|
||||
|
|
|
@ -3906,8 +3906,11 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx,
|
|||
|
||||
static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
|
||||
{
|
||||
struct dentry *dentry = ctx->dentry;
|
||||
if (ctx->state == NULL)
|
||||
return;
|
||||
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
|
||||
nfs4_inode_set_return_delegation_on_close(d_inode(dentry));
|
||||
if (is_sync)
|
||||
nfs4_close_sync(ctx->state, _nfs4_ctx_to_openmode(ctx));
|
||||
else
|
||||
|
|
Loading…
Add table
Reference in a new issue