mirror of
https://github.com/torvalds/linux.git
synced 2026-05-23 14:42:08 +02:00
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/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>
This commit is contained in:
commit
c1bc6d2178
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -498,3 +519,9 @@ ip_route_add()
|
|||
ip route add "$@"
|
||||
defer ip route del "$@"
|
||||
}
|
||||
|
||||
bridge_vlan_add()
|
||||
{
|
||||
bridge vlan add "$@"
|
||||
defer bridge vlan del "$@"
|
||||
}
|
||||
|
|
|
|||
256
tools/testing/selftests/net/vlan_bridge_binding.sh
Executable file
256
tools/testing/selftests/net/vlan_bridge_binding.sh
Executable file
|
|
@ -0,0 +1,256 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
source lib.sh
|
||||
|
||||
ALL_TESTS="
|
||||
test_binding_on
|
||||
test_binding_off
|
||||
test_binding_toggle_on
|
||||
test_binding_toggle_off
|
||||
test_binding_toggle_on_when_upper_down
|
||||
test_binding_toggle_off_when_upper_down
|
||||
test_binding_toggle_on_when_lower_down
|
||||
test_binding_toggle_off_when_lower_down
|
||||
"
|
||||
|
||||
setup_prepare()
|
||||
{
|
||||
local port
|
||||
|
||||
ip_link_add br up type bridge vlan_filtering 1
|
||||
|
||||
for port in d1 d2 d3; do
|
||||
ip_link_add $port type veth peer name r$port
|
||||
ip_link_set_up $port
|
||||
ip_link_set_up r$port
|
||||
ip_link_set_master $port br
|
||||
done
|
||||
|
||||
bridge_vlan_add vid 11 dev br self
|
||||
bridge_vlan_add vid 11 dev d1 master
|
||||
|
||||
bridge_vlan_add vid 12 dev br self
|
||||
bridge_vlan_add vid 12 dev d2 master
|
||||
|
||||
bridge_vlan_add vid 13 dev br self
|
||||
bridge_vlan_add vid 13 dev d1 master
|
||||
bridge_vlan_add vid 13 dev d2 master
|
||||
|
||||
bridge_vlan_add vid 14 dev br self
|
||||
bridge_vlan_add vid 14 dev d1 master
|
||||
bridge_vlan_add vid 14 dev d2 master
|
||||
bridge_vlan_add vid 14 dev d3 master
|
||||
}
|
||||
|
||||
operstate_is()
|
||||
{
|
||||
local dev=$1; shift
|
||||
local expect=$1; shift
|
||||
|
||||
local operstate=$(ip -j link show $dev | jq -r .[].operstate)
|
||||
if [[ $operstate == UP ]]; then
|
||||
operstate=1
|
||||
elif [[ $operstate == DOWN || $operstate == LOWERLAYERDOWN ]]; then
|
||||
operstate=0
|
||||
fi
|
||||
echo -n $operstate
|
||||
[[ $operstate == $expect ]]
|
||||
}
|
||||
|
||||
check_operstate()
|
||||
{
|
||||
local dev=$1; shift
|
||||
local expect=$1; shift
|
||||
local operstate
|
||||
|
||||
operstate=$(busywait 1000 \
|
||||
operstate_is "$dev" "$expect")
|
||||
check_err $? "Got operstate of $operstate, expected $expect"
|
||||
}
|
||||
|
||||
add_one_vlan()
|
||||
{
|
||||
local link=$1; shift
|
||||
local id=$1; shift
|
||||
|
||||
ip_link_add $link.$id link $link type vlan id $id "$@"
|
||||
}
|
||||
|
||||
add_vlans()
|
||||
{
|
||||
add_one_vlan br 11 "$@"
|
||||
add_one_vlan br 12 "$@"
|
||||
add_one_vlan br 13 "$@"
|
||||
add_one_vlan br 14 "$@"
|
||||
}
|
||||
|
||||
set_vlans()
|
||||
{
|
||||
ip link set dev br.11 "$@"
|
||||
ip link set dev br.12 "$@"
|
||||
ip link set dev br.13 "$@"
|
||||
ip link set dev br.14 "$@"
|
||||
}
|
||||
|
||||
down_netdevs()
|
||||
{
|
||||
local dev
|
||||
|
||||
for dev in "$@"; do
|
||||
ip_link_set_down $dev
|
||||
done
|
||||
}
|
||||
|
||||
check_operstates()
|
||||
{
|
||||
local opst_11=$1; shift
|
||||
local opst_12=$1; shift
|
||||
local opst_13=$1; shift
|
||||
local opst_14=$1; shift
|
||||
|
||||
check_operstate br.11 $opst_11
|
||||
check_operstate br.12 $opst_12
|
||||
check_operstate br.13 $opst_13
|
||||
check_operstate br.14 $opst_14
|
||||
}
|
||||
|
||||
do_test_binding()
|
||||
{
|
||||
local inject=$1; shift
|
||||
local what=$1; shift
|
||||
local opsts_d1=$1; shift
|
||||
local opsts_d2=$1; shift
|
||||
local opsts_d12=$1; shift
|
||||
local opsts_d123=$1; shift
|
||||
|
||||
RET=0
|
||||
|
||||
defer_scope_push
|
||||
down_netdevs d1
|
||||
$inject
|
||||
check_operstates $opsts_d1
|
||||
defer_scope_pop
|
||||
|
||||
defer_scope_push
|
||||
down_netdevs d2
|
||||
$inject
|
||||
check_operstates $opsts_d2
|
||||
defer_scope_pop
|
||||
|
||||
defer_scope_push
|
||||
down_netdevs d1 d2
|
||||
$inject
|
||||
check_operstates $opsts_d12
|
||||
defer_scope_pop
|
||||
|
||||
defer_scope_push
|
||||
down_netdevs d1 d2 d3
|
||||
$inject
|
||||
check_operstates $opsts_d123
|
||||
defer_scope_pop
|
||||
|
||||
log_test "Test bridge_binding $what"
|
||||
}
|
||||
|
||||
do_test_binding_on()
|
||||
{
|
||||
local inject=$1; shift
|
||||
local what=$1; shift
|
||||
|
||||
do_test_binding "$inject" "$what" \
|
||||
"0 1 1 1" \
|
||||
"1 0 1 1" \
|
||||
"0 0 0 1" \
|
||||
"0 0 0 0"
|
||||
}
|
||||
|
||||
do_test_binding_off()
|
||||
{
|
||||
local inject=$1; shift
|
||||
local what=$1; shift
|
||||
|
||||
do_test_binding "$inject" "$what" \
|
||||
"1 1 1 1" \
|
||||
"1 1 1 1" \
|
||||
"1 1 1 1" \
|
||||
"0 0 0 0"
|
||||
}
|
||||
|
||||
test_binding_on()
|
||||
{
|
||||
add_vlans bridge_binding on
|
||||
set_vlans up
|
||||
do_test_binding_on : "on"
|
||||
}
|
||||
|
||||
test_binding_off()
|
||||
{
|
||||
add_vlans bridge_binding off
|
||||
set_vlans up
|
||||
do_test_binding_off : "off"
|
||||
}
|
||||
|
||||
test_binding_toggle_on()
|
||||
{
|
||||
add_vlans bridge_binding off
|
||||
set_vlans up
|
||||
set_vlans type vlan bridge_binding on
|
||||
do_test_binding_on : "off->on"
|
||||
}
|
||||
|
||||
test_binding_toggle_off()
|
||||
{
|
||||
add_vlans bridge_binding on
|
||||
set_vlans up
|
||||
set_vlans type vlan bridge_binding off
|
||||
do_test_binding_off : "on->off"
|
||||
}
|
||||
|
||||
dfr_set_binding_on()
|
||||
{
|
||||
set_vlans type vlan bridge_binding on
|
||||
defer set_vlans type vlan bridge_binding off
|
||||
}
|
||||
|
||||
dfr_set_binding_off()
|
||||
{
|
||||
set_vlans type vlan bridge_binding off
|
||||
defer set_vlans type vlan bridge_binding on
|
||||
}
|
||||
|
||||
test_binding_toggle_on_when_lower_down()
|
||||
{
|
||||
add_vlans bridge_binding off
|
||||
set_vlans up
|
||||
do_test_binding_on dfr_set_binding_on "off->on when lower down"
|
||||
}
|
||||
|
||||
test_binding_toggle_off_when_lower_down()
|
||||
{
|
||||
add_vlans bridge_binding on
|
||||
set_vlans up
|
||||
do_test_binding_off dfr_set_binding_off "on->off when lower down"
|
||||
}
|
||||
|
||||
test_binding_toggle_on_when_upper_down()
|
||||
{
|
||||
add_vlans bridge_binding off
|
||||
set_vlans type vlan bridge_binding on
|
||||
set_vlans up
|
||||
do_test_binding_on : "off->on when upper down"
|
||||
}
|
||||
|
||||
test_binding_toggle_off_when_upper_down()
|
||||
{
|
||||
add_vlans bridge_binding on
|
||||
set_vlans type vlan bridge_binding off
|
||||
set_vlans up
|
||||
do_test_binding_off : "on->off when upper down"
|
||||
}
|
||||
|
||||
trap defer_scopes_cleanup EXIT
|
||||
setup_prepare
|
||||
tests_run
|
||||
|
||||
exit $EXIT_STATUS
|
||||
Loading…
Reference in New Issue
Block a user