NFSv4: Fix delegation state recovery
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 19 Jul 2019 18:08:37 +0000 (14:08 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Sep 2019 06:22:22 +0000 (08:22 +0200)
[ Upstream commit 5eb8d18ca0e001c6055da2b7f30d8f6dca23a44f ]

Once we clear the NFS_DELEGATED_STATE flag, we're telling
nfs_delegation_claim_opens() that we're done recovering all open state
for that stateid, so we really need to ensure that we test for all
open modes that are currently cached and recover them before exiting
nfs4_open_delegation_recall().

Fixes: 24311f884189d ("NFSv4: Recovery of recalled read delegations...")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Cc: stable@vger.kernel.org # v4.3+
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4proc.c

index 75fe92eaa68188800ec2a00f9fc4a2fa972bfd08..1624618c2bc72a456070e77311b8b070d063a605 100644 (file)
@@ -153,7 +153,7 @@ static int nfs_delegation_claim_opens(struct inode *inode,
                /* Block nfs4_proc_unlck */
                mutex_lock(&sp->so_delegreturn_mutex);
                seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
-               err = nfs4_open_delegation_recall(ctx, state, stateid, type);
+               err = nfs4_open_delegation_recall(ctx, state, stateid);
                if (!err)
                        err = nfs_delegation_claim_locks(ctx, state, stateid);
                if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
index bb1ef8c37af42706c909d9f6880ca44fd33b5d00..c95477823fa6b0eff0acc5651f5f84ae049475a2 100644 (file)
@@ -61,7 +61,7 @@ void nfs_reap_expired_delegations(struct nfs_client *clp);
 
 /* NFSv4 delegation-related procedures */
 int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync);
-int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type);
+int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid);
 int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid);
 bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_stateid *dst, struct rpc_cred **cred);
 bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
index 31ae3bd5d9d208e44509ebf4ad549d543ee10a7c..621e3cf90f4eb9faf67711feef1da9cfa30f9229 100644 (file)
@@ -2113,12 +2113,10 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                case -NFS4ERR_BAD_HIGH_SLOT:
                case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
                case -NFS4ERR_DEADSESSION:
-                       set_bit(NFS_DELEGATED_STATE, &state->flags);
                        nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
                        return -EAGAIN;
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_STALE_STATEID:
-                       set_bit(NFS_DELEGATED_STATE, &state->flags);
                        /* Don't recall a delegation if it was lost */
                        nfs4_schedule_lease_recovery(server->nfs_client);
                        return -EAGAIN;
@@ -2139,7 +2137,6 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                        return -EAGAIN;
                case -NFS4ERR_DELAY:
                case -NFS4ERR_GRACE:
-                       set_bit(NFS_DELEGATED_STATE, &state->flags);
                        ssleep(1);
                        return -EAGAIN;
                case -ENOMEM:
@@ -2155,8 +2152,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
 }
 
 int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
-               struct nfs4_state *state, const nfs4_stateid *stateid,
-               fmode_t type)
+               struct nfs4_state *state, const nfs4_stateid *stateid)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
        struct nfs4_opendata *opendata;
@@ -2167,20 +2163,23 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx,
        if (IS_ERR(opendata))
                return PTR_ERR(opendata);
        nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
-       nfs_state_clear_delegation(state);
-       switch (type & (FMODE_READ|FMODE_WRITE)) {
-       case FMODE_READ|FMODE_WRITE:
-       case FMODE_WRITE:
+       if (!test_bit(NFS_O_RDWR_STATE, &state->flags)) {
                err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
                if (err)
-                       break;
+                       goto out;
+       }
+       if (!test_bit(NFS_O_WRONLY_STATE, &state->flags)) {
                err = nfs4_open_recover_helper(opendata, FMODE_WRITE);
                if (err)
-                       break;
-               /* Fall through */
-       case FMODE_READ:
+                       goto out;
+       }
+       if (!test_bit(NFS_O_RDONLY_STATE, &state->flags)) {
                err = nfs4_open_recover_helper(opendata, FMODE_READ);
+               if (err)
+                       goto out;
        }
+       nfs_state_clear_delegation(state);
+out:
        nfs4_opendata_put(opendata);
        return nfs4_handle_delegation_recall_error(server, state, stateid, NULL, err);
 }