NFSv4: Retry CLOSE and DELEGRETURN on NFS4ERR_OLD_STATEID.
authorTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 6 Nov 2017 20:28:05 +0000 (15:28 -0500)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Fri, 17 Nov 2017 21:43:47 +0000 (16:43 -0500)
If we're racing with an OPEN, then retry the operation instead of
declaring it a success.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
[Andrew W Elble: Fix a typo in nfs4_refresh_open_stateid]
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c

index 606dd3871f66b0881c2760af3bbc3839748f9bc2..ade44ca0c66c391d7dd6472a7d42622307842a27 100644 (file)
@@ -1040,6 +1040,33 @@ int nfs_delegations_present(struct nfs_client *clp)
        return ret;
 }
 
+/**
+ * nfs4_refresh_delegation_stateid - Update delegation stateid seqid
+ * @dst: stateid to refresh
+ * @inode: inode to check
+ *
+ * Returns "true" and updates "dst->seqid" * if inode had a delegation
+ * that matches our delegation stateid. Otherwise "false" is returned.
+ */
+bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+       bool ret = false;
+       if (!inode)
+               goto out;
+
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation != NULL &&
+           nfs4_stateid_match_other(dst, &delegation->stateid)) {
+               dst->seqid = delegation->stateid.seqid;
+               return ret;
+       }
+       rcu_read_unlock();
+out:
+       return ret;
+}
+
 /**
  * nfs4_copy_delegation_stateid - Copy inode's state ID information
  * @inode: inode to check
index e9d55579687393552b0d83c46dfcafc56dbace89..fe9f3882adaedf2e3d415e79e1d96e30ec8d0b39 100644 (file)
@@ -61,6 +61,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
 int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type);
 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);
 
 void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
 int nfs4_have_delegation(struct inode *inode, fmode_t flags);
index fdcfbab886bbb0a4e3d2083a83feb22532c48291..feb084bc55659e711aa035498d9b768f51a84f0c 100644 (file)
@@ -460,6 +460,8 @@ extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
 extern int nfs4_select_rw_stateid(struct nfs4_state *, fmode_t,
                const struct nfs_lock_context *, nfs4_stateid *,
                struct rpc_cred **);
+extern bool nfs4_refresh_open_stateid(nfs4_stateid *dst,
+               struct nfs4_state *state);
 
 extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask);
 extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task);
index b2d628c9425cad955ff46eed0904fc85bcf8b16f..3e385154196ae6539c44d34fb6ac98c5338ead7b 100644 (file)
@@ -3199,13 +3199,21 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
 
                        }
                        break;
+               case -NFS4ERR_OLD_STATEID:
+                       /* Did we race with OPEN? */
+                       if (nfs4_refresh_open_stateid(&calldata->arg.stateid,
+                                               state)) {
+                               task->tk_status = 0;
+                               rpc_restart_call_prepare(task);
+                       }
+                       goto out_release;
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
                        nfs4_free_revoked_stateid(server,
                                        &calldata->arg.stateid,
                                        task->tk_msg.rpc_cred);
-               case -NFS4ERR_OLD_STATEID:
+                       /* Fallthrough */
                case -NFS4ERR_BAD_STATEID:
                        if (!nfs4_stateid_match(&calldata->arg.stateid,
                                                &state->open_stateid)) {
@@ -3214,6 +3222,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                        }
                        if (calldata->arg.fmode == 0)
                                break;
+                       /* Fallthrough */
                default:
                        if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN) {
                                rpc_restart_call_prepare(task);
@@ -5793,11 +5802,19 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
                nfs4_free_revoked_stateid(data->res.server,
                                data->args.stateid,
                                task->tk_msg.rpc_cred);
+               /* Fallthrough */
        case -NFS4ERR_BAD_STATEID:
-       case -NFS4ERR_OLD_STATEID:
        case -NFS4ERR_STALE_STATEID:
                task->tk_status = 0;
                break;
+       case -NFS4ERR_OLD_STATEID:
+               if (nfs4_refresh_delegation_stateid(&data->stateid, data->inode)) {
+                       task->tk_status = 0;
+                       rpc_restart_call_prepare(task);
+                       return;
+               }
+               task->tk_status = 0;
+               break;
        case -NFS4ERR_ACCESS:
                if (data->args.bitmask) {
                        data->args.bitmask = NULL;
index 6e3f372883488f26d958db4b2de76abeb98f74ea..cee1e000b41eac05b670a4b5688f1297a7d24ef8 100644 (file)
@@ -986,6 +986,22 @@ out:
        return ret;
 }
 
+bool nfs4_refresh_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
+{
+       bool ret;
+       int seq;
+
+       do {
+               ret = false;
+               seq = read_seqbegin(&state->seqlock);
+               if (nfs4_state_match_open_stateid_other(state, dst)) {
+                       dst->seqid = state->open_stateid.seqid;
+                       ret = true;
+               }
+       } while (read_seqretry(&state->seqlock, seq));
+       return ret;
+}
+
 static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
 {
        const nfs4_stateid *src;