]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: designate queue counts as "double ops protected" by instance lock
authorJakub Kicinski <kuba@kernel.org>
Mon, 24 Mar 2025 22:45:31 +0000 (15:45 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 25 Mar 2025 17:06:49 +0000 (10:06 -0700)
Drivers which opt into instance lock protection of ops should
only call set_real_num_*_queues() under the instance lock.
This means that queue counts are double protected (writes
are under both rtnl_lock and instance lock, readers under
either).

Some readers may still be under the rtnl_lock, however, so for
now we need double protection of writers.

OTOH queue API paths are only under the protection of the instance
lock, so we need to validate that the instance is actually locking
ops, otherwise the input checks we do against queue count are racy.

Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Link: https://patch.msgid.link/20250324224537.248800-6-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/netdevice.h
net/core/dev.c
net/core/net-sysfs.c
net/core/netdev-genl.c
net/core/netdev_rx_queue.c

index 2b91fb96a41141333234e99d2e59d6b9571313bd..60ef367d857569cbb8b8b9c92d5b0f37af39276a 100644 (file)
@@ -2523,6 +2523,9 @@ struct net_device {
         * Double protects:
         *      @up
         *
+        * Double ops protects:
+        *      @real_num_rx_queues, @real_num_tx_queues
+        *
         * Also protects some fields in struct napi_struct.
         *
         * Ordering: take after rtnl_lock.
index 6295f00e97a73d32ee828569969cd40ed8bc8bc0..2d9be3ecd5e6a5e3c68063569830b4beea4231da 100644 (file)
@@ -3130,6 +3130,7 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq)
        if (dev->reg_state == NETREG_REGISTERED ||
            dev->reg_state == NETREG_UNREGISTERING) {
                ASSERT_RTNL();
+               netdev_ops_assert_locked(dev);
 
                rc = netdev_queue_update_kobjects(dev, dev->real_num_tx_queues,
                                                  txq);
@@ -3179,6 +3180,7 @@ int netif_set_real_num_rx_queues(struct net_device *dev, unsigned int rxq)
 
        if (dev->reg_state == NETREG_REGISTERED) {
                ASSERT_RTNL();
+               netdev_ops_assert_locked(dev);
 
                rc = net_rx_queue_update_kobjects(dev, dev->real_num_rx_queues,
                                                  rxq);
index b6fbe629ccee1ea874a796c6146e4a6e8296c5dc..1ace0cd01adce7efefb6bfe4a9a763a33dff05ba 100644 (file)
@@ -2148,8 +2148,10 @@ static void remove_queue_kobjects(struct net_device *dev)
        net_rx_queue_update_kobjects(dev, real_rx, 0);
        netdev_queue_update_kobjects(dev, real_tx, 0);
 
+       netdev_lock_ops(dev);
        dev->real_num_rx_queues = 0;
        dev->real_num_tx_queues = 0;
+       netdev_unlock_ops(dev);
 #ifdef CONFIG_SYSFS
        kset_unregister(dev->queues_kset);
 #endif
index 9e4882a22407dcf1d9deedbacfc41c3a72fb7e6a..fd1cfa9707dc8a77bed199263bb69992c42b8cd1 100644 (file)
@@ -867,6 +867,13 @@ int netdev_nl_bind_rx_doit(struct sk_buff *skb, struct genl_info *info)
                goto err_unlock_sock;
        }
 
+       if (!netdev_need_ops_lock(netdev)) {
+               err = -EOPNOTSUPP;
+               NL_SET_BAD_ATTR(info->extack,
+                               info->attrs[NETDEV_A_DEV_IFINDEX]);
+               goto err_unlock;
+       }
+
        if (dev_xdp_prog_count(netdev)) {
                NL_SET_ERR_MSG(info->extack, "unable to bind dmabuf to device with XDP program attached");
                err = -EEXIST;
index a5b234b33cd5b93bcf05fc1316d444aa04155a59..3af716f77a13bf80c18ab71e286e507ec27f5c45 100644 (file)
@@ -92,6 +92,9 @@ static int __net_mp_open_rxq(struct net_device *dev, unsigned ifq_idx,
        struct netdev_rx_queue *rxq;
        int ret;
 
+       if (!netdev_need_ops_lock(dev))
+               return -EOPNOTSUPP;
+
        if (ifq_idx >= dev->real_num_rx_queues)
                return -EINVAL;
        ifq_idx = array_index_nospec(ifq_idx, dev->real_num_rx_queues);