]> git.dujemihanovic.xyz Git - linux.git/commitdiff
rtnetlink: do not depend on RTNL in rtnl_fill_proto_down()
authorEric Dumazet <edumazet@google.com>
Fri, 3 May 2024 19:20:57 +0000 (19:20 +0000)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 7 May 2024 09:14:50 +0000 (11:14 +0200)
Change dev_change_proto_down() and dev_change_proto_down_reason()
to write once on dev->proto_down and dev->proto_down_reason.

Then rtnl_fill_proto_down() can use READ_ONCE() annotations
and run locklessly.

rtnl_proto_down_size() should assume worst case,
because readng dev->proto_down_reason multiple
times would be racy without RTNL in the future.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/core/dev.c
net/core/rtnetlink.c

index d9d9a0d91593c9cfdb9de5646bd55c6eefb4a370..d2ce91a334c1de276ea7e419f1eeb00bd705a6d1 100644 (file)
@@ -9223,7 +9223,7 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)
                netif_carrier_off(dev);
        else
                netif_carrier_on(dev);
-       dev->proto_down = proto_down;
+       WRITE_ONCE(dev->proto_down, proto_down);
        return 0;
 }
 
@@ -9237,18 +9237,21 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)
 void dev_change_proto_down_reason(struct net_device *dev, unsigned long mask,
                                  u32 value)
 {
+       u32 proto_down_reason;
        int b;
 
        if (!mask) {
-               dev->proto_down_reason = value;
+               proto_down_reason = value;
        } else {
+               proto_down_reason = dev->proto_down_reason;
                for_each_set_bit(b, &mask, 32) {
                        if (value & (1 << b))
-                               dev->proto_down_reason |= BIT(b);
+                               proto_down_reason |= BIT(b);
                        else
-                               dev->proto_down_reason &= ~BIT(b);
+                               proto_down_reason &= ~BIT(b);
                }
        }
+       WRITE_ONCE(dev->proto_down_reason, proto_down_reason);
 }
 
 struct bpf_xdp_link {
index 16b5f9c68bbf4f61440ece89f59880784a54f239..dfdd08006faec016c24c0a3ff3bba4f47e9357c3 100644 (file)
@@ -1036,8 +1036,8 @@ static size_t rtnl_proto_down_size(const struct net_device *dev)
 {
        size_t size = nla_total_size(1);
 
-       if (dev->proto_down_reason)
-               size += nla_total_size(0) + nla_total_size(4);
+       /* Assume dev->proto_down_reason is not zero. */
+       size += nla_total_size(0) + nla_total_size(4);
 
        return size;
 }
@@ -1737,10 +1737,10 @@ static int rtnl_fill_proto_down(struct sk_buff *skb,
        struct nlattr *pr;
        u32 preason;
 
-       if (nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down))
+       if (nla_put_u8(skb, IFLA_PROTO_DOWN, READ_ONCE(dev->proto_down)))
                goto nla_put_failure;
 
-       preason = dev->proto_down_reason;
+       preason = READ_ONCE(dev->proto_down_reason);
        if (!preason)
                return 0;