* [PATCH net-next] net: run netdev work under the ops-compat lock
@ 2026-09-04 18:00 Jakub Kicinski
0 siblings, 0 replies; only message in thread
From: Jakub Kicinski @ 2026-09-04 18:00 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
corbet, skhan, rdunlap, sdf, kuniyu, linux-doc
netdev_work_proc() took rtnl_lock() for the whole batch, forcing
even ops-locked drivers onto rtnl just to run their rx_mode / ndo_work
callbacks. Take the per-device ops-compat lock instead: the instance
lock for ops-locked drivers, rtnl_lock for the rest. That's what the
callbacks already assert (e.g. netif_rx_mode_run() asserts
netdev_assert_locked_ops_compat()), and non-ops-locked work
such as vlan ndo_work keeps rtnl, since its compat lock resolves
to rtnl anyway.
Without this adding more uses of ndo_work, for cases which don't
need rtnl_lock feels like a step back.
We can drop the comment about the extra ref now, with the relocking
it's a necessity.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
CC: corbet@lwn.net
CC: skhan@linuxfoundation.org
CC: rdunlap@infradead.org
CC: sdf@fomichev.me
CC: kuniyu@google.com
CC: linux-doc@vger.kernel.org
---
Documentation/networking/netdevices.rst | 14 ++++++++++----
include/linux/netdevice.h | 5 ++++-
net/core/netdev_work.c | 15 ++-------------
3 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
index db71d4283032..aac33497813f 100644
--- a/Documentation/networking/netdevices.rst
+++ b/Documentation/networking/netdevices.rst
@@ -292,15 +292,21 @@ struct net_device synchronization rules
in process context.
ndo_set_rx_mode_async:
- Synchronization: rtnl_lock() semaphore. In addition, netdev instance
- lock if the driver implements queue management or shaper API.
+ Synchronization: netdev instance lock for "ops locked" drivers,
+ rtnl_lock() semaphore for all other drivers.
Context: process (from a work queue)
Notes: Async version of ndo_set_rx_mode which runs in process
context. Receives snapshots of the unicast and multicast address lists.
ndo_change_rx_flags:
- Synchronization: rtnl_lock() semaphore. In addition, netdev instance
- lock if the driver implements queue management or shaper API.
+ Synchronization: netdev instance lock for "ops locked" drivers,
+ rtnl_lock() semaphore for all other drivers.
+
+ndo_work:
+ Synchronization: netdev instance lock for "ops locked" drivers,
+ rtnl_lock() semaphore for all other drivers.
+ Context: process (from a work queue)
+ Notes: Runs deferred work scheduled with netdev_work_sched().
ndo_setup_tc:
Locking depends on ``tc_setup_type``. For most types the callback
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 8454646d6a45..17b8fb8e94fe 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1135,13 +1135,16 @@ struct netdev_net_notifier {
* struct netdev_hw_addr_list *uc,
* struct netdev_hw_addr_list *mc);
* Async version of ndo_set_rx_mode which runs in process context
- * with rtnl_lock and netdev_lock_ops(dev) held. The uc/mc parameters
+ * under the netdev instance lock for "ops locked" drivers, or
+ * rtnl_lock for all other drivers. The uc/mc parameters
* are snapshots of the address lists - iterate with
* netdev_hw_addr_list_for_each(ha, uc). Return 0 on success or a
* negative errno to request a retry via the core backoff.
*
* void (*ndo_work)(struct net_device *dev, unsigned long events);
* Run deferred work scheduled with netdev_work_sched(@events).
+ * Runs in process context under the netdev instance lock for "ops
+ * locked" drivers, or rtnl_lock for all other drivers.
*
* int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
* This function is called when the Media Access Control address
diff --git a/net/core/netdev_work.c b/net/core/netdev_work.c
index e721a06d58df..c99967907e18 100644
--- a/net/core/netdev_work.c
+++ b/net/core/netdev_work.c
@@ -3,7 +3,6 @@
#include <linux/export.h>
#include <linux/list.h>
#include <linux/netdevice.h>
-#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <net/netdev_lock.h>
@@ -129,8 +128,6 @@ static void netdev_work_run(struct net_device *dev, unsigned long events,
static void netdev_work_proc(struct work_struct *work)
{
- rtnl_lock();
-
while (true) {
unsigned long events = 0, core = 0;
netdevice_tracker tracker;
@@ -143,16 +140,10 @@ static void netdev_work_proc(struct work_struct *work)
}
dev = list_first_entry(&netdev_work_list, struct net_device,
work_node);
- /* Take a temporary reference so @dev can't be freed while we
- * drop the lock to grab its ops lock; the work reference is
- * only released once we claim the work below.
- * The re-locking dance is to ensure that ops lock is enough
- * to ensure canceling work is not racy with dequeue.
- */
netdev_hold(dev, &tracker, GFP_ATOMIC);
spin_unlock_bh(&netdev_work_lock);
- netdev_lock_ops(dev);
+ netdev_lock_ops_compat(dev);
spin_lock_bh(&netdev_work_lock);
if (!list_empty(&dev->work_node)) {
list_del_init(&dev->work_node);
@@ -169,10 +160,8 @@ static void netdev_work_proc(struct work_struct *work)
spin_unlock_bh(&netdev_work_lock);
netdev_work_run(dev, events, core);
- netdev_unlock_ops(dev);
+ netdev_unlock_ops_compat(dev);
netdev_put(dev, &tracker);
}
-
- rtnl_unlock();
}
--
2.55.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-04 18:01 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 18:00 [PATCH net-next] net: run netdev work under the ops-compat lock Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox