2015-09-20 20:45:14

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 1/5] NFSv4: Refactor NFSv4 error handling

Prepare for unification of the synchronous and asynchronous error
handling.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4_fs.h | 6 ++++--
fs/nfs/nfs4proc.c | 38 +++++++++++++++++++++++++++++++-------
2 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 50cfc4ca7a02..4afdee420d25 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -183,10 +183,12 @@ struct nfs4_state {


struct nfs4_exception {
- long timeout;
- int retry;
struct nfs4_state *state;
struct inode *inode;
+ long timeout;
+ unsigned char delay : 1,
+ recovering : 1,
+ retry : 1;
};

struct nfs4_state_recovery_ops {
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index ef6b46e08ac6..a77736bc9d7b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -344,13 +344,15 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
/* This is the error handling routine for processes that are allowed
* to sleep.
*/
-int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
+int nfs4_do_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
struct nfs_client *clp = server->nfs_client;
struct nfs4_state *state = exception->state;
struct inode *inode = exception->inode;
int ret = errorcode;

+ exception->delay = 0;
+ exception->recovering = 0;
exception->retry = 0;
switch(errorcode) {
case 0:
@@ -411,9 +413,9 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
}
case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
- ret = nfs4_delay(server->client, &exception->timeout);
- if (ret != 0)
- break;
+ exception->delay = 1;
+ return 0;
+
case -NFS4ERR_RETRY_UNCACHED_REP:
case -NFS4ERR_OLD_STATEID:
exception->retry = 1;
@@ -434,9 +436,31 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
/* We failed to handle the error */
return nfs4_map_errors(ret);
wait_on_recovery:
- ret = nfs4_wait_clnt_recover(clp);
- if (test_bit(NFS_MIG_FAILED, &server->mig_status))
- return -EIO;
+ exception->recovering = 1;
+ return 0;
+}
+
+/* This is the error handling routine for processes that are allowed
+ * to sleep.
+ */
+int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
+{
+ struct nfs_client *clp = server->nfs_client;
+ int ret;
+
+ ret = nfs4_do_handle_exception(server, errorcode, exception);
+ if (exception->delay) {
+ ret = nfs4_delay(server->client, &exception->timeout);
+ goto out_retry;
+ }
+ if (exception->recovering) {
+ ret = nfs4_wait_clnt_recover(clp);
+ if (test_bit(NFS_MIG_FAILED, &server->mig_status))
+ return -EIO;
+ goto out_retry;
+ }
+ return ret;
+out_retry:
if (ret == 0)
exception->retry = 1;
return ret;
--
2.4.3



2015-09-20 20:45:14

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 2/5] NFSv4: Update the delay statistics counter for synchronous delays

Currently, we only do so for asynchronous delays.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4proc.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a77736bc9d7b..9ab48308f195 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -411,8 +411,9 @@ int nfs4_do_handle_exception(struct nfs_server *server, int errorcode, struct nf
ret = -EBUSY;
break;
}
- case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
+ nfs_inc_server_stats(server, NFSIOS_DELAY);
+ case -NFS4ERR_GRACE:
exception->delay = 1;
return 0;

--
2.4.3


2015-09-20 20:45:16

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 4/5] NFSv4: Don't use synchronous delegation recall in exception handling

The code needs to be able to work from inside an asynchronous context.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/delegation.c | 6 ++----
fs/nfs/nfs4proc.c | 8 +++-----
2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index be806ead7f4d..5166adcfc0fb 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -721,14 +721,12 @@ int nfs_async_inode_return_delegation(struct inode *inode,
struct nfs_client *clp = server->nfs_client;
struct nfs_delegation *delegation;

- filemap_flush(inode->i_mapping);
-
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation == NULL)
goto out_enoent;
-
- if (!clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
+ if (stateid != NULL &&
+ !clp->cl_mvops->match_stateid(&delegation->stateid, stateid))
goto out_enoent;
nfs_mark_return_delegation(server, delegation);
rcu_read_unlock();
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index b6bf246b9b6c..af3168c2b35d 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -361,11 +361,9 @@ int nfs4_do_handle_exception(struct nfs_server *server, int errorcode, struct nf
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
- if (inode && nfs4_have_delegation(inode, FMODE_READ)) {
- nfs4_inode_return_delegation(inode);
- exception->retry = 1;
- return 0;
- }
+ if (inode && nfs_async_inode_return_delegation(inode,
+ NULL) == 0)
+ goto wait_on_recovery;
if (state == NULL)
break;
ret = nfs4_schedule_stateid_recovery(server, state);
--
2.4.3


2015-09-20 20:45:17

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 5/5] NFSv4: Unify synchronous and asynchronous error handling

They now only differ in the way we handle waiting, so let's unify.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4proc.c | 115 +++++++++++++++++++-----------------------------------
1 file changed, 41 insertions(+), 74 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index af3168c2b35d..18079763f5d0 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -78,7 +78,6 @@ struct nfs4_opendata;
static int _nfs4_proc_open(struct nfs4_opendata *data);
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
-static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *, struct nfs4_state *, long *);
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
@@ -465,6 +464,47 @@ out_retry:
return ret;
}

+static int
+nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server,
+ struct nfs4_state *state, long *timeout)
+{
+ struct nfs_client *clp = server->nfs_client;
+ struct nfs4_exception exception = {
+ .state = state,
+ };
+ int ret;
+
+ if (task->tk_status >= 0)
+ return 0;
+ if (timeout)
+ exception.timeout = *timeout;
+ ret = nfs4_do_handle_exception(server, task->tk_status, &exception);
+ if (exception.delay) {
+ nfs_inc_server_stats(server, NFSIOS_DELAY);
+ rpc_delay(task, nfs4_update_delay(&exception.timeout));
+ if (timeout)
+ *timeout = exception.timeout;
+ goto out_retry;
+ }
+ if (exception.recovering) {
+ rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
+ if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
+ rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
+ goto out_retry;
+ }
+ task->tk_status = ret;
+ if (exception.retry)
+ return -EAGAIN;
+ return 0;
+out_retry:
+ if (test_bit(NFS_MIG_FAILED, &server->mig_status)) {
+ task->tk_status = -EIO;
+ return 0;
+ }
+ task->tk_status = 0;
+ return -EAGAIN;
+}
+
/*
* Return 'true' if 'clp' is using an rpc_client that is integrity protected
* or 'false' otherwise.
@@ -4953,79 +4993,6 @@ out:
#endif /* CONFIG_NFS_V4_SECURITY_LABEL */


-static int
-nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server,
- struct nfs4_state *state, long *timeout)
-{
- struct nfs_client *clp = server->nfs_client;
-
- if (task->tk_status >= 0)
- return 0;
- switch(task->tk_status) {
- case -NFS4ERR_DELEG_REVOKED:
- case -NFS4ERR_ADMIN_REVOKED:
- case -NFS4ERR_BAD_STATEID:
- case -NFS4ERR_OPENMODE:
- if (state == NULL)
- break;
- if (nfs4_schedule_stateid_recovery(server, state) < 0)
- goto recovery_failed;
- goto wait_on_recovery;
- case -NFS4ERR_EXPIRED:
- if (state != NULL) {
- if (nfs4_schedule_stateid_recovery(server, state) < 0)
- goto recovery_failed;
- }
- case -NFS4ERR_STALE_STATEID:
- case -NFS4ERR_STALE_CLIENTID:
- nfs4_schedule_lease_recovery(clp);
- goto wait_on_recovery;
- case -NFS4ERR_MOVED:
- if (nfs4_schedule_migration_recovery(server) < 0)
- goto recovery_failed;
- goto wait_on_recovery;
- case -NFS4ERR_LEASE_MOVED:
- nfs4_schedule_lease_moved_recovery(clp);
- goto wait_on_recovery;
-#if defined(CONFIG_NFS_V4_1)
- case -NFS4ERR_BADSESSION:
- case -NFS4ERR_BADSLOT:
- case -NFS4ERR_BAD_HIGH_SLOT:
- case -NFS4ERR_DEADSESSION:
- case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
- case -NFS4ERR_SEQ_FALSE_RETRY:
- case -NFS4ERR_SEQ_MISORDERED:
- dprintk("%s ERROR %d, Reset session\n", __func__,
- task->tk_status);
- nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
- goto wait_on_recovery;
-#endif /* CONFIG_NFS_V4_1 */
- case -NFS4ERR_DELAY:
- nfs_inc_server_stats(server, NFSIOS_DELAY);
- rpc_delay(task, nfs4_update_delay(timeout));
- goto restart_call;
- case -NFS4ERR_GRACE:
- rpc_delay(task, NFS4_POLL_RETRY_MAX);
- case -NFS4ERR_RETRY_UNCACHED_REP:
- case -NFS4ERR_OLD_STATEID:
- goto restart_call;
- }
- task->tk_status = nfs4_map_errors(task->tk_status);
- return 0;
-recovery_failed:
- task->tk_status = -EIO;
- return 0;
-wait_on_recovery:
- rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
- if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
- rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
- if (test_bit(NFS_MIG_FAILED, &server->mig_status))
- goto recovery_failed;
-restart_call:
- task->tk_status = 0;
- return -EAGAIN;
-}
-
static void nfs4_init_boot_verifier(const struct nfs_client *clp,
nfs4_verifier *bootverf)
{
--
2.4.3


2015-09-20 20:45:15

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 3/5] NFSv4: nfs4_async_handle_error should take a non-const nfs_server

For symmetry with the synchronous handler, and so that we can potentially
handle errors such as NFS4ERR_BADNAME.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/nfs4proc.c | 6 +++---
include/linux/nfs_xdr.h | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9ab48308f195..b6bf246b9b6c 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -78,7 +78,7 @@ struct nfs4_opendata;
static int _nfs4_proc_open(struct nfs4_opendata *data);
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
-static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *, long *);
+static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *, struct nfs4_state *, long *);
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
@@ -4956,7 +4956,7 @@ out:


static int
-nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
+nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server,
struct nfs4_state *state, long *timeout)
{
struct nfs_client *clp = server->nfs_client;
@@ -5530,7 +5530,7 @@ struct nfs4_unlockdata {
struct nfs4_lock_state *lsp;
struct nfs_open_context *ctx;
struct file_lock fl;
- const struct nfs_server *server;
+ struct nfs_server *server;
unsigned long timestamp;
};

diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 52faf7e96c65..53f2acc68baf 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -528,7 +528,7 @@ struct nfs4_delegreturnargs {
struct nfs4_delegreturnres {
struct nfs4_sequence_res seq_res;
struct nfs_fattr * fattr;
- const struct nfs_server *server;
+ struct nfs_server *server;
};

/*
@@ -601,7 +601,7 @@ struct nfs_removeargs {

struct nfs_removeres {
struct nfs4_sequence_res seq_res;
- const struct nfs_server *server;
+ struct nfs_server *server;
struct nfs_fattr *dir_attr;
struct nfs4_change_info cinfo;
};
@@ -619,7 +619,7 @@ struct nfs_renameargs {

struct nfs_renameres {
struct nfs4_sequence_res seq_res;
- const struct nfs_server *server;
+ struct nfs_server *server;
struct nfs4_change_info old_cinfo;
struct nfs_fattr *old_fattr;
struct nfs4_change_info new_cinfo;
--
2.4.3


2015-09-21 15:10:44

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 5/5] NFSv4: Unify synchronous and asynchronous error handling

On Sun, Sep 20, 2015 at 04:45:11PM -0400, Trond Myklebust wrote:
> They now only differ in the way we handle waiting, so let's unify.

Neat, yes, nice not to have that big switch duplicated....

--b.

>
> Signed-off-by: Trond Myklebust <[email protected]>
> ---
> fs/nfs/nfs4proc.c | 115 +++++++++++++++++++-----------------------------------
> 1 file changed, 41 insertions(+), 74 deletions(-)
>
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index af3168c2b35d..18079763f5d0 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -78,7 +78,6 @@ struct nfs4_opendata;
> static int _nfs4_proc_open(struct nfs4_opendata *data);
> static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
> static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
> -static int nfs4_async_handle_error(struct rpc_task *, struct nfs_server *, struct nfs4_state *, long *);
> static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
> static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
> static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
> @@ -465,6 +464,47 @@ out_retry:
> return ret;
> }
>
> +static int
> +nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server,
> + struct nfs4_state *state, long *timeout)
> +{
> + struct nfs_client *clp = server->nfs_client;
> + struct nfs4_exception exception = {
> + .state = state,
> + };
> + int ret;
> +
> + if (task->tk_status >= 0)
> + return 0;
> + if (timeout)
> + exception.timeout = *timeout;
> + ret = nfs4_do_handle_exception(server, task->tk_status, &exception);
> + if (exception.delay) {
> + nfs_inc_server_stats(server, NFSIOS_DELAY);
> + rpc_delay(task, nfs4_update_delay(&exception.timeout));
> + if (timeout)
> + *timeout = exception.timeout;
> + goto out_retry;
> + }
> + if (exception.recovering) {
> + rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
> + if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
> + rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
> + goto out_retry;
> + }
> + task->tk_status = ret;
> + if (exception.retry)
> + return -EAGAIN;
> + return 0;
> +out_retry:
> + if (test_bit(NFS_MIG_FAILED, &server->mig_status)) {
> + task->tk_status = -EIO;
> + return 0;
> + }
> + task->tk_status = 0;
> + return -EAGAIN;
> +}
> +
> /*
> * Return 'true' if 'clp' is using an rpc_client that is integrity protected
> * or 'false' otherwise.
> @@ -4953,79 +4993,6 @@ out:
> #endif /* CONFIG_NFS_V4_SECURITY_LABEL */
>
>
> -static int
> -nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server,
> - struct nfs4_state *state, long *timeout)
> -{
> - struct nfs_client *clp = server->nfs_client;
> -
> - if (task->tk_status >= 0)
> - return 0;
> - switch(task->tk_status) {
> - case -NFS4ERR_DELEG_REVOKED:
> - case -NFS4ERR_ADMIN_REVOKED:
> - case -NFS4ERR_BAD_STATEID:
> - case -NFS4ERR_OPENMODE:
> - if (state == NULL)
> - break;
> - if (nfs4_schedule_stateid_recovery(server, state) < 0)
> - goto recovery_failed;
> - goto wait_on_recovery;
> - case -NFS4ERR_EXPIRED:
> - if (state != NULL) {
> - if (nfs4_schedule_stateid_recovery(server, state) < 0)
> - goto recovery_failed;
> - }
> - case -NFS4ERR_STALE_STATEID:
> - case -NFS4ERR_STALE_CLIENTID:
> - nfs4_schedule_lease_recovery(clp);
> - goto wait_on_recovery;
> - case -NFS4ERR_MOVED:
> - if (nfs4_schedule_migration_recovery(server) < 0)
> - goto recovery_failed;
> - goto wait_on_recovery;
> - case -NFS4ERR_LEASE_MOVED:
> - nfs4_schedule_lease_moved_recovery(clp);
> - goto wait_on_recovery;
> -#if defined(CONFIG_NFS_V4_1)
> - case -NFS4ERR_BADSESSION:
> - case -NFS4ERR_BADSLOT:
> - case -NFS4ERR_BAD_HIGH_SLOT:
> - case -NFS4ERR_DEADSESSION:
> - case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
> - case -NFS4ERR_SEQ_FALSE_RETRY:
> - case -NFS4ERR_SEQ_MISORDERED:
> - dprintk("%s ERROR %d, Reset session\n", __func__,
> - task->tk_status);
> - nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
> - goto wait_on_recovery;
> -#endif /* CONFIG_NFS_V4_1 */
> - case -NFS4ERR_DELAY:
> - nfs_inc_server_stats(server, NFSIOS_DELAY);
> - rpc_delay(task, nfs4_update_delay(timeout));
> - goto restart_call;
> - case -NFS4ERR_GRACE:
> - rpc_delay(task, NFS4_POLL_RETRY_MAX);
> - case -NFS4ERR_RETRY_UNCACHED_REP:
> - case -NFS4ERR_OLD_STATEID:
> - goto restart_call;
> - }
> - task->tk_status = nfs4_map_errors(task->tk_status);
> - return 0;
> -recovery_failed:
> - task->tk_status = -EIO;
> - return 0;
> -wait_on_recovery:
> - rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
> - if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
> - rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
> - if (test_bit(NFS_MIG_FAILED, &server->mig_status))
> - goto recovery_failed;
> -restart_call:
> - task->tk_status = 0;
> - return -EAGAIN;
> -}
> -
> static void nfs4_init_boot_verifier(const struct nfs_client *clp,
> nfs4_verifier *bootverf)
> {
> --
> 2.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html