diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2016-06-26 08:44:35 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2016-06-30 15:29:57 -0400 |
commit | 8487c479e2668dd1231e9c3c77a203d744aec081 (patch) | |
tree | 886f0174f465e4b27d97087118dd229db0358502 | |
parent | ca857cc1d4cf17aba4bbb3b95d35454ad96924b3 (diff) |
NFSv4: Allow retry of operations that used a returned delegation stateid
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>
-rw-r--r-- | fs/nfs/nfs4_fs.h | 1 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 79 |
2 files changed, 47 insertions, 33 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 768456fa1b17..4be567a54958 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -185,6 +185,7 @@ struct nfs4_state { | |||
185 | struct nfs4_exception { | 185 | struct nfs4_exception { |
186 | struct nfs4_state *state; | 186 | struct nfs4_state *state; |
187 | struct inode *inode; | 187 | struct inode *inode; |
188 | nfs4_stateid *stateid; | ||
188 | long timeout; | 189 | long timeout; |
189 | unsigned char delay : 1, | 190 | unsigned char delay : 1, |
190 | recovering : 1, | 191 | recovering : 1, |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 6191b7e46913..519368b98762 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -363,6 +363,7 @@ static int nfs4_do_handle_exception(struct nfs_server *server, | |||
363 | { | 363 | { |
364 | struct nfs_client *clp = server->nfs_client; | 364 | struct nfs_client *clp = server->nfs_client; |
365 | struct nfs4_state *state = exception->state; | 365 | struct nfs4_state *state = exception->state; |
366 | const nfs4_stateid *stateid = exception->stateid; | ||
366 | struct inode *inode = exception->inode; | 367 | struct inode *inode = exception->inode; |
367 | int ret = errorcode; | 368 | int ret = errorcode; |
368 | 369 | ||
@@ -376,9 +377,18 @@ static int nfs4_do_handle_exception(struct nfs_server *server, | |||
376 | case -NFS4ERR_DELEG_REVOKED: | 377 | case -NFS4ERR_DELEG_REVOKED: |
377 | case -NFS4ERR_ADMIN_REVOKED: | 378 | case -NFS4ERR_ADMIN_REVOKED: |
378 | case -NFS4ERR_BAD_STATEID: | 379 | case -NFS4ERR_BAD_STATEID: |
379 | if (inode && nfs_async_inode_return_delegation(inode, | 380 | if (inode) { |
380 | NULL) == 0) | 381 | int err; |
381 | goto wait_on_recovery; | 382 | |
383 | err = nfs_async_inode_return_delegation(inode, | ||
384 | stateid); | ||
385 | if (err == 0) | ||
386 | goto wait_on_recovery; | ||
387 | if (stateid != NULL && stateid->type == NFS4_DELEGATION_STATEID_TYPE) { | ||
388 | exception->retry = 1; | ||
389 | break; | ||
390 | } | ||
391 | } | ||
382 | if (state == NULL) | 392 | if (state == NULL) |
383 | break; | 393 | break; |
384 | ret = nfs4_schedule_stateid_recovery(server, state); | 394 | ret = nfs4_schedule_stateid_recovery(server, state); |
@@ -2669,28 +2679,17 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, | |||
2669 | return res; | 2679 | return res; |
2670 | } | 2680 | } |
2671 | 2681 | ||
2672 | static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, | 2682 | static int _nfs4_do_setattr(struct inode *inode, |
2673 | struct nfs_fattr *fattr, struct iattr *sattr, | 2683 | struct nfs_setattrargs *arg, |
2674 | struct nfs4_state *state, struct nfs4_label *ilabel, | 2684 | struct nfs_setattrres *res, |
2675 | struct nfs4_label *olabel) | 2685 | struct rpc_cred *cred, |
2686 | struct nfs4_state *state) | ||
2676 | { | 2687 | { |
2677 | struct nfs_server *server = NFS_SERVER(inode); | 2688 | struct nfs_server *server = NFS_SERVER(inode); |
2678 | struct nfs_setattrargs arg = { | ||
2679 | .fh = NFS_FH(inode), | ||
2680 | .iap = sattr, | ||
2681 | .server = server, | ||
2682 | .bitmask = server->attr_bitmask, | ||
2683 | .label = ilabel, | ||
2684 | }; | ||
2685 | struct nfs_setattrres res = { | ||
2686 | .fattr = fattr, | ||
2687 | .label = olabel, | ||
2688 | .server = server, | ||
2689 | }; | ||
2690 | struct rpc_message msg = { | 2689 | struct rpc_message msg = { |
2691 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], | 2690 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], |
2692 | .rpc_argp = &arg, | 2691 | .rpc_argp = arg, |
2693 | .rpc_resp = &res, | 2692 | .rpc_resp = res, |
2694 | .rpc_cred = cred, | 2693 | .rpc_cred = cred, |
2695 | }; | 2694 | }; |
2696 | struct rpc_cred *delegation_cred = NULL; | 2695 | struct rpc_cred *delegation_cred = NULL; |
@@ -2699,17 +2698,13 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, | |||
2699 | bool truncate; | 2698 | bool truncate; |
2700 | int status; | 2699 | int status; |
2701 | 2700 | ||
2702 | arg.bitmask = nfs4_bitmask(server, ilabel); | 2701 | nfs_fattr_init(res->fattr); |
2703 | if (ilabel) | ||
2704 | arg.bitmask = nfs4_bitmask(server, olabel); | ||
2705 | |||
2706 | nfs_fattr_init(fattr); | ||
2707 | 2702 | ||
2708 | /* Servers should only apply open mode checks for file size changes */ | 2703 | /* Servers should only apply open mode checks for file size changes */ |
2709 | truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false; | 2704 | truncate = (arg->iap->ia_valid & ATTR_SIZE) ? true : false; |
2710 | fmode = truncate ? FMODE_WRITE : FMODE_READ; | 2705 | fmode = truncate ? FMODE_WRITE : FMODE_READ; |
2711 | 2706 | ||
2712 | if (nfs4_copy_delegation_stateid(inode, fmode, &arg.stateid, &delegation_cred)) { | 2707 | if (nfs4_copy_delegation_stateid(inode, fmode, &arg->stateid, &delegation_cred)) { |
2713 | /* Use that stateid */ | 2708 | /* Use that stateid */ |
2714 | } else if (truncate && state != NULL) { | 2709 | } else if (truncate && state != NULL) { |
2715 | struct nfs_lockowner lockowner = { | 2710 | struct nfs_lockowner lockowner = { |
@@ -2719,19 +2714,19 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, | |||
2719 | if (!nfs4_valid_open_stateid(state)) | 2714 | if (!nfs4_valid_open_stateid(state)) |
2720 | return -EBADF; | 2715 | return -EBADF; |
2721 | if (nfs4_select_rw_stateid(state, FMODE_WRITE, &lockowner, | 2716 | if (nfs4_select_rw_stateid(state, FMODE_WRITE, &lockowner, |
2722 | &arg.stateid, &delegation_cred) == -EIO) | 2717 | &arg->stateid, &delegation_cred) == -EIO) |
2723 | return -EBADF; | 2718 | return -EBADF; |
2724 | } else | 2719 | } else |
2725 | nfs4_stateid_copy(&arg.stateid, &zero_stateid); | 2720 | nfs4_stateid_copy(&arg->stateid, &zero_stateid); |
2726 | if (delegation_cred) | 2721 | if (delegation_cred) |
2727 | msg.rpc_cred = delegation_cred; | 2722 | msg.rpc_cred = delegation_cred; |
2728 | 2723 | ||
2729 | status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); | 2724 | status = nfs4_call_sync(server->client, server, &msg, &arg->seq_args, &res->seq_res, 1); |
2730 | 2725 | ||
2731 | put_rpccred(delegation_cred); | 2726 | put_rpccred(delegation_cred); |
2732 | if (status == 0 && state != NULL) | 2727 | if (status == 0 && state != NULL) |
2733 | renew_lease(server, timestamp); | 2728 | renew_lease(server, timestamp); |
2734 | trace_nfs4_setattr(inode, &arg.stateid, status); | 2729 | trace_nfs4_setattr(inode, &arg->stateid, status); |
2735 | return status; | 2730 | return status; |
2736 | } | 2731 | } |
2737 | 2732 | ||
@@ -2741,13 +2736,31 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, | |||
2741 | struct nfs4_label *olabel) | 2736 | struct nfs4_label *olabel) |
2742 | { | 2737 | { |
2743 | struct nfs_server *server = NFS_SERVER(inode); | 2738 | struct nfs_server *server = NFS_SERVER(inode); |
2739 | struct nfs_setattrargs arg = { | ||
2740 | .fh = NFS_FH(inode), | ||
2741 | .iap = sattr, | ||
2742 | .server = server, | ||
2743 | .bitmask = server->attr_bitmask, | ||
2744 | .label = ilabel, | ||
2745 | }; | ||
2746 | struct nfs_setattrres res = { | ||
2747 | .fattr = fattr, | ||
2748 | .label = olabel, | ||
2749 | .server = server, | ||
2750 | }; | ||
2744 | struct nfs4_exception exception = { | 2751 | struct nfs4_exception exception = { |
2745 | .state = state, | 2752 | .state = state, |
2746 | .inode = inode, | 2753 | .inode = inode, |
2754 | .stateid = &arg.stateid, | ||
2747 | }; | 2755 | }; |
2748 | int err; | 2756 | int err; |
2757 | |||
2758 | arg.bitmask = nfs4_bitmask(server, ilabel); | ||
2759 | if (ilabel) | ||
2760 | arg.bitmask = nfs4_bitmask(server, olabel); | ||
2761 | |||
2749 | do { | 2762 | do { |
2750 | err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel); | 2763 | err = _nfs4_do_setattr(inode, &arg, &res, cred, state); |
2751 | switch (err) { | 2764 | switch (err) { |
2752 | case -NFS4ERR_OPENMODE: | 2765 | case -NFS4ERR_OPENMODE: |
2753 | if (!(sattr->ia_valid & ATTR_SIZE)) { | 2766 | if (!(sattr->ia_valid & ATTR_SIZE)) { |