diff options
author | David Howells <dhowells@redhat.com> | 2018-03-30 16:05:33 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2018-03-30 16:05:33 -0400 |
commit | 31f5f9a1691ebef2113c8bdb3edcb8859f30f702 (patch) | |
tree | 20de5ce61ea1a3aad4e58426e24b038203c53af8 | |
parent | 09d2bf595db4b4075ea721acd61e180d6bb18f88 (diff) |
rxrpc: Fix apparent leak of rxrpc_local objects
rxrpc_local objects cannot be disposed of until all the connections that
point to them have been RCU'd as a connection object holds refcount on the
local endpoint it is communicating through. Currently, this can cause an
assertion failure to occur when a network namespace is destroyed as there's
no check that the RCU destructors for the connections have been run before
we start trying to destroy local endpoints.
The kernel reports:
rxrpc: AF_RXRPC: Leaked local 0000000036a41bc1 {5}
------------[ cut here ]------------
kernel BUG at ../net/rxrpc/local_object.c:439!
Fix this by keeping a count of the live connections and waiting for it to
go to zero at the end of rxrpc_destroy_all_connections().
Fixes: dee46364ce6f ("rxrpc: Add RCU destruction for connections and calls")
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | net/rxrpc/ar-internal.h | 1 | ||||
-rw-r--r-- | net/rxrpc/call_accept.c | 2 | ||||
-rw-r--r-- | net/rxrpc/conn_client.c | 1 | ||||
-rw-r--r-- | net/rxrpc/conn_object.c | 8 | ||||
-rw-r--r-- | net/rxrpc/conn_service.c | 1 | ||||
-rw-r--r-- | net/rxrpc/net_ns.c | 1 |
6 files changed, 14 insertions, 0 deletions
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index cc51d3eb0548..d40d54b78567 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h | |||
@@ -77,6 +77,7 @@ struct rxrpc_net { | |||
77 | rwlock_t call_lock; /* Lock for ->calls */ | 77 | rwlock_t call_lock; /* Lock for ->calls */ |
78 | atomic_t nr_calls; /* Count of allocated calls */ | 78 | atomic_t nr_calls; /* Count of allocated calls */ |
79 | 79 | ||
80 | atomic_t nr_conns; | ||
80 | struct list_head conn_proc_list; /* List of conns in this namespace for proc */ | 81 | struct list_head conn_proc_list; /* List of conns in this namespace for proc */ |
81 | struct list_head service_conns; /* Service conns in this namespace */ | 82 | struct list_head service_conns; /* Service conns in this namespace */ |
82 | rwlock_t conn_lock; /* Lock for ->conn_proc_list, ->service_conns */ | 83 | rwlock_t conn_lock; /* Lock for ->conn_proc_list, ->service_conns */ |
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 5a9b1d916124..f67017dcb25e 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c | |||
@@ -219,6 +219,8 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx) | |||
219 | list_del(&conn->proc_link); | 219 | list_del(&conn->proc_link); |
220 | write_unlock(&rxnet->conn_lock); | 220 | write_unlock(&rxnet->conn_lock); |
221 | kfree(conn); | 221 | kfree(conn); |
222 | if (atomic_dec_and_test(&rxnet->nr_conns)) | ||
223 | wake_up_atomic_t(&rxnet->nr_conns); | ||
222 | tail = (tail + 1) & (size - 1); | 224 | tail = (tail + 1) & (size - 1); |
223 | } | 225 | } |
224 | 226 | ||
diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index 041da40dbf93..5736f643c516 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c | |||
@@ -207,6 +207,7 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp) | |||
207 | if (ret < 0) | 207 | if (ret < 0) |
208 | goto error_2; | 208 | goto error_2; |
209 | 209 | ||
210 | atomic_inc(&rxnet->nr_conns); | ||
210 | write_lock(&rxnet->conn_lock); | 211 | write_lock(&rxnet->conn_lock); |
211 | list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); | 212 | list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); |
212 | write_unlock(&rxnet->conn_lock); | 213 | write_unlock(&rxnet->conn_lock); |
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index bfc46fd69a62..0950ee3d26f5 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c | |||
@@ -365,6 +365,9 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu) | |||
365 | key_put(conn->params.key); | 365 | key_put(conn->params.key); |
366 | key_put(conn->server_key); | 366 | key_put(conn->server_key); |
367 | rxrpc_put_peer(conn->params.peer); | 367 | rxrpc_put_peer(conn->params.peer); |
368 | |||
369 | if (atomic_dec_and_test(&conn->params.local->rxnet->nr_conns)) | ||
370 | wake_up_atomic_t(&conn->params.local->rxnet->nr_conns); | ||
368 | rxrpc_put_local(conn->params.local); | 371 | rxrpc_put_local(conn->params.local); |
369 | 372 | ||
370 | kfree(conn); | 373 | kfree(conn); |
@@ -458,6 +461,7 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet) | |||
458 | 461 | ||
459 | _enter(""); | 462 | _enter(""); |
460 | 463 | ||
464 | atomic_dec(&rxnet->nr_conns); | ||
461 | rxrpc_destroy_all_client_connections(rxnet); | 465 | rxrpc_destroy_all_client_connections(rxnet); |
462 | 466 | ||
463 | del_timer_sync(&rxnet->service_conn_reap_timer); | 467 | del_timer_sync(&rxnet->service_conn_reap_timer); |
@@ -475,5 +479,9 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet) | |||
475 | 479 | ||
476 | ASSERT(list_empty(&rxnet->conn_proc_list)); | 480 | ASSERT(list_empty(&rxnet->conn_proc_list)); |
477 | 481 | ||
482 | /* We need to wait for the connections to be destroyed by RCU as they | ||
483 | * pin things that we still need to get rid of. | ||
484 | */ | ||
485 | wait_on_atomic_t(&rxnet->nr_conns, atomic_t_wait, TASK_UNINTERRUPTIBLE); | ||
478 | _leave(""); | 486 | _leave(""); |
479 | } | 487 | } |
diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index f6fcdb3130a1..80773a50c755 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c | |||
@@ -132,6 +132,7 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn | |||
132 | conn->state = RXRPC_CONN_SERVICE_PREALLOC; | 132 | conn->state = RXRPC_CONN_SERVICE_PREALLOC; |
133 | atomic_set(&conn->usage, 2); | 133 | atomic_set(&conn->usage, 2); |
134 | 134 | ||
135 | atomic_inc(&rxnet->nr_conns); | ||
135 | write_lock(&rxnet->conn_lock); | 136 | write_lock(&rxnet->conn_lock); |
136 | list_add_tail(&conn->link, &rxnet->service_conns); | 137 | list_add_tail(&conn->link, &rxnet->service_conns); |
137 | list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); | 138 | list_add_tail(&conn->proc_link, &rxnet->conn_proc_list); |
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index 101019b0be34..fa9ce60e7bfa 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c | |||
@@ -57,6 +57,7 @@ static __net_init int rxrpc_init_net(struct net *net) | |||
57 | rwlock_init(&rxnet->call_lock); | 57 | rwlock_init(&rxnet->call_lock); |
58 | atomic_set(&rxnet->nr_calls, 1); | 58 | atomic_set(&rxnet->nr_calls, 1); |
59 | 59 | ||
60 | atomic_set(&rxnet->nr_conns, 1); | ||
60 | INIT_LIST_HEAD(&rxnet->conn_proc_list); | 61 | INIT_LIST_HEAD(&rxnet->conn_proc_list); |
61 | INIT_LIST_HEAD(&rxnet->service_conns); | 62 | INIT_LIST_HEAD(&rxnet->service_conns); |
62 | rwlock_init(&rxnet->conn_lock); | 63 | rwlock_init(&rxnet->conn_lock); |