Commit 8478eaa1 authored by NeilBrown's avatar NeilBrown Committed by Trond Myklebust

NFSv4: use exponential retry on NFS4ERR_DELAY for async requests.

Currently asynchronous NFSv4 request will be retried with
exponential timeout (from 1/10 to 15 seconds), but async
requests will always use a 15second retry.

Some "async" requests are really synchronous though.  The
async mechanism is used to allow the request to continue if
the requesting process is killed.
In those cases, an exponential retry is appropriate.

For example, if two different clients both open a file and
get a READ delegation, and one client then unlinks the file
(while still holding an open file descriptor), that unlink
will used the "silly-rename" handling which is async.
The first rename will result in NFS4ERR_DELAY while the
delegation is reclaimed from the other client.  The rename
will not be retried for 15 seconds, causing an unlink to take
15 seconds rather than 100msec.

This patch only added exponential timeout for async unlink and
async rename.  Other async calls, such as 'close' are sometimes
waited for so they might benefit from exponential timeout too.
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent 3dedbb5c
...@@ -77,7 +77,7 @@ struct nfs4_opendata; ...@@ -77,7 +77,7 @@ struct nfs4_opendata;
static int _nfs4_proc_open(struct nfs4_opendata *data); static int _nfs4_proc_open(struct nfs4_opendata *data);
static int _nfs4_recover_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_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 *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *, long *);
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr); 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 *, 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); static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
...@@ -314,20 +314,30 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent ...@@ -314,20 +314,30 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
kunmap_atomic(start); kunmap_atomic(start);
} }
static long nfs4_update_delay(long *timeout)
{
long ret;
if (!timeout)
return NFS4_POLL_RETRY_MAX;
if (*timeout <= 0)
*timeout = NFS4_POLL_RETRY_MIN;
if (*timeout > NFS4_POLL_RETRY_MAX)
*timeout = NFS4_POLL_RETRY_MAX;
ret = *timeout;
*timeout <<= 1;
return ret;
}
static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
{ {
int res = 0; int res = 0;
might_sleep(); might_sleep();
if (*timeout <= 0) freezable_schedule_timeout_killable_unsafe(
*timeout = NFS4_POLL_RETRY_MIN; nfs4_update_delay(timeout));
if (*timeout > NFS4_POLL_RETRY_MAX)
*timeout = NFS4_POLL_RETRY_MAX;
freezable_schedule_timeout_killable_unsafe(*timeout);
if (fatal_signal_pending(current)) if (fatal_signal_pending(current))
res = -ERESTARTSYS; res = -ERESTARTSYS;
*timeout <<= 1;
return res; return res;
} }
...@@ -2583,7 +2593,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data) ...@@ -2583,7 +2593,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
if (calldata->arg.fmode == 0) if (calldata->arg.fmode == 0)
break; break;
default: default:
if (nfs4_async_handle_error(task, server, state) == -EAGAIN) { if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
goto out_release; goto out_release;
} }
...@@ -3572,7 +3582,8 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) ...@@ -3572,7 +3582,8 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
if (!nfs4_sequence_done(task, &res->seq_res)) if (!nfs4_sequence_done(task, &res->seq_res))
return 0; return 0;
if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) if (nfs4_async_handle_error(task, res->server, NULL,
&data->timeout) == -EAGAIN)
return 0; return 0;
update_changeattr(dir, &res->cinfo); update_changeattr(dir, &res->cinfo);
return 1; return 1;
...@@ -3605,7 +3616,7 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir, ...@@ -3605,7 +3616,7 @@ static int nfs4_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
if (!nfs4_sequence_done(task, &res->seq_res)) if (!nfs4_sequence_done(task, &res->seq_res))
return 0; return 0;
if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) if (nfs4_async_handle_error(task, res->server, NULL, &data->timeout) == -EAGAIN)
return 0; return 0;
update_changeattr(old_dir, &res->old_cinfo); update_changeattr(old_dir, &res->old_cinfo);
...@@ -4109,7 +4120,8 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr) ...@@ -4109,7 +4120,8 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr)
trace_nfs4_read(hdr, task->tk_status); trace_nfs4_read(hdr, task->tk_status);
if (nfs4_async_handle_error(task, server, if (nfs4_async_handle_error(task, server,
hdr->args.context->state) == -EAGAIN) { hdr->args.context->state,
NULL) == -EAGAIN) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return -EAGAIN; return -EAGAIN;
} }
...@@ -4177,10 +4189,11 @@ static int nfs4_write_done_cb(struct rpc_task *task, ...@@ -4177,10 +4189,11 @@ static int nfs4_write_done_cb(struct rpc_task *task,
struct nfs_pgio_header *hdr) struct nfs_pgio_header *hdr)
{ {
struct inode *inode = hdr->inode; struct inode *inode = hdr->inode;
trace_nfs4_write(hdr, task->tk_status); trace_nfs4_write(hdr, task->tk_status);
if (nfs4_async_handle_error(task, NFS_SERVER(inode), if (nfs4_async_handle_error(task, NFS_SERVER(inode),
hdr->args.context->state) == -EAGAIN) { hdr->args.context->state,
NULL) == -EAGAIN) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return -EAGAIN; return -EAGAIN;
} }
...@@ -4260,7 +4273,8 @@ static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_commit_data *da ...@@ -4260,7 +4273,8 @@ static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_commit_data *da
struct inode *inode = data->inode; struct inode *inode = data->inode;
trace_nfs4_commit(data, task->tk_status); trace_nfs4_commit(data, task->tk_status);
if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) { if (nfs4_async_handle_error(task, NFS_SERVER(inode),
NULL, NULL) == -EAGAIN) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return -EAGAIN; return -EAGAIN;
} }
...@@ -4813,7 +4827,8 @@ nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen) ...@@ -4813,7 +4827,8 @@ nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen)
static int static int
nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
struct nfs4_state *state, long *timeout)
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
...@@ -4863,6 +4878,8 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, ...@@ -4863,6 +4878,8 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
#endif /* CONFIG_NFS_V4_1 */ #endif /* CONFIG_NFS_V4_1 */
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
nfs_inc_server_stats(server, NFSIOS_DELAY); nfs_inc_server_stats(server, NFSIOS_DELAY);
rpc_delay(task, nfs4_update_delay(timeout));
goto restart_call;
case -NFS4ERR_GRACE: case -NFS4ERR_GRACE:
rpc_delay(task, NFS4_POLL_RETRY_MAX); rpc_delay(task, NFS4_POLL_RETRY_MAX);
case -NFS4ERR_RETRY_UNCACHED_REP: case -NFS4ERR_RETRY_UNCACHED_REP:
...@@ -5103,8 +5120,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) ...@@ -5103,8 +5120,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
pnfs_roc_set_barrier(data->inode, data->roc_barrier); pnfs_roc_set_barrier(data->inode, data->roc_barrier);
break; break;
default: default:
if (nfs4_async_handle_error(task, data->res.server, NULL) == if (nfs4_async_handle_error(task, data->res.server,
-EAGAIN) { NULL, NULL) == -EAGAIN) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
} }
...@@ -5368,7 +5385,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) ...@@ -5368,7 +5385,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
case -NFS4ERR_EXPIRED: case -NFS4ERR_EXPIRED:
break; break;
default: default:
if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN) if (nfs4_async_handle_error(task, calldata->server,
NULL, NULL) == -EAGAIN)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
} }
nfs_release_seqid(calldata->arg.seqid); nfs_release_seqid(calldata->arg.seqid);
...@@ -5974,7 +5992,8 @@ static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata) ...@@ -5974,7 +5992,8 @@ static void nfs4_release_lockowner_done(struct rpc_task *task, void *calldata)
break; break;
case -NFS4ERR_LEASE_MOVED: case -NFS4ERR_LEASE_MOVED:
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) if (nfs4_async_handle_error(task, server,
NULL, NULL) == -EAGAIN)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
} }
} }
...@@ -7591,7 +7610,7 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) ...@@ -7591,7 +7610,7 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
} }
} }
if (nfs4_async_handle_error(task, server, state) == -EAGAIN) if (nfs4_async_handle_error(task, server, state, NULL) == -EAGAIN)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
out: out:
dprintk("<-- %s\n", __func__); dprintk("<-- %s\n", __func__);
...@@ -7751,7 +7770,7 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata) ...@@ -7751,7 +7770,7 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
case 0: case 0:
break; break;
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
if (nfs4_async_handle_error(task, server, NULL) != -EAGAIN) if (nfs4_async_handle_error(task, server, NULL, NULL) != -EAGAIN)
break; break;
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
...@@ -7882,7 +7901,7 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) ...@@ -7882,7 +7901,7 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
case 0: case 0:
break; break;
default: default:
if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) { if (nfs4_async_handle_error(task, server, NULL, NULL) == -EAGAIN) {
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
} }
...@@ -8178,7 +8197,7 @@ static void nfs41_free_stateid_done(struct rpc_task *task, void *calldata) ...@@ -8178,7 +8197,7 @@ static void nfs41_free_stateid_done(struct rpc_task *task, void *calldata)
switch (task->tk_status) { switch (task->tk_status) {
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
if (nfs4_async_handle_error(task, data->server, NULL) == -EAGAIN) if (nfs4_async_handle_error(task, data->server, NULL, NULL) == -EAGAIN)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
} }
} }
......
...@@ -1339,6 +1339,7 @@ struct nfs_unlinkdata { ...@@ -1339,6 +1339,7 @@ struct nfs_unlinkdata {
struct inode *dir; struct inode *dir;
struct rpc_cred *cred; struct rpc_cred *cred;
struct nfs_fattr dir_attr; struct nfs_fattr dir_attr;
long timeout;
}; };
struct nfs_renamedata { struct nfs_renamedata {
...@@ -1352,6 +1353,7 @@ struct nfs_renamedata { ...@@ -1352,6 +1353,7 @@ struct nfs_renamedata {
struct dentry *new_dentry; struct dentry *new_dentry;
struct nfs_fattr new_fattr; struct nfs_fattr new_fattr;
void (*complete)(struct rpc_task *, struct nfs_renamedata *); void (*complete)(struct rpc_task *, struct nfs_renamedata *);
long timeout;
}; };
struct nfs_access_entry; struct nfs_access_entry;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment