Skip to content

Commit

Permalink
Merge branch 'bridge-handle-changes-in-vlan_flag_bridge_binding'
Browse files Browse the repository at this point in the history
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/cover.1660100506.git.sevinj.aghayeva@gmail.com/
====================

Link: https://patch.msgid.link/cover.1734540770.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
kuba-moo committed Dec 20, 2024
2 parents 05dd04b + dca12e9 commit c1bc6d2
Show file tree
Hide file tree
Showing 6 changed files with 340 additions and 8 deletions.
7 changes: 7 additions & 0 deletions net/bridge/br.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
}
}

if (is_vlan_dev(dev)) {
struct net_device *real_dev = vlan_dev_real_dev(dev);

if (netif_is_bridge_master(real_dev))
br_vlan_vlan_upper_event(real_dev, dev, event);
}

/* not a port of a bridge */
p = br_port_get_rtnl(dev);
if (!p)
Expand Down
9 changes: 9 additions & 0 deletions net/bridge/br_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,9 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event);
int br_vlan_bridge_event(struct net_device *dev, unsigned long event,
void *ptr);
void br_vlan_vlan_upper_event(struct net_device *br_dev,
struct net_device *vlan_dev,
unsigned long event);
int br_vlan_rtnl_init(void);
void br_vlan_rtnl_uninit(void);
void br_vlan_notify(const struct net_bridge *br,
Expand Down Expand Up @@ -1802,6 +1805,12 @@ static inline int br_vlan_bridge_event(struct net_device *dev,
return 0;
}

static inline void br_vlan_vlan_upper_event(struct net_device *br_dev,
struct net_device *vlan_dev,
unsigned long event)
{
}

static inline int br_vlan_rtnl_init(void)
{
return 0;
Expand Down
44 changes: 38 additions & 6 deletions net/bridge/br_vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,18 @@ static void br_vlan_set_all_vlan_dev_state(struct net_bridge_port *p)
}
}

static void br_vlan_toggle_bridge_binding(struct net_device *br_dev,
bool enable)
{
struct net_bridge *br = netdev_priv(br_dev);

if (enable)
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
else
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
br_vlan_has_upper_bind_vlan_dev(br_dev));
}

static void br_vlan_upper_change(struct net_device *dev,
struct net_device *upper_dev,
bool linking)
Expand All @@ -1673,13 +1685,9 @@ static void br_vlan_upper_change(struct net_device *dev,
if (!br_vlan_is_bind_vlan_dev(upper_dev))
return;

if (linking) {
br_vlan_toggle_bridge_binding(dev, linking);
if (linking)
br_vlan_set_vlan_dev_state(br, upper_dev);
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING, true);
} else {
br_opt_toggle(br, BROPT_VLAN_BRIDGE_BINDING,
br_vlan_has_upper_bind_vlan_dev(dev));
}
}

struct br_vlan_link_state_walk_data {
Expand Down Expand Up @@ -1764,6 +1772,30 @@ int br_vlan_bridge_event(struct net_device *dev, unsigned long event, void *ptr)
return ret;
}

void br_vlan_vlan_upper_event(struct net_device *br_dev,
struct net_device *vlan_dev,
unsigned long event)
{
struct vlan_dev_priv *vlan = vlan_dev_priv(vlan_dev);
struct net_bridge *br = netdev_priv(br_dev);
bool bridge_binding;

switch (event) {
case NETDEV_CHANGE:
case NETDEV_UP:
break;
default:
return;
}

bridge_binding = vlan->flags & VLAN_FLAG_BRIDGE_BINDING;
br_vlan_toggle_bridge_binding(br_dev, bridge_binding);
if (bridge_binding)
br_vlan_set_vlan_dev_state(br, vlan_dev);
else if (!bridge_binding && netif_carrier_ok(br_dev))
netif_carrier_on(vlan_dev);
}

/* Must be protected by RTNL. */
void br_vlan_port_event(struct net_bridge_port *p, unsigned long event)
{
Expand Down
1 change: 1 addition & 0 deletions tools/testing/selftests/net/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ TEST_PROGS += test_bridge_backup_port.sh
TEST_PROGS += fdb_flush.sh fdb_notify.sh
TEST_PROGS += fq_band_pktlimit.sh
TEST_PROGS += vlan_hw_filter.sh
TEST_PROGS += vlan_bridge_binding.sh
TEST_PROGS += bpf_offload.py
TEST_PROGS += ipv6_route_update_soft_lockup.sh
TEST_PROGS += busy_poll_test.sh
Expand Down
31 changes: 29 additions & 2 deletions tools/testing/selftests/net/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -477,12 +477,33 @@ ip_link_set_addr()
defer ip link set dev "$name" address "$old_addr"
}

ip_link_is_up()
{
local name=$1; shift

local state=$(ip -j link show "$name" |
jq -r '(.[].flags[] | select(. == "UP")) // "DOWN"')
[[ $state == "UP" ]]
}

ip_link_set_up()
{
local name=$1; shift

ip link set dev "$name" up
defer ip link set dev "$name" down
if ! ip_link_is_up "$name"; then
ip link set dev "$name" up
defer ip link set dev "$name" down
fi
}

ip_link_set_down()
{
local name=$1; shift

if ip_link_is_up "$name"; then
ip link set dev "$name" down
defer ip link set dev "$name" up
fi
}

ip_addr_add()
Expand All @@ -498,3 +519,9 @@ ip_route_add()
ip route add "$@"
defer ip route del "$@"
}

bridge_vlan_add()
{
bridge vlan add "$@"
defer bridge vlan del "$@"
}
Loading

0 comments on commit c1bc6d2

Please sign in to comment.