Skip to content

Commit 3cec055

Browse files
committed
rxrpc: Don't hold a ref for connection workqueue
Currently, rxrpc gives the connection's work item a ref on the connection when it queues it - and this is called from the timer expiration function. The problem comes when queue_work() fails (ie. the work item is already queued): the timer routine must put the ref - but this may cause the cleanup code to run. This has the unfortunate effect that the cleanup code may then be run in softirq context - which means that any spinlocks it might need to touch have to be guarded to disable softirqs (ie. they need a "_bh" suffix). (1) Don't give a ref to the work item. (2) Simplify handling of service connections by adding a separate active count so that the refcount isn't also used for this. (3) Connection destruction for both client and service connections can then be cleaned up by putting rxrpc_put_connection() out of line and making a tidy progression through the destruction code (offloaded to a workqueue if put from softirq or processor function context). The RCU part of the cleanup then only deals with the freeing at the end. (4) Make rxrpc_queue_conn() return immediately if it sees the active count is -1 rather then queuing the connection. (5) Make sure that the cleanup routine waits for the work item to complete. (6) Stash the rxrpc_net pointer in the conn struct so that the rcu free routine can use it, even if the local endpoint has been freed. Unfortunately, neither the timer nor the work item can simply get around the problem by just using refcount_inc_not_zero() as the waits would still have to be done, and there would still be the possibility of having to put the ref in the expiration function. Note the connection work item is mostly going to go away with the main event work being transferred to the I/O thread, so the wait in (6) will become obsolete. Signed-off-by: David Howells <[email protected]> cc: Marc Dionne <[email protected]> cc: [email protected]
1 parent 3feda9d commit 3cec055

File tree

9 files changed

+123
-129
lines changed

9 files changed

+123
-129
lines changed

include/trace/events/rxrpc.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@
112112
EM(rxrpc_conn_get_service_conn, "GET svc-conn") \
113113
EM(rxrpc_conn_new_client, "NEW client ") \
114114
EM(rxrpc_conn_new_service, "NEW service ") \
115-
EM(rxrpc_conn_put_already_queued, "PUT alreadyq") \
116115
EM(rxrpc_conn_put_call, "PUT call ") \
117116
EM(rxrpc_conn_put_call_input, "PUT inp-call") \
118117
EM(rxrpc_conn_put_conn_input, "PUT inp-conn") \
@@ -121,13 +120,13 @@
121120
EM(rxrpc_conn_put_local_dead, "PUT loc-dead") \
122121
EM(rxrpc_conn_put_noreuse, "PUT noreuse ") \
123122
EM(rxrpc_conn_put_poke, "PUT poke ") \
123+
EM(rxrpc_conn_put_service_reaped, "PUT svc-reap") \
124124
EM(rxrpc_conn_put_unbundle, "PUT unbundle") \
125125
EM(rxrpc_conn_put_unidle, "PUT unidle ") \
126-
EM(rxrpc_conn_put_work, "PUT work ") \
127-
EM(rxrpc_conn_queue_challenge, "GQ chall ") \
128-
EM(rxrpc_conn_queue_retry_work, "GQ retry-wk") \
129-
EM(rxrpc_conn_queue_rx_work, "GQ rx-work ") \
130-
EM(rxrpc_conn_queue_timer, "GQ timer ") \
126+
EM(rxrpc_conn_queue_challenge, "QUE chall ") \
127+
EM(rxrpc_conn_queue_retry_work, "QUE retry-wk") \
128+
EM(rxrpc_conn_queue_rx_work, "QUE rx-work ") \
129+
EM(rxrpc_conn_queue_timer, "QUE timer ") \
131130
EM(rxrpc_conn_see_new_service_conn, "SEE new-svc ") \
132131
EM(rxrpc_conn_see_reap_service, "SEE reap-svc") \
133132
E_(rxrpc_conn_see_work, "SEE work ")

net/rxrpc/ar-internal.h

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ struct rxrpc_net {
7676
bool kill_all_client_conns;
7777
atomic_t nr_client_conns;
7878
spinlock_t client_conn_cache_lock; /* Lock for ->*_client_conns */
79-
spinlock_t client_conn_discard_lock; /* Prevent multiple discarders */
79+
struct mutex client_conn_discard_lock; /* Prevent multiple discarders */
8080
struct list_head idle_client_conns;
8181
struct work_struct client_conn_reaper;
8282
struct timer_list client_conn_reap_timer;
@@ -432,9 +432,11 @@ struct rxrpc_connection {
432432
struct rxrpc_conn_proto proto;
433433
struct rxrpc_local *local; /* Representation of local endpoint */
434434
struct rxrpc_peer *peer; /* Remote endpoint */
435+
struct rxrpc_net *rxnet; /* Network namespace to which call belongs */
435436
struct key *key; /* Security details */
436437

437438
refcount_t ref;
439+
atomic_t active; /* Active count for service conns */
438440
struct rcu_head rcu;
439441
struct list_head cache_link;
440442

@@ -455,6 +457,7 @@ struct rxrpc_connection {
455457

456458
struct timer_list timer; /* Conn event timer */
457459
struct work_struct processor; /* connection event processor */
460+
struct work_struct destructor; /* In-process-context destroyer */
458461
struct rxrpc_bundle *bundle; /* Client connection bundle */
459462
struct rb_node service_node; /* Node in peer->service_conns */
460463
struct list_head proc_link; /* link in procfs list */
@@ -897,20 +900,20 @@ void rxrpc_process_delayed_final_acks(struct rxrpc_connection *, bool);
897900
extern unsigned int rxrpc_connection_expiry;
898901
extern unsigned int rxrpc_closed_conn_expiry;
899902

900-
struct rxrpc_connection *rxrpc_alloc_connection(gfp_t);
903+
struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *, gfp_t);
901904
struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *,
902905
struct sk_buff *,
903906
struct rxrpc_peer **);
904907
void __rxrpc_disconnect_call(struct rxrpc_connection *, struct rxrpc_call *);
905908
void rxrpc_disconnect_call(struct rxrpc_call *);
906-
void rxrpc_kill_connection(struct rxrpc_connection *);
907-
bool rxrpc_queue_conn(struct rxrpc_connection *, enum rxrpc_conn_trace);
909+
void rxrpc_kill_client_conn(struct rxrpc_connection *);
910+
void rxrpc_queue_conn(struct rxrpc_connection *, enum rxrpc_conn_trace);
908911
void rxrpc_see_connection(struct rxrpc_connection *, enum rxrpc_conn_trace);
909912
struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *,
910913
enum rxrpc_conn_trace);
911914
struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *,
912915
enum rxrpc_conn_trace);
913-
void rxrpc_put_service_conn(struct rxrpc_connection *, enum rxrpc_conn_trace);
916+
void rxrpc_put_connection(struct rxrpc_connection *, enum rxrpc_conn_trace);
914917
void rxrpc_service_connection_reaper(struct work_struct *);
915918
void rxrpc_destroy_all_connections(struct rxrpc_net *);
916919

@@ -924,18 +927,6 @@ static inline bool rxrpc_conn_is_service(const struct rxrpc_connection *conn)
924927
return !rxrpc_conn_is_client(conn);
925928
}
926929

927-
static inline void rxrpc_put_connection(struct rxrpc_connection *conn,
928-
enum rxrpc_conn_trace why)
929-
{
930-
if (!conn)
931-
return;
932-
933-
if (rxrpc_conn_is_client(conn))
934-
rxrpc_put_client_conn(conn, why);
935-
else
936-
rxrpc_put_service_conn(conn, why);
937-
}
938-
939930
static inline void rxrpc_reduce_conn_timer(struct rxrpc_connection *conn,
940931
unsigned long expire_at)
941932
{

net/rxrpc/call_accept.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
308308
rxrpc_new_incoming_connection(rx, conn, sec, skb);
309309
} else {
310310
rxrpc_get_connection(conn, rxrpc_conn_get_service_conn);
311+
atomic_inc(&conn->active);
311312
}
312313

313314
/* And now we can allocate and set up a new call */

net/rxrpc/conn_client.c

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle);
5151
static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn,
5252
gfp_t gfp)
5353
{
54-
struct rxrpc_net *rxnet = conn->local->rxnet;
54+
struct rxrpc_net *rxnet = conn->rxnet;
5555
int id;
5656

5757
_enter("");
@@ -179,7 +179,7 @@ rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp)
179179

180180
_enter("");
181181

182-
conn = rxrpc_alloc_connection(gfp);
182+
conn = rxrpc_alloc_connection(rxnet, gfp);
183183
if (!conn) {
184184
_leave(" = -ENOMEM");
185185
return ERR_PTR(-ENOMEM);
@@ -243,7 +243,7 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn)
243243
if (!conn)
244244
goto dont_reuse;
245245

246-
rxnet = conn->local->rxnet;
246+
rxnet = conn->rxnet;
247247
if (test_bit(RXRPC_CONN_DONT_REUSE, &conn->flags))
248248
goto dont_reuse;
249249

@@ -970,7 +970,7 @@ static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle)
970970
/*
971971
* Clean up a dead client connection.
972972
*/
973-
static void rxrpc_kill_client_conn(struct rxrpc_connection *conn)
973+
void rxrpc_kill_client_conn(struct rxrpc_connection *conn)
974974
{
975975
struct rxrpc_local *local = conn->local;
976976
struct rxrpc_net *rxnet = local->rxnet;
@@ -981,23 +981,6 @@ static void rxrpc_kill_client_conn(struct rxrpc_connection *conn)
981981
atomic_dec(&rxnet->nr_client_conns);
982982

983983
rxrpc_put_client_connection_id(conn);
984-
rxrpc_kill_connection(conn);
985-
}
986-
987-
/*
988-
* Clean up a dead client connections.
989-
*/
990-
void rxrpc_put_client_conn(struct rxrpc_connection *conn,
991-
enum rxrpc_conn_trace why)
992-
{
993-
unsigned int debug_id = conn->debug_id;
994-
bool dead;
995-
int r;
996-
997-
dead = __refcount_dec_and_test(&conn->ref, &r);
998-
trace_rxrpc_conn(debug_id, r - 1, why);
999-
if (dead)
1000-
rxrpc_kill_client_conn(conn);
1001984
}
1002985

1003986
/*
@@ -1023,7 +1006,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
10231006
}
10241007

10251008
/* Don't double up on the discarding */
1026-
if (!spin_trylock(&rxnet->client_conn_discard_lock)) {
1009+
if (!mutex_trylock(&rxnet->client_conn_discard_lock)) {
10271010
_leave(" [already]");
10281011
return;
10291012
}
@@ -1061,6 +1044,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
10611044
goto not_yet_expired;
10621045
}
10631046

1047+
atomic_dec(&conn->active);
10641048
trace_rxrpc_client(conn, -1, rxrpc_client_discard);
10651049
list_del_init(&conn->cache_link);
10661050

@@ -1087,7 +1071,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
10871071

10881072
out:
10891073
spin_unlock(&rxnet->client_conn_cache_lock);
1090-
spin_unlock(&rxnet->client_conn_discard_lock);
1074+
mutex_unlock(&rxnet->client_conn_discard_lock);
10911075
_leave("");
10921076
}
10931077

@@ -1127,6 +1111,7 @@ void rxrpc_clean_up_local_conns(struct rxrpc_local *local)
11271111
list_for_each_entry_safe(conn, tmp, &rxnet->idle_client_conns,
11281112
cache_link) {
11291113
if (conn->local == local) {
1114+
atomic_dec(&conn->active);
11301115
trace_rxrpc_client(conn, -1, rxrpc_client_discard);
11311116
list_move(&conn->cache_link, &graveyard);
11321117
}

net/rxrpc/conn_event.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,4 @@ void rxrpc_process_connection(struct work_struct *work)
478478
rxrpc_do_process_connection(conn);
479479
rxrpc_unuse_local(conn->local, rxrpc_local_unuse_conn_work);
480480
}
481-
482-
rxrpc_put_connection(conn, rxrpc_conn_put_work);
483-
_leave("");
484-
return;
485481
}

0 commit comments

Comments
 (0)