提交 3be0f80b 编写于 作者: T Trond Myklebust 提交者: Anna Schumaker

NFSv4.1: Fix up replays of interrupted requests

If the previous request on a slot was interrupted before it was
processed by the server, then our slot sequence number may be out of whack,
and so we try the next operation using the old sequence number.

The problem with this, is that not all servers check to see that the
client is replaying the same operations as previously when they decide
to go to the replay cache, and so instead of the expected error of
NFS4ERR_SEQ_FALSE_RETRY, we get a replay of the old reply, which could
(if the operations match up) be mistaken by the client for a new reply.

To fix this, we attempt to send a COMPOUND containing only the SEQUENCE op
in order to resync our slot sequence number.

Cc: Olga Kornievskaia <olga.kornievskaia@gmail.com>
[olga.kornievskaia@gmail.com: fix an Oops]
Signed-off-by: NTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: NAnna Schumaker <Anna.Schumaker@Netapp.com>
上级 6f0afc28
...@@ -464,7 +464,7 @@ extern void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid); ...@@ -464,7 +464,7 @@ extern void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid);
extern void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid); extern void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid);
extern void nfs_release_seqid(struct nfs_seqid *seqid); extern void nfs_release_seqid(struct nfs_seqid *seqid);
extern void nfs_free_seqid(struct nfs_seqid *seqid); extern void nfs_free_seqid(struct nfs_seqid *seqid);
extern int nfs4_setup_sequence(const struct nfs_client *client, extern int nfs4_setup_sequence(struct nfs_client *client,
struct nfs4_sequence_args *args, struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, struct nfs4_sequence_res *res,
struct rpc_task *task); struct rpc_task *task);
......
...@@ -96,6 +96,10 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, ...@@ -96,6 +96,10 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
struct nfs_open_context *ctx, struct nfs4_label *ilabel, struct nfs_open_context *ctx, struct nfs4_label *ilabel,
struct nfs4_label *olabel); struct nfs4_label *olabel);
#ifdef CONFIG_NFS_V4_1 #ifdef CONFIG_NFS_V4_1
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
struct rpc_cred *cred,
struct nfs4_slot *slot,
bool is_privileged);
static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *, static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
struct rpc_cred *); struct rpc_cred *);
static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *, static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
...@@ -644,13 +648,14 @@ static int nfs40_sequence_done(struct rpc_task *task, ...@@ -644,13 +648,14 @@ static int nfs40_sequence_done(struct rpc_task *task,
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) static void nfs41_release_slot(struct nfs4_slot *slot)
{ {
struct nfs4_session *session; struct nfs4_session *session;
struct nfs4_slot_table *tbl; struct nfs4_slot_table *tbl;
struct nfs4_slot *slot = res->sr_slot;
bool send_new_highest_used_slotid = false; bool send_new_highest_used_slotid = false;
if (!slot)
return;
tbl = slot->table; tbl = slot->table;
session = tbl->session; session = tbl->session;
...@@ -676,13 +681,18 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) ...@@ -676,13 +681,18 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
send_new_highest_used_slotid = false; send_new_highest_used_slotid = false;
out_unlock: out_unlock:
spin_unlock(&tbl->slot_tbl_lock); spin_unlock(&tbl->slot_tbl_lock);
res->sr_slot = NULL;
if (send_new_highest_used_slotid) if (send_new_highest_used_slotid)
nfs41_notify_server(session->clp); nfs41_notify_server(session->clp);
if (waitqueue_active(&tbl->slot_waitq)) if (waitqueue_active(&tbl->slot_waitq))
wake_up_all(&tbl->slot_waitq); wake_up_all(&tbl->slot_waitq);
} }
static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
{
nfs41_release_slot(res->sr_slot);
res->sr_slot = NULL;
}
static int nfs41_sequence_process(struct rpc_task *task, static int nfs41_sequence_process(struct rpc_task *task,
struct nfs4_sequence_res *res) struct nfs4_sequence_res *res)
{ {
...@@ -710,13 +720,6 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -710,13 +720,6 @@ static int nfs41_sequence_process(struct rpc_task *task,
/* Check the SEQUENCE operation status */ /* Check the SEQUENCE operation status */
switch (res->sr_status) { switch (res->sr_status) {
case 0: case 0:
/* If previous op on slot was interrupted and we reused
* the seq# and got a reply from the cache, then retry
*/
if (task->tk_status == -EREMOTEIO && interrupted) {
++slot->seq_nr;
goto retry_nowait;
}
/* Update the slot's sequence and clientid lease timer */ /* Update the slot's sequence and clientid lease timer */
slot->seq_done = 1; slot->seq_done = 1;
clp = session->clp; clp = session->clp;
...@@ -750,16 +753,16 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -750,16 +753,16 @@ static int nfs41_sequence_process(struct rpc_task *task,
* The slot id we used was probably retired. Try again * The slot id we used was probably retired. Try again
* using a different slot id. * using a different slot id.
*/ */
if (slot->seq_nr < slot->table->target_highest_slotid)
goto session_recover;
goto retry_nowait; goto retry_nowait;
case -NFS4ERR_SEQ_MISORDERED: case -NFS4ERR_SEQ_MISORDERED:
/* /*
* Was the last operation on this sequence interrupted? * Was the last operation on this sequence interrupted?
* If so, retry after bumping the sequence number. * If so, retry after bumping the sequence number.
*/ */
if (interrupted) { if (interrupted)
++slot->seq_nr; goto retry_new_seq;
goto retry_nowait;
}
/* /*
* Could this slot have been previously retired? * Could this slot have been previously retired?
* If so, then the server may be expecting seq_nr = 1! * If so, then the server may be expecting seq_nr = 1!
...@@ -768,10 +771,11 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -768,10 +771,11 @@ static int nfs41_sequence_process(struct rpc_task *task,
slot->seq_nr = 1; slot->seq_nr = 1;
goto retry_nowait; goto retry_nowait;
} }
break; goto session_recover;
case -NFS4ERR_SEQ_FALSE_RETRY: case -NFS4ERR_SEQ_FALSE_RETRY:
++slot->seq_nr; if (interrupted)
goto retry_nowait; goto retry_new_seq;
goto session_recover;
default: default:
/* Just update the slot sequence no. */ /* Just update the slot sequence no. */
slot->seq_done = 1; slot->seq_done = 1;
...@@ -781,6 +785,11 @@ static int nfs41_sequence_process(struct rpc_task *task, ...@@ -781,6 +785,11 @@ static int nfs41_sequence_process(struct rpc_task *task,
dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
out_noaction: out_noaction:
return ret; return ret;
session_recover:
nfs4_schedule_session_recovery(session, res->sr_status);
goto retry_nowait;
retry_new_seq:
++slot->seq_nr;
retry_nowait: retry_nowait:
if (rpc_restart_call_prepare(task)) { if (rpc_restart_call_prepare(task)) {
nfs41_sequence_free_slot(res); nfs41_sequence_free_slot(res);
...@@ -857,6 +866,17 @@ static const struct rpc_call_ops nfs41_call_sync_ops = { ...@@ -857,6 +866,17 @@ static const struct rpc_call_ops nfs41_call_sync_ops = {
.rpc_call_done = nfs41_call_sync_done, .rpc_call_done = nfs41_call_sync_done,
}; };
static void
nfs4_sequence_process_interrupted(struct nfs_client *client,
struct nfs4_slot *slot, struct rpc_cred *cred)
{
struct rpc_task *task;
task = _nfs41_proc_sequence(client, cred, slot, true);
if (!IS_ERR(task))
rpc_put_task_async(task);
}
#else /* !CONFIG_NFS_V4_1 */ #else /* !CONFIG_NFS_V4_1 */
static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res) static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res)
...@@ -877,9 +897,34 @@ int nfs4_sequence_done(struct rpc_task *task, ...@@ -877,9 +897,34 @@ int nfs4_sequence_done(struct rpc_task *task,
} }
EXPORT_SYMBOL_GPL(nfs4_sequence_done); EXPORT_SYMBOL_GPL(nfs4_sequence_done);
static void
nfs4_sequence_process_interrupted(struct nfs_client *client,
struct nfs4_slot *slot, struct rpc_cred *cred)
{
WARN_ON_ONCE(1);
slot->interrupted = 0;
}
#endif /* !CONFIG_NFS_V4_1 */ #endif /* !CONFIG_NFS_V4_1 */
int nfs4_setup_sequence(const struct nfs_client *client, static
void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res,
struct nfs4_slot *slot)
{
if (!slot)
return;
slot->privileged = args->sa_privileged ? 1 : 0;
args->sa_slot = slot;
res->sr_slot = slot;
res->sr_timestamp = jiffies;
res->sr_status_flags = 0;
res->sr_status = 1;
}
int nfs4_setup_sequence(struct nfs_client *client,
struct nfs4_sequence_args *args, struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, struct nfs4_sequence_res *res,
struct rpc_task *task) struct rpc_task *task)
...@@ -897,29 +942,28 @@ int nfs4_setup_sequence(const struct nfs_client *client, ...@@ -897,29 +942,28 @@ int nfs4_setup_sequence(const struct nfs_client *client,
task->tk_timeout = 0; task->tk_timeout = 0;
} }
spin_lock(&tbl->slot_tbl_lock); for (;;) {
/* The state manager will wait until the slot table is empty */ spin_lock(&tbl->slot_tbl_lock);
if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged) /* The state manager will wait until the slot table is empty */
goto out_sleep; if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged)
goto out_sleep;
slot = nfs4_alloc_slot(tbl);
if (IS_ERR(slot)) {
/* Try again in 1/4 second */
if (slot == ERR_PTR(-ENOMEM))
task->tk_timeout = HZ >> 2;
goto out_sleep;
}
spin_unlock(&tbl->slot_tbl_lock);
slot = nfs4_alloc_slot(tbl); if (likely(!slot->interrupted))
if (IS_ERR(slot)) { break;
/* Try again in 1/4 second */ nfs4_sequence_process_interrupted(client,
if (slot == ERR_PTR(-ENOMEM)) slot, task->tk_msg.rpc_cred);
task->tk_timeout = HZ >> 2;
goto out_sleep;
} }
spin_unlock(&tbl->slot_tbl_lock);
slot->privileged = args->sa_privileged ? 1 : 0;
args->sa_slot = slot;
res->sr_slot = slot; nfs4_sequence_attach_slot(args, res, slot);
if (session) {
res->sr_timestamp = jiffies;
res->sr_status_flags = 0;
res->sr_status = 1;
}
trace_nfs4_setup_sequence(session, args); trace_nfs4_setup_sequence(session, args);
out_start: out_start:
...@@ -8118,6 +8162,7 @@ static const struct rpc_call_ops nfs41_sequence_ops = { ...@@ -8118,6 +8162,7 @@ static const struct rpc_call_ops nfs41_sequence_ops = {
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
struct rpc_cred *cred, struct rpc_cred *cred,
struct nfs4_slot *slot,
bool is_privileged) bool is_privileged)
{ {
struct nfs4_sequence_data *calldata; struct nfs4_sequence_data *calldata;
...@@ -8131,15 +8176,18 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, ...@@ -8131,15 +8176,18 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
.callback_ops = &nfs41_sequence_ops, .callback_ops = &nfs41_sequence_ops,
.flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT, .flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
}; };
struct rpc_task *ret;
ret = ERR_PTR(-EIO);
if (!atomic_inc_not_zero(&clp->cl_count)) if (!atomic_inc_not_zero(&clp->cl_count))
return ERR_PTR(-EIO); goto out_err;
ret = ERR_PTR(-ENOMEM);
calldata = kzalloc(sizeof(*calldata), GFP_NOFS); calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
if (calldata == NULL) { if (calldata == NULL)
nfs_put_client(clp); goto out_put_clp;
return ERR_PTR(-ENOMEM);
}
nfs4_init_sequence(&calldata->args, &calldata->res, 0); nfs4_init_sequence(&calldata->args, &calldata->res, 0);
nfs4_sequence_attach_slot(&calldata->args, &calldata->res, slot);
if (is_privileged) if (is_privileged)
nfs4_set_sequence_privileged(&calldata->args); nfs4_set_sequence_privileged(&calldata->args);
msg.rpc_argp = &calldata->args; msg.rpc_argp = &calldata->args;
...@@ -8147,7 +8195,15 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, ...@@ -8147,7 +8195,15 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
calldata->clp = clp; calldata->clp = clp;
task_setup_data.callback_data = calldata; task_setup_data.callback_data = calldata;
return rpc_run_task(&task_setup_data); ret = rpc_run_task(&task_setup_data);
if (IS_ERR(ret))
goto out_err;
return ret;
out_put_clp:
nfs_put_client(clp);
out_err:
nfs41_release_slot(slot);
return ret;
} }
static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags) static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred, unsigned renew_flags)
...@@ -8157,7 +8213,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr ...@@ -8157,7 +8213,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr
if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0) if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
return -EAGAIN; return -EAGAIN;
task = _nfs41_proc_sequence(clp, cred, false); task = _nfs41_proc_sequence(clp, cred, NULL, false);
if (IS_ERR(task)) if (IS_ERR(task))
ret = PTR_ERR(task); ret = PTR_ERR(task);
else else
...@@ -8171,7 +8227,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) ...@@ -8171,7 +8227,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
struct rpc_task *task; struct rpc_task *task;
int ret; int ret;
task = _nfs41_proc_sequence(clp, cred, true); task = _nfs41_proc_sequence(clp, cred, NULL, true);
if (IS_ERR(task)) { if (IS_ERR(task)) {
ret = PTR_ERR(task); ret = PTR_ERR(task);
goto out; goto out;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册