Merge branch 'net-add-skb_drop_reason_recursion_limit'

Eric Dumazet says:

====================
net: add SKB_DROP_REASON_RECURSION_LIMIT

Add a new drop reason : SKB_DROP_REASON_RECURSION_LIMIT

Used for packets dropped in a too deep virtual device chain,
from tunnels and __dev_queue_xmit()

__dev_queue_xmit() can also return SKB_DROP_REASON_DEV_READY
====================

Link: https://patch.msgid.link/20260312201824.203093-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-03-14 08:38:08 -07:00
commit bfeb2c029e
4 changed files with 58 additions and 52 deletions

View File

@ -123,6 +123,7 @@
FN(PFMEMALLOC) \
FN(PSP_INPUT) \
FN(PSP_OUTPUT) \
FN(RECURSION_LIMIT) \
FNe(MAX)
/**
@ -582,6 +583,8 @@ enum skb_drop_reason {
SKB_DROP_REASON_PSP_INPUT,
/** @SKB_DROP_REASON_PSP_OUTPUT: PSP output checks failed */
SKB_DROP_REASON_PSP_OUTPUT,
/** @SKB_DROP_REASON_RECURSION_LIMIT: Dead loop on virtual device. */
SKB_DROP_REASON_RECURSION_LIMIT,
/**
* @SKB_DROP_REASON_MAX: the maximum of core drop reasons, which
* shouldn't be used as a real 'reason' - only for tracing code gen

View File

@ -162,7 +162,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,
dev->name);
DEV_STATS_INC(dev, tx_errors);
}
kfree_skb(skb);
kfree_skb_reason(skb, SKB_DROP_REASON_RECURSION_LIMIT);
return;
}

View File

@ -4745,9 +4745,10 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
{
struct net_device *dev = skb->dev;
struct netdev_queue *txq = NULL;
struct Qdisc *q;
int rc = -ENOMEM;
enum skb_drop_reason reason;
int cpu, rc = -ENOMEM;
bool again = false;
struct Qdisc *q;
skb_reset_mac_header(skb);
skb_assert_len(skb);
@ -4816,59 +4817,61 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
* Check this and shot the lock. It is not prone from deadlocks.
*Either shot noqueue qdisc, it is even simpler 8)
*/
if (dev->flags & IFF_UP) {
int cpu = smp_processor_id(); /* ok because BHs are off */
if (!netif_tx_owned(txq, cpu)) {
bool is_list = false;
if (dev_xmit_recursion())
goto recursion_alert;
skb = validate_xmit_skb(skb, dev, &again);
if (!skb)
goto out;
HARD_TX_LOCK(dev, txq, cpu);
if (!netif_xmit_stopped(txq)) {
is_list = !!skb->next;
dev_xmit_recursion_inc();
skb = dev_hard_start_xmit(skb, dev, txq, &rc);
dev_xmit_recursion_dec();
/* GSO segments a single SKB into
* a list of frames. TCP expects error
* to mean none of the data was sent.
*/
if (is_list)
rc = NETDEV_TX_OK;
}
HARD_TX_UNLOCK(dev, txq);
if (!skb) /* xmit completed */
goto out;
net_crit_ratelimited("Virtual device %s asks to queue packet!\n",
dev->name);
/* NETDEV_TX_BUSY or queue was stopped */
if (!is_list)
rc = -ENETDOWN;
} else {
/* Recursion is detected! It is possible,
* unfortunately
*/
recursion_alert:
net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n",
dev->name);
rc = -ENETDOWN;
}
if (unlikely(!(dev->flags & IFF_UP))) {
reason = SKB_DROP_REASON_DEV_READY;
goto drop;
}
cpu = smp_processor_id(); /* ok because BHs are off */
if (likely(!netif_tx_owned(txq, cpu))) {
bool is_list = false;
if (dev_xmit_recursion())
goto recursion_alert;
skb = validate_xmit_skb(skb, dev, &again);
if (!skb)
goto out;
HARD_TX_LOCK(dev, txq, cpu);
if (!netif_xmit_stopped(txq)) {
is_list = !!skb->next;
dev_xmit_recursion_inc();
skb = dev_hard_start_xmit(skb, dev, txq, &rc);
dev_xmit_recursion_dec();
/* GSO segments a single SKB into a list of frames.
* TCP expects error to mean none of the data was sent.
*/
if (is_list)
rc = NETDEV_TX_OK;
}
HARD_TX_UNLOCK(dev, txq);
if (!skb) /* xmit completed */
goto out;
net_crit_ratelimited("Virtual device %s asks to queue packet!\n",
dev->name);
/* NETDEV_TX_BUSY or queue was stopped */
if (!is_list)
rc = -ENETDOWN;
} else {
/* Recursion is detected! It is possible unfortunately. */
recursion_alert:
net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n",
dev->name);
rc = -ENETDOWN;
}
reason = SKB_DROP_REASON_RECURSION_LIMIT;
drop:
rcu_read_unlock_bh();
dev_core_stats_tx_dropped_inc(dev);
kfree_skb_list(skb);
kfree_skb_list_reason(skb, reason);
return rc;
out:
rcu_read_unlock_bh();

View File

@ -65,7 +65,7 @@ void iptunnel_xmit(struct sock *sk, struct rtable *rt, struct sk_buff *skb,
DEV_STATS_INC(dev, tx_errors);
}
ip_rt_put(rt);
kfree_skb(skb);
kfree_skb_reason(skb, SKB_DROP_REASON_RECURSION_LIMIT);
return;
}