Commit 3d18cbb7 authored by David Howells's avatar David Howells

rxrpc: Fix conn expiry timers

Fix the rxrpc connection expiry timers so that connections for closed
AF_RXRPC sockets get deleted in a more timely fashion, freeing up the
transport UDP port much more quickly.

 (1) Replace the delayed work items with work items plus timers so that
     timer_reduce() can be used to shorten them and so that the timer
     doesn't requeue the work item if the net namespace is dead.

 (2) Don't use queue_delayed_work() as that won't alter the timeout if the
     timer is already running.

 (3) Don't rearm the timers if the network namespace is dead.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent f859ab61
...@@ -895,6 +895,8 @@ static int rxrpc_release_sock(struct sock *sk) ...@@ -895,6 +895,8 @@ static int rxrpc_release_sock(struct sock *sk)
rxrpc_release_calls_on_socket(rx); rxrpc_release_calls_on_socket(rx);
flush_workqueue(rxrpc_workqueue); flush_workqueue(rxrpc_workqueue);
rxrpc_purge_queue(&sk->sk_receive_queue); rxrpc_purge_queue(&sk->sk_receive_queue);
rxrpc_queue_work(&rx->local->rxnet->service_conn_reaper);
rxrpc_queue_work(&rx->local->rxnet->client_conn_reaper);
rxrpc_put_local(rx->local); rxrpc_put_local(rx->local);
rx->local = NULL; rx->local = NULL;
......
...@@ -79,7 +79,8 @@ struct rxrpc_net { ...@@ -79,7 +79,8 @@ struct rxrpc_net {
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 */
struct delayed_work service_conn_reaper; struct work_struct service_conn_reaper;
struct timer_list service_conn_reap_timer;
unsigned int nr_client_conns; unsigned int nr_client_conns;
unsigned int nr_active_client_conns; unsigned int nr_active_client_conns;
...@@ -90,7 +91,8 @@ struct rxrpc_net { ...@@ -90,7 +91,8 @@ struct rxrpc_net {
struct list_head waiting_client_conns; struct list_head waiting_client_conns;
struct list_head active_client_conns; struct list_head active_client_conns;
struct list_head idle_client_conns; struct list_head idle_client_conns;
struct delayed_work client_conn_reaper; struct work_struct client_conn_reaper;
struct timer_list client_conn_reap_timer;
struct list_head local_endpoints; struct list_head local_endpoints;
struct mutex local_mutex; /* Lock for ->local_endpoints */ struct mutex local_mutex; /* Lock for ->local_endpoints */
......
...@@ -691,7 +691,7 @@ int rxrpc_connect_call(struct rxrpc_call *call, ...@@ -691,7 +691,7 @@ int rxrpc_connect_call(struct rxrpc_call *call,
_enter("{%d,%lx},", call->debug_id, call->user_call_ID); _enter("{%d,%lx},", call->debug_id, call->user_call_ID);
rxrpc_discard_expired_client_conns(&rxnet->client_conn_reaper.work); rxrpc_discard_expired_client_conns(&rxnet->client_conn_reaper);
rxrpc_cull_active_client_conns(rxnet); rxrpc_cull_active_client_conns(rxnet);
ret = rxrpc_get_client_conn(call, cp, srx, gfp); ret = rxrpc_get_client_conn(call, cp, srx, gfp);
...@@ -756,6 +756,18 @@ void rxrpc_expose_client_call(struct rxrpc_call *call) ...@@ -756,6 +756,18 @@ void rxrpc_expose_client_call(struct rxrpc_call *call)
} }
} }
/*
* Set the reap timer.
*/
static void rxrpc_set_client_reap_timer(struct rxrpc_net *rxnet)
{
unsigned long now = jiffies;
unsigned long reap_at = now + rxrpc_conn_idle_client_expiry;
if (rxnet->live)
timer_reduce(&rxnet->client_conn_reap_timer, reap_at);
}
/* /*
* Disconnect a client call. * Disconnect a client call.
*/ */
...@@ -896,9 +908,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call) ...@@ -896,9 +908,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
list_move_tail(&conn->cache_link, &rxnet->idle_client_conns); list_move_tail(&conn->cache_link, &rxnet->idle_client_conns);
if (rxnet->idle_client_conns.next == &conn->cache_link && if (rxnet->idle_client_conns.next == &conn->cache_link &&
!rxnet->kill_all_client_conns) !rxnet->kill_all_client_conns)
queue_delayed_work(rxrpc_workqueue, rxrpc_set_client_reap_timer(rxnet);
&rxnet->client_conn_reaper,
rxrpc_conn_idle_client_expiry);
} else { } else {
trace_rxrpc_client(conn, channel, rxrpc_client_to_inactive); trace_rxrpc_client(conn, channel, rxrpc_client_to_inactive);
conn->cache_state = RXRPC_CONN_CLIENT_INACTIVE; conn->cache_state = RXRPC_CONN_CLIENT_INACTIVE;
...@@ -1036,8 +1046,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work) ...@@ -1036,8 +1046,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
{ {
struct rxrpc_connection *conn; struct rxrpc_connection *conn;
struct rxrpc_net *rxnet = struct rxrpc_net *rxnet =
container_of(to_delayed_work(work), container_of(work, struct rxrpc_net, client_conn_reaper);
struct rxrpc_net, client_conn_reaper);
unsigned long expiry, conn_expires_at, now; unsigned long expiry, conn_expires_at, now;
unsigned int nr_conns; unsigned int nr_conns;
bool did_discard = false; bool did_discard = false;
...@@ -1116,9 +1125,8 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work) ...@@ -1116,9 +1125,8 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
*/ */
_debug("not yet"); _debug("not yet");
if (!rxnet->kill_all_client_conns) if (!rxnet->kill_all_client_conns)
queue_delayed_work(rxrpc_workqueue, timer_reduce(&rxnet->client_conn_reap_timer,
&rxnet->client_conn_reaper, conn_expires_at);
conn_expires_at - now);
out: out:
spin_unlock(&rxnet->client_conn_cache_lock); spin_unlock(&rxnet->client_conn_cache_lock);
...@@ -1138,9 +1146,9 @@ void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet) ...@@ -1138,9 +1146,9 @@ void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet)
rxnet->kill_all_client_conns = true; rxnet->kill_all_client_conns = true;
spin_unlock(&rxnet->client_conn_cache_lock); spin_unlock(&rxnet->client_conn_cache_lock);
cancel_delayed_work(&rxnet->client_conn_reaper); del_timer_sync(&rxnet->client_conn_reap_timer);
if (!queue_delayed_work(rxrpc_workqueue, &rxnet->client_conn_reaper, 0)) if (!rxrpc_queue_work(&rxnet->client_conn_reaper))
_debug("destroy: queue failed"); _debug("destroy: queue failed");
_leave(""); _leave("");
......
...@@ -310,22 +310,30 @@ rxrpc_get_connection_maybe(struct rxrpc_connection *conn) ...@@ -310,22 +310,30 @@ rxrpc_get_connection_maybe(struct rxrpc_connection *conn)
return conn; return conn;
} }
/*
* Set the service connection reap timer.
*/
static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet,
unsigned long reap_at)
{
if (rxnet->live)
timer_reduce(&rxnet->service_conn_reap_timer, reap_at);
}
/* /*
* Release a service connection * Release a service connection
*/ */
void rxrpc_put_service_conn(struct rxrpc_connection *conn) void rxrpc_put_service_conn(struct rxrpc_connection *conn)
{ {
struct rxrpc_net *rxnet;
const void *here = __builtin_return_address(0); const void *here = __builtin_return_address(0);
int n; int n;
n = atomic_dec_return(&conn->usage); n = atomic_dec_return(&conn->usage);
trace_rxrpc_conn(conn, rxrpc_conn_put_service, n, here); trace_rxrpc_conn(conn, rxrpc_conn_put_service, n, here);
ASSERTCMP(n, >=, 0); ASSERTCMP(n, >=, 0);
if (n == 1) { if (n == 1)
rxnet = conn->params.local->rxnet; rxrpc_set_service_reap_timer(conn->params.local->rxnet,
rxrpc_queue_delayed_work(&rxnet->service_conn_reaper, 0); jiffies + rxrpc_connection_expiry);
}
} }
/* /*
...@@ -362,8 +370,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) ...@@ -362,8 +370,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work)
{ {
struct rxrpc_connection *conn, *_p; struct rxrpc_connection *conn, *_p;
struct rxrpc_net *rxnet = struct rxrpc_net *rxnet =
container_of(to_delayed_work(work), container_of(work, struct rxrpc_net, service_conn_reaper);
struct rxrpc_net, service_conn_reaper);
unsigned long expire_at, earliest, idle_timestamp, now; unsigned long expire_at, earliest, idle_timestamp, now;
LIST_HEAD(graveyard); LIST_HEAD(graveyard);
...@@ -417,8 +424,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work) ...@@ -417,8 +424,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work)
if (earliest != now + MAX_JIFFY_OFFSET) { if (earliest != now + MAX_JIFFY_OFFSET) {
_debug("reschedule reaper %ld", (long)earliest - (long)now); _debug("reschedule reaper %ld", (long)earliest - (long)now);
ASSERT(time_after(earliest, now)); ASSERT(time_after(earliest, now));
rxrpc_queue_delayed_work(&rxnet->service_conn_reaper, rxrpc_set_service_reap_timer(rxnet, earliest);
earliest - now);
} }
while (!list_empty(&graveyard)) { while (!list_empty(&graveyard)) {
...@@ -446,8 +452,8 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet) ...@@ -446,8 +452,8 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
rxrpc_destroy_all_client_connections(rxnet); rxrpc_destroy_all_client_connections(rxnet);
cancel_delayed_work(&rxnet->client_conn_reaper); del_timer_sync(&rxnet->service_conn_reap_timer);
rxrpc_queue_delayed_work(&rxnet->client_conn_reaper, 0); rxrpc_queue_work(&rxnet->service_conn_reaper);
flush_workqueue(rxrpc_workqueue); flush_workqueue(rxrpc_workqueue);
write_lock(&rxnet->conn_lock); write_lock(&rxnet->conn_lock);
......
...@@ -14,6 +14,24 @@ ...@@ -14,6 +14,24 @@
unsigned int rxrpc_net_id; unsigned int rxrpc_net_id;
static void rxrpc_client_conn_reap_timeout(struct timer_list *timer)
{
struct rxrpc_net *rxnet =
container_of(timer, struct rxrpc_net, client_conn_reap_timer);
if (rxnet->live)
rxrpc_queue_work(&rxnet->client_conn_reaper);
}
static void rxrpc_service_conn_reap_timeout(struct timer_list *timer)
{
struct rxrpc_net *rxnet =
container_of(timer, struct rxrpc_net, service_conn_reap_timer);
if (rxnet->live)
rxrpc_queue_work(&rxnet->service_conn_reaper);
}
/* /*
* Initialise a per-network namespace record. * Initialise a per-network namespace record.
*/ */
...@@ -32,8 +50,10 @@ static __net_init int rxrpc_init_net(struct net *net) ...@@ -32,8 +50,10 @@ static __net_init int rxrpc_init_net(struct net *net)
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);
INIT_DELAYED_WORK(&rxnet->service_conn_reaper, INIT_WORK(&rxnet->service_conn_reaper,
rxrpc_service_connection_reaper); rxrpc_service_connection_reaper);
timer_setup(&rxnet->service_conn_reap_timer,
rxrpc_service_conn_reap_timeout, 0);
rxnet->nr_client_conns = 0; rxnet->nr_client_conns = 0;
rxnet->nr_active_client_conns = 0; rxnet->nr_active_client_conns = 0;
...@@ -43,8 +63,10 @@ static __net_init int rxrpc_init_net(struct net *net) ...@@ -43,8 +63,10 @@ static __net_init int rxrpc_init_net(struct net *net)
INIT_LIST_HEAD(&rxnet->waiting_client_conns); INIT_LIST_HEAD(&rxnet->waiting_client_conns);
INIT_LIST_HEAD(&rxnet->active_client_conns); INIT_LIST_HEAD(&rxnet->active_client_conns);
INIT_LIST_HEAD(&rxnet->idle_client_conns); INIT_LIST_HEAD(&rxnet->idle_client_conns);
INIT_DELAYED_WORK(&rxnet->client_conn_reaper, INIT_WORK(&rxnet->client_conn_reaper,
rxrpc_discard_expired_client_conns); rxrpc_discard_expired_client_conns);
timer_setup(&rxnet->client_conn_reap_timer,
rxrpc_client_conn_reap_timeout, 0);
INIT_LIST_HEAD(&rxnet->local_endpoints); INIT_LIST_HEAD(&rxnet->local_endpoints);
mutex_init(&rxnet->local_mutex); mutex_init(&rxnet->local_mutex);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment