Skip to content

Commit 88922fe

Browse files
committed
Merge branch 'bridge-mc-per-vlan-qquery'
Yong Wang says: ==================== bridge: multicast: per vlan query improvement when port or vlan state changes The current implementation of br_multicast_enable_port() only operates on port's multicast context, which doesn't take into account in case of vlan snooping, one downside is the port's igmp query timer will NOT resume when port state gets changed from BR_STATE_BLOCKING to BR_STATE_FORWARDING etc. Such code flow will briefly look like: 1.vlan snooping --> br_multicast_port_query_expired with per vlan port_mcast_ctx --> port in BR_STATE_BLOCKING state --> then one-shot timer discontinued The port state could be changed by STP daemon or kernel STP, taking mstpd as example: 2.mstpd --> netlink_sendmsg --> br_setlink --> br_set_port_state with non blocking states, i.e. BR_STATE_LEARNING or BR_STATE_FORWARDING --> br_port_state_selection --> br_multicast_enable_port --> enable multicast with port's multicast_ctx Here for per vlan snooping, the vlan context of the port should be used instead of port's multicast_ctx. The first patch corrects such behavior. Similarly, vlan state change also impacts multicast behavior, the 2nd patch adds function to update the corresponding multicast context when vlan state changes. The 3rd patch adds the selftests to confirm that IGMP/MLD query does happen when the STP state becomes forwarding. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 0e0a7e3 + aea4536 commit 88922fe

File tree

6 files changed

+261
-19
lines changed

6 files changed

+261
-19
lines changed

net/bridge/br_mst.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,10 @@ static void br_mst_vlan_set_state(struct net_bridge_vlan_group *vg,
8080
if (br_vlan_get_state(v) == state)
8181
return;
8282

83-
br_vlan_set_state(v, state);
84-
8583
if (v->vid == vg->pvid)
8684
br_vlan_set_pvid_state(vg, state);
85+
86+
br_vlan_set_state(v, state);
8787
}
8888

8989
int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,

net/bridge/br_multicast.c

+95-8
Original file line numberDiff line numberDiff line change
@@ -2105,12 +2105,17 @@ static void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
21052105
}
21062106
}
21072107

2108-
void br_multicast_enable_port(struct net_bridge_port *port)
2108+
static void br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
21092109
{
2110-
struct net_bridge *br = port->br;
2110+
struct net_bridge *br = pmctx->port->br;
21112111

21122112
spin_lock_bh(&br->multicast_lock);
2113-
__br_multicast_enable_port_ctx(&port->multicast_ctx);
2113+
if (br_multicast_port_ctx_is_vlan(pmctx) &&
2114+
!(pmctx->vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED)) {
2115+
spin_unlock_bh(&br->multicast_lock);
2116+
return;
2117+
}
2118+
__br_multicast_enable_port_ctx(pmctx);
21142119
spin_unlock_bh(&br->multicast_lock);
21152120
}
21162121

@@ -2137,11 +2142,67 @@ static void __br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx)
21372142
br_multicast_rport_del_notify(pmctx, del);
21382143
}
21392144

2145+
static void br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx)
2146+
{
2147+
struct net_bridge *br = pmctx->port->br;
2148+
2149+
spin_lock_bh(&br->multicast_lock);
2150+
if (br_multicast_port_ctx_is_vlan(pmctx) &&
2151+
!(pmctx->vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED)) {
2152+
spin_unlock_bh(&br->multicast_lock);
2153+
return;
2154+
}
2155+
2156+
__br_multicast_disable_port_ctx(pmctx);
2157+
spin_unlock_bh(&br->multicast_lock);
2158+
}
2159+
2160+
static void br_multicast_toggle_port(struct net_bridge_port *port, bool on)
2161+
{
2162+
#if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING)
2163+
if (br_opt_get(port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
2164+
struct net_bridge_vlan_group *vg;
2165+
struct net_bridge_vlan *vlan;
2166+
2167+
rcu_read_lock();
2168+
vg = nbp_vlan_group_rcu(port);
2169+
if (!vg) {
2170+
rcu_read_unlock();
2171+
return;
2172+
}
2173+
2174+
/* iterate each vlan, toggle vlan multicast context */
2175+
list_for_each_entry_rcu(vlan, &vg->vlan_list, vlist) {
2176+
struct net_bridge_mcast_port *pmctx =
2177+
&vlan->port_mcast_ctx;
2178+
u8 state = br_vlan_get_state(vlan);
2179+
/* enable vlan multicast context when state is
2180+
* LEARNING or FORWARDING
2181+
*/
2182+
if (on && br_vlan_state_allowed(state, true))
2183+
br_multicast_enable_port_ctx(pmctx);
2184+
else
2185+
br_multicast_disable_port_ctx(pmctx);
2186+
}
2187+
rcu_read_unlock();
2188+
return;
2189+
}
2190+
#endif
2191+
/* toggle port multicast context when vlan snooping is disabled */
2192+
if (on)
2193+
br_multicast_enable_port_ctx(&port->multicast_ctx);
2194+
else
2195+
br_multicast_disable_port_ctx(&port->multicast_ctx);
2196+
}
2197+
2198+
void br_multicast_enable_port(struct net_bridge_port *port)
2199+
{
2200+
br_multicast_toggle_port(port, true);
2201+
}
2202+
21402203
void br_multicast_disable_port(struct net_bridge_port *port)
21412204
{
2142-
spin_lock_bh(&port->br->multicast_lock);
2143-
__br_multicast_disable_port_ctx(&port->multicast_ctx);
2144-
spin_unlock_bh(&port->br->multicast_lock);
2205+
br_multicast_toggle_port(port, false);
21452206
}
21462207

21472208
static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
@@ -4211,6 +4272,32 @@ static void __br_multicast_stop(struct net_bridge_mcast *brmctx)
42114272
#endif
42124273
}
42134274

4275+
void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v, u8 state)
4276+
{
4277+
#if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING)
4278+
struct net_bridge *br;
4279+
4280+
if (!br_vlan_should_use(v))
4281+
return;
4282+
4283+
if (br_vlan_is_master(v))
4284+
return;
4285+
4286+
br = v->port->br;
4287+
4288+
if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
4289+
return;
4290+
4291+
if (br_vlan_state_allowed(state, true))
4292+
br_multicast_enable_port_ctx(&v->port_mcast_ctx);
4293+
4294+
/* Multicast is not disabled for the vlan when it goes in
4295+
* blocking state because the timers will expire and stop by
4296+
* themselves without sending more queries.
4297+
*/
4298+
#endif
4299+
}
4300+
42144301
void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
42154302
{
42164303
struct net_bridge *br;
@@ -4304,9 +4391,9 @@ int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
43044391
__br_multicast_open(&br->multicast_ctx);
43054392
list_for_each_entry(p, &br->port_list, list) {
43064393
if (on)
4307-
br_multicast_disable_port(p);
4394+
br_multicast_disable_port_ctx(&p->multicast_ctx);
43084395
else
4309-
br_multicast_enable_port(p);
4396+
br_multicast_enable_port_ctx(&p->multicast_ctx);
43104397
}
43114398

43124399
list_for_each_entry(vlan, &vg->vlan_list, vlist)

net/bridge/br_private.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -1055,6 +1055,7 @@ void br_multicast_port_ctx_init(struct net_bridge_port *port,
10551055
struct net_bridge_vlan *vlan,
10561056
struct net_bridge_mcast_port *pmctx);
10571057
void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx);
1058+
void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v, u8 state);
10581059
void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on);
10591060
int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
10601061
struct netlink_ext_ack *extack);
@@ -1521,6 +1522,11 @@ static inline void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pm
15211522
{
15221523
}
15231524

1525+
static inline void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v,
1526+
u8 state)
1527+
{
1528+
}
1529+
15241530
static inline void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan,
15251531
bool on)
15261532
{
@@ -1881,7 +1887,9 @@ bool br_vlan_global_opts_can_enter_range(const struct net_bridge_vlan *v_curr,
18811887
bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
18821888
const struct net_bridge_vlan *v_opts);
18831889

1884-
/* vlan state manipulation helpers using *_ONCE to annotate lock-free access */
1890+
/* vlan state manipulation helpers using *_ONCE to annotate lock-free access,
1891+
* while br_vlan_set_state() may access data protected by multicast_lock.
1892+
*/
18851893
static inline u8 br_vlan_get_state(const struct net_bridge_vlan *v)
18861894
{
18871895
return READ_ONCE(v->state);
@@ -1890,6 +1898,7 @@ static inline u8 br_vlan_get_state(const struct net_bridge_vlan *v)
18901898
static inline void br_vlan_set_state(struct net_bridge_vlan *v, u8 state)
18911899
{
18921900
WRITE_ONCE(v->state, state);
1901+
br_multicast_update_vlan_mcast_ctx(v, state);
18931902
}
18941903

18951904
static inline u8 br_vlan_get_pvid_state(const struct net_bridge_vlan_group *vg)

tools/testing/selftests/net/forwarding/bridge_igmp.sh

+76-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
#!/bin/bash
22
# SPDX-License-Identifier: GPL-2.0
33

4-
ALL_TESTS="v2reportleave_test v3include_test v3inc_allow_test v3inc_is_include_test \
5-
v3inc_is_exclude_test v3inc_to_exclude_test v3exc_allow_test v3exc_is_include_test \
6-
v3exc_is_exclude_test v3exc_to_exclude_test v3inc_block_test v3exc_block_test \
7-
v3exc_timeout_test v3star_ex_auto_add_test"
4+
ALL_TESTS="
5+
v2reportleave_test
6+
v3include_test
7+
v3inc_allow_test
8+
v3inc_is_include_test
9+
v3inc_is_exclude_test
10+
v3inc_to_exclude_test
11+
v3exc_allow_test
12+
v3exc_is_include_test
13+
v3exc_is_exclude_test
14+
v3exc_to_exclude_test
15+
v3inc_block_test
16+
v3exc_block_test
17+
v3exc_timeout_test
18+
v3star_ex_auto_add_test
19+
v2per_vlan_snooping_port_stp_test
20+
v2per_vlan_snooping_vlan_stp_test
21+
"
822
NUM_NETIFS=4
923
CHECK_TC="yes"
1024
TEST_GROUP="239.10.10.10"
@@ -554,6 +568,64 @@ v3star_ex_auto_add_test()
554568
v3cleanup $swp2 $TEST_GROUP
555569
}
556570

571+
v2per_vlan_snooping_stp_test()
572+
{
573+
local is_port=$1
574+
575+
local msg="port"
576+
[[ $is_port -ne 1 ]] && msg="vlan"
577+
578+
ip link set br0 up type bridge vlan_filtering 1 \
579+
mcast_igmp_version 2 \
580+
mcast_snooping 1 \
581+
mcast_vlan_snooping 1 \
582+
mcast_querier 1 \
583+
mcast_stats_enabled 1
584+
bridge vlan global set vid 1 dev br0 \
585+
mcast_snooping 1 \
586+
mcast_querier 1 \
587+
mcast_query_interval 100 \
588+
mcast_startup_query_count 0
589+
[[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 0
590+
[[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 4
591+
sleep 5
592+
local tx_s=$(ip -j -p stats show dev $swp1 \
593+
group xstats_slave subgroup bridge suite mcast \
594+
| jq '.[]["multicast"]["igmp_queries"]["tx_v2"]')
595+
596+
[[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 3
597+
[[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 3
598+
sleep 5
599+
local tx_e=$(ip -j -p stats show dev $swp1 \
600+
group xstats_slave subgroup bridge suite mcast \
601+
| jq '.[]["multicast"]["igmp_queries"]["tx_v2"]')
602+
603+
RET=0
604+
local tx=$(expr $tx_e - $tx_s)
605+
test $tx -gt 0
606+
check_err $? "No IGMP queries after STP state becomes forwarding"
607+
log_test "per vlan snooping with $msg stp state change"
608+
609+
# restore settings
610+
bridge vlan global set vid 1 dev br0 \
611+
mcast_querier 0 \
612+
mcast_query_interval 12500 \
613+
mcast_startup_query_count 2
614+
ip link set br0 up type bridge vlan_filtering 0 \
615+
mcast_vlan_snooping 0 \
616+
mcast_stats_enabled 0
617+
}
618+
619+
v2per_vlan_snooping_port_stp_test()
620+
{
621+
v2per_vlan_snooping_stp_test 1
622+
}
623+
624+
v2per_vlan_snooping_vlan_stp_test()
625+
{
626+
v2per_vlan_snooping_stp_test 0
627+
}
628+
557629
trap cleanup EXIT
558630

559631
setup_prepare

tools/testing/selftests/net/forwarding/bridge_mld.sh

+77-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
#!/bin/bash
22
# SPDX-License-Identifier: GPL-2.0
33

4-
ALL_TESTS="mldv2include_test mldv2inc_allow_test mldv2inc_is_include_test mldv2inc_is_exclude_test \
5-
mldv2inc_to_exclude_test mldv2exc_allow_test mldv2exc_is_include_test \
6-
mldv2exc_is_exclude_test mldv2exc_to_exclude_test mldv2inc_block_test \
7-
mldv2exc_block_test mldv2exc_timeout_test mldv2star_ex_auto_add_test"
4+
ALL_TESTS="
5+
mldv2include_test
6+
mldv2inc_allow_test
7+
mldv2inc_is_include_test
8+
mldv2inc_is_exclude_test
9+
mldv2inc_to_exclude_test
10+
mldv2exc_allow_test
11+
mldv2exc_is_include_test
12+
mldv2exc_is_exclude_test
13+
mldv2exc_to_exclude_test
14+
mldv2inc_block_test
15+
mldv2exc_block_test
16+
mldv2exc_timeout_test
17+
mldv2star_ex_auto_add_test
18+
mldv2per_vlan_snooping_port_stp_test
19+
mldv2per_vlan_snooping_vlan_stp_test
20+
"
821
NUM_NETIFS=4
922
CHECK_TC="yes"
1023
TEST_GROUP="ff02::cc"
@@ -554,6 +567,66 @@ mldv2star_ex_auto_add_test()
554567
mldv2cleanup $swp2
555568
}
556569

570+
mldv2per_vlan_snooping_stp_test()
571+
{
572+
local is_port=$1
573+
574+
local msg="port"
575+
[[ $is_port -ne 1 ]] && msg="vlan"
576+
577+
ip link set br0 up type bridge vlan_filtering 1 \
578+
mcast_mld_version 2 \
579+
mcast_snooping 1 \
580+
mcast_vlan_snooping 1 \
581+
mcast_querier 1 \
582+
mcast_stats_enabled 1
583+
bridge vlan global set vid 1 dev br0 \
584+
mcast_mld_version 2 \
585+
mcast_snooping 1 \
586+
mcast_querier 1 \
587+
mcast_query_interval 100 \
588+
mcast_startup_query_count 0
589+
590+
[[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 0
591+
[[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 4
592+
sleep 5
593+
local tx_s=$(ip -j -p stats show dev $swp1 \
594+
group xstats_slave subgroup bridge suite mcast \
595+
| jq '.[]["multicast"]["mld_queries"]["tx_v2"]')
596+
[[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 3
597+
[[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 3
598+
sleep 5
599+
local tx_e=$(ip -j -p stats show dev $swp1 \
600+
group xstats_slave subgroup bridge suite mcast \
601+
| jq '.[]["multicast"]["mld_queries"]["tx_v2"]')
602+
603+
RET=0
604+
local tx=$(expr $tx_e - $tx_s)
605+
test $tx -gt 0
606+
check_err $? "No MLD queries after STP state becomes forwarding"
607+
log_test "per vlan snooping with $msg stp state change"
608+
609+
# restore settings
610+
bridge vlan global set vid 1 dev br0 \
611+
mcast_querier 0 \
612+
mcast_query_interval 12500 \
613+
mcast_startup_query_count 2 \
614+
mcast_mld_version 1
615+
ip link set br0 up type bridge vlan_filtering 0 \
616+
mcast_vlan_snooping 0 \
617+
mcast_stats_enabled 0
618+
}
619+
620+
mldv2per_vlan_snooping_port_stp_test()
621+
{
622+
mldv2per_vlan_snooping_stp_test 1
623+
}
624+
625+
mldv2per_vlan_snooping_vlan_stp_test()
626+
{
627+
mldv2per_vlan_snooping_stp_test 0
628+
}
629+
557630
trap cleanup EXIT
558631

559632
setup_prepare

tools/testing/selftests/net/forwarding/config

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
CONFIG_BRIDGE=m
22
CONFIG_VLAN_8021Q=m
33
CONFIG_BRIDGE_VLAN_FILTERING=y
4+
CONFIG_BRIDGE_IGMP_SNOOPING=y
45
CONFIG_NET_L3_MASTER_DEV=y
56
CONFIG_IPV6_MULTIPLE_TABLES=y
67
CONFIG_NET_VRF=m

0 commit comments

Comments
 (0)