Merge tag 'nfs-for-4.9-1' of git://git.linux-nfs.org/projects/anna/linux-nfs
[cascardo/linux.git] / net / sunrpc / clnt.c
index 66f23b3..34dd7b2 100644 (file)
@@ -184,7 +184,6 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
                                   struct super_block *sb)
 {
        struct dentry *dentry;
-       int err = 0;
 
        switch (event) {
        case RPC_PIPEFS_MOUNT:
@@ -201,7 +200,7 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
                printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
                return -ENOTSUPP;
        }
-       return err;
+       return 0;
 }
 
 static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
@@ -988,7 +987,6 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
 {
 
        if (clnt != NULL) {
-               rpc_task_release_client(task);
                if (task->tk_xprt == NULL)
                        task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
                task->tk_client = clnt;
@@ -1693,6 +1691,7 @@ call_allocate(struct rpc_task *task)
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_xprt *xprt = req->rq_xprt;
        struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
+       int status;
 
        dprint_status(task);
 
@@ -1718,11 +1717,14 @@ call_allocate(struct rpc_task *task)
        req->rq_rcvsize = RPC_REPHDRSIZE + slack + proc->p_replen;
        req->rq_rcvsize <<= 2;
 
-       req->rq_buffer = xprt->ops->buf_alloc(task,
-                                       req->rq_callsize + req->rq_rcvsize);
-       if (req->rq_buffer != NULL)
-               return;
+       status = xprt->ops->buf_alloc(task);
        xprt_inject_disconnect(xprt);
+       if (status == 0)
+               return;
+       if (status != -ENOMEM) {
+               rpc_exit(task, status);
+               return;
+       }
 
        dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid);
 
@@ -1748,18 +1750,6 @@ rpc_task_force_reencode(struct rpc_task *task)
        task->tk_rqstp->rq_bytes_sent = 0;
 }
 
-static inline void
-rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
-{
-       buf->head[0].iov_base = start;
-       buf->head[0].iov_len = len;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->flags = 0;
-       buf->len = 0;
-       buf->buflen = len;
-}
-
 /*
  * 3.  Encode arguments of an RPC call
  */
@@ -1772,12 +1762,12 @@ rpc_xdr_encode(struct rpc_task *task)
 
        dprint_status(task);
 
-       rpc_xdr_buf_init(&req->rq_snd_buf,
-                        req->rq_buffer,
-                        req->rq_callsize);
-       rpc_xdr_buf_init(&req->rq_rcv_buf,
-                        (char *)req->rq_buffer + req->rq_callsize,
-                        req->rq_rcvsize);
+       xdr_buf_init(&req->rq_snd_buf,
+                    req->rq_buffer,
+                    req->rq_callsize);
+       xdr_buf_init(&req->rq_rcv_buf,
+                    req->rq_rbuffer,
+                    req->rq_rcvsize);
 
        p = rpc_encode_header(task);
        if (p == NULL) {
@@ -2615,6 +2605,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
@@ -2697,6 +2751,34 @@ rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo)
 }
 EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout);
 
+void rpc_clnt_xprt_switch_put(struct rpc_clnt *clnt)
+{
+       xprt_switch_put(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put);
+
+void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+{
+       rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch),
+                                xprt);
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt);
+
+bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+                                  const struct sockaddr *sap)
+{
+       struct rpc_xprt_switch *xps;
+       bool ret;
+
+       xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+
+       rcu_read_lock();
+       ret = rpc_xprt_switch_has_addr(xps, sap);
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_has_addr);
+
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
 static void rpc_show_header(void)
 {