From: Maxime Chevallier <maxime.chevallier@bootlin.com> mainline inclusion from mainline-v7.4-rc2 commit 8dc87fc5d6a0eb337744737008177342a708bf36 category: bugfix bugzilla: https://atomgit.com/openeuler/kernel/issues/10109 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- 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. 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.c... Signed-off-by: Jakub Kicinski <kuba@kernel.org> Conflicts: include/linux/netdevice.h net/core/dev.c [Missing prerequisites in OLK-6.6: 1b23cdbd2bbc net: protect netdev->napi_list with netdev_lock() 413f0271f396 net: protect NAPI enablement with netdev_lock() c18d4b190a46 net: Extend NAPI threaded polling to allow kthread based busy polling OLK-6.6 therefore has no netif_napi_add_weight_locked(), napi_disable_locked(), napi_enable_locked(), or NAPI_STATE_THREADED_BUSY_POLL.] Signed-off-by: JiangJieHua <jiangjiehua1@huawei.com> --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8668973f363c9..e5d8afe4d714a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -411,6 +411,7 @@ enum { NAPI_STATE_PREFER_BUSY_POLL, /* prefer busy-polling over softirq processing*/ NAPI_STATE_THREADED, /* The poll is performed inside its own thread*/ NAPI_STATE_SCHED_THREADED, /* Napi is currently scheduled in threaded mode */ + NAPI_STATE_DISABLED, /* NAPI is disabled */ }; enum { @@ -424,6 +425,7 @@ enum { NAPIF_STATE_PREFER_BUSY_POLL = BIT(NAPI_STATE_PREFER_BUSY_POLL), NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED), NAPIF_STATE_SCHED_THREADED = BIT(NAPI_STATE_SCHED_THREADED), + NAPIF_STATE_DISABLED = BIT(NAPI_STATE_DISABLED), }; enum gro_result { diff --git a/net/core/dev.c b/net/core/dev.c index 0a2b0ad2296c0..199a2b97d005b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6655,6 +6655,12 @@ 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); + goto stop; + } + /* Wait until the napi STATE_THREADED is unset. */ while (true) { val = READ_ONCE(napi->state); @@ -6684,6 +6690,7 @@ static void napi_stop_kthread(struct napi_struct *napi) msleep(20); } +stop: kthread_stop(napi->thread); napi->thread = NULL; } @@ -6795,6 +6802,7 @@ void netif_napi_add_weight(struct net_device *dev, struct napi_struct *napi, 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); list_add_rcu(&napi->dev_list, &dev->napi_list); napi_hash_add(napi); napi_get_frags_check(napi); @@ -6822,7 +6830,8 @@ void napi_disable(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_PREFER_BUSY_POLL); } while (!try_cmpxchg(&n->state, &val, new)); @@ -6846,7 +6855,8 @@ void napi_enable(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)); -- 2.33.8