nfsd4: allow restarting callbacks

If we lose the backchannel and then the client repairs the problem,
resend any callbacks.

We use a new cb_done flag to track whether there is still work to be
done for the callback or whether it can be destroyed with the rpc.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
J. Bruce Fields 2011-01-10 16:44:41 -05:00
parent 3ff3600e7e
commit 5ce8ba25d6
3 changed files with 32 additions and 6 deletions

View File

@ -639,6 +639,10 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
if (!nfsd41_cb_get_slot(clp, task)) if (!nfsd41_cb_get_slot(clp, task))
return; return;
} }
cb->cb_done = false;
spin_lock(&clp->cl_lock);
list_add(&cb->cb_per_client, &clp->cl_callbacks);
spin_unlock(&clp->cl_lock);
rpc_call_start(task); rpc_call_start(task);
} }
@ -681,8 +685,11 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
return; return;
} }
if (cb->cb_done)
return;
switch (task->tk_status) { switch (task->tk_status) {
case 0: case 0:
cb->cb_done = true;
return; return;
case -EBADHANDLE: case -EBADHANDLE:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
@ -695,7 +702,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
if (current_rpc_client != task->tk_client) { if (current_rpc_client != task->tk_client) {
/* queue a callback on the new connection: */ /* queue a callback on the new connection: */
atomic_inc(&dp->dl_count); atomic_inc(&dp->dl_count);
nfsd4_cb_recall(dp); run_nfsd4_cb(&dp->dl_recall);
return; return;
} }
} }
@ -704,17 +711,24 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
task->tk_status = 0; task->tk_status = 0;
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
} else }
nfsd4_mark_cb_down(clp, task->tk_status); nfsd4_mark_cb_down(clp, task->tk_status);
cb->cb_done = true;
} }
static void nfsd4_cb_recall_release(void *calldata) static void nfsd4_cb_recall_release(void *calldata)
{ {
struct nfsd4_callback *cb = calldata; struct nfsd4_callback *cb = calldata;
struct nfs4_client *clp = cb->cb_clp;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
if (cb->cb_done) {
spin_lock(&clp->cl_lock);
list_del(&cb->cb_per_client);
spin_unlock(&clp->cl_lock);
nfs4_put_delegation(dp); nfs4_put_delegation(dp);
} }
}
static const struct rpc_call_ops nfsd4_cb_recall_ops = { static const struct rpc_call_ops nfsd4_cb_recall_ops = {
.rpc_call_prepare = nfsd4_cb_prepare, .rpc_call_prepare = nfsd4_cb_prepare,
@ -808,8 +822,13 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
err = setup_callback_client(clp, &conn, ses); err = setup_callback_client(clp, &conn, ses);
if (err) if (err) {
warn_no_callback_path(clp, err); warn_no_callback_path(clp, err);
return;
}
/* Yay, the callback channel's back! Restart any callbacks: */
list_for_each_entry(cb, &clp->cl_callbacks, cb_per_client)
run_nfsd4_cb(cb);
} }
void nfsd4_do_callback_rpc(struct work_struct *w) void nfsd4_do_callback_rpc(struct work_struct *w)
@ -834,10 +853,11 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
void nfsd4_cb_recall(struct nfs4_delegation *dp) void nfsd4_cb_recall(struct nfs4_delegation *dp)
{ {
struct nfsd4_callback *cb = &dp->dl_recall; struct nfsd4_callback *cb = &dp->dl_recall;
struct nfs4_client *clp = dp->dl_client;
dp->dl_retries = 1; dp->dl_retries = 1;
cb->cb_op = dp; cb->cb_op = dp;
cb->cb_clp = dp->dl_client; cb->cb_clp = clp;
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
cb->cb_msg.rpc_argp = cb; cb->cb_msg.rpc_argp = cb;
cb->cb_msg.rpc_resp = cb; cb->cb_msg.rpc_resp = cb;
@ -846,5 +866,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
cb->cb_ops = &nfsd4_cb_recall_ops; cb->cb_ops = &nfsd4_cb_recall_ops;
dp->dl_retries = 1; dp->dl_retries = 1;
cb->cb_done = true;
run_nfsd4_cb(&dp->dl_recall); run_nfsd4_cb(&dp->dl_recall);
} }

View File

@ -1077,6 +1077,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
INIT_LIST_HEAD(&clp->cl_openowners); INIT_LIST_HEAD(&clp->cl_openowners);
INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_lru); INIT_LIST_HEAD(&clp->cl_lru);
INIT_LIST_HEAD(&clp->cl_callbacks);
spin_lock_init(&clp->cl_lock); spin_lock_init(&clp->cl_lock);
INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
clp->cl_time = get_seconds(); clp->cl_time = get_seconds();

View File

@ -68,10 +68,12 @@ typedef struct {
struct nfsd4_callback { struct nfsd4_callback {
void *cb_op; void *cb_op;
struct nfs4_client *cb_clp; struct nfs4_client *cb_clp;
struct list_head cb_per_client;
u32 cb_minorversion; u32 cb_minorversion;
struct rpc_message cb_msg; struct rpc_message cb_msg;
const struct rpc_call_ops *cb_ops; const struct rpc_call_ops *cb_ops;
struct work_struct cb_work; struct work_struct cb_work;
bool cb_done;
}; };
struct nfs4_delegation { struct nfs4_delegation {
@ -248,6 +250,7 @@ struct nfs4_client {
int cl_cb_state; int cl_cb_state;
struct nfsd4_callback cl_cb_null; struct nfsd4_callback cl_cb_null;
struct nfsd4_session *cl_cb_session; struct nfsd4_session *cl_cb_session;
struct list_head cl_callbacks; /* list of in-progress callbacks */
/* for all client information that callback code might need: */ /* for all client information that callback code might need: */
spinlock_t cl_lock; spinlock_t cl_lock;