]> git.baikalelectronics.ru Git - kernel.git/commitdiff
NFSv4: Fix a deadlock between nfs4_open_recover_helper() and delegreturn
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 4 Nov 2022 17:20:01 +0000 (13:20 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 18 Jan 2023 10:41:07 +0000 (11:41 +0100)
[ Upstream commit 51069e4aef6257b0454057359faed0ab0c9af083 ]

If we're asked to recover open state while a delegation return is
outstanding, then the state manager thread cannot use a cached open, so
if the server returns a delegation, we can end up deadlocked behind the
pending delegreturn.
To avoid this problem, let's just ask the server not to give us a
delegation unless we're explicitly reclaiming one.

Fixes: edc2d2f52cc9 ("NFSv4: nfs4_open_recover_helper() must set share access")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/nfs/nfs4proc.c

index 3da48dd67b834d8db5b4470f0adbd699d9a32415..a76550d927e7c72d1141bddefc3f8e96f2cf976b 100644 (file)
@@ -2085,18 +2085,18 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
 }
 
 static int nfs4_open_recover_helper(struct nfs4_opendata *opendata,
-               fmode_t fmode)
+                                   fmode_t fmode)
 {
        struct nfs4_state *newstate;
+       struct nfs_server *server = NFS_SB(opendata->dentry->d_sb);
+       int openflags = opendata->o_arg.open_flags;
        int ret;
 
        if (!nfs4_mode_match_open_stateid(opendata->state, fmode))
                return 0;
-       opendata->o_arg.open_flags = 0;
        opendata->o_arg.fmode = fmode;
-       opendata->o_arg.share_access = nfs4_map_atomic_open_share(
-                       NFS_SB(opendata->dentry->d_sb),
-                       fmode, 0);
+       opendata->o_arg.share_access =
+               nfs4_map_atomic_open_share(server, fmode, openflags);
        memset(&opendata->o_res, 0, sizeof(opendata->o_res));
        memset(&opendata->c_res, 0, sizeof(opendata->c_res));
        nfs4_init_opendata_res(opendata);
@@ -2671,10 +2671,15 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
        struct nfs4_opendata *opendata;
        int ret;
 
-       opendata = nfs4_open_recoverdata_alloc(ctx, state,
-                       NFS4_OPEN_CLAIM_FH);
+       opendata = nfs4_open_recoverdata_alloc(ctx, state, NFS4_OPEN_CLAIM_FH);
        if (IS_ERR(opendata))
                return PTR_ERR(opendata);
+       /*
+        * We're not recovering a delegation, so ask for no delegation.
+        * Otherwise the recovery thread could deadlock with an outstanding
+        * delegation return.
+        */
+       opendata->o_arg.open_flags = O_DIRECT;
        ret = nfs4_open_recover(opendata, state);
        if (ret == -ESTALE)
                d_drop(ctx->dentry);