Re: [PATCH net] net: consume xmit errors of GSO frames
From: Jakub Kicinski <kuba@kernel.org>
Date: 2026-02-20 18:32:56
Subsystem:
networking [general], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
On Fri, 20 Feb 2026 18:17:14 +0100 Eric Dumazet wrote:
This rings a bell. I sent back in October something in the same vein. https://www.spinics.net/lists/netdev/msg1131452.html
Yes, I had a recollection of you explaining this problem in the past. Otherwise I would have never thought of this path! :) If we want to cover the BUSY case maybe the patch below? Too messy?
diff --git a/net/core/dev.c b/net/core/dev.c
index 096b3ff13f6b..65b7c54e1bef 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c@@ -4822,6 +4822,8 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) * to -1 or to their cpu id, but not to our id. */ if (READ_ONCE(txq->xmit_lock_owner) != cpu) { + bool is_list = false, completed = false; + if (dev_xmit_recursion()) goto recursion_alert;
@@ -4832,17 +4834,27 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) HARD_TX_LOCK(dev, txq, cpu); if (!netif_xmit_stopped(txq)) { + /* GSO segments a single SKB into + * a list of frames. TCP expects error + * to mean none of the data was sent. + */ + is_list = !!skb->next; + dev_xmit_recursion_inc(); skb = dev_hard_start_xmit(skb, dev, txq, &rc); dev_xmit_recursion_dec(); - if (dev_xmit_complete(rc)) { - HARD_TX_UNLOCK(dev, txq); - goto out; - } + completed = dev_xmit_complete(rc); + if (is_list) + rc = NETDEV_TX_OK; } HARD_TX_UNLOCK(dev, txq); + if (completed) + goto out; + net_crit_ratelimited("Virtual device %s asks to queue packet!\n", dev->name); + if (!is_list) + rc = -ENETDOWN; } else { /* Recursion is detected! It is possible, * unfortunately
@@ -4850,10 +4862,10 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev) recursion_alert: net_crit_ratelimited("Dead loop on virtual device %s, fix it urgently!\n", dev->name); + rc = -ENETDOWN; } } - rc = -ENETDOWN; rcu_read_unlock_bh(); dev_core_stats_tx_dropped_inc(dev);