net/smc: release clcsock from tcp_listen_worker
authorUrsula Braun <ubraun@linux.vnet.ibm.com>
Fri, 26 Jan 2018 08:28:49 +0000 (09:28 +0100)
committerDavid S. Miller <davem@davemloft.net>
Fri, 26 Jan 2018 15:41:56 +0000 (10:41 -0500)
Closing a listen socket may hit the warning
WARN_ON(sock_owned_by_user(sk)) of tcp_close(), if the wake up of
the smc_tcp_listen_worker has not yet finished.
This patch introduces smc_close_wait_listen_clcsock() making sure
the listening internal clcsock has been closed in smc_tcp_listen_work(),
before the listening external SMC socket finishes closing.

Signed-off-by: Ursula Braun <ubraun@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/smc/af_smc.c
net/smc/smc_close.c

index 732a37ddbc21492dcb578c8322487e4625cd6ccb..267e6837911068000fbce7edbd48919c1b54304c 100644 (file)
@@ -670,6 +670,10 @@ struct sock *smc_accept_dequeue(struct sock *parent,
 
                smc_accept_unlink(new_sk);
                if (new_sk->sk_state == SMC_CLOSED) {
+                       if (isk->clcsock) {
+                               sock_release(isk->clcsock);
+                               isk->clcsock = NULL;
+                       }
                        new_sk->sk_prot->unhash(new_sk);
                        sock_put(new_sk); /* final */
                        continue;
@@ -969,8 +973,15 @@ static void smc_tcp_listen_work(struct work_struct *work)
        }
 
 out:
+       if (lsmc->clcsock) {
+               sock_release(lsmc->clcsock);
+               lsmc->clcsock = NULL;
+       }
        release_sock(lsk);
-       lsk->sk_data_ready(lsk); /* no more listening, wake accept */
+       /* no more listening, wake up smc_close_wait_listen_clcsock and
+        * accept
+        */
+       lsk->sk_state_change(lsk);
        sock_put(&lsmc->sk); /* sock_hold in smc_listen */
 }
 
index 4339852a8910bb241464969ab2aa0ab8ab047b2c..e339c0186dcfc2990d50c0b733e050a10f24b661 100644 (file)
@@ -19,6 +19,8 @@
 #include "smc_cdc.h"
 #include "smc_close.h"
 
+#define SMC_CLOSE_WAIT_LISTEN_CLCSOCK_TIME     (5 * HZ)
+
 static void smc_close_cleanup_listen(struct sock *parent)
 {
        struct sock *sk;
@@ -28,6 +30,27 @@ static void smc_close_cleanup_listen(struct sock *parent)
                smc_close_non_accepted(sk);
 }
 
+static void smc_close_wait_listen_clcsock(struct smc_sock *smc)
+{
+       DEFINE_WAIT_FUNC(wait, woken_wake_function);
+       struct sock *sk = &smc->sk;
+       signed long timeout;
+
+       timeout = SMC_CLOSE_WAIT_LISTEN_CLCSOCK_TIME;
+       add_wait_queue(sk_sleep(sk), &wait);
+       do {
+               release_sock(sk);
+               if (smc->clcsock)
+                       timeout = wait_woken(&wait, TASK_UNINTERRUPTIBLE,
+                                            timeout);
+               sched_annotate_sleep();
+               lock_sock(sk);
+               if (!smc->clcsock)
+                       break;
+       } while (timeout);
+       remove_wait_queue(sk_sleep(sk), &wait);
+}
+
 /* wait for sndbuf data being transmitted */
 static void smc_close_stream_wait(struct smc_sock *smc, long timeout)
 {
@@ -114,7 +137,6 @@ static void smc_close_active_abort(struct smc_sock *smc)
                break;
        case SMC_APPCLOSEWAIT1:
        case SMC_APPCLOSEWAIT2:
-               sock_release(smc->clcsock);
                if (!smc_cdc_rxed_any_close(&smc->conn))
                        sk->sk_state = SMC_PEERABORTWAIT;
                else
@@ -128,7 +150,6 @@ static void smc_close_active_abort(struct smc_sock *smc)
                if (!txflags->peer_conn_closed) {
                        /* just SHUTDOWN_SEND done */
                        sk->sk_state = SMC_PEERABORTWAIT;
-                       sock_release(smc->clcsock);
                } else {
                        sk->sk_state = SMC_CLOSED;
                }
@@ -136,8 +157,6 @@ static void smc_close_active_abort(struct smc_sock *smc)
                break;
        case SMC_PROCESSABORT:
        case SMC_APPFINCLOSEWAIT:
-               if (!txflags->peer_conn_closed)
-                       sock_release(smc->clcsock);
                sk->sk_state = SMC_CLOSED;
                break;
        case SMC_PEERFINCLOSEWAIT:
@@ -177,8 +196,6 @@ again:
        switch (sk->sk_state) {
        case SMC_INIT:
                sk->sk_state = SMC_CLOSED;
-               if (smc->smc_listen_work.func)
-                       cancel_work_sync(&smc->smc_listen_work);
                break;
        case SMC_LISTEN:
                sk->sk_state = SMC_CLOSED;
@@ -187,11 +204,9 @@ again:
                        rc = kernel_sock_shutdown(smc->clcsock, SHUT_RDWR);
                        /* wake up kernel_accept of smc_tcp_listen_worker */
                        smc->clcsock->sk->sk_data_ready(smc->clcsock->sk);
+                       smc_close_wait_listen_clcsock(smc);
                }
-               release_sock(sk);
                smc_close_cleanup_listen(sk);
-               cancel_work_sync(&smc->smc_listen_work);
-               lock_sock(sk);
                break;
        case SMC_ACTIVE:
                smc_close_stream_wait(smc, timeout);