From: Chris J Arges <carges@cloudflare.com>
To: stable@vger.kernel.org, "David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
Andrew Lunn <andrew+netdev@lunn.ch>,
Samiullah Khawaja <skhawaja@google.com>
Cc: netdev@vger.kernel.org, kernel-team@cloudflare.com,
Maxime Chevallier <maxime.chevallier@bootlin.com>,
Jakub Kicinski <kuba@kernel.org>,
linux-kernel@vger.kernel.org,
Daniel Machon <daniel.machon@microchip.com>,
Chris J Arges <carges@cloudflare.com>
Subject: [PATCH 7.3.y] net: Use a dedicated flag to track disabled napi instances
Date: Fri, 13 Nov 2026 14:01:23 -0600 [thread overview]
Message-ID: <20261113-napi-disabled-backport-7-3-v1-1-99560caf8bca@cloudflare.com> (raw)
From: Maxime Chevallier <maxime.chevallier@bootlin.com>
commit 8dc87fc5d6a0eb337744737008177342a708bf36
("net: Use a dedicated flag to track disabled napi instances") upstream.
Trying to disable the napi threaded state while the napi instance is
disabled will hang forever in napi_stop_kthread(), as it waits for
NAPIF_STATE_SCHED_THREADED to clear.
This won't happen until the next ____napi_schedule() call, which won't
happen as it's disabled.
This happens in 2 instances :
- Drivers that create but don't use napi instances (stmmac's rxtx napi
for AF_XDP for example), here it's a driver bug
- Drivers that create their napi instances in .probe(). Here, setting
threaded off while the interface is down will hang :
ip link set eth0 down
echo 1 > /sys/class/net/eth0/threaded
echo 0 > /sys/class/net/eth0/threaded
-> hang
Checking on netif_running() isn't enough, as napi instances may be
transiently disabled when changing the MTU for example, let's use a
dedicated flag to track if the instance is disabled.
This allows immediately disabling the kthread when the instance is
disabled.
This was tested on mvpp2 (probe-time napi_add) and stmmac.
[ Backport: place the disabled state after the threaded busy-poll state;
the adjacent per-CPU NAPI state is not present in 7.3.y. ]
Fixes: 689883de94dd ("net: stop napi kthreads when THREADED napi is disabled")
Tested-by: Daniel Machon <daniel.machon@microchip.com>
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20261029153130.121204-1-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Chris J Arges <carges@cloudflare.com>
---
Backport upstream commit 8dc87fc5d6a0 ("net: Use a dedicated flag to
track disabled napi instances") to 7.3.y.
The commit fixes a reproducible hang in napi_stop_kthread() when
threaded NAPI is turned off while the NAPI instance is disabled. The
sysfs writer holds the netdev instance lock while waiting, which can
also block device recovery.
The upstream patch does not apply cleanly because the adjacent per-CPU
NAPI state is not present in 7.3.y. Place the new disabled state after
the threaded busy-poll state; the functional changes are unchanged.
---
include/linux/netdevice.h | 2 ++
net/core/dev.c | 15 +++++++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 3e64af0b932d..02119fa153c9 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -433,6 +433,7 @@ enum {
NAPI_STATE_SCHED_THREADED, /* Napi is currently scheduled in threaded mode */
NAPI_STATE_HAS_NOTIFIER, /* Napi has an IRQ notifier */
NAPI_STATE_THREADED_BUSY_POLL, /* The threaded NAPI poller will busy poll */
+ NAPI_STATE_DISABLED, /* NAPI is disabled */
};
enum {
@@ -448,6 +449,7 @@ enum {
NAPIF_STATE_SCHED_THREADED = BIT(NAPI_STATE_SCHED_THREADED),
NAPIF_STATE_HAS_NOTIFIER = BIT(NAPI_STATE_HAS_NOTIFIER),
NAPIF_STATE_THREADED_BUSY_POLL = BIT(NAPI_STATE_THREADED_BUSY_POLL),
+ NAPIF_STATE_DISABLED = BIT(NAPI_STATE_DISABLED),
};
enum gro_result {
diff --git a/net/core/dev.c b/net/core/dev.c
index cc7b32ab5608..0e0a11ba2ada 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -7264,6 +7264,13 @@ static void napi_stop_kthread(struct napi_struct *napi)
{
unsigned long val, new;
+ /* Instance is disabled, we can immediately stop the kthread */
+ if (test_bit(NAPI_STATE_DISABLED, &napi->state)) {
+ clear_bit(NAPI_STATE_THREADED, &napi->state);
+ clear_bit(NAPI_STATE_THREADED_BUSY_POLL, &napi->state);
+ goto stop;
+ }
+
/* Wait until the napi STATE_THREADED is unset. */
while (true) {
val = READ_ONCE(napi->state);
@@ -7294,6 +7301,7 @@ static void napi_stop_kthread(struct napi_struct *napi)
msleep(20);
}
+stop:
kthread_stop(napi->thread);
napi->thread = NULL;
}
@@ -7689,6 +7697,7 @@ void netif_napi_add_weight_locked(struct net_device *dev,
napi->list_owner = -1;
set_bit(NAPI_STATE_SCHED, &napi->state);
set_bit(NAPI_STATE_NPSVC, &napi->state);
+ set_bit(NAPI_STATE_DISABLED, &napi->state);
netif_napi_dev_list_add(dev, napi);
/* default settings from sysfs are applied to all NAPIs. any per-NAPI
@@ -7725,7 +7734,8 @@ void napi_disable_locked(struct napi_struct *n)
val = READ_ONCE(n->state);
}
- new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC;
+ new = val | NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC |
+ NAPIF_STATE_DISABLED;
new &= ~(NAPIF_STATE_THREADED |
NAPIF_STATE_THREADED_BUSY_POLL |
NAPIF_STATE_PREFER_BUSY_POLL);
@@ -7770,7 +7780,8 @@ void napi_enable_locked(struct napi_struct *n)
do {
BUG_ON(!test_bit(NAPI_STATE_SCHED, &val));
- new = val & ~(NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC);
+ new = val & ~(NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC |
+ NAPIF_STATE_DISABLED);
if (n->dev->threaded && n->thread)
new |= NAPIF_STATE_THREADED;
} while (!try_cmpxchg(&n->state, &val, new));
---
base-commit: 7595a62d45ee1a2297eafa5f2ef5d0a45142d84d
change-id: 20261113-napi-disabled-backport-7-3-051408a00d4b
Best regards,
--
Chris J Arges <carges@cloudflare.com>
next reply other threads:[~2026-11-13 20:01 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 20:01 Chris J Arges [this message]
2026-11-14 17:42 ` Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113-napi-disabled-backport-7-3-v1-1-99560caf8bca@cloudflare.com \
--to=carges@cloudflare.com \
--cc=andrew+netdev@lunn.ch \
--cc=daniel.machon@microchip.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kernel-team@cloudflare.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=maxime.chevallier@bootlin.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=skhawaja@google.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®