diff options
author | David Howells <dhowells@redhat.com> | 2016-06-27 17:11:19 +0100 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2016-07-06 10:43:51 +0100 |
commit | dee46364ce6fd0815ad9da625783eda21ccf7b06 (patch) | |
tree | 1d4b7d3d3aa9198475f272bbc2ce8be81368b9ad /net/rxrpc/conn_object.c | |
parent | e653cfe49cec540529217933e07caf6c0f25ac93 (diff) |
rxrpc: Add RCU destruction for connections and calls
Add RCU destruction for connections and calls as the RCU lookup from the
transport socket data_ready handler is going to come along shortly.
Whilst we're at it, move the cleanup workqueue flushing and RCU barrierage
into the destruction code for the objects that need it (locals and
connections) and add the extra RCU barrier required for connection cleanup.
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'net/rxrpc/conn_object.c')
-rw-r--r-- | net/rxrpc/conn_object.c | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 99d18107421f..0165a629388b 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -542,7 +542,7 @@ void rxrpc_disconnect_call(struct rxrpc_call *call) spin_lock(&conn->channel_lock); - if (conn->channels[chan] == call) { + if (rcu_access_pointer(conn->channels[chan]) == call) { rcu_assign_pointer(conn->channels[chan], NULL); atomic_inc(&conn->avail_chans); wake_up(&conn->channel_wq); @@ -580,9 +580,12 @@ void rxrpc_put_connection(struct rxrpc_connection *conn) /* * destroy a virtual connection */ -static void rxrpc_destroy_connection(struct rxrpc_connection *conn) +static void rxrpc_destroy_connection(struct rcu_head *rcu) { - _enter("%p{%d}", conn, atomic_read(&conn->usage)); + struct rxrpc_connection *conn = + container_of(rcu, struct rxrpc_connection, rcu); + + _enter("{%d,u=%d}", conn->debug_id, atomic_read(&conn->usage)); ASSERTCMP(atomic_read(&conn->usage), ==, 0); @@ -677,7 +680,8 @@ static void rxrpc_connection_reaper(struct work_struct *work) list_del_init(&conn->link); ASSERTCMP(atomic_read(&conn->usage), ==, 0); - rxrpc_destroy_connection(conn); + skb_queue_purge(&conn->rx_queue); + call_rcu(&conn->rcu, rxrpc_destroy_connection); } _leave(""); @@ -689,11 +693,30 @@ static void rxrpc_connection_reaper(struct work_struct *work) */ void __exit rxrpc_destroy_all_connections(void) { + struct rxrpc_connection *conn, *_p; + bool leak = false; + _enter(""); rxrpc_connection_expiry = 0; cancel_delayed_work(&rxrpc_connection_reap); rxrpc_queue_delayed_work(&rxrpc_connection_reap, 0); + flush_workqueue(rxrpc_workqueue); + + write_lock(&rxrpc_connection_lock); + list_for_each_entry_safe(conn, _p, &rxrpc_connections, link) { + pr_err("AF_RXRPC: Leaked conn %p {%d}\n", + conn, atomic_read(&conn->usage)); + leak = true; + } + write_unlock(&rxrpc_connection_lock); + BUG_ON(leak); + + /* Make sure the local and peer records pinned by any dying connections + * are released. + */ + rcu_barrier(); + rxrpc_destroy_client_conn_ids(); _leave(""); } |