Skip to content

Commit 8f8df95

Browse files
Trond MyklebustAnna Schumaker
authored andcommitted
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 <[email protected]> Fixes: adb4b42 ("Return the delegation when deleting sillyrenamed files") Signed-off-by: Trond Myklebust <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent 5bbd6e8 commit 8f8df95

File tree

3 files changed

+41
-0
lines changed

3 files changed

+41
-0
lines changed

fs/nfs/delegation.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,43 @@ int nfs4_inode_return_delegation(struct inode *inode)
780780
return 0;
781781
}
782782

783+
/**
784+
* nfs4_inode_set_return_delegation_on_close - asynchronously return a delegation
785+
* @inode: inode to process
786+
*
787+
* This routine is called to request that the delegation be returned as soon
788+
* as the file is closed. If the file is already closed, the delegation is
789+
* immediately returned.
790+
*/
791+
void nfs4_inode_set_return_delegation_on_close(struct inode *inode)
792+
{
793+
struct nfs_delegation *delegation;
794+
struct nfs_delegation *ret = NULL;
795+
796+
if (!inode)
797+
return;
798+
rcu_read_lock();
799+
delegation = nfs4_get_valid_delegation(inode);
800+
if (!delegation)
801+
goto out;
802+
spin_lock(&delegation->lock);
803+
if (!delegation->inode)
804+
goto out_unlock;
805+
if (list_empty(&NFS_I(inode)->open_files) &&
806+
!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
807+
/* Refcount matched in nfs_end_delegation_return() */
808+
ret = nfs_get_delegation(delegation);
809+
} else
810+
set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
811+
out_unlock:
812+
spin_unlock(&delegation->lock);
813+
if (ret)
814+
nfs_clear_verifier_delegated(inode);
815+
out:
816+
rcu_read_unlock();
817+
nfs_end_delegation_return(inode, ret, 0);
818+
}
819+
783820
/**
784821
* nfs4_inode_return_delegation_on_close - asynchronously return a delegation
785822
* @inode: inode to process

fs/nfs/delegation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
4949
unsigned long pagemod_limit, u32 deleg_type);
5050
int nfs4_inode_return_delegation(struct inode *inode);
5151
void nfs4_inode_return_delegation_on_close(struct inode *inode);
52+
void nfs4_inode_set_return_delegation_on_close(struct inode *inode);
5253
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
5354
void nfs_inode_evict_delegation(struct inode *inode);
5455

fs/nfs/nfs4proc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3906,8 +3906,11 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx,
39063906

39073907
static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
39083908
{
3909+
struct dentry *dentry = ctx->dentry;
39093910
if (ctx->state == NULL)
39103911
return;
3912+
if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
3913+
nfs4_inode_set_return_delegation_on_close(d_inode(dentry));
39113914
if (is_sync)
39123915
nfs4_close_sync(ctx->state, _nfs4_ctx_to_openmode(ctx));
39133916
else

0 commit comments

Comments
 (0)