Skip to content

Commit c1bc6d2

Browse files
committed
Merge branch 'bridge-handle-changes-in-vlan_flag_bridge_binding'
Petr Machata says: ==================== bridge: Handle changes in VLAN_FLAG_BRIDGE_BINDING When bridge binding is enabled on a VLAN netdevice, its link state should track bridge ports that are members of the corresponding VLAN. This works for a newly-added netdevices. However toggling the option does not have the effect of enabling or disabling the behavior as appropriate. In this patchset, have bridge react to bridge_binding toggles on VLAN uppers. There has been another attempt at supporting this behavior in 2022 by Sevinj Aghayeva [0]. A discussion ensued that informed how this new patchset is constructed, namely that the logic is in the bridge as opposed to the 8021q driver, and the bridge reacts to NETDEV_CHANGE events on the 8021q upper. Patches #1 and #2 contain the implementation, patches #3 and #4 a selftest. [0] https://lore.kernel.org/netdev/[email protected]/ ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 05dd04b + dca12e9 commit c1bc6d2

File tree

6 files changed

+340
-8
lines changed

6 files changed

+340
-8
lines changed

net/bridge/br.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
5151
}
5252
}
5353

54+
if (is_vlan_dev(dev)) {
55+
struct net_device *real_dev = vlan_dev_real_dev(dev);
56+
57+
if (netif_is_bridge_master(real_dev))
58+
br_vlan_vlan_upper_event(real_dev, dev, event);
59+
}
60+
5461
/* not a port of a bridge */
5562
p = br_port_get_rtnl(dev);
5663
if (!p)

net/bridge/br_private.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,9 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
15711571
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event);
15721572
int br_vlan_bridge_event(struct net_device *dev, unsigned long event,
15731573
void *ptr);
1574+
void br_vlan_vlan_upper_event(struct net_device *br_dev,
1575+
struct net_device *vlan_dev,
1576+
unsigned long event);
15741577
int br_vlan_rtnl_init(void);
15751578
void br_vlan_rtnl_uninit(void);
15761579
void br_vlan_notify(const struct net_bridge *br,
@@ -1802,6 +1805,12 @@ static inline int br_vlan_bridge_event(struct net_device *dev,
18021805
return 0;
18031806
}
18041807

1808+
static inline void br_vlan_vlan_upper_event(struct net_device *br_dev,
1809+
struct net_device *vlan_dev,
1810+
unsigned long event)
1811+
{
1812+
}
1813+
18051814
static inline int br_vlan_rtnl_init(void)
18061815
{
18071816
return 0;

net/bridge/br_vlan.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,18 @@ static void br_vlan_set_all_vlan_dev_state(struct net_bridge_port *p)
16641664
}
16651665
}
16661666

1667+
static void br_vlan_toggle_bridge_binding(struct net_device *br_dev,
1668+
bool enable)
1669+
{
1670+
struct net_bridge *br = netdev_priv(br_dev);
1671+
1672+
if (enable)
1673+
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
1674+
else
1675+
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
1676+
br_vlan_has_upper_bind_vlan_dev(br_dev));
1677+
}
1678+
16671679
static void br_vlan_upper_change(struct net_device *dev,
16681680
struct net_device *upper_dev,
16691681
bool linking)
@@ -1673,13 +1685,9 @@ static void br_vlan_upper_change(struct net_device *dev,
16731685
if (!br_vlan_is_bind_vlan_dev(upper_dev))
16741686
return;
16751687

1676-
if (linking) {
1688+
br_vlan_toggle_bridge_binding(dev, linking);
1689+
if (linking)
16771690
br_vlan_set_vlan_dev_state(br, upper_dev);
1678-
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
1679-
} else {
1680-
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
1681-
br_vlan_has_upper_bind_vlan_dev(dev));
1682-
}
16831691
}
16841692

16851693
struct br_vlan_link_state_walk_data {
@@ -1764,6 +1772,30 @@ int br_vlan_bridge_event(struct net_device *dev, unsigned long event, void *ptr)
17641772
return ret;
17651773
}
17661774

1775+
void br_vlan_vlan_upper_event(struct net_device *br_dev,
1776+
struct net_device *vlan_dev,
1777+
unsigned long event)
1778+
{
1779+
struct vlan_dev_priv *vlan = vlan_dev_priv(vlan_dev);
1780+
struct net_bridge *br = netdev_priv(br_dev);
1781+
bool bridge_binding;
1782+
1783+
switch (event) {
1784+
case NETDEV_CHANGE:
1785+
case NETDEV_UP:
1786+
break;
1787+
default:
1788+
return;
1789+
}
1790+
1791+
bridge_binding = vlan->flags & VLAN_FLAG_BRIDGE_BINDING;
1792+
br_vlan_toggle_bridge_binding(br_dev, bridge_binding);
1793+
if (bridge_binding)
1794+
br_vlan_set_vlan_dev_state(br, vlan_dev);
1795+
else if (!bridge_binding && netif_carrier_ok(br_dev))
1796+
netif_carrier_on(vlan_dev);
1797+
}
1798+
17671799
/* Must be protected by RTNL. */
17681800
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event)
17691801
{

tools/testing/selftests/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ TEST_PROGS += test_bridge_backup_port.sh
9696
TEST_PROGS += fdb_flush.sh fdb_notify.sh
9797
TEST_PROGS += fq_band_pktlimit.sh
9898
TEST_PROGS += vlan_hw_filter.sh
99+
TEST_PROGS += vlan_bridge_binding.sh
99100
TEST_PROGS += bpf_offload.py
100101
TEST_PROGS += ipv6_route_update_soft_lockup.sh
101102
TEST_PROGS += busy_poll_test.sh

tools/testing/selftests/net/lib.sh

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,33 @@ ip_link_set_addr()
477477
defer ip link set dev "$name" address "$old_addr"
478478
}
479479

480+
ip_link_is_up()
481+
{
482+
local name=$1; shift
483+
484+
local state=$(ip -j link show "$name" |
485+
jq -r '(.[].flags[] | select(. == "UP")) // "DOWN"')
486+
[[ $state == "UP" ]]
487+
}
488+
480489
ip_link_set_up()
481490
{
482491
local name=$1; shift
483492

484-
ip link set dev "$name" up
485-
defer ip link set dev "$name" down
493+
if ! ip_link_is_up "$name"; then
494+
ip link set dev "$name" up
495+
defer ip link set dev "$name" down
496+
fi
497+
}
498+
499+
ip_link_set_down()
500+
{
501+
local name=$1; shift
502+
503+
if ip_link_is_up "$name"; then
504+
ip link set dev "$name" down
505+
defer ip link set dev "$name" up
506+
fi
486507
}
487508

488509
ip_addr_add()
@@ -498,3 +519,9 @@ ip_route_add()
498519
ip route add "$@"
499520
defer ip route del "$@"
500521
}
522+
523+
bridge_vlan_add()
524+
{
525+
bridge vlan add "$@"
526+
defer bridge vlan del "$@"
527+
}

0 commit comments

Comments
 (0)