Commit 7ba6cad6 authored by Kinglong Mee's avatar Kinglong Mee Committed by J. Bruce Fields

nfsd: New helper nfsd4_cb_sequence_done() for processing more cb errors

According to Christoph's advice, this patch introduce a new helper
nfsd4_cb_sequence_done() for processing more callback errors, following
the example of the client's nfs41_sequence_done().
Signed-off-by: default avatarKinglong Mee <kinglongmee@gmail.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 9056fff3
...@@ -435,12 +435,12 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr, ...@@ -435,12 +435,12 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
*/ */
status = 0; status = 0;
out: out:
if (status) cb->cb_seq_status = status;
nfsd4_mark_cb_fault(cb->cb_clp, status);
return status; return status;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
return -EIO; status = -EIO;
goto out;
} }
static int decode_cb_sequence4res(struct xdr_stream *xdr, static int decode_cb_sequence4res(struct xdr_stream *xdr,
...@@ -451,11 +451,10 @@ static int decode_cb_sequence4res(struct xdr_stream *xdr, ...@@ -451,11 +451,10 @@ static int decode_cb_sequence4res(struct xdr_stream *xdr,
if (cb->cb_minorversion == 0) if (cb->cb_minorversion == 0)
return 0; return 0;
status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_status); status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_seq_status);
if (unlikely(status || cb->cb_status)) if (unlikely(status || cb->cb_seq_status))
return status; return status;
cb->cb_update_seq_nr = true;
return decode_cb_sequence4resok(xdr, cb); return decode_cb_sequence4resok(xdr, cb);
} }
...@@ -527,7 +526,7 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, ...@@ -527,7 +526,7 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
if (cb != NULL) { if (cb != NULL) {
status = decode_cb_sequence4res(xdr, cb); status = decode_cb_sequence4res(xdr, cb);
if (unlikely(status || cb->cb_status)) if (unlikely(status || cb->cb_seq_status))
return status; return status;
} }
...@@ -617,7 +616,7 @@ static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp, ...@@ -617,7 +616,7 @@ static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp,
if (cb) { if (cb) {
status = decode_cb_sequence4res(xdr, cb); status = decode_cb_sequence4res(xdr, cb);
if (unlikely(status || cb->cb_status)) if (unlikely(status || cb->cb_seq_status))
return status; return status;
} }
return decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &cb->cb_status); return decode_cb_op_status(xdr, OP_CB_LAYOUTRECALL, &cb->cb_status);
...@@ -876,7 +875,11 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) ...@@ -876,7 +875,11 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
u32 minorversion = clp->cl_minorversion; u32 minorversion = clp->cl_minorversion;
cb->cb_minorversion = minorversion; cb->cb_minorversion = minorversion;
cb->cb_update_seq_nr = false; /*
* cb_seq_status is only set in decode_cb_sequence4res,
* and so will remain 1 if an rpc level failure occurs.
*/
cb->cb_seq_status = 1;
cb->cb_status = 0; cb->cb_status = 0;
if (minorversion) { if (minorversion) {
if (!nfsd41_cb_get_slot(clp, task)) if (!nfsd41_cb_get_slot(clp, task))
...@@ -885,15 +888,30 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) ...@@ -885,15 +888,30 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
rpc_call_start(task); rpc_call_start(task);
} }
static void nfsd4_cb_done(struct rpc_task *task, void *calldata) static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback *cb)
{ {
struct nfsd4_callback *cb = calldata;
struct nfs4_client *clp = cb->cb_clp; struct nfs4_client *clp = cb->cb_clp;
struct nfsd4_session *session = clp->cl_cb_session;
bool ret = true;
dprintk("%s: minorversion=%d\n", __func__, if (!clp->cl_minorversion) {
clp->cl_minorversion); /*
* If the backchannel connection was shut down while this
* task was queued, we need to resubmit it after setting up
* a new backchannel connection.
*
* Note that if we lost our callback connection permanently
* the submission code will error out, so we don't need to
* handle that case here.
*/
if (task->tk_flags & RPC_TASK_KILLED)
goto need_restart;
if (clp->cl_minorversion) { return true;
}
switch (cb->cb_seq_status) {
case 0:
/* /*
* No need for lock, access serialized in nfsd4_cb_prepare * No need for lock, access serialized in nfsd4_cb_prepare
* *
...@@ -901,29 +919,63 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) ...@@ -901,29 +919,63 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
* If CB_SEQUENCE returns an error, then the state of the slot * If CB_SEQUENCE returns an error, then the state of the slot
* (sequence ID, cached reply) MUST NOT change. * (sequence ID, cached reply) MUST NOT change.
*/ */
if (cb->cb_update_seq_nr) ++session->se_cb_seq_nr;
++clp->cl_cb_session->se_cb_seq_nr; break;
case -ESERVERFAULT:
++session->se_cb_seq_nr;
case 1:
case -NFS4ERR_BADSESSION:
nfsd4_mark_cb_fault(cb->cb_clp, cb->cb_seq_status);
ret = false;
break;
case -NFS4ERR_DELAY:
if (!rpc_restart_call(task))
goto out;
rpc_delay(task, 2 * HZ);
return false;
case -NFS4ERR_BADSLOT:
goto retry_nowait;
case -NFS4ERR_SEQ_MISORDERED:
if (session->se_cb_seq_nr != 1) {
session->se_cb_seq_nr = 1;
goto retry_nowait;
}
break;
default:
dprintk("%s: unprocessed error %d\n", __func__,
cb->cb_seq_status);
}
clear_bit(0, &clp->cl_cb_slot_busy); clear_bit(0, &clp->cl_cb_slot_busy);
rpc_wake_up_next(&clp->cl_cb_waitq); rpc_wake_up_next(&clp->cl_cb_waitq);
dprintk("%s: freed slot, new seqid=%d\n", __func__, dprintk("%s: freed slot, new seqid=%d\n", __func__,
clp->cl_cb_session->se_cb_seq_nr); clp->cl_cb_session->se_cb_seq_nr);
}
/* if (task->tk_flags & RPC_TASK_KILLED)
* If the backchannel connection was shut down while this goto need_restart;
* task was queued, we need to resubmit it after setting up out:
* a new backchannel connection. return ret;
* retry_nowait:
* Note that if we lost our callback connection permanently if (rpc_restart_call_prepare(task))
* the submission code will error out, so we don't need to ret = false;
* handle that case here. goto out;
*/ need_restart:
if (task->tk_flags & RPC_TASK_KILLED) {
task->tk_status = 0; task->tk_status = 0;
cb->cb_need_restart = true; cb->cb_need_restart = true;
return false;
}
static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
{
struct nfsd4_callback *cb = calldata;
struct nfs4_client *clp = cb->cb_clp;
dprintk("%s: minorversion=%d\n", __func__,
clp->cl_minorversion);
if (!nfsd4_cb_sequence_done(task, cb))
return; return;
}
if (cb->cb_status) { if (cb->cb_status) {
WARN_ON_ONCE(task->tk_status); WARN_ON_ONCE(task->tk_status);
...@@ -1099,8 +1151,8 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp, ...@@ -1099,8 +1151,8 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
cb->cb_msg.rpc_resp = cb; cb->cb_msg.rpc_resp = cb;
cb->cb_ops = ops; cb->cb_ops = ops;
INIT_WORK(&cb->cb_work, nfsd4_run_cb_work); INIT_WORK(&cb->cb_work, nfsd4_run_cb_work);
cb->cb_seq_status = 1;
cb->cb_status = 0; cb->cb_status = 0;
cb->cb_update_seq_nr = false;
cb->cb_need_restart = false; cb->cb_need_restart = false;
} }
......
...@@ -67,8 +67,8 @@ struct nfsd4_callback { ...@@ -67,8 +67,8 @@ struct nfsd4_callback {
struct rpc_message cb_msg; struct rpc_message cb_msg;
struct nfsd4_callback_ops *cb_ops; struct nfsd4_callback_ops *cb_ops;
struct work_struct cb_work; struct work_struct cb_work;
int cb_seq_status;
int cb_status; int cb_status;
bool cb_update_seq_nr;
bool cb_need_restart; bool cb_need_restart;
}; };
......
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