rxrpc: Fix the putting of client connections
authorDavid Howells <dhowells@redhat.com>
Sat, 17 Sep 2016 09:49:12 +0000 (10:49 +0100)
committerDavid Howells <dhowells@redhat.com>
Sat, 17 Sep 2016 09:53:20 +0000 (10:53 +0100)
In rxrpc_put_one_client_conn(), if a connection has RXRPC_CONN_COUNTED set
on it, then it's accounted for in rxrpc_nr_client_conns and may be on
various lists - and this is cleaned up correctly.

However, if the connection doesn't have RXRPC_CONN_COUNTED set on it, then
the put routine returns rather than just skipping the extra bit of cleanup.

Fix this by making the extra bit of clean up conditional instead and always
killing off the connection.

This manifests itself as connections with a zero usage count hanging around
in /proc/net/rxrpc_conns because the connection allocated, but discarded,
due to a race with another process that set up a parallel connection, which
was then shared instead.

Signed-off-by: David Howells <dhowells@redhat.com>
net/rxrpc/conn_client.c

index 9344a8416ceb4cc4ca9a3682a2a3ce4e04c229f5..5a675c43cace11f3658a2d274d5c108de5b46c35 100644 (file)
@@ -818,7 +818,7 @@ idle_connection:
 static struct rxrpc_connection *
 rxrpc_put_one_client_conn(struct rxrpc_connection *conn)
 {
-       struct rxrpc_connection *next;
+       struct rxrpc_connection *next = NULL;
        struct rxrpc_local *local = conn->params.local;
        unsigned int nr_conns;
 
@@ -834,24 +834,22 @@ rxrpc_put_one_client_conn(struct rxrpc_connection *conn)
 
        ASSERTCMP(conn->cache_state, ==, RXRPC_CONN_CLIENT_INACTIVE);
 
-       if (!test_bit(RXRPC_CONN_COUNTED, &conn->flags))
-               return NULL;
-
-       spin_lock(&rxrpc_client_conn_cache_lock);
-       nr_conns = --rxrpc_nr_client_conns;
+       if (test_bit(RXRPC_CONN_COUNTED, &conn->flags)) {
+               spin_lock(&rxrpc_client_conn_cache_lock);
+               nr_conns = --rxrpc_nr_client_conns;
+
+               if (nr_conns < rxrpc_max_client_connections &&
+                   !list_empty(&rxrpc_waiting_client_conns)) {
+                       next = list_entry(rxrpc_waiting_client_conns.next,
+                                         struct rxrpc_connection, cache_link);
+                       rxrpc_get_connection(next);
+                       rxrpc_activate_conn(next);
+               }
 
-       next = NULL;
-       if (nr_conns < rxrpc_max_client_connections &&
-           !list_empty(&rxrpc_waiting_client_conns)) {
-               next = list_entry(rxrpc_waiting_client_conns.next,
-                                 struct rxrpc_connection, cache_link);
-               rxrpc_get_connection(next);
-               rxrpc_activate_conn(next);
+               spin_unlock(&rxrpc_client_conn_cache_lock);
        }
 
-       spin_unlock(&rxrpc_client_conn_cache_lock);
        rxrpc_kill_connection(conn);
-
        if (next)
                rxrpc_activate_channels(next);