Merge branch 'net-bridge-mcast-fix-mdb_n_entries-counting-warning'

Nikolay Aleksandrov says:

====================
net: bridge: mcast: fix mdb_n_entries counting warning

The first patch fixes a warning in the mdb_n_entries code which was
reported by syzbot, the second patch adds tests for different ways
which used to trigger said warning. For more information please check
the individual commit messages.
====================

Link: https://patch.msgid.link/20260213070031.1400003-1-nikolay@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-02-17 13:00:15 +01:00
commit 9874b1ba46
2 changed files with 106 additions and 29 deletions

View File

@ -244,14 +244,11 @@ br_multicast_port_vid_to_port_ctx(struct net_bridge_port *port, u16 vid)
lockdep_assert_held_once(&port->br->multicast_lock);
if (!br_opt_get(port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED))
return NULL;
/* Take RCU to access the vlan. */
rcu_read_lock();
vlan = br_vlan_find(nbp_vlan_group_rcu(port), vid);
if (vlan && !br_multicast_port_ctx_vlan_disabled(&vlan->port_mcast_ctx))
if (vlan)
pmctx = &vlan->port_mcast_ctx;
rcu_read_unlock();
@ -701,7 +698,10 @@ br_multicast_port_ngroups_inc_one(struct net_bridge_mcast_port *pmctx,
u32 max = READ_ONCE(pmctx->mdb_max_entries);
u32 n = READ_ONCE(pmctx->mdb_n_entries);
if (max && n >= max) {
/* enforce the max limit when it's a port pmctx or a port-vlan pmctx
* with snooping enabled
*/
if (!br_multicast_port_ctx_vlan_disabled(pmctx) && max && n >= max) {
NL_SET_ERR_MSG_FMT_MOD(extack, "%s is already in %u groups, and mcast_max_groups=%u",
what, n, max);
return -E2BIG;
@ -736,9 +736,7 @@ static int br_multicast_port_ngroups_inc(struct net_bridge_port *port,
return err;
}
/* Only count on the VLAN context if VID is given, and if snooping on
* that VLAN is enabled.
*/
/* Only count on the VLAN context if VID is given */
if (!group->vid)
return 0;
@ -2011,6 +2009,18 @@ void br_multicast_port_ctx_init(struct net_bridge_port *port,
timer_setup(&pmctx->ip6_own_query.timer,
br_ip6_multicast_port_query_expired, 0);
#endif
/* initialize mdb_n_entries if a new port vlan is being created */
if (vlan) {
struct net_bridge_port_group *pg;
u32 n = 0;
spin_lock_bh(&port->br->multicast_lock);
hlist_for_each_entry(pg, &port->mglist, mglist)
if (pg->key.addr.vid == vlan->vid)
n++;
WRITE_ONCE(pmctx->mdb_n_entries, n);
spin_unlock_bh(&port->br->multicast_lock);
}
}
void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx)
@ -2094,25 +2104,6 @@ static void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
br_ip4_multicast_add_router(brmctx, pmctx);
br_ip6_multicast_add_router(brmctx, pmctx);
}
if (br_multicast_port_ctx_is_vlan(pmctx)) {
struct net_bridge_port_group *pg;
u32 n = 0;
/* The mcast_n_groups counter might be wrong. First,
* BR_VLFLAG_MCAST_ENABLED is toggled before temporary entries
* are flushed, thus mcast_n_groups after the toggle does not
* reflect the true values. And second, permanent entries added
* while BR_VLFLAG_MCAST_ENABLED was disabled, are not reflected
* either. Thus we have to refresh the counter.
*/
hlist_for_each_entry(pg, &pmctx->port->mglist, mglist) {
if (pg->key.addr.vid == pmctx->vlan->vid)
n++;
}
WRITE_ONCE(pmctx->mdb_n_entries, n);
}
}
static void br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)

View File

@ -28,6 +28,7 @@ ALL_TESTS="
test_8021d
test_8021q
test_8021qvs
test_mdb_count_warning
"
NUM_NETIFS=4
@ -83,8 +84,6 @@ switch_create_8021q()
{
local br_flags=$1; shift
log_info "802.1q $br_flags${br_flags:+ }tests"
ip link add name br0 type bridge vlan_filtering 1 vlan_default_pvid 0 \
mcast_snooping 1 $br_flags \
mcast_igmp_version 3 mcast_mld_version 2
@ -106,6 +105,7 @@ switch_create_8021q()
switch_create_8021qvs()
{
log_info "802.1q mcast_vlan_snooping 1 tests"
switch_create_8021q "mcast_vlan_snooping 1"
bridge vlan global set dev br0 vid 10 mcast_igmp_version 3
bridge vlan global set dev br0 vid 10 mcast_mld_version 2
@ -1272,6 +1272,76 @@ test_8021qvs_toggle_vlan_snooping()
test_toggle_vlan_snooping_permanent
}
mdb_count_check_warn()
{
local msg=$1; shift
dmesg | grep -q "WARNING:.*br_multicast_port_ngroups_dec.*"
check_fail $? "$msg"
}
test_mdb_count_mcast_vlan_snooping_flush()
{
RET=0
# check if we already have a warning
mdb_count_check_warn "Check MDB entries count warning before test"
bridge mdb add dev br0 port "$swp1" grp 239.0.0.1 permanent vid 10
ip link set dev br0 down
ip link set dev br0 type bridge mcast_vlan_snooping 1
bridge mdb flush dev br0
mdb_count_check_warn "Check MDB entries count warning after test"
ip link set dev br0 type bridge mcast_vlan_snooping 0
ip link set dev br0 up
log_test "MDB count warning: mcast_vlan_snooping and MDB flush"
}
test_mdb_count_mcast_snooping_flush()
{
RET=0
# check if we already have a warning
mdb_count_check_warn "Check MDB entries count warning before test"
bridge mdb add dev br0 port "$swp1" grp 239.0.0.1 permanent vid 10
ip link set dev br0 type bridge mcast_snooping 0
ip link set dev br0 type bridge mcast_vlan_snooping 1
bridge mdb flush dev br0
mdb_count_check_warn "Check MDB entries count warning after test"
ip link set dev br0 type bridge mcast_vlan_snooping 0
ip link set dev br0 type bridge mcast_snooping 1
log_test "MDB count warning: mcast_snooping and MDB flush"
}
test_mdb_count_vlan_state_flush()
{
RET=0
# check if we already have a warning
mdb_count_check_warn "Check MDB entries count warning before test"
bridge mdb add dev br0 port "$swp1" grp 239.0.0.1 permanent vid 10
ip link set dev br0 down
bridge vlan set vid 10 dev "$swp1" state blocking
ip link set dev br0 type bridge mcast_vlan_snooping 1
ip link set dev br0 up
bridge mdb flush dev br0
mdb_count_check_warn "Check MDB entries count warning after test"
bridge vlan set vid 10 dev "$swp1" state forwarding
ip link set dev br0 type bridge mcast_vlan_snooping 0
log_test "MDB count warning: disabled vlan state and MDB flush"
}
# test groups
test_8021d()
@ -1297,6 +1367,7 @@ test_8021q()
{
# Tests for vlan_filtering 1 mcast_vlan_snooping 0.
log_info "802.1q tests"
switch_create_8021q
setup_wait
@ -1334,6 +1405,21 @@ test_8021qvs()
switch_destroy
}
test_mdb_count_warning()
{
# Tests for mdb_n_entries warning
log_info "MDB count warning tests"
switch_create_8021q
setup_wait
test_mdb_count_mcast_vlan_snooping_flush
test_mdb_count_mcast_snooping_flush
test_mdb_count_vlan_state_flush
switch_destroy
}
if ! bridge link help 2>&1 | grep -q "mcast_max_groups"; then
echo "SKIP: iproute2 too old, missing bridge \"mcast_max_groups\" support"
exit $ksft_skip