Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rxrpc: Fix irq-disabled in local_bh_enable()

The rxrpc_assess_MTU_size() function calls down into the IP layer to find
out the MTU size for a route. When accepting an incoming call, this is
called from rxrpc_new_incoming_call() which holds interrupts disabled
across the code that calls down to it. Unfortunately, the IP layer uses
local_bh_enable() which, config dependent, throws a warning if IRQs are
enabled:

WARNING: CPU: 1 PID: 5544 at kernel/softirq.c:387 __local_bh_enable_ip+0x43/0xd0
...
RIP: 0010:__local_bh_enable_ip+0x43/0xd0
...
Call Trace:
<TASK>
rt_cache_route+0x7e/0xa0
rt_set_nexthop.isra.0+0x3b3/0x3f0
__mkroute_output+0x43a/0x460
ip_route_output_key_hash+0xf7/0x140
ip_route_output_flow+0x1b/0x90
rxrpc_assess_MTU_size.isra.0+0x2a0/0x590
rxrpc_new_incoming_peer+0x46/0x120
rxrpc_alloc_incoming_call+0x1b1/0x400
rxrpc_new_incoming_call+0x1da/0x5e0
rxrpc_input_packet+0x827/0x900
rxrpc_io_thread+0x403/0xb60
kthread+0x2f7/0x310
ret_from_fork+0x2a/0x230
ret_from_fork_asm+0x1a/0x30
...
hardirqs last enabled at (23): _raw_spin_unlock_irq+0x24/0x50
hardirqs last disabled at (24): _raw_read_lock_irq+0x17/0x70
softirqs last enabled at (0): copy_process+0xc61/0x2730
softirqs last disabled at (25): rt_add_uncached_list+0x3c/0x90

Fix this by moving the call to rxrpc_assess_MTU_size() out of
rxrpc_init_peer() and further up the stack where it can be done without
interrupts disabled.

It shouldn't be a problem for rxrpc_new_incoming_call() to do it after the
locks are dropped as pmtud is going to be performed by the I/O thread - and
we're in the I/O thread at this point.

Fixes: a2ea9a907260 ("rxrpc: Use irq-disabling spinlocks between app and I/O thread")
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Junvyyang, Tencent Zhuque Lab <zhuque@tencent.com>
cc: LePremierHomme <kwqcheii@proton.me>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
Link: https://patch.msgid.link/20250717074350.3767366-2-dhowells@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

David Howells and committed by
Jakub Kicinski
e4d28783 88b06e4f

+4 -4
+1
net/rxrpc/ar-internal.h
··· 1383 1383 const struct sockaddr_rxrpc *); 1384 1384 struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local, 1385 1385 struct sockaddr_rxrpc *srx, gfp_t gfp); 1386 + void rxrpc_assess_MTU_size(struct rxrpc_local *local, struct rxrpc_peer *peer); 1386 1387 struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t, 1387 1388 enum rxrpc_peer_trace); 1388 1389 void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer);
+1
net/rxrpc/call_accept.c
··· 406 406 407 407 spin_unlock(&rx->incoming_lock); 408 408 read_unlock_irq(&local->services_lock); 409 + rxrpc_assess_MTU_size(local, call->peer); 409 410 410 411 if (hlist_unhashed(&call->error_link)) { 411 412 spin_lock_irq(&call->peer->lock);
+2 -4
net/rxrpc/peer_object.c
··· 149 149 * assess the MTU size for the network interface through which this peer is 150 150 * reached 151 151 */ 152 - static void rxrpc_assess_MTU_size(struct rxrpc_local *local, 153 - struct rxrpc_peer *peer) 152 + void rxrpc_assess_MTU_size(struct rxrpc_local *local, struct rxrpc_peer *peer) 154 153 { 155 154 struct net *net = local->net; 156 155 struct dst_entry *dst; ··· 276 277 277 278 peer->hdrsize += sizeof(struct rxrpc_wire_header); 278 279 peer->max_data = peer->if_mtu - peer->hdrsize; 279 - 280 - rxrpc_assess_MTU_size(local, peer); 281 280 } 282 281 283 282 /* ··· 294 297 if (peer) { 295 298 memcpy(&peer->srx, srx, sizeof(*srx)); 296 299 rxrpc_init_peer(local, peer, hash_key); 300 + rxrpc_assess_MTU_size(local, peer); 297 301 } 298 302 299 303 _leave(" = %p", peer);