forked from luck/tmp_suning_uos_patched
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: dee46364ce
("rxrpc: Add RCU destruction for connections and calls")
Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
parent
09d2bf595d
commit
31f5f9a169
|
@ -77,6 +77,7 @@ struct rxrpc_net {
|
||||||
rwlock_t call_lock; /* Lock for ->calls */
|
rwlock_t call_lock; /* Lock for ->calls */
|
||||||
atomic_t nr_calls; /* Count of allocated calls */
|
atomic_t nr_calls; /* Count of allocated calls */
|
||||||
|
|
||||||
|
atomic_t nr_conns;
|
||||||
struct list_head conn_proc_list; /* List of conns in this namespace for proc */
|
struct list_head conn_proc_list; /* List of conns in this namespace for proc */
|
||||||
struct list_head service_conns; /* Service conns in this namespace */
|
struct list_head service_conns; /* Service conns in this namespace */
|
||||||
rwlock_t conn_lock; /* Lock for ->conn_proc_list, ->service_conns */
|
rwlock_t conn_lock; /* Lock for ->conn_proc_list, ->service_conns */
|
||||||
|
|
|
@ -219,6 +219,8 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx)
|
||||||
list_del(&conn->proc_link);
|
list_del(&conn->proc_link);
|
||||||
write_unlock(&rxnet->conn_lock);
|
write_unlock(&rxnet->conn_lock);
|
||||||
kfree(conn);
|
kfree(conn);
|
||||||
|
if (atomic_dec_and_test(&rxnet->nr_conns))
|
||||||
|
wake_up_atomic_t(&rxnet->nr_conns);
|
||||||
tail = (tail + 1) & (size - 1);
|
tail = (tail + 1) & (size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,6 +207,7 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error_2;
|
goto error_2;
|
||||||
|
|
||||||
|
atomic_inc(&rxnet->nr_conns);
|
||||||
write_lock(&rxnet->conn_lock);
|
write_lock(&rxnet->conn_lock);
|
||||||
list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
|
list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
|
||||||
write_unlock(&rxnet->conn_lock);
|
write_unlock(&rxnet->conn_lock);
|
||||||
|
|
|
@ -365,6 +365,9 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu)
|
||||||
key_put(conn->params.key);
|
key_put(conn->params.key);
|
||||||
key_put(conn->server_key);
|
key_put(conn->server_key);
|
||||||
rxrpc_put_peer(conn->params.peer);
|
rxrpc_put_peer(conn->params.peer);
|
||||||
|
|
||||||
|
if (atomic_dec_and_test(&conn->params.local->rxnet->nr_conns))
|
||||||
|
wake_up_atomic_t(&conn->params.local->rxnet->nr_conns);
|
||||||
rxrpc_put_local(conn->params.local);
|
rxrpc_put_local(conn->params.local);
|
||||||
|
|
||||||
kfree(conn);
|
kfree(conn);
|
||||||
|
@ -458,6 +461,7 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
|
||||||
|
|
||||||
_enter("");
|
_enter("");
|
||||||
|
|
||||||
|
atomic_dec(&rxnet->nr_conns);
|
||||||
rxrpc_destroy_all_client_connections(rxnet);
|
rxrpc_destroy_all_client_connections(rxnet);
|
||||||
|
|
||||||
del_timer_sync(&rxnet->service_conn_reap_timer);
|
del_timer_sync(&rxnet->service_conn_reap_timer);
|
||||||
|
@ -475,5 +479,9 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
|
||||||
|
|
||||||
ASSERT(list_empty(&rxnet->conn_proc_list));
|
ASSERT(list_empty(&rxnet->conn_proc_list));
|
||||||
|
|
||||||
|
/* We need to wait for the connections to be destroyed by RCU as they
|
||||||
|
* pin things that we still need to get rid of.
|
||||||
|
*/
|
||||||
|
wait_on_atomic_t(&rxnet->nr_conns, atomic_t_wait, TASK_UNINTERRUPTIBLE);
|
||||||
_leave("");
|
_leave("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,7 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn
|
||||||
conn->state = RXRPC_CONN_SERVICE_PREALLOC;
|
conn->state = RXRPC_CONN_SERVICE_PREALLOC;
|
||||||
atomic_set(&conn->usage, 2);
|
atomic_set(&conn->usage, 2);
|
||||||
|
|
||||||
|
atomic_inc(&rxnet->nr_conns);
|
||||||
write_lock(&rxnet->conn_lock);
|
write_lock(&rxnet->conn_lock);
|
||||||
list_add_tail(&conn->link, &rxnet->service_conns);
|
list_add_tail(&conn->link, &rxnet->service_conns);
|
||||||
list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
|
list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
|
||||||
|
|
|
@ -57,6 +57,7 @@ static __net_init int rxrpc_init_net(struct net *net)
|
||||||
rwlock_init(&rxnet->call_lock);
|
rwlock_init(&rxnet->call_lock);
|
||||||
atomic_set(&rxnet->nr_calls, 1);
|
atomic_set(&rxnet->nr_calls, 1);
|
||||||
|
|
||||||
|
atomic_set(&rxnet->nr_conns, 1);
|
||||||
INIT_LIST_HEAD(&rxnet->conn_proc_list);
|
INIT_LIST_HEAD(&rxnet->conn_proc_list);
|
||||||
INIT_LIST_HEAD(&rxnet->service_conns);
|
INIT_LIST_HEAD(&rxnet->service_conns);
|
||||||
rwlock_init(&rxnet->conn_lock);
|
rwlock_init(&rxnet->conn_lock);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user