net: lwtunnel: disable BHs when required
[ Upstream commit c03a49f3093a4903c8a93c8b5c9a297b5343b169 ]
In lwtunnel_{output|xmit}(), dev_xmit_recursion() may be called in
preemptible scope for PREEMPT kernels. This patch disables BHs before
calling dev_xmit_recursion(). BHs are re-enabled only at the end, since
we must ensure the same CPU is used for both dev_xmit_recursion_inc()
and dev_xmit_recursion_dec() (and any other recursion levels in some
cases) in order to maintain valid per-cpu counters.
Reported-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Closes: https://lore.kernel.org/netdev/CAADnVQJFWn3dBFJtY+ci6oN1pDFL=TzCmNbRgey7MdYxt_AP2g@mail.gmail.com/
Reported-by: Eduard Zingerman <eddyz87@gmail.com>
Closes: https://lore.kernel.org/netdev/m2h62qwf34.fsf@gmail.com/
Fixes: 986ffb3a57c5 ("net: lwtunnel: fix recursion loops")
Signed-off-by: Justin Iurman <justin.iurman@uliege.be>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250416160716.8823-1-justin.iurman@uliege.be
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
eeab661803
commit
3340654bbf
@@ -332,6 +332,8 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||
struct dst_entry *dst;
|
||||
int ret;
|
||||
|
||||
local_bh_disable();
|
||||
|
||||
if (dev_xmit_recursion()) {
|
||||
net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
|
||||
__func__);
|
||||
@@ -347,8 +349,10 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||
lwtstate = dst->lwtstate;
|
||||
|
||||
if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
|
||||
lwtstate->type > LWTUNNEL_ENCAP_MAX)
|
||||
return 0;
|
||||
lwtstate->type > LWTUNNEL_ENCAP_MAX) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EOPNOTSUPP;
|
||||
rcu_read_lock();
|
||||
@@ -363,11 +367,13 @@ int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb)
|
||||
if (ret == -EOPNOTSUPP)
|
||||
goto drop;
|
||||
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
|
||||
out:
|
||||
local_bh_enable();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lwtunnel_output);
|
||||
@@ -379,6 +385,8 @@ int lwtunnel_xmit(struct sk_buff *skb)
|
||||
struct dst_entry *dst;
|
||||
int ret;
|
||||
|
||||
local_bh_disable();
|
||||
|
||||
if (dev_xmit_recursion()) {
|
||||
net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
|
||||
__func__);
|
||||
@@ -395,8 +403,10 @@ int lwtunnel_xmit(struct sk_buff *skb)
|
||||
lwtstate = dst->lwtstate;
|
||||
|
||||
if (lwtstate->type == LWTUNNEL_ENCAP_NONE ||
|
||||
lwtstate->type > LWTUNNEL_ENCAP_MAX)
|
||||
return 0;
|
||||
lwtstate->type > LWTUNNEL_ENCAP_MAX) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EOPNOTSUPP;
|
||||
rcu_read_lock();
|
||||
@@ -411,11 +421,13 @@ int lwtunnel_xmit(struct sk_buff *skb)
|
||||
if (ret == -EOPNOTSUPP)
|
||||
goto drop;
|
||||
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
|
||||
out:
|
||||
local_bh_enable();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lwtunnel_xmit);
|
||||
@@ -427,6 +439,8 @@ int lwtunnel_input(struct sk_buff *skb)
|
||||
struct dst_entry *dst;
|
||||
int ret;
|
||||
|
||||
DEBUG_NET_WARN_ON_ONCE(!in_softirq());
|
||||
|
||||
if (dev_xmit_recursion()) {
|
||||
net_crit_ratelimited("%s(): recursion limit reached on datapath\n",
|
||||
__func__);
|
||||
|
||||
Reference in New Issue
Block a user