mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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: David Howells <dhowells@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									f859ab6187
								
							
						
					
					
						commit
						3d18cbb7fd
					
				
					 5 changed files with 68 additions and 28 deletions
				
			
		| 
						 | 
				
			
			@ -895,6 +895,8 @@ static int rxrpc_release_sock(struct sock *sk)
 | 
			
		|||
	rxrpc_release_calls_on_socket(rx);
 | 
			
		||||
	flush_workqueue(rxrpc_workqueue);
 | 
			
		||||
	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);
 | 
			
		||||
	rx->local = NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,8 @@ struct rxrpc_net {
 | 
			
		|||
	struct list_head	conn_proc_list;	/* List of conns in this namespace for proc */
 | 
			
		||||
	struct list_head	service_conns;	/* Service conns in this namespace */
 | 
			
		||||
	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_active_client_conns;
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +91,8 @@ struct rxrpc_net {
 | 
			
		|||
	struct list_head	waiting_client_conns;
 | 
			
		||||
	struct list_head	active_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 mutex		local_mutex;	/* Lock for ->local_endpoints */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -691,7 +691,7 @@ int rxrpc_connect_call(struct rxrpc_call *call,
 | 
			
		|||
 | 
			
		||||
	_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);
 | 
			
		||||
 | 
			
		||||
	ret = rxrpc_get_client_conn(call, cp, srx, gfp);
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -896,9 +908,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_call *call)
 | 
			
		|||
		list_move_tail(&conn->cache_link, &rxnet->idle_client_conns);
 | 
			
		||||
		if (rxnet->idle_client_conns.next == &conn->cache_link &&
 | 
			
		||||
		    !rxnet->kill_all_client_conns)
 | 
			
		||||
			queue_delayed_work(rxrpc_workqueue,
 | 
			
		||||
					   &rxnet->client_conn_reaper,
 | 
			
		||||
					   rxrpc_conn_idle_client_expiry);
 | 
			
		||||
			rxrpc_set_client_reap_timer(rxnet);
 | 
			
		||||
	} else {
 | 
			
		||||
		trace_rxrpc_client(conn, channel, rxrpc_client_to_inactive);
 | 
			
		||||
		conn->cache_state = RXRPC_CONN_CLIENT_INACTIVE;
 | 
			
		||||
| 
						 | 
				
			
			@ -1036,8 +1046,7 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
 | 
			
		|||
{
 | 
			
		||||
	struct rxrpc_connection *conn;
 | 
			
		||||
	struct rxrpc_net *rxnet =
 | 
			
		||||
		container_of(to_delayed_work(work),
 | 
			
		||||
			     struct rxrpc_net, client_conn_reaper);
 | 
			
		||||
		container_of(work, struct rxrpc_net, client_conn_reaper);
 | 
			
		||||
	unsigned long expiry, conn_expires_at, now;
 | 
			
		||||
	unsigned int nr_conns;
 | 
			
		||||
	bool did_discard = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -1116,9 +1125,8 @@ void rxrpc_discard_expired_client_conns(struct work_struct *work)
 | 
			
		|||
	 */
 | 
			
		||||
	_debug("not yet");
 | 
			
		||||
	if (!rxnet->kill_all_client_conns)
 | 
			
		||||
		queue_delayed_work(rxrpc_workqueue,
 | 
			
		||||
				   &rxnet->client_conn_reaper,
 | 
			
		||||
				   conn_expires_at - now);
 | 
			
		||||
		timer_reduce(&rxnet->client_conn_reap_timer,
 | 
			
		||||
			     conn_expires_at);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	spin_unlock(&rxnet->client_conn_cache_lock);
 | 
			
		||||
| 
						 | 
				
			
			@ -1138,9 +1146,9 @@ void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet)
 | 
			
		|||
	rxnet->kill_all_client_conns = true;
 | 
			
		||||
	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");
 | 
			
		||||
 | 
			
		||||
	_leave("");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -310,22 +310,30 @@ rxrpc_get_connection_maybe(struct rxrpc_connection *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
 | 
			
		||||
 */
 | 
			
		||||
void rxrpc_put_service_conn(struct rxrpc_connection *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct rxrpc_net *rxnet;
 | 
			
		||||
	const void *here = __builtin_return_address(0);
 | 
			
		||||
	int n;
 | 
			
		||||
 | 
			
		||||
	n = atomic_dec_return(&conn->usage);
 | 
			
		||||
	trace_rxrpc_conn(conn, rxrpc_conn_put_service, n, here);
 | 
			
		||||
	ASSERTCMP(n, >=, 0);
 | 
			
		||||
	if (n == 1) {
 | 
			
		||||
		rxnet = conn->params.local->rxnet;
 | 
			
		||||
		rxrpc_queue_delayed_work(&rxnet->service_conn_reaper, 0);
 | 
			
		||||
	}
 | 
			
		||||
	if (n == 1)
 | 
			
		||||
		rxrpc_set_service_reap_timer(conn->params.local->rxnet,
 | 
			
		||||
					     jiffies + rxrpc_connection_expiry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -362,8 +370,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work)
 | 
			
		|||
{
 | 
			
		||||
	struct rxrpc_connection *conn, *_p;
 | 
			
		||||
	struct rxrpc_net *rxnet =
 | 
			
		||||
		container_of(to_delayed_work(work),
 | 
			
		||||
			     struct rxrpc_net, service_conn_reaper);
 | 
			
		||||
		container_of(work, struct rxrpc_net, service_conn_reaper);
 | 
			
		||||
	unsigned long expire_at, earliest, idle_timestamp, now;
 | 
			
		||||
 | 
			
		||||
	LIST_HEAD(graveyard);
 | 
			
		||||
| 
						 | 
				
			
			@ -417,8 +424,7 @@ void rxrpc_service_connection_reaper(struct work_struct *work)
 | 
			
		|||
	if (earliest != now + MAX_JIFFY_OFFSET) {
 | 
			
		||||
		_debug("reschedule reaper %ld", (long)earliest - (long)now);
 | 
			
		||||
		ASSERT(time_after(earliest, now));
 | 
			
		||||
		rxrpc_queue_delayed_work(&rxnet->service_conn_reaper,
 | 
			
		||||
					 earliest - now);
 | 
			
		||||
		rxrpc_set_service_reap_timer(rxnet, earliest);		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (!list_empty(&graveyard)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -446,8 +452,8 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
 | 
			
		|||
 | 
			
		||||
	rxrpc_destroy_all_client_connections(rxnet);
 | 
			
		||||
 | 
			
		||||
	cancel_delayed_work(&rxnet->client_conn_reaper);
 | 
			
		||||
	rxrpc_queue_delayed_work(&rxnet->client_conn_reaper, 0);
 | 
			
		||||
	del_timer_sync(&rxnet->service_conn_reap_timer);
 | 
			
		||||
	rxrpc_queue_work(&rxnet->service_conn_reaper);
 | 
			
		||||
	flush_workqueue(rxrpc_workqueue);
 | 
			
		||||
 | 
			
		||||
	write_lock(&rxnet->conn_lock);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,24 @@
 | 
			
		|||
 | 
			
		||||
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.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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->service_conns);
 | 
			
		||||
	rwlock_init(&rxnet->conn_lock);
 | 
			
		||||
	INIT_DELAYED_WORK(&rxnet->service_conn_reaper,
 | 
			
		||||
			  rxrpc_service_connection_reaper);
 | 
			
		||||
	INIT_WORK(&rxnet->service_conn_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_active_client_conns = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -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->active_client_conns);
 | 
			
		||||
	INIT_LIST_HEAD(&rxnet->idle_client_conns);
 | 
			
		||||
	INIT_DELAYED_WORK(&rxnet->client_conn_reaper,
 | 
			
		||||
			  rxrpc_discard_expired_client_conns);
 | 
			
		||||
	INIT_WORK(&rxnet->client_conn_reaper,
 | 
			
		||||
		  rxrpc_discard_expired_client_conns);
 | 
			
		||||
	timer_setup(&rxnet->client_conn_reap_timer,
 | 
			
		||||
		    rxrpc_client_conn_reap_timeout, 0);
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&rxnet->local_endpoints);
 | 
			
		||||
	mutex_init(&rxnet->local_mutex);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue