nfsd4: allow restarting callbacks
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 10 Jan 2011 21:44:41 +0000 (16:44 -0500)
committerJ. Bruce Fields <bfields@redhat.com>
Tue, 11 Jan 2011 20:04:11 +0000 (15:04 -0500)
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>
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4state.c
fs/nfsd/state.h

index 69955e98e086e9f61efb21d859334dd0d2e1ab37..f1d9dd45553a51f62ee5c853c11aa528d8a1d73d 100644 (file)
@@ -639,6 +639,10 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
                if (!nfsd41_cb_get_slot(clp, task))
                        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);
 }
 
@@ -681,8 +685,11 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
                return;
        }
 
+       if (cb->cb_done)
+               return;
        switch (task->tk_status) {
        case 0:
+               cb->cb_done = true;
                return;
        case -EBADHANDLE:
        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) {
                        /* queue a callback on the new connection: */
                        atomic_inc(&dp->dl_count);
-                       nfsd4_cb_recall(dp);
+                       run_nfsd4_cb(&dp->dl_recall);
                        return;
                }
        }
@@ -704,16 +711,23 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
                task->tk_status = 0;
                rpc_restart_call_prepare(task);
                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)
 {
        struct nfsd4_callback *cb = calldata;
+       struct nfs4_client *clp = cb->cb_clp;
        struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
 
-       nfs4_put_delegation(dp);
+       if (cb->cb_done) {
+               spin_lock(&clp->cl_lock);
+               list_del(&cb->cb_per_client);
+               spin_unlock(&clp->cl_lock);
+               nfs4_put_delegation(dp);
+       }
 }
 
 static const struct rpc_call_ops nfsd4_cb_recall_ops = {
@@ -808,8 +822,13 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
        spin_unlock(&clp->cl_lock);
 
        err = setup_callback_client(clp, &conn, ses);
-       if (err)
+       if (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)
@@ -834,10 +853,11 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
 void nfsd4_cb_recall(struct nfs4_delegation *dp)
 {
        struct nfsd4_callback *cb = &dp->dl_recall;
+       struct nfs4_client *clp = dp->dl_client;
 
        dp->dl_retries = 1;
        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_argp = 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;
        dp->dl_retries = 1;
 
+       cb->cb_done = true;
+
        run_nfsd4_cb(&dp->dl_recall);
 }
index 408957cf60161c9df8d307db72fc06f4a54538f1..6e1f9aadd4395761141f8408b1839745f1599634 100644 (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_delegations);
        INIT_LIST_HEAD(&clp->cl_lru);
+       INIT_LIST_HEAD(&clp->cl_callbacks);
        spin_lock_init(&clp->cl_lock);
        INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
        clp->cl_time = get_seconds();
index 4e5bdfd9169c2c288fa123c7540658119eef8299..3074656ba7bf96cc23c024bc7b9ec35256b5cd80 100644 (file)
@@ -68,10 +68,12 @@ typedef struct {
 struct nfsd4_callback {
        void *cb_op;
        struct nfs4_client *cb_clp;
+       struct list_head cb_per_client;
        u32 cb_minorversion;
        struct rpc_message cb_msg;
        const struct rpc_call_ops *cb_ops;
        struct work_struct cb_work;
+       bool cb_done;
 };
 
 struct nfs4_delegation {
@@ -248,6 +250,7 @@ struct nfs4_client {
        int                     cl_cb_state;
        struct nfsd4_callback   cl_cb_null;
        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: */
        spinlock_t              cl_lock;