Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-prevent-deadlocks-and-mis-configuration-with-per-napi-threaded-config'

Jakub Kicinski says:

====================
net: prevent deadlocks and mis-configuration with per-NAPI threaded config

Running the test added with a recent fix on a driver with persistent
NAPI config leads to a deadlock. The deadlock is fixed by patch 3,
patch 2 is I think a more fundamental problem with the way we
implemented the config.

I hope the fix makes sense, my own thinking is definitely colored
by my preference (IOW how the per-queue config RFC was implemented).

v1: https://lore.kernel.org/20250808014952.724762-1-kuba@kernel.org
====================

Link: https://patch.msgid.link/20250809001205.1147153-1-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

Paolo Abeni 74078816 e93f7af1

+27 -8
+4 -1
include/linux/netdevice.h
··· 2071 2071 * @max_pacing_offload_horizon: max EDT offload horizon in nsec. 2072 2072 * @napi_config: An array of napi_config structures containing per-NAPI 2073 2073 * settings. 2074 + * @num_napi_configs: number of allocated NAPI config structs, 2075 + * always >= max(num_rx_queues, num_tx_queues). 2074 2076 * @gro_flush_timeout: timeout for GRO layer in NAPI 2075 2077 * @napi_defer_hard_irqs: If not zero, provides a counter that would 2076 2078 * allow to avoid NIC hard IRQ, on busy queues. ··· 2484 2482 2485 2483 u64 max_pacing_offload_horizon; 2486 2484 struct napi_config *napi_config; 2487 - unsigned long gro_flush_timeout; 2485 + u32 num_napi_configs; 2488 2486 u32 napi_defer_hard_irqs; 2487 + unsigned long gro_flush_timeout; 2489 2488 2490 2489 /** 2491 2490 * @up: copy of @state's IFF_UP, but safe to read with just @lock.
+9 -3
net/core/dev.c
··· 6999 6999 enum netdev_napi_threaded threaded) 7000 7000 { 7001 7001 struct napi_struct *napi; 7002 - int err = 0; 7002 + int i, err = 0; 7003 7003 7004 7004 netdev_assert_locked_or_invisible(dev); 7005 7005 ··· 7020 7020 /* The error should not occur as the kthreads are already created. */ 7021 7021 list_for_each_entry(napi, &dev->napi_list, dev_list) 7022 7022 WARN_ON_ONCE(napi_set_threaded(napi, threaded)); 7023 + 7024 + /* Override the config for all NAPIs even if currently not listed */ 7025 + for (i = 0; i < dev->num_napi_configs; i++) 7026 + dev->napi_config[i].threaded = threaded; 7023 7027 7024 7028 return err; 7025 7029 } ··· 7357 7353 * Clear dev->threaded if kthread creation failed so that 7358 7354 * threaded mode will not be enabled in napi_enable(). 7359 7355 */ 7360 - if (dev->threaded && napi_kthread_create(napi)) 7361 - dev->threaded = NETDEV_NAPI_THREADED_DISABLED; 7356 + if (napi_get_threaded_config(dev, napi)) 7357 + if (napi_kthread_create(napi)) 7358 + dev->threaded = NETDEV_NAPI_THREADED_DISABLED; 7362 7359 netif_napi_set_irq_locked(napi, -1); 7363 7360 } 7364 7361 EXPORT_SYMBOL(netif_napi_add_weight_locked); ··· 11878 11873 goto free_all; 11879 11874 dev->cfg_pending = dev->cfg; 11880 11875 11876 + dev->num_napi_configs = maxqs; 11881 11877 napi_config_sz = array_size(maxqs, sizeof(*dev->napi_config)); 11882 11878 dev->napi_config = kvzalloc(napi_config_sz, GFP_KERNEL_ACCOUNT); 11883 11879 if (!dev->napi_config)
+8
net/core/dev.h
··· 323 323 return NETDEV_NAPI_THREADED_DISABLED; 324 324 } 325 325 326 + static inline enum netdev_napi_threaded 327 + napi_get_threaded_config(struct net_device *dev, struct napi_struct *n) 328 + { 329 + if (n->config) 330 + return n->config->threaded; 331 + return dev->threaded; 332 + } 333 + 326 334 int napi_set_threaded(struct napi_struct *n, 327 335 enum netdev_napi_threaded threaded); 328 336
+6 -4
tools/testing/selftests/drivers/net/napi_threaded.py
··· 35 35 threaded = cmd(f"cat /sys/class/net/{cfg.ifname}/threaded").stdout 36 36 defer(_set_threaded_state, cfg, threaded) 37 37 38 + return combined 39 + 38 40 39 41 def enable_dev_threaded_disable_napi_threaded(cfg, nl) -> None: 40 42 """ ··· 51 49 napi0_id = napis[0]['id'] 52 50 napi1_id = napis[1]['id'] 53 51 54 - _setup_deferred_cleanup(cfg) 52 + qcnt = _setup_deferred_cleanup(cfg) 55 53 56 54 # set threaded 57 55 _set_threaded_state(cfg, 1) ··· 64 62 nl.napi_set({'id': napi1_id, 'threaded': 'disabled'}) 65 63 66 64 cmd(f"ethtool -L {cfg.ifname} combined 1") 67 - cmd(f"ethtool -L {cfg.ifname} combined 2") 65 + cmd(f"ethtool -L {cfg.ifname} combined {qcnt}") 68 66 _assert_napi_threaded_enabled(nl, napi0_id) 69 67 _assert_napi_threaded_disabled(nl, napi1_id) 70 68 ··· 82 80 napi0_id = napis[0]['id'] 83 81 napi1_id = napis[1]['id'] 84 82 85 - _setup_deferred_cleanup(cfg) 83 + qcnt = _setup_deferred_cleanup(cfg) 86 84 87 85 # set threaded 88 86 _set_threaded_state(cfg, 1) ··· 92 90 _assert_napi_threaded_enabled(nl, napi1_id) 93 91 94 92 cmd(f"ethtool -L {cfg.ifname} combined 1") 95 - cmd(f"ethtool -L {cfg.ifname} combined 2") 93 + cmd(f"ethtool -L {cfg.ifname} combined {qcnt}") 96 94 97 95 # check napi threaded is set for both napis 98 96 _assert_napi_threaded_enabled(nl, napi0_id)