diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-05-09 02:50:29 +0300 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-05-09 02:50:30 +0300 |
| commit | d3f65237fa68ec83f025a718e11e9c503ff715c8 (patch) | |
| tree | 543e8622d5eec102bc7570fc262c85b584214147 /net | |
| parent | dfc86291d99b1fe513fd0b470592eb4ed58212d5 (diff) | |
| parent | c0375944d99781e13285a62a3485257ad488b30c (diff) | |
| download | linux-d3f65237fa68ec83f025a718e11e9c503ff715c8.tar.xz | |
Merge branch 'net-fix-protodown-with-macvlan'
Ido Schimmel says:
====================
net: Fix protodown with macvlan
When protodown is enabled on a macvlan, two bugs cause the macvlan to
incorrectly gain carrier:
1. Toggling the lower device's carrier while protodown is enabled on the
macvlan causes the macvlan to gain carrier, effectively bypassing the
protodown mechanism.
2. Toggling protodown on and then off on the macvlan while the lower
device has no carrier causes the macvlan to gain carrier, since
netif_change_proto_down() unconditionally turns the carrier on.
Patch #1 is a preparation.
Patch #2 solves the first problem by making netif_carrier_on() return
early when protodown is on.
Patch #3 solves the second problem by only calling netif_carrier_on()
when protodown is turned off if there is no linked net device or if the
linked net device has a carrier.
Patch #4 adds a selftest covering both bugs and the basic protodown
functionality.
Targeting at net-next since these are not regressions (i.e., never
worked).
Note that while these changes are in the core, they should only affect
macvlan as protodown is only supported by macvlan and vxlan and only
the former has a linked net device.
====================
Link: https://patch.msgid.link/20260507105906.891817-1-idosch@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net')
| -rw-r--r-- | net/core/dev.c | 23 | ||||
| -rw-r--r-- | net/sched/sch_generic.c | 3 |
2 files changed, 24 insertions, 2 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 8bfa8313ef62..b0691e03dd6b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -10141,17 +10141,36 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b) } EXPORT_SYMBOL(netdev_port_same_parent_id); +static struct net_device *dev_get_iflink_dev(struct net_device *dev) +{ + struct net *net; + + ASSERT_RTNL(); + + if (!dev->netdev_ops->ndo_get_iflink || !dev->rtnl_link_ops || + !dev->rtnl_link_ops->get_link_net) + return dev; + + net = dev->rtnl_link_ops->get_link_net(dev); + return __dev_get_by_index(net, dev_get_iflink(dev)); +} + int netif_change_proto_down(struct net_device *dev, bool proto_down) { + struct net_device *iflink_dev; + if (!dev->change_proto_down) return -EOPNOTSUPP; if (!netif_device_present(dev)) return -ENODEV; + iflink_dev = dev_get_iflink_dev(dev); + if (!iflink_dev) + return -ENODEV; + WRITE_ONCE(dev->proto_down, proto_down); if (proto_down) netif_carrier_off(dev); - else + else if (dev == iflink_dev || netif_carrier_ok(iflink_dev)) netif_carrier_on(dev); - WRITE_ONCE(dev->proto_down, proto_down); return 0; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a93321db8fd7..05c250c483f0 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -609,6 +609,9 @@ static void netdev_watchdog_down(struct net_device *dev) */ void netif_carrier_on(struct net_device *dev) { + if (READ_ONCE(dev->proto_down)) + return; + if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) { if (dev->reg_state == NETREG_UNINITIALIZED) return; |
