]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
rxrpc: Fix lack of conn cleanup when local endpoint is cleaned up [ver #2]
authorDavid Howells <dhowells@redhat.com>
Thu, 29 Aug 2019 13:12:11 +0000 (14:12 +0100)
committerKhalid Elmously <khalid.elmously@canonical.com>
Fri, 14 Feb 2020 05:29:37 +0000 (00:29 -0500)
BugLink: https://bugs.launchpad.net/bugs/1863019
[ Upstream commit d12040b6933f684a26773afad46dbba9778608d7 ]

When a local endpoint is ceases to be in use, such as when the kafs module
is unloaded, the kernel will emit an assertion failure if there are any
outstanding client connections:

rxrpc: Assertion failed
------------[ cut here ]------------
kernel BUG at net/rxrpc/local_object.c:433!

and even beyond that, will evince other oopses if there are service
connections still present.

Fix this by:

 (1) Removing the triggering of connection reaping when an rxrpc socket is
     released.  These don't actually clean up the connections anyway - and
     further, the local endpoint may still be in use through another
     socket.

 (2) Mark the local endpoint as dead when we start the process of tearing
     it down.

 (3) When destroying a local endpoint, strip all of its client connections
     from the idle list and discard the ref on each that the list was
     holding.

 (4) When destroying a local endpoint, call the service connection reaper
     directly (rather than through a workqueue) to immediately kill off all
     outstanding service connections.

 (5) Make the service connection reaper reap connections for which the
     local endpoint is marked dead.

Only after destroying the connections can we close the socket lest we get
an oops in a workqueue that's looking at a connection or a peer.

Fixes: 3d18cbb7fd0c ("rxrpc: Fix conn expiry timers")
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
net/rxrpc/af_rxrpc.c
net/rxrpc/ar-internal.h
net/rxrpc/conn_client.c
net/rxrpc/conn_object.c
net/rxrpc/local_object.c

index efd6e84c0e9ab208b15b645e5468b709ab86a3ab..3a074855342e7d32a1a5050ce491bca00874aa86 100644 (file)
@@ -860,7 +860,6 @@ static void rxrpc_sock_destructor(struct sock *sk)
 static int rxrpc_release_sock(struct sock *sk)
 {
        struct rxrpc_sock *rx = rxrpc_sk(sk);
-       struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk));
 
        _enter("%p{%d,%d}", sk, sk->sk_state, refcount_read(&sk->sk_refcnt));
 
@@ -896,8 +895,6 @@ static int rxrpc_release_sock(struct sock *sk)
        rxrpc_release_calls_on_socket(rx);
        flush_workqueue(rxrpc_workqueue);
        rxrpc_purge_queue(&sk->sk_receive_queue);
-       rxrpc_queue_work(&rxnet->service_conn_reaper);
-       rxrpc_queue_work(&rxnet->client_conn_reaper);
 
        rxrpc_put_local(rx->local);
        rx->local = NULL;
index bf2ba77a656f8688cfff908a05c8a1a8c3e4ee56..68322daf7c4baf546c3862fd01f44deb6a7aa75c 100644 (file)
@@ -887,6 +887,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *);
 void rxrpc_put_client_conn(struct rxrpc_connection *);
 void rxrpc_discard_expired_client_conns(struct work_struct *);
 void rxrpc_destroy_all_client_connections(struct rxrpc_net *);
+void rxrpc_clean_up_local_conns(struct rxrpc_local *);
 
 /*
  * conn_event.c
index 577eca09afa041b179ac938fb86a437c28152380..360ab6f30054bf5a9abaf2efc91108552efd9139 100644 (file)
@@ -1163,3 +1163,47 @@ void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet)
 
        _leave("");
 }
+
+/*
+ * Clean up the client connections on a local endpoint.
+ */
+void rxrpc_clean_up_local_conns(struct rxrpc_local *local)
+{
+       struct rxrpc_connection *conn, *tmp;
+       struct rxrpc_net *rxnet = local->rxnet;
+       unsigned int nr_active;
+       LIST_HEAD(graveyard);
+
+       _enter("");
+
+       spin_lock(&rxnet->client_conn_cache_lock);
+       nr_active = rxnet->nr_active_client_conns;
+
+       list_for_each_entry_safe(conn, tmp, &rxnet->idle_client_conns,
+                                cache_link) {
+               if (conn->params.local == local) {
+                       ASSERTCMP(conn->cache_state, ==, RXRPC_CONN_CLIENT_IDLE);
+
+                       trace_rxrpc_client(conn, -1, rxrpc_client_discard);
+                       if (!test_and_clear_bit(RXRPC_CONN_EXPOSED, &conn->flags))
+                               BUG();
+                       conn->cache_state = RXRPC_CONN_CLIENT_INACTIVE;
+                       list_move(&conn->cache_link, &graveyard);
+                       nr_active--;
+               }
+       }
+
+       rxnet->nr_active_client_conns = nr_active;
+       spin_unlock(&rxnet->client_conn_cache_lock);
+       ASSERTCMP(nr_active, >=, 0);
+
+       while (!list_empty(&graveyard)) {
+               conn = list_entry(graveyard.next,
+                                 struct rxrpc_connection, cache_link);
+               list_del_init(&conn->cache_link);
+
+               rxrpc_put_connection(conn);
+       }
+
+       _leave(" [culled]");
+}
index ed867cc84dfc2573d0ca0df59979e69ea40ab091..d6d326ee22e38028f789aacba5ab7197b615c86d 100644 (file)
@@ -388,7 +388,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work)
                if (conn->state == RXRPC_CONN_SERVICE_PREALLOC)
                        continue;
 
-               if (rxnet->live) {
+               if (rxnet->live && !conn->params.local->dead) {
                        idle_timestamp = READ_ONCE(conn->idle_timestamp);
                        expire_at = idle_timestamp + rxrpc_connection_expiry * HZ;
                        if (conn->params.local->service_closed)
index 383292adcac6dc4a2bccb12ce3281ca2fe5343b4..62f599fdb887f04055867df1b4c7ea7b1b5cffcc 100644 (file)
@@ -319,11 +319,14 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local)
        }
        local->dead = true;
 
+       local->dead = true;
+
        mutex_lock(&rxnet->local_mutex);
        list_del_init(&local->link);
        mutex_unlock(&rxnet->local_mutex);
 
-       ASSERT(RB_EMPTY_ROOT(&local->client_conns));
+       rxrpc_clean_up_local_conns(local);
+       rxrpc_service_connection_reaper(&rxnet->service_conn_reaper);
        ASSERT(!local->service);
 
        if (socket) {