SUNRPC: rpc_clnt_add_xprt setup function for NFS layer
authorAndy Adamson <andros@netapp.com>
Fri, 9 Sep 2016 13:22:26 +0000 (09:22 -0400)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Mon, 19 Sep 2016 17:08:36 +0000 (13:08 -0400)
Use a setup function to call into the NFS layer to test an rpc_xprt
for session trunking so as to not leak the rpc_xprt_switch into
the nfs layer.

Search for the address in the rpc_xprt_switch first so as not to
put an unnecessary EXCHANGE_ID on the wire.

Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
include/linux/sunrpc/clnt.h
net/sunrpc/clnt.c

index 35cc539e292195ad3d16b3096954f6cc20b74446..85cc819676e81d9f0e85331a17f70b271cd680d1 100644 (file)
@@ -125,6 +125,13 @@ struct rpc_create_args {
        struct svc_xprt         *bc_xprt;       /* NFSv4.1 backchannel */
 };
 
+struct rpc_add_xprt_test {
+       int (*add_xprt_test)(struct rpc_clnt *,
+               struct rpc_xprt *,
+               void *calldata);
+       void *data;
+};
+
 /* Values for "flags" field */
 #define RPC_CLNT_CREATE_HARDRTRY       (1UL << 0)
 #define RPC_CLNT_CREATE_AUTOBIND       (1UL << 2)
@@ -198,6 +205,11 @@ int                rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *,
 void           rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
                        unsigned long timeo);
 
+int            rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *,
+                       struct rpc_xprt_switch *,
+                       struct rpc_xprt *,
+                       void *);
+
 const char *rpc_proc_name(const struct rpc_task *task);
 
 void rpc_clnt_xprt_switch_put(struct rpc_clnt *);
index 8d68efd2026feb12751fdfcae906a775248e5b34..b614cb1356b93718afc8efb3431b0a539e5ae06e 100644 (file)
@@ -2613,6 +2613,70 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
 }
 EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt);
 
+/**
+ * rpc_clnt_setup_test_and_add_xprt()
+ *
+ * This is an rpc_clnt_add_xprt setup() function which returns 1 so:
+ *   1) caller of the test function must dereference the rpc_xprt_switch
+ *   and the rpc_xprt.
+ *   2) test function must call rpc_xprt_switch_add_xprt, usually in
+ *   the rpc_call_done routine.
+ *
+ * Upon success (return of 1), the test function adds the new
+ * transport to the rpc_clnt xprt switch
+ *
+ * @clnt: struct rpc_clnt to get the new transport
+ * @xps:  the rpc_xprt_switch to hold the new transport
+ * @xprt: the rpc_xprt to test
+ * @data: a struct rpc_add_xprt_test pointer that holds the test function
+ *        and test function call data
+ */
+int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt,
+                                    struct rpc_xprt_switch *xps,
+                                    struct rpc_xprt *xprt,
+                                    void *data)
+{
+       struct rpc_cred *cred;
+       struct rpc_task *task;
+       struct rpc_add_xprt_test *xtest = (struct rpc_add_xprt_test *)data;
+       int status = -EADDRINUSE;
+
+       xprt = xprt_get(xprt);
+       xprt_switch_get(xps);
+
+       if (rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr))
+               goto out_err;
+
+       /* Test the connection */
+       cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+       task = rpc_call_null_helper(clnt, xprt, cred,
+                                   RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
+                                   NULL, NULL);
+       put_rpccred(cred);
+       if (IS_ERR(task)) {
+               status = PTR_ERR(task);
+               goto out_err;
+       }
+       status = task->tk_status;
+       rpc_put_task(task);
+
+       if (status < 0)
+               goto out_err;
+
+       /* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */
+       xtest->add_xprt_test(clnt, xprt, xtest->data);
+
+       /* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */
+       return 1;
+out_err:
+       xprt_put(xprt);
+       xprt_switch_put(xps);
+       pr_info("RPC:   rpc_clnt_test_xprt failed: %d addr %s not added\n",
+               status, xprt->address_strings[RPC_DISPLAY_ADDR]);
+       return status;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_setup_test_and_add_xprt);
+
 /**
  * rpc_clnt_add_xprt - Add a new transport to a rpc_clnt
  * @clnt: pointer to struct rpc_clnt