Merge tag 'nfs-for-4.15-3' of git://git.linux-nfs.org/projects/anna/linux-nfs
[sfrench/cifs-2.6.git] / net / sunrpc / xprt.c
index 5b06f6906a27ea3b2ae0683a167dbde6fe1820ec..33b74fd8405185d906d07e315c9b5a83775e747d 100644 (file)
@@ -696,9 +696,9 @@ xprt_schedule_autodisconnect(struct rpc_xprt *xprt)
 }
 
 static void
-xprt_init_autodisconnect(unsigned long data)
+xprt_init_autodisconnect(struct timer_list *t)
 {
-       struct rpc_xprt *xprt = (struct rpc_xprt *)data;
+       struct rpc_xprt *xprt = from_timer(xprt, t, timer);
 
        spin_lock(&xprt->transport_lock);
        if (!list_empty(&xprt->recv))
@@ -1344,7 +1344,7 @@ void xprt_release(struct rpc_task *task)
                rpc_count_iostats(task, task->tk_client->cl_metrics);
        spin_lock(&xprt->recv_lock);
        if (!list_empty(&req->rq_list)) {
-               list_del(&req->rq_list);
+               list_del_init(&req->rq_list);
                xprt_wait_on_pinned_rqst(req);
        }
        spin_unlock(&xprt->recv_lock);
@@ -1433,10 +1433,9 @@ found:
                xprt->idle_timeout = 0;
        INIT_WORK(&xprt->task_cleanup, xprt_autoclose);
        if (xprt_has_timer(xprt))
-               setup_timer(&xprt->timer, xprt_init_autodisconnect,
-                           (unsigned long)xprt);
+               timer_setup(&xprt->timer, xprt_init_autodisconnect, 0);
        else
-               init_timer(&xprt->timer);
+               timer_setup(&xprt->timer, NULL, 0);
 
        if (strlen(args->servername) > RPC_MAXNETNAMELEN) {
                xprt_destroy(xprt);
@@ -1456,6 +1455,23 @@ out:
        return xprt;
 }
 
+static void xprt_destroy_cb(struct work_struct *work)
+{
+       struct rpc_xprt *xprt =
+               container_of(work, struct rpc_xprt, task_cleanup);
+
+       rpc_xprt_debugfs_unregister(xprt);
+       rpc_destroy_wait_queue(&xprt->binding);
+       rpc_destroy_wait_queue(&xprt->pending);
+       rpc_destroy_wait_queue(&xprt->sending);
+       rpc_destroy_wait_queue(&xprt->backlog);
+       kfree(xprt->servername);
+       /*
+        * Tear down transport state and free the rpc_xprt
+        */
+       xprt->ops->destroy(xprt);
+}
+
 /**
  * xprt_destroy - destroy an RPC transport, killing off all requests.
  * @xprt: transport to destroy
@@ -1465,22 +1481,19 @@ static void xprt_destroy(struct rpc_xprt *xprt)
 {
        dprintk("RPC:       destroying transport %p\n", xprt);
 
-       /* Exclude transport connect/disconnect handlers */
+       /*
+        * Exclude transport connect/disconnect handlers and autoclose
+        */
        wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_UNINTERRUPTIBLE);
 
        del_timer_sync(&xprt->timer);
 
-       rpc_xprt_debugfs_unregister(xprt);
-       rpc_destroy_wait_queue(&xprt->binding);
-       rpc_destroy_wait_queue(&xprt->pending);
-       rpc_destroy_wait_queue(&xprt->sending);
-       rpc_destroy_wait_queue(&xprt->backlog);
-       cancel_work_sync(&xprt->task_cleanup);
-       kfree(xprt->servername);
        /*
-        * Tear down transport state and free the rpc_xprt
+        * Destroy sockets etc from the system workqueue so they can
+        * safely flush receive work running on rpciod.
         */
-       xprt->ops->destroy(xprt);
+       INIT_WORK(&xprt->task_cleanup, xprt_destroy_cb);
+       schedule_work(&xprt->task_cleanup);
 }
 
 static void xprt_destroy_kref(struct kref *kref)