Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <davem@davemloft.net>

+261 -19
+2 -2
net/bridge/br_mst.c
··· 80 80 if (br_vlan_get_state(v) == state) 81 81 return; 82 82 83 - br_vlan_set_state(v, state); 84 - 85 83 if (v->vid == vg->pvid) 86 84 br_vlan_set_pvid_state(vg, state); 85 + 86 + br_vlan_set_state(v, state); 87 87 } 88 88 89 89 int br_mst_set_state(struct net_bridge_port *p, u16 msti, u8 state,
+95 -8
net/bridge/br_multicast.c
··· 2105 2105 } 2106 2106 } 2107 2107 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) 2109 2109 { 2110 - struct net_bridge *br = port->br; 2110 + struct net_bridge *br = pmctx->port->br; 2111 2111 2112 2112 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); 2114 2119 spin_unlock_bh(&br->multicast_lock); 2115 2120 } 2116 2121 ··· 2142 2137 br_multicast_rport_del_notify(pmctx, del); 2143 2138 } 2144 2139 2140 + static void br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx) 2141 + { 2142 + struct net_bridge *br = pmctx->port->br; 2143 + 2144 + spin_lock_bh(&br->multicast_lock); 2145 + if (br_multicast_port_ctx_is_vlan(pmctx) && 2146 + !(pmctx->vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED)) { 2147 + spin_unlock_bh(&br->multicast_lock); 2148 + return; 2149 + } 2150 + 2151 + __br_multicast_disable_port_ctx(pmctx); 2152 + spin_unlock_bh(&br->multicast_lock); 2153 + } 2154 + 2155 + static void br_multicast_toggle_port(struct net_bridge_port *port, bool on) 2156 + { 2157 + #if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) 2158 + if (br_opt_get(port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) { 2159 + struct net_bridge_vlan_group *vg; 2160 + struct net_bridge_vlan *vlan; 2161 + 2162 + rcu_read_lock(); 2163 + vg = nbp_vlan_group_rcu(port); 2164 + if (!vg) { 2165 + rcu_read_unlock(); 2166 + return; 2167 + } 2168 + 2169 + /* iterate each vlan, toggle vlan multicast context */ 2170 + list_for_each_entry_rcu(vlan, &vg->vlan_list, vlist) { 2171 + struct net_bridge_mcast_port *pmctx = 2172 + &vlan->port_mcast_ctx; 2173 + u8 state = br_vlan_get_state(vlan); 2174 + /* enable vlan multicast context when state is 2175 + * LEARNING or FORWARDING 2176 + */ 2177 + if (on && br_vlan_state_allowed(state, true)) 2178 + br_multicast_enable_port_ctx(pmctx); 2179 + else 2180 + br_multicast_disable_port_ctx(pmctx); 2181 + } 2182 + rcu_read_unlock(); 2183 + return; 2184 + } 2185 + #endif 2186 + /* toggle port multicast context when vlan snooping is disabled */ 2187 + if (on) 2188 + br_multicast_enable_port_ctx(&port->multicast_ctx); 2189 + else 2190 + br_multicast_disable_port_ctx(&port->multicast_ctx); 2191 + } 2192 + 2193 + void br_multicast_enable_port(struct net_bridge_port *port) 2194 + { 2195 + br_multicast_toggle_port(port, true); 2196 + } 2197 + 2145 2198 void br_multicast_disable_port(struct net_bridge_port *port) 2146 2199 { 2147 - spin_lock_bh(&port->br->multicast_lock); 2148 - __br_multicast_disable_port_ctx(&port->multicast_ctx); 2149 - spin_unlock_bh(&port->br->multicast_lock); 2200 + br_multicast_toggle_port(port, false); 2150 2201 } 2151 2202 2152 2203 static int __grp_src_delete_marked(struct net_bridge_port_group *pg) ··· 4272 4211 #endif 4273 4212 } 4274 4213 4214 + void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v, u8 state) 4215 + { 4216 + #if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) 4217 + struct net_bridge *br; 4218 + 4219 + if (!br_vlan_should_use(v)) 4220 + return; 4221 + 4222 + if (br_vlan_is_master(v)) 4223 + return; 4224 + 4225 + br = v->port->br; 4226 + 4227 + if (!br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) 4228 + return; 4229 + 4230 + if (br_vlan_state_allowed(state, true)) 4231 + br_multicast_enable_port_ctx(&v->port_mcast_ctx); 4232 + 4233 + /* Multicast is not disabled for the vlan when it goes in 4234 + * blocking state because the timers will expire and stop by 4235 + * themselves without sending more queries. 4236 + */ 4237 + #endif 4238 + } 4239 + 4275 4240 void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on) 4276 4241 { 4277 4242 struct net_bridge *br; ··· 4391 4304 __br_multicast_open(&br->multicast_ctx); 4392 4305 list_for_each_entry(p, &br->port_list, list) { 4393 4306 if (on) 4394 - br_multicast_disable_port(p); 4307 + br_multicast_disable_port_ctx(&p->multicast_ctx); 4395 4308 else 4396 - br_multicast_enable_port(p); 4309 + br_multicast_enable_port_ctx(&p->multicast_ctx); 4397 4310 } 4398 4311 4399 4312 list_for_each_entry(vlan, &vg->vlan_list, vlist)
+10 -1
net/bridge/br_private.h
··· 1055 1055 struct net_bridge_vlan *vlan, 1056 1056 struct net_bridge_mcast_port *pmctx); 1057 1057 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); 1058 1059 void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on); 1059 1060 int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on, 1060 1061 struct netlink_ext_ack *extack); ··· 1522 1521 { 1523 1522 } 1524 1523 1524 + static inline void br_multicast_update_vlan_mcast_ctx(struct net_bridge_vlan *v, 1525 + u8 state) 1526 + { 1527 + } 1528 + 1525 1529 static inline void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, 1526 1530 bool on) 1527 1531 { ··· 1887 1881 bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, 1888 1882 const struct net_bridge_vlan *v_opts); 1889 1883 1890 - /* vlan state manipulation helpers using *_ONCE to annotate lock-free access */ 1884 + /* vlan state manipulation helpers using *_ONCE to annotate lock-free access, 1885 + * while br_vlan_set_state() may access data protected by multicast_lock. 1886 + */ 1891 1887 static inline u8 br_vlan_get_state(const struct net_bridge_vlan *v) 1892 1888 { 1893 1889 return READ_ONCE(v->state); ··· 1898 1890 static inline void br_vlan_set_state(struct net_bridge_vlan *v, u8 state) 1899 1891 { 1900 1892 WRITE_ONCE(v->state, state); 1893 + br_multicast_update_vlan_mcast_ctx(v, state); 1901 1894 } 1902 1895 1903 1896 static inline u8 br_vlan_get_pvid_state(const struct net_bridge_vlan_group *vg)
+76 -4
tools/testing/selftests/net/forwarding/bridge_igmp.sh
··· 1 1 #!/bin/bash 2 2 # SPDX-License-Identifier: GPL-2.0 3 3 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 + " 8 22 NUM_NETIFS=4 9 23 CHECK_TC="yes" 10 24 TEST_GROUP="239.10.10.10" ··· 566 552 567 553 v3cleanup $swp1 $TEST_GROUP 568 554 v3cleanup $swp2 $TEST_GROUP 555 + } 556 + 557 + v2per_vlan_snooping_stp_test() 558 + { 559 + local is_port=$1 560 + 561 + local msg="port" 562 + [[ $is_port -ne 1 ]] && msg="vlan" 563 + 564 + ip link set br0 up type bridge vlan_filtering 1 \ 565 + mcast_igmp_version 2 \ 566 + mcast_snooping 1 \ 567 + mcast_vlan_snooping 1 \ 568 + mcast_querier 1 \ 569 + mcast_stats_enabled 1 570 + bridge vlan global set vid 1 dev br0 \ 571 + mcast_snooping 1 \ 572 + mcast_querier 1 \ 573 + mcast_query_interval 100 \ 574 + mcast_startup_query_count 0 575 + [[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 0 576 + [[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 4 577 + sleep 5 578 + local tx_s=$(ip -j -p stats show dev $swp1 \ 579 + group xstats_slave subgroup bridge suite mcast \ 580 + | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]') 581 + 582 + [[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 3 583 + [[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 3 584 + sleep 5 585 + local tx_e=$(ip -j -p stats show dev $swp1 \ 586 + group xstats_slave subgroup bridge suite mcast \ 587 + | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]') 588 + 589 + RET=0 590 + local tx=$(expr $tx_e - $tx_s) 591 + test $tx -gt 0 592 + check_err $? "No IGMP queries after STP state becomes forwarding" 593 + log_test "per vlan snooping with $msg stp state change" 594 + 595 + # restore settings 596 + bridge vlan global set vid 1 dev br0 \ 597 + mcast_querier 0 \ 598 + mcast_query_interval 12500 \ 599 + mcast_startup_query_count 2 600 + ip link set br0 up type bridge vlan_filtering 0 \ 601 + mcast_vlan_snooping 0 \ 602 + mcast_stats_enabled 0 603 + } 604 + 605 + v2per_vlan_snooping_port_stp_test() 606 + { 607 + v2per_vlan_snooping_stp_test 1 608 + } 609 + 610 + v2per_vlan_snooping_vlan_stp_test() 611 + { 612 + v2per_vlan_snooping_stp_test 0 569 613 } 570 614 571 615 trap cleanup EXIT
+77 -4
tools/testing/selftests/net/forwarding/bridge_mld.sh
··· 1 1 #!/bin/bash 2 2 # SPDX-License-Identifier: GPL-2.0 3 3 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 + " 8 21 NUM_NETIFS=4 9 22 CHECK_TC="yes" 10 23 TEST_GROUP="ff02::cc" ··· 565 552 566 553 mldv2cleanup $swp1 567 554 mldv2cleanup $swp2 555 + } 556 + 557 + mldv2per_vlan_snooping_stp_test() 558 + { 559 + local is_port=$1 560 + 561 + local msg="port" 562 + [[ $is_port -ne 1 ]] && msg="vlan" 563 + 564 + ip link set br0 up type bridge vlan_filtering 1 \ 565 + mcast_mld_version 2 \ 566 + mcast_snooping 1 \ 567 + mcast_vlan_snooping 1 \ 568 + mcast_querier 1 \ 569 + mcast_stats_enabled 1 570 + bridge vlan global set vid 1 dev br0 \ 571 + mcast_mld_version 2 \ 572 + mcast_snooping 1 \ 573 + mcast_querier 1 \ 574 + mcast_query_interval 100 \ 575 + mcast_startup_query_count 0 576 + 577 + [[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 0 578 + [[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 4 579 + sleep 5 580 + local tx_s=$(ip -j -p stats show dev $swp1 \ 581 + group xstats_slave subgroup bridge suite mcast \ 582 + | jq '.[]["multicast"]["mld_queries"]["tx_v2"]') 583 + [[ $is_port -eq 1 ]] && bridge link set dev $swp1 state 3 584 + [[ $is_port -ne 1 ]] && bridge vlan set vid 1 dev $swp1 state 3 585 + sleep 5 586 + local tx_e=$(ip -j -p stats show dev $swp1 \ 587 + group xstats_slave subgroup bridge suite mcast \ 588 + | jq '.[]["multicast"]["mld_queries"]["tx_v2"]') 589 + 590 + RET=0 591 + local tx=$(expr $tx_e - $tx_s) 592 + test $tx -gt 0 593 + check_err $? "No MLD queries after STP state becomes forwarding" 594 + log_test "per vlan snooping with $msg stp state change" 595 + 596 + # restore settings 597 + bridge vlan global set vid 1 dev br0 \ 598 + mcast_querier 0 \ 599 + mcast_query_interval 12500 \ 600 + mcast_startup_query_count 2 \ 601 + mcast_mld_version 1 602 + ip link set br0 up type bridge vlan_filtering 0 \ 603 + mcast_vlan_snooping 0 \ 604 + mcast_stats_enabled 0 605 + } 606 + 607 + mldv2per_vlan_snooping_port_stp_test() 608 + { 609 + mldv2per_vlan_snooping_stp_test 1 610 + } 611 + 612 + mldv2per_vlan_snooping_vlan_stp_test() 613 + { 614 + mldv2per_vlan_snooping_stp_test 0 568 615 } 569 616 570 617 trap cleanup EXIT
+1
tools/testing/selftests/net/forwarding/config
··· 1 1 CONFIG_BRIDGE=m 2 2 CONFIG_VLAN_8021Q=m 3 3 CONFIG_BRIDGE_VLAN_FILTERING=y 4 + CONFIG_BRIDGE_IGMP_SNOOPING=y 4 5 CONFIG_NET_L3_MASTER_DEV=y 5 6 CONFIG_IPV6_MULTIPLE_TABLES=y 6 7 CONFIG_NET_VRF=m