gtp: disable BH before calling udp_tunnel_xmit_skb()
Subsystems (4)
PATCH 1/1 gtp: disable BH before calling udp_tunnel_xmit_skb()
gtp_genl_send_echo_req() runs as a generic netlink doit handler in process context with BH not disabled. It calls udp_tunnel_xmit_skb(), which eventually invokes iptunnel_xmit() — that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.
Without local_bh_disable(), the task may migrate between dev_xmit_recursion_inc() and dev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positive SKB_DROP_REASON_RECURSION_LIMIT drops on either CPU.
The other udp_tunnel_xmit_skb() call sites in gtp.c are unaffected:
the data path runs under ndo_start_xmit and the echo response handlers run from the UDP encap rx softirq, both with BH already disabled.
Fix it by disabling BH around the udp_tunnel_xmit_skb() call, mirroring commit 2cd7e6971fc2 ("sctp: disable BH before calling udp_tunnel_xmit_skb()").
--- drivers/net/gtp.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/gtp.c +2 -0
@@ -2400,6 +2400,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info)return -ENODEV ; } +local_bh_disable (); udp_tunnel_xmit_skb(rt, sk, skb_to_send, fl4.saddr, fl4.daddr, inet_dscp_to_dsfield(fl4.flowi4_dscp),
@@ -2409,6 +2410,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) !net_eq (sock_net (sk),dev_net (gtp->dev)), false, 0); +local_bh_enable ();return 0 ; }
Signature · 1 line
2.53.0
Quoted 11 lines · David Carlier
gtp_genl_send_echo_req()runs as a generic netlink doit handler in process context with BH not disabled. It callsudp_tunnel_xmit_skb(), which eventually invokesiptunnel_xmit()— that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.Without
local_bh_disable(), the task may migrate betweendev_xmit_recursion_inc()anddev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positiveSKB_DROP_REASON_RECURSION_LIMITdrops on either CPU.
Quoted 7 lines
The other
udp_tunnel_xmit_skb()call sites ingtp.care unaffected:
the data path runs underndo_start_xmitand the echo response handlers run from the UDP encap rx softirq, both with BH already disabled.Fix it by disabling BH around the
udp_tunnel_xmit_skb()call, mirroring commit2cd7e6971fc2("sctp: disable BH before callingudp_tunnel_xmit_skb()").
Why not fix iptunnel_xmit() directly, rather than fixing all possible callers? Basically, jut like we did for lwtunnel_{output|xmit}(). The advantage would be that we no longer have to worry about BHs in the callers, and BHs would only be disabled when necessary.
Hi Julian,
Quoted 17 lines · Justin Iurman
Quoted 15 lines · David Carlier
gtp_genl_send_echo_req()runs as a generic netlink doit handler in process context with BH not disabled. It callsudp_tunnel_xmit_skb(), which eventually invokesiptunnel_xmit()— that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.Without
local_bh_disable(), the task may migrate betweendev_xmit_recursion_inc()anddev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positiveSKB_DROP_REASON_RECURSION_LIMITdrops on either CPU.The other
udp_tunnel_xmit_skb()call sites ingtp.care unaffected:
the data path runs underndo_start_xmitand the echo response handlers run from the UDP encap rx softirq, both with BH already disabled.
Quoted 8 lines
Quoted 3 lines
Fix it by disabling BH around the
udp_tunnel_xmit_skb()call, mirroring commit2cd7e6971fc2("sctp: disable BH before callingudp_tunnel_xmit_skb()").Why not fix
iptunnel_xmit()directly, rather than fixing all possible callers? Basically, jut like we did for lwtunnel_{output|xmit}(). The advantage would be that we no longer have to worry about BHs in the callers, and BHs would only be disabled when necessary.
Good point — your lwtunnel fix (c03a49f3093a) is a close parallel, and
a central fix would avoid chasing callers one by one (sctp was patched last week, gtp is this one, and tipc/wireguard/ovpn genl paths look similar).
Happy to respin as v2 with local_bh_disable/enable moved into iptunnel_xmit() (and ip6tunnel_xmit() for symmetry), and drop the gtp-local hunk. That would also supersede Xin Long's recent sctp
commit
(2cd7e6971fc2), so I'll make sure to Cc him.
One thing I'd like your take on before I send: iptunnel_xmit() feels like the natural home since it owns the recursion counter, but would you rather see it in udp_tunnel_xmit_skb()? I don't want to pick the wrong spot if you already have a preference.
Cheers !
Quoted 34 lines · David CARLIER
Hi Julian,
Quoted 17 lines · Justin Iurman
Quoted 15 lines · David Carlier
gtp_genl_send_echo_req()runs as a generic netlink doit handler in process context with BH not disabled. It callsudp_tunnel_xmit_skb(), which eventually invokesiptunnel_xmit()— that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.Without
local_bh_disable(), the task may migrate betweendev_xmit_recursion_inc()anddev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positiveSKB_DROP_REASON_RECURSION_LIMITdrops on either CPU.The other
udp_tunnel_xmit_skb()call sites ingtp.care unaffected:
the data path runs underndo_start_xmitand the echo response handlers run from the UDP encap rx softirq, both with BH already disabled.Quoted 8 lines
Quoted 3 lines
Fix it by disabling BH around the
udp_tunnel_xmit_skb()call, mirroring commit2cd7e6971fc2("sctp: disable BH before callingudp_tunnel_xmit_skb()").Why not fix
iptunnel_xmit()directly, rather than fixing all possible callers? Basically, jut like we did for lwtunnel_{output|xmit}(). The advantage would be that we no longer have to worry about BHs in the callers, and BHs would only be disabled when necessary.Good point — your lwtunnel fix (
c03a49f3093a) is a close parallel, and
a central fix would avoid chasing callers one by one (sctp was patched last week, gtp is this one, and tipc/wireguard/ovpn genl paths look similar).
Quoted 5 lines
Happy to respin as v2 with local_bh_disable/enable moved into
iptunnel_xmit()(andip6tunnel_xmit()for symmetry), and drop the gtp-local hunk. That would also supersede Xin Long's recent sctp
commit
(2cd7e6971fc2), so I'll make sure to Cc him.
Jakub merged it already, so no need to respin. I guess we could revisit later if required.
Quoted 4 lines
One thing I'd like your take on before I send:
iptunnel_xmit()feels like the natural home since it owns the recursion counter, but would you rather see it inudp_tunnel_xmit_skb()? I don't want to pick the wrong spot if you already have a preference.
Since udp_tunnel_xmit_skb() is just another caller, I'd definitely do it in iptunnel_xmit() to centralize things (same for v6).
Jakub Kicinski
maintainerQuoted 16 lines · On Mon, 20 Apr 2026 21:02:55 +0200 Justin Iurman wrote:
Quoted 15 lines · David Carlier
gtp_genl_send_echo_req()runs as a generic netlink doit handler in process context with BH not disabled. It callsudp_tunnel_xmit_skb(), which eventually invokesiptunnel_xmit()— that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.Without
local_bh_disable(), the task may migrate betweendev_xmit_recursion_inc()anddev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positiveSKB_DROP_REASON_RECURSION_LIMITdrops on either CPU.The other
udp_tunnel_xmit_skb()call sites ingtp.care unaffected:
the data path runs underndo_start_xmitand the echo response handlers run from the UDP encap rx softirq, both with BH already disabled.
Quoted 8 lines
Quoted 3 lines
Fix it by disabling BH around the
udp_tunnel_xmit_skb()call, mirroring commit2cd7e6971fc2("sctp: disable BH before callingudp_tunnel_xmit_skb()").Why not fix
iptunnel_xmit()directly, rather than fixing all possible callers? Basically, jut like we did for lwtunnel_{output|xmit}(). The advantage would be that we no longer have to worry about BHs in the callers, and BHs would only be disabled when necessary.
Oops, I pushed this already. The bot hasn't caught up yet. Let's revisit this if we find another caller in process context?
Quoted 21 lines · Jakub Kicinski
Quoted 20 lines · On Mon, 20 Apr 2026 21:02:55 +0200 Justin Iurman wrote:
Quoted 18 lines · David Carlier
gtp_genl_send_echo_req()runs as a generic netlink doit handler in process context with BH not disabled. It callsudp_tunnel_xmit_skb(), which eventually invokesiptunnel_xmit()— that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.Without
local_bh_disable(), the task may migrate betweendev_xmit_recursion_inc()anddev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positiveSKB_DROP_REASON_RECURSION_LIMITdrops on either CPU.The other
udp_tunnel_xmit_skb()call sites ingtp.care unaffected:
the data path runs underndo_start_xmitand the echo response handlers run from the UDP encap rx softirq, both with BH already disabled.Fix it by disabling BH around the
udp_tunnel_xmit_skb()call, mirroring commit2cd7e6971fc2("sctp: disable BH before callingudp_tunnel_xmit_skb()").
Quoted 7 lines
Quoted 4 lines
Why not fix
iptunnel_xmit()directly, rather than fixing all possible callers? Basically, jut like we did for lwtunnel_{output|xmit}(). The advantage would be that we no longer have to worry about BHs in the callers, and BHs would only be disabled when necessary.Oops, I pushed this already. The bot hasn't caught up yet. Let's revisit this if we find another caller in process context?
No worries, works for me!
This patch was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:
Quoted 5 lines · On Fri, 17 Apr 2026 06:54:08 +0100 you wrote:
gtp_genl_send_echo_req()runs as a generic netlink doit handler in process context with BH not disabled. It callsudp_tunnel_xmit_skb(), which eventually invokesiptunnel_xmit()— that uses __this_cpu_inc/dec on softnet_data.xmit.recursion to track the tunnel xmit recursion level.
Quoted 7 lines
Without
local_bh_disable(), the task may migrate betweendev_xmit_recursion_inc()anddev_xmit_recursion_dec(), breaking the per-CPU counter pairing. The result is stale or negative recursion levels that can later produce false-positiveSKB_DROP_REASON_RECURSION_LIMITdrops on either CPU.[...]
Here is the summary with links:
- gtp: disable BH before calling udp_tunnel_xmit_skb()
https://git.kernel.org/netdev/net/c/5638504a2aa9
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html