1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

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:
Trond Myklebust 2025-02-01 15:00:09 -05:00 committed by Anna Schumaker
parent 5bbd6e863b
commit 8f8df955f0
3 changed files with 41 additions and 0 deletions

View file

@ -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

View file

@ -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);

View file

@ -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