NFSv4: Allow retry of operations that used a returned delegation stateid
authorTrond Myklebust <trond.myklebust@primarydata.com>
Sun, 26 Jun 2016 12:44:35 +0000 (08:44 -0400)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Thu, 30 Jun 2016 19:29:57 +0000 (15:29 -0400)
Fix up nfs4_do_handle_exception() so that it can check if the operation
that received the NFS4ERR_BAD_STATEID was using a defunct delegation.
Apply that to the case of SETATTR, which will currently return EIO
in some cases where this happens.

Reported-by: Olga Kornievskaia <kolga@netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c

index 768456fa1b177a2f3b35c9352d1949c9fc44a03b..4be567a549585387f5c439338eb197db81983bfb 100644 (file)
@@ -185,6 +185,7 @@ struct nfs4_state {
 struct nfs4_exception {
        struct nfs4_state *state;
        struct inode *inode;
+       nfs4_stateid *stateid;
        long timeout;
        unsigned char delay : 1,
                      recovering : 1,
index 6191b7e469133229366f824566d0feb4c4a6155b..519368b987622ea23bea210929bebfd0c327e14e 100644 (file)
@@ -363,6 +363,7 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_state *state = exception->state;
+       const nfs4_stateid *stateid = exception->stateid;
        struct inode *inode = exception->inode;
        int ret = errorcode;
 
@@ -376,9 +377,18 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
-                       if (inode && nfs_async_inode_return_delegation(inode,
-                                               NULL) == 0)
-                               goto wait_on_recovery;
+                       if (inode) {
+                               int err;
+
+                               err = nfs_async_inode_return_delegation(inode,
+                                               stateid);
+                               if (err == 0)
+                                       goto wait_on_recovery;
+                               if (stateid != NULL && stateid->type == NFS4_DELEGATION_STATEID_TYPE) {
+                                       exception->retry = 1;
+                                       break;
+                               }
+                       }
                        if (state == NULL)
                                break;
                        ret = nfs4_schedule_stateid_recovery(server, state);
@@ -2669,28 +2679,17 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
        return res;
 }
 
-static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
-                           struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state, struct nfs4_label *ilabel,
-                           struct nfs4_label *olabel)
+static int _nfs4_do_setattr(struct inode *inode,
+                           struct nfs_setattrargs *arg,
+                           struct nfs_setattrres *res,
+                           struct rpc_cred *cred,
+                           struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(inode);
-        struct nfs_setattrargs  arg = {
-                .fh             = NFS_FH(inode),
-                .iap            = sattr,
-               .server         = server,
-               .bitmask = server->attr_bitmask,
-               .label          = ilabel,
-        };
-        struct nfs_setattrres  res = {
-               .fattr          = fattr,
-               .label          = olabel,
-               .server         = server,
-        };
         struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
-               .rpc_argp       = &arg,
-               .rpc_resp       = &res,
+               .rpc_argp       = arg,
+               .rpc_resp       = res,
                .rpc_cred       = cred,
         };
        struct rpc_cred *delegation_cred = NULL;
@@ -2699,17 +2698,13 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        bool truncate;
        int status;
 
-       arg.bitmask = nfs4_bitmask(server, ilabel);
-       if (ilabel)
-               arg.bitmask = nfs4_bitmask(server, olabel);
-
-       nfs_fattr_init(fattr);
+       nfs_fattr_init(res->fattr);
 
        /* Servers should only apply open mode checks for file size changes */
-       truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false;
+       truncate = (arg->iap->ia_valid & ATTR_SIZE) ? true : false;
        fmode = truncate ? FMODE_WRITE : FMODE_READ;
 
-       if (nfs4_copy_delegation_stateid(inode, fmode, &arg.stateid, &delegation_cred)) {
+       if (nfs4_copy_delegation_stateid(inode, fmode, &arg->stateid, &delegation_cred)) {
                /* Use that stateid */
        } else if (truncate && state != NULL) {
                struct nfs_lockowner lockowner = {
@@ -2719,19 +2714,19 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                if (!nfs4_valid_open_stateid(state))
                        return -EBADF;
                if (nfs4_select_rw_stateid(state, FMODE_WRITE, &lockowner,
-                               &arg.stateid, &delegation_cred) == -EIO)
+                               &arg->stateid, &delegation_cred) == -EIO)
                        return -EBADF;
        } else
-               nfs4_stateid_copy(&arg.stateid, &zero_stateid);
+               nfs4_stateid_copy(&arg->stateid, &zero_stateid);
        if (delegation_cred)
                msg.rpc_cred = delegation_cred;
 
-       status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
+       status = nfs4_call_sync(server->client, server, &msg, &arg->seq_args, &res->seq_res, 1);
 
        put_rpccred(delegation_cred);
        if (status == 0 && state != NULL)
                renew_lease(server, timestamp);
-       trace_nfs4_setattr(inode, &arg.stateid, status);
+       trace_nfs4_setattr(inode, &arg->stateid, status);
        return status;
 }
 
@@ -2741,13 +2736,31 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                           struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
+        struct nfs_setattrargs  arg = {
+                .fh             = NFS_FH(inode),
+                .iap            = sattr,
+               .server         = server,
+               .bitmask = server->attr_bitmask,
+               .label          = ilabel,
+        };
+        struct nfs_setattrres  res = {
+               .fattr          = fattr,
+               .label          = olabel,
+               .server         = server,
+        };
        struct nfs4_exception exception = {
                .state = state,
                .inode = inode,
+               .stateid = &arg.stateid,
        };
        int err;
+
+       arg.bitmask = nfs4_bitmask(server, ilabel);
+       if (ilabel)
+               arg.bitmask = nfs4_bitmask(server, olabel);
+
        do {
-               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel);
+               err = _nfs4_do_setattr(inode, &arg, &res, cred, state);
                switch (err) {
                case -NFS4ERR_OPENMODE:
                        if (!(sattr->ia_valid & ATTR_SIZE)) {