]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: add netdev->up protected by netdev_lock()
authorJakub Kicinski <kuba@kernel.org>
Wed, 15 Jan 2025 03:53:12 +0000 (19:53 -0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 16 Jan 2025 03:13:34 +0000 (19:13 -0800)
Some uAPI (netdev netlink) hide net_device's sub-objects while
the interface is down to ensure uniform behavior across drivers.
To remove the rtnl_lock dependency from those uAPIs we need a way
to safely tell if the device is down or up.

Add an indication of whether device is open or closed, protected
by netdev->lock. The semantics are the same as IFF_UP, but taking
netdev_lock around every write to ->flags would be a lot of code
churn.

We don't want to blanket the entire open / close path by netdev_lock,
because it will prevent us from applying it to specific structures -
core helpers won't be able to take that lock from any function
called by the drivers on open/close paths.

So the state of the flag is "pessimistic", as in it may report false
negatives, but never false positives.

Reviewed-by: Joe Damato <jdamato@fastly.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250115035319.559603-5-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/netdevice.h
net/core/dev.c
net/core/dev.h

index 007bcfa383c9ba1fd8bc31cca032ed4a4ea0df0b..cac81b0a166f08015560d06de4bea597dba54a27 100644 (file)
@@ -2443,12 +2443,24 @@ struct net_device {
        unsigned long           gro_flush_timeout;
        u32                     napi_defer_hard_irqs;
 
+       /**
+        * @up: copy of @state's IFF_UP, but safe to read with just @lock.
+        *      May report false negatives while the device is being opened
+        *      or closed (@lock does not protect .ndo_open, or .ndo_close).
+        */
+       bool                    up;
+
        /**
         * @lock: netdev-scope lock, protects a small selection of fields.
         * Should always be taken using netdev_lock() / netdev_unlock() helpers.
         * Drivers are free to use it for other protection.
         *
-        * Protects: @reg_state, @net_shaper_hierarchy.
+        * Protects:
+        *      @net_shaper_hierarchy, @reg_state
+        *
+        * Partially protects (writers must hold both @lock and rtnl_lock):
+        *      @up
+        *
         * Ordering: take after rtnl_lock.
         */
        struct mutex            lock;
index 968603cfed095ce8550f036e29966eee30acab34..65bf95593da7d3a14c91e9f9dcda6ae565dada78 100644 (file)
@@ -1619,7 +1619,7 @@ static int __dev_open(struct net_device *dev, struct netlink_ext_ack *extack)
        if (ret)
                clear_bit(__LINK_STATE_START, &dev->state);
        else {
-               dev->flags |= IFF_UP;
+               netif_set_up(dev, true);
                dev_set_rx_mode(dev);
                dev_activate(dev);
                add_device_randomness(dev->dev_addr, dev->addr_len);
@@ -1698,7 +1698,7 @@ static void __dev_close_many(struct list_head *head)
                if (ops->ndo_stop)
                        ops->ndo_stop(dev);
 
-               dev->flags &= ~IFF_UP;
+               netif_set_up(dev, false);
                netpoll_poll_enable(dev);
        }
 }
index 25ae732c07758e2d2227796257561c2e680f61f0..ef37e2dd44f4417cbb23cd5ad60f2154cb130b04 100644 (file)
@@ -128,6 +128,18 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
 void unregister_netdevice_many_notify(struct list_head *head,
                                      u32 portid, const struct nlmsghdr *nlh);
 
+static inline void netif_set_up(struct net_device *dev, bool value)
+{
+       if (value)
+               dev->flags |= IFF_UP;
+       else
+               dev->flags &= ~IFF_UP;
+
+       netdev_lock(dev);
+       dev->up = value;
+       netdev_unlock(dev);
+}
+
 static inline void netif_set_gso_max_size(struct net_device *dev,
                                          unsigned int size)
 {