nfsd4: allow 4.0 clients to change callback path
[safe/jmp/linux-2.6] / fs / nfsd / nfs4callback.c
index d6c46a9..ea77aa6 100644 (file)
@@ -457,9 +457,8 @@ static int max_cb_time(void)
 /* Reference counting, callback cleanup, etc., all look racy as heck.
  * And why is cl_cb_set an atomic? */
 
-int setup_callback_client(struct nfs4_client *clp)
+int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
 {
-       struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
        struct rpc_timeout      timeparms = {
                .to_initval     = max_cb_time(),
                .to_retries     = 0,
@@ -481,7 +480,7 @@ int setup_callback_client(struct nfs4_client *clp)
        if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
                return -EINVAL;
        if (cb->cb_minorversion) {
-               args.bc_xprt = clp->cl_cb_conn.cb_xprt;
+               args.bc_xprt = cb->cb_xprt;
                args.protocol = XPRT_TRANSPORT_BC_TCP;
        }
        /* Create RPC client */
@@ -491,7 +490,7 @@ int setup_callback_client(struct nfs4_client *clp)
                        PTR_ERR(client));
                return PTR_ERR(client);
        }
-       clp->cl_cb_client = client;
+       nfsd4_set_callback_client(clp, client);
        return 0;
 
 }
@@ -548,14 +547,13 @@ void do_probe_callback(struct nfs4_client *clp)
 /*
  * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
  */
-void
-nfsd4_probe_callback(struct nfs4_client *clp)
+void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
 {
        int status;
 
        BUG_ON(atomic_read(&clp->cl_cb_set));
 
-       status = setup_callback_client(clp);
+       status = setup_callback_client(clp, cb);
        if (status) {
                warn_no_callback_path(clp, status);
                return;
@@ -645,18 +643,32 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
        }
 }
 
+
 static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
 {
        struct nfs4_delegation *dp = calldata;
        struct nfs4_client *clp = dp->dl_client;
+       struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
 
        nfsd4_cb_done(task, calldata);
 
+       if (current_rpc_client == NULL) {
+               /* We're shutting down; give up. */
+               /* XXX: err, or is it ok just to fall through
+                * and rpc_restart_call? */
+               return;
+       }
+
        switch (task->tk_status) {
        case -EIO:
                /* Network partition? */
                atomic_set(&clp->cl_cb_set, 0);
                warn_no_callback_path(clp, task->tk_status);
+               if (current_rpc_client != task->tk_client) {
+                       /* queue a callback on the new connection: */
+                       nfsd4_cb_recall(dp);
+                       return;
+               }
        case -EBADHANDLE:
        case -NFS4ERR_BAD_STATEID:
                /* Race: client probably got cb_recall
@@ -705,8 +717,7 @@ void nfsd4_destroy_callback_queue(void)
        destroy_workqueue(callback_wq);
 }
 
-void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt
-*new)
+void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
 {
        struct rpc_clnt *old = clp->cl_cb_client;