netdev: fix the locking for netdev notifications
authorJakub Kicinski <kuba@kernel.org>
Wed, 16 Apr 2025 03:04:47 +0000 (20:04 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 18 Apr 2025 01:55:14 +0000 (18:55 -0700)
Kuniyuki reports that the assert for netdev lock fires when
there are netdev event listeners (otherwise we skip the netlink
event generation).

Correct the locking when coming from the notifier.

The NETDEV_XDP_FEAT_CHANGE notifier is already fully locked,
it's the documentation that's incorrect.

Fixes: 99e44f39a8f7 ("netdev: depend on netdev->lock for xdp features")
Reported-by: syzkaller <syzkaller@googlegroups.com>
Reported-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://lore.kernel.org/20250410171019.62128-1-kuniyu@amazon.com
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20250416030447.1077551-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/networking/netdevices.rst
include/linux/netdevice.h
include/net/netdev_lock.h
net/core/lock_debug.c
net/core/netdev-genl.c

index 77fe1f7b93be0e6901d30f17b51e05cdfc70b439..7ebb6c36482deb63dbff9babfc939905d84e5a2c 100644 (file)
@@ -387,12 +387,14 @@ For device drivers that implement shaping or queue management APIs,
 some of the notifiers (``enum netdev_cmd``) are running under the netdev
 instance lock.
 
+The following netdev notifiers are always run under the instance lock:
+* ``NETDEV_XDP_FEAT_CHANGE``
+
 For devices with locked ops, currently only the following notifiers are
 running under the lock:
 * ``NETDEV_CHANGE``
 * ``NETDEV_REGISTER``
 * ``NETDEV_UP``
-* ``NETDEV_XDP_FEAT_CHANGE``
 
 The following notifiers are running without the lock:
 * ``NETDEV_UNREGISTER``
index e6036b82ef4cf6375ec63bcb782bd6ee75bd40d6..0321fd952f70887735ac789d72f72948a3879832 100644 (file)
@@ -2520,7 +2520,7 @@ struct net_device {
         *      @net_shaper_hierarchy, @reg_state, @threaded
         *
         * Double protects:
-        *      @up, @moving_ns, @nd_net, @xdp_flags
+        *      @up, @moving_ns, @nd_net, @xdp_features
         *
         * Double ops protects:
         *      @real_num_rx_queues, @real_num_tx_queues
index 5706835a660c1fb5345c441e36ec355372e1382e..2a753813f84962cdd9ab66d4052b9bc6af97ce3a 100644 (file)
@@ -48,6 +48,22 @@ static inline void netdev_unlock_ops(struct net_device *dev)
                netdev_unlock(dev);
 }
 
+static inline void netdev_lock_ops_to_full(struct net_device *dev)
+{
+       if (netdev_need_ops_lock(dev))
+               netdev_assert_locked(dev);
+       else
+               netdev_lock(dev);
+}
+
+static inline void netdev_unlock_full_to_ops(struct net_device *dev)
+{
+       if (netdev_need_ops_lock(dev))
+               netdev_assert_locked(dev);
+       else
+               netdev_unlock(dev);
+}
+
 static inline void netdev_ops_assert_locked(const struct net_device *dev)
 {
        if (netdev_need_ops_lock(dev))
index 6fade574bc2a9edfebd3af40e278dd75f53545b9..9e9fb25314b9c3cffbd66b6714b95b2d03e021ec 100644 (file)
@@ -18,10 +18,12 @@ int netdev_debug_event(struct notifier_block *nb, unsigned long event,
 
        /* Keep enum and don't add default to trigger -Werror=switch */
        switch (cmd) {
+       case NETDEV_XDP_FEAT_CHANGE:
+               netdev_assert_locked(dev);
+               fallthrough;
        case NETDEV_CHANGE:
        case NETDEV_REGISTER:
        case NETDEV_UP:
-       case NETDEV_XDP_FEAT_CHANGE:
                netdev_ops_assert_locked(dev);
                fallthrough;
        case NETDEV_DOWN:
index b64c614a00c47450035ded97f8a55ed695162a39..2c104947d224f2e8ecedbacbe1d59fb7ae499b56 100644 (file)
@@ -963,10 +963,14 @@ static int netdev_genl_netdevice_event(struct notifier_block *nb,
 
        switch (event) {
        case NETDEV_REGISTER:
+               netdev_lock_ops_to_full(netdev);
                netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_ADD_NTF);
+               netdev_unlock_full_to_ops(netdev);
                break;
        case NETDEV_UNREGISTER:
+               netdev_lock(netdev);
                netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF);
+               netdev_unlock(netdev);
                break;
        case NETDEV_XDP_FEAT_CHANGE:
                netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_CHANGE_NTF);