Commit 8f8df955 authored by Trond Myklebust's avatar Trond Myklebust Committed by Anna Schumaker
Browse files

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: default avatarLi Lingfeng <lilingfeng3@huawei.com>
Fixes: adb4b42d ("Return the delegation when deleting sillyrenamed files")
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarAnna Schumaker <anna.schumaker@oracle.com>
parent 5bbd6e86
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);

+3 −0
Original line number Diff line number Diff line
@@ -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