[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250722071508.12497-1-suchitkarunakaran@gmail.com>
Date: Tue, 22 Jul 2025 12:45:08 +0530
From: Suchit Karunakaran <suchitkarunakaran@...il.com>
To: davem@...emloft.net,
edumazet@...gle.com,
kuba@...nel.org,
pabeni@...hat.com,
horms@...nel.org,
jhs@...atatu.com,
xiyou.wangcong@...il.com,
jiri@...nulli.us,
sdf@...ichev.me,
kuniyu@...gle.com,
aleksander.lobakin@...el.com,
netdev@...r.kernel.org
Cc: skhan@...uxfoundation.org,
linux-kernel-mentees@...ts.linux.dev,
linux-kernel@...r.kernel.org,
Suchit Karunakaran <suchitkarunakaran@...il.com>
Subject: [PATCH] net: Revert tx queue length on partial failure in dev_qdisc_change_tx_queue_len()
When changing the tx queue length via dev_qdisc_change_tx_queue_len(),
if one of the updates fails, the function currently exits
without rolling back previously modified queues. This can leave the
device and its qdiscs in an inconsistent state. This patch adds rollback logic
that restores the original dev->tx_queue_len and re-applies it to each previously
updated queue's qdisc by invoking qdisc_change_tx_queue_len() again.
To support this, dev_qdisc_change_tx_queue_len() now takes an additional
parameter old_len to remember the original tx_queue_len value.
Note: I have built the kernel with these changes to ensure it compiles, but I
have not tested the runtime behavior, as I am currently unsure how to test this
change.
Signed-off-by: Suchit Karunakaran <suchitkarunakaran@...il.com>
---
include/net/sch_generic.h | 2 +-
net/core/dev.c | 2 +-
net/sched/sch_generic.c | 12 +++++++++---
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 638948be4c50..a4f59df2982f 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -681,7 +681,7 @@ void qdisc_class_hash_remove(struct Qdisc_class_hash *,
void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *);
void qdisc_class_hash_destroy(struct Qdisc_class_hash *);
-int dev_qdisc_change_tx_queue_len(struct net_device *dev);
+int dev_qdisc_change_tx_queue_len(struct net_device *dev, unsigned int old_len);
void dev_qdisc_change_real_num_tx(struct net_device *dev,
unsigned int new_real_tx);
void dev_init_scheduler(struct net_device *dev);
diff --git a/net/core/dev.c b/net/core/dev.c
index be97c440ecd5..afa3c5a9bba1 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -9630,7 +9630,7 @@ int netif_change_tx_queue_len(struct net_device *dev, unsigned long new_len)
res = notifier_to_errno(res);
if (res)
goto err_rollback;
- res = dev_qdisc_change_tx_queue_len(dev);
+ res = dev_qdisc_change_tx_queue_len(dev, orig_len);
if (res)
goto err_rollback;
}
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 16afb834fe4a..701dfbe722ed 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1445,7 +1445,7 @@ void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx)
}
EXPORT_SYMBOL(mq_change_real_num_tx);
-int dev_qdisc_change_tx_queue_len(struct net_device *dev)
+int dev_qdisc_change_tx_queue_len(struct net_device *dev, unsigned int old_len)
{
bool up = dev->flags & IFF_UP;
unsigned int i;
@@ -1456,12 +1456,18 @@ int dev_qdisc_change_tx_queue_len(struct net_device *dev)
for (i = 0; i < dev->num_tx_queues; i++) {
ret = qdisc_change_tx_queue_len(dev, &dev->_tx[i]);
-
- /* TODO: revert changes on a partial failure */
if (ret)
break;
}
+ if (ret) {
+ dev->tx_queue_len = old_len;
+ while (i >= 0) {
+ qdisc_change_tx_queue_len(dev, &dev->_tx[i]);
+ i--;
+ }
+ }
+
if (up)
dev_activate(dev);
return ret;
--
2.50.1
Powered by blists - more mailing lists