Commit 4b30ae9a authored by Yong Wang's avatar Yong Wang Committed by David S. Miller
Browse files

net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions



When a bridge port STP state is changed from BLOCKING/DISABLED to
FORWARDING, the port's igmp query timer will NOT re-arm itself if the
bridge has been configured as per-VLAN multicast snooping.

Solve this by choosing the correct multicast context(s) to enable/disable
port multicast based on whether per-VLAN multicast snooping is enabled or
not, i.e. using per-{port, VLAN} context in case of per-VLAN multicast
snooping by re-implementing br_multicast_enable_port() and
br_multicast_disable_port() functions.

Before the patch, the IGMP query does not happen in the last step of the
following test sequence, i.e. no growth for tx counter:
 # ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 mcast_vlan_snooping 1 mcast_querier 1 mcast_stats_enabled 1
 # bridge vlan global set vid 1 dev br1 mcast_snooping 1 mcast_querier 1 mcast_query_interval 100 mcast_startup_query_count 0
 # ip link add name swp1 up master br1 type dummy
 # bridge link set dev swp1 state 0
 # ip -j -p stats show dev swp1 group xstats_slave subgroup bridge suite mcast | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]'
1
 # sleep 1
 # ip -j -p stats show dev swp1 group xstats_slave subgroup bridge suite mcast | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]'
1
 # bridge link set dev swp1 state 3
 # sleep 2
 # ip -j -p stats show dev swp1 group xstats_slave subgroup bridge suite mcast | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]'
1

After the patch, the IGMP query happens in the last step of the test:
 # ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 mcast_vlan_snooping 1 mcast_querier 1 mcast_stats_enabled 1
 # bridge vlan global set vid 1 dev br1 mcast_snooping 1 mcast_querier 1 mcast_query_interval 100 mcast_startup_query_count 0
 # ip link add name swp1 up master br1 type dummy
 # bridge link set dev swp1 state 0
 # ip -j -p stats show dev swp1 group xstats_slave subgroup bridge suite mcast | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]'
1
 # sleep 1
 # ip -j -p stats show dev swp1 group xstats_slave subgroup bridge suite mcast | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]'
1
 # bridge link set dev swp1 state 3
 # sleep 2
 # ip -j -p stats show dev swp1 group xstats_slave subgroup bridge suite mcast | jq '.[]["multicast"]["igmp_queries"]["tx_v2"]'
3

Signed-off-by: default avatarYong Wang <yongwang@nvidia.com>
Reviewed-by: default avatarAndy Roulin <aroulin@nvidia.com>
Reviewed-by: default avatarIdo Schimmel <idosch@nvidia.com>
Signed-off-by: default avatarPetr Machata <petrm@nvidia.com>
Acked-by: default avatarNikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0e0a7e37
Loading
Loading
Loading
Loading
+69 −8
Original line number Diff line number Diff line
@@ -2105,12 +2105,17 @@ static void __br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
	}
}

void br_multicast_enable_port(struct net_bridge_port *port)
static void br_multicast_enable_port_ctx(struct net_bridge_mcast_port *pmctx)
{
	struct net_bridge *br = port->br;
	struct net_bridge *br = pmctx->port->br;

	spin_lock_bh(&br->multicast_lock);
	__br_multicast_enable_port_ctx(&port->multicast_ctx);
	if (br_multicast_port_ctx_is_vlan(pmctx) &&
	    !(pmctx->vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED)) {
		spin_unlock_bh(&br->multicast_lock);
		return;
	}
	__br_multicast_enable_port_ctx(pmctx);
	spin_unlock_bh(&br->multicast_lock);
}

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

static void br_multicast_disable_port_ctx(struct net_bridge_mcast_port *pmctx)
{
	struct net_bridge *br = pmctx->port->br;

	spin_lock_bh(&br->multicast_lock);
	if (br_multicast_port_ctx_is_vlan(pmctx) &&
	    !(pmctx->vlan->priv_flags & BR_VLFLAG_MCAST_ENABLED)) {
		spin_unlock_bh(&br->multicast_lock);
		return;
	}

	__br_multicast_disable_port_ctx(pmctx);
	spin_unlock_bh(&br->multicast_lock);
}

static void br_multicast_toggle_port(struct net_bridge_port *port, bool on)
{
#if IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING)
	if (br_opt_get(port->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) {
		struct net_bridge_vlan_group *vg;
		struct net_bridge_vlan *vlan;

		rcu_read_lock();
		vg = nbp_vlan_group_rcu(port);
		if (!vg) {
			rcu_read_unlock();
			return;
		}

		/* iterate each vlan, toggle vlan multicast context */
		list_for_each_entry_rcu(vlan, &vg->vlan_list, vlist) {
			struct net_bridge_mcast_port *pmctx =
						&vlan->port_mcast_ctx;
			u8 state = br_vlan_get_state(vlan);
			/* enable vlan multicast context when state is
			 * LEARNING or FORWARDING
			 */
			if (on && br_vlan_state_allowed(state, true))
				br_multicast_enable_port_ctx(pmctx);
			else
				br_multicast_disable_port_ctx(pmctx);
		}
		rcu_read_unlock();
		return;
	}
#endif
	/* toggle port multicast context when vlan snooping is disabled */
	if (on)
		br_multicast_enable_port_ctx(&port->multicast_ctx);
	else
		br_multicast_disable_port_ctx(&port->multicast_ctx);
}

void br_multicast_enable_port(struct net_bridge_port *port)
{
	br_multicast_toggle_port(port, true);
}

void br_multicast_disable_port(struct net_bridge_port *port)
{
	spin_lock_bh(&port->br->multicast_lock);
	__br_multicast_disable_port_ctx(&port->multicast_ctx);
	spin_unlock_bh(&port->br->multicast_lock);
	br_multicast_toggle_port(port, false);
}

static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
@@ -4304,9 +4365,9 @@ int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
		__br_multicast_open(&br->multicast_ctx);
	list_for_each_entry(p, &br->port_list, list) {
		if (on)
			br_multicast_disable_port(p);
			br_multicast_disable_port_ctx(&p->multicast_ctx);
		else
			br_multicast_enable_port(p);
			br_multicast_enable_port_ctx(&p->multicast_ctx);
	}

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