* [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA
@ 2025-05-22 19:17 Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Linus Lüssing
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Linus Lüssing @ 2025-05-22 19:17 UTC (permalink / raw)
To: bridge
Cc: netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork
For a plain Linux bridge we have a safety mechanism before applying
multicast snooping to payload IP packets in the fast path: We only apply it
if both multicast snooping is enabled and an active IGMP/MLD querier was
detected. Otherwise we default to flooding IPv4/IPv6 multicast traffic.
This reduces the risk of creating multicast packet loss and by that
packet loss for IPv6 unicast, too, which relies on multicast to work.
Without an active IGMP/MLD querier on the link we are not able to get
IGMP/MLD reports reliably and by that wouldn't have a complete picture
about all multicast listeners.
This safety mechanism was introduced in commit
b00589af3b04 ("bridge: disable snooping if there is no querier").
To be able to use this safty mechanism on DSA/switchdev capable hardware
switches, too, and to ensure that a DSA bridge behaves similar to
a plain software bridge this patchset adds a new variable to track
if multicast snooping is active / safely applicable. And notifies DSA
and switchdev when this changes.
This has been tested on an OpenWrt powered Realtek RTL8382 switch,
a ZyXEL GS1900-24HP v1, with the following, pending patchset for OpenWrt
to integrate this: https://github.com/openwrt/openwrt/pull/18780
Regards, Linus
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state
2025-05-22 19:17 [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA Linus Lüssing
@ 2025-05-22 19:17 ` Linus Lüssing
2025-05-23 9:44 ` kernel test robot
2025-05-25 17:13 ` Ido Schimmel
2025-05-22 19:17 ` [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink Linus Lüssing
` (3 subsequent siblings)
4 siblings, 2 replies; 12+ messages in thread
From: Linus Lüssing @ 2025-05-22 19:17 UTC (permalink / raw)
To: bridge
Cc: netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork,
Linus Lüssing
Combining and tracking the active state into new, per protocol family
variables.
For one thing this slightly reduces the work and checks in fast path
for multicast payload traffic. For another this is in preparation to
be able to notify DSA/switchdev on the (in)applicability of multicast
snooping on multicast payload traffic. Especially on vanishing IGMP/MLD
queriers.
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
net/bridge/br_device.c | 5 +-
net/bridge/br_input.c | 2 +-
net/bridge/br_multicast.c | 179 +++++++++++++++++++++++++++++++++++---
net/bridge/br_private.h | 33 ++-----
4 files changed, 180 insertions(+), 39 deletions(-)
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index a818fdc22da9..315ed3d33406 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -102,7 +102,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
- br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst))
+ br_multicast_snooping_active(brmctx, eth_hdr(skb), mdst))
br_multicast_flood(mdst, skb, brmctx, false, true);
else
br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
@@ -168,7 +168,10 @@ static int br_dev_open(struct net_device *dev)
netdev_update_features(dev);
netif_start_queue(dev);
br_stp_enable_bridge(br);
+
+ spin_lock_bh(&br->multicast_lock);
br_multicast_open(br);
+ spin_unlock_bh(&br->multicast_lock);
if (br_opt_get(br, BROPT_MULTICAST_ENABLED))
br_multicast_join_snoopers(br);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 232133a0fd21..0c632655d66c 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -187,7 +187,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
case BR_PKT_MULTICAST:
mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
- br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) {
+ br_multicast_snooping_active(brmctx, eth_hdr(skb), mdst)) {
if ((mdst && mdst->host_joined) ||
br_multicast_is_router(brmctx, skb)) {
local_rcv = true;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index dcbf058de1e3..b66d2173e321 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1069,6 +1069,125 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
return skb;
}
+static bool
+__br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
+ struct bridge_mcast_other_query *querier,
+ const bool is_ipv6)
+{
+ bool own_querier_enabled;
+
+ if (brmctx->multicast_querier) {
+ if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR))
+ own_querier_enabled = false;
+ else
+ own_querier_enabled = true;
+ } else {
+ own_querier_enabled = false;
+ }
+
+ return !timer_pending(&querier->delay_timer) &&
+ (own_querier_enabled || timer_pending(&querier->timer));
+}
+
+static bool br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
+ struct ethhdr *eth,
+ const struct net_bridge_mdb_entry *mdb)
+{
+ switch (eth->h_proto) {
+ case (htons(ETH_P_IP)):
+ return __br_multicast_querier_exists(brmctx,
+ &brmctx->ip4_other_query, false);
+#if IS_ENABLED(CONFIG_IPV6)
+ case (htons(ETH_P_IPV6)):
+ return __br_multicast_querier_exists(brmctx,
+ &brmctx->ip6_other_query, true);
+#endif
+ default:
+ return !!mdb && br_group_is_l2(&mdb->addr);
+ }
+}
+
+static bool br_ip4_multicast_check_active(struct net_bridge_mcast *brmctx,
+ bool *active)
+{
+ if (!__br_multicast_querier_exists(brmctx, &brmctx->ip4_other_query,
+ false))
+ *active = false;
+
+ if (brmctx->ip4_active == *active)
+ return false;
+
+ return true;
+}
+
+static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
+ bool *active)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+ if (!__br_multicast_querier_exists(brmctx, &brmctx->ip6_other_query,
+ true))
+ *active = false;
+
+ if (brmctx->ip6_active == *active)
+ return false;
+
+ return true;
+#elif
+ *active = false;
+ return false;
+#endif
+}
+
+/**
+ * __br_multicast_update_active() - update mcast active state
+ * @brmctx: the bridge multicast context to check
+ * @force_inactive: forcefully deactivate mcast active state
+ * @extack: netlink extended ACK structure
+ *
+ * This (potentially) updates the IPv4/IPv6 multicast active state. And by
+ * that enables or disables snooping of multicast payload traffic in fast
+ * path.
+ *
+ * The multicast active state is set, per protocol family, if:
+ *
+ * - an IGMP/MLD querier is present
+ * - for own IPv6 MLD querier: an IPv6 address is configured on the bridge
+ *
+ * And is unset otherwise.
+ *
+ * This function should be called by anything that changes one of the
+ * above prerequisites.
+ *
+ * Return: 0 on success, a negative value otherwise.
+ */
+static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
+ bool force_inactive,
+ struct netlink_ext_ack *extack)
+{
+ bool ip4_active, ip6_active, ip4_changed, ip6_changed;
+ int ret = 0;
+
+ lockdep_assert_held_once(&brmctx->br->multicast_lock);
+
+ ip4_active = !force_inactive;
+ ip6_active = !force_inactive;
+ ip4_changed = br_ip4_multicast_check_active(brmctx, &ip4_active);
+ ip6_changed = br_ip6_multicast_check_active(brmctx, &ip6_active);
+
+ if (ip4_changed)
+ brmctx->ip4_active = ip4_active;
+ if (ip6_changed)
+ brmctx->ip6_active = ip6_active;
+
+ return ret;
+}
+
+static int br_multicast_update_active(struct net_bridge_mcast *brmctx,
+ struct netlink_ext_ack *extack)
+{
+ return __br_multicast_update_active(brmctx, false, extack);
+}
+
#if IS_ENABLED(CONFIG_IPV6)
static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brmctx,
struct net_bridge_mcast_port *pmctx,
@@ -1147,10 +1266,12 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
&ip6h->daddr, 0, &ip6h->saddr)) {
kfree_skb(skb);
br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, false);
+ br_multicast_update_active(brmctx, NULL);
return NULL;
}
br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, true);
+ br_multicast_update_active(brmctx, NULL);
ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
hopopt = (u8 *)(ip6h + 1);
@@ -1762,10 +1883,28 @@ static void br_ip6_multicast_querier_expired(struct timer_list *t)
}
#endif
-static void br_multicast_query_delay_expired(struct timer_list *t)
+static void br_ip4_multicast_query_delay_expired(struct timer_list *t)
{
+ struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
+ ip4_other_query.delay_timer);
+
+ spin_lock(&brmctx->br->multicast_lock);
+ br_multicast_update_active(brmctx, NULL);
+ spin_unlock(&brmctx->br->multicast_lock);
}
+#if IS_ENABLED(CONFIG_IPV6)
+static void br_ip6_multicast_query_delay_expired(struct timer_list *t)
+{
+ struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
+ ip6_other_query.delay_timer);
+
+ spin_lock(&brmctx->br->multicast_lock);
+ br_multicast_update_active(brmctx, NULL);
+ spin_unlock(&brmctx->br->multicast_lock);
+}
+#endif
+
static void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx,
struct br_ip *ip,
struct sk_buff *skb)
@@ -3981,16 +4120,13 @@ static void br_multicast_query_expired(struct net_bridge_mcast *brmctx,
struct bridge_mcast_own_query *query,
struct bridge_mcast_querier *querier)
{
- spin_lock(&brmctx->br->multicast_lock);
if (br_multicast_ctx_vlan_disabled(brmctx))
- goto out;
+ return;
if (query->startup_sent < brmctx->multicast_startup_query_count)
query->startup_sent++;
br_multicast_send_query(brmctx, NULL, query);
-out:
- spin_unlock(&brmctx->br->multicast_lock);
}
static void br_ip4_multicast_query_expired(struct timer_list *t)
@@ -3998,8 +4134,11 @@ static void br_ip4_multicast_query_expired(struct timer_list *t)
struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
ip4_own_query.timer);
+ spin_lock(&brmctx->br->multicast_lock);
br_multicast_query_expired(brmctx, &brmctx->ip4_own_query,
&brmctx->ip4_querier);
+ br_multicast_update_active(brmctx, NULL);
+ spin_unlock(&brmctx->br->multicast_lock);
}
#if IS_ENABLED(CONFIG_IPV6)
@@ -4008,8 +4147,11 @@ static void br_ip6_multicast_query_expired(struct timer_list *t)
struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
ip6_own_query.timer);
+ spin_lock(&brmctx->br->multicast_lock);
br_multicast_query_expired(brmctx, &brmctx->ip6_own_query,
&brmctx->ip6_querier);
+ br_multicast_update_active(brmctx, NULL);
+ spin_unlock(&brmctx->br->multicast_lock);
}
#endif
@@ -4044,11 +4186,13 @@ void br_multicast_ctx_init(struct net_bridge *br,
brmctx->multicast_membership_interval = 260 * HZ;
brmctx->ip4_querier.port_ifidx = 0;
+ brmctx->ip4_active = 0;
seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock);
brmctx->multicast_igmp_version = 2;
#if IS_ENABLED(CONFIG_IPV6)
brmctx->multicast_mld_version = 1;
brmctx->ip6_querier.port_ifidx = 0;
+ brmctx->ip6_active = 0;
seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
#endif
@@ -4057,7 +4201,7 @@ void br_multicast_ctx_init(struct net_bridge *br,
timer_setup(&brmctx->ip4_other_query.timer,
br_ip4_multicast_querier_expired, 0);
timer_setup(&brmctx->ip4_other_query.delay_timer,
- br_multicast_query_delay_expired, 0);
+ br_ip4_multicast_query_delay_expired, 0);
timer_setup(&brmctx->ip4_own_query.timer,
br_ip4_multicast_query_expired, 0);
#if IS_ENABLED(CONFIG_IPV6)
@@ -4066,7 +4210,7 @@ void br_multicast_ctx_init(struct net_bridge *br,
timer_setup(&brmctx->ip6_other_query.timer,
br_ip6_multicast_querier_expired, 0);
timer_setup(&brmctx->ip6_other_query.delay_timer,
- br_multicast_query_delay_expired, 0);
+ br_ip6_multicast_query_delay_expired, 0);
timer_setup(&brmctx->ip6_own_query.timer,
br_ip6_multicast_query_expired, 0);
#endif
@@ -4171,6 +4315,8 @@ static void __br_multicast_open(struct net_bridge_mcast *brmctx)
#if IS_ENABLED(CONFIG_IPV6)
__br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query);
#endif
+
+ br_multicast_update_active(brmctx, NULL);
}
void br_multicast_open(struct net_bridge *br)
@@ -4209,6 +4355,10 @@ static void __br_multicast_stop(struct net_bridge_mcast *brmctx)
timer_delete_sync(&brmctx->ip6_other_query.delay_timer);
timer_delete_sync(&brmctx->ip6_own_query.timer);
#endif
+
+ spin_lock_bh(&brmctx->br->multicast_lock);
+ __br_multicast_update_active(brmctx, true, NULL);
+ spin_unlock_bh(&brmctx->br->multicast_lock);
}
void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
@@ -4234,10 +4384,13 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED;
spin_unlock_bh(&br->multicast_lock);
- if (on)
+ if (on) {
+ spin_lock_bh(&br->multicast_lock);
__br_multicast_open(&vlan->br_mcast_ctx);
- else
+ spin_unlock_bh(&br->multicast_lock);
+ } else {
__br_multicast_stop(&vlan->br_mcast_ctx);
+ }
} else {
struct net_bridge_mcast *brmctx;
@@ -4298,10 +4451,13 @@ int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on);
/* disable/enable non-vlan mcast contexts based on vlan snooping */
- if (on)
+ if (on) {
__br_multicast_stop(&br->multicast_ctx);
- else
+ } else {
+ spin_lock_bh(&br->multicast_lock);
__br_multicast_open(&br->multicast_ctx);
+ spin_unlock_bh(&br->multicast_lock);
+ }
list_for_each_entry(p, &br->port_list, list) {
if (on)
br_multicast_disable_port(p);
@@ -4663,6 +4819,7 @@ int br_multicast_set_querier(struct net_bridge_mcast *brmctx, unsigned long val)
#endif
unlock:
+ br_multicast_update_active(brmctx, NULL);
spin_unlock_bh(&brmctx->br->multicast_lock);
return 0;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d5b3c5936a79..3d05895a437f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -158,12 +158,14 @@ struct net_bridge_mcast {
struct bridge_mcast_other_query ip4_other_query;
struct bridge_mcast_own_query ip4_own_query;
struct bridge_mcast_querier ip4_querier;
+ bool ip4_active;
#if IS_ENABLED(CONFIG_IPV6)
struct hlist_head ip6_mc_router_list;
struct timer_list ip6_mc_router_timer;
struct bridge_mcast_other_query ip6_other_query;
struct bridge_mcast_own_query ip6_own_query;
struct bridge_mcast_querier ip6_querier;
+ bool ip6_active;
#endif /* IS_ENABLED(CONFIG_IPV6) */
#endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
};
@@ -1144,37 +1146,16 @@ br_multicast_is_router(struct net_bridge_mcast *brmctx, struct sk_buff *skb)
}
static inline bool
-__br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
- struct bridge_mcast_other_query *querier,
- const bool is_ipv6)
-{
- bool own_querier_enabled;
-
- if (brmctx->multicast_querier) {
- if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR))
- own_querier_enabled = false;
- else
- own_querier_enabled = true;
- } else {
- own_querier_enabled = false;
- }
-
- return !timer_pending(&querier->delay_timer) &&
- (own_querier_enabled || timer_pending(&querier->timer));
-}
-
-static inline bool br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
- struct ethhdr *eth,
- const struct net_bridge_mdb_entry *mdb)
+br_multicast_snooping_active(struct net_bridge_mcast *brmctx,
+ struct ethhdr *eth,
+ const struct net_bridge_mdb_entry *mdb)
{
switch (eth->h_proto) {
case (htons(ETH_P_IP)):
- return __br_multicast_querier_exists(brmctx,
- &brmctx->ip4_other_query, false);
+ return brmctx->ip4_active;
#if IS_ENABLED(CONFIG_IPV6)
case (htons(ETH_P_IPV6)):
- return __br_multicast_querier_exists(brmctx,
- &brmctx->ip6_other_query, true);
+ return brmctx->ip6_active;
#endif
default:
return !!mdb && br_group_is_l2(&mdb->addr);
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink
2025-05-22 19:17 [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Linus Lüssing
@ 2025-05-22 19:17 ` Linus Lüssing
2025-05-25 17:20 ` Ido Schimmel
2025-05-22 19:17 ` [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state Linus Lüssing
` (2 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Linus Lüssing @ 2025-05-22 19:17 UTC (permalink / raw)
To: bridge
Cc: netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork,
Linus Lüssing
Export the new ip{4,6}_active variables to netlink, to be able to
check from userspace that they are updated as intended.
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
include/uapi/linux/if_bridge.h | 2 ++
include/uapi/linux/if_link.h | 12 ++++++++++++
net/bridge/br_netlink.c | 10 +++++++++-
net/bridge/br_vlan_options.c | 10 +++++++++-
net/core/rtnetlink.c | 2 +-
5 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index a5b743a2f775..fe26646c38c8 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -584,6 +584,8 @@ enum {
BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS,
BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE,
BRIDGE_VLANDB_GOPTS_MSTI,
+ BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4,
+ BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6,
__BRIDGE_VLANDB_GOPTS_MAX
};
#define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 318386cc5b0d..41f6c461ab32 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -742,6 +742,16 @@ enum in6_addr_gen_mode {
* @IFLA_BR_FDB_MAX_LEARNED
* Set the number of max dynamically learned FDB entries for the current
* bridge.
+ *
+ * @IFLA_BR_MCAST_ACTIVE_V4
+ * Bridge IPv4 mcast active state, read only.
+ *
+ * 1 if an IGMP querier is present, 0 otherwise.
+ *
+ * @IFLA_BR_MCAST_ACTIVE_V6
+ * Bridge IPv4 mcast active state, read only.
+ *
+ * 1 if an MLD querier is present, 0 otherwise.
*/
enum {
IFLA_BR_UNSPEC,
@@ -794,6 +804,8 @@ enum {
IFLA_BR_MCAST_QUERIER_STATE,
IFLA_BR_FDB_N_LEARNED,
IFLA_BR_FDB_MAX_LEARNED,
+ IFLA_BR_MCAST_ACTIVE_V4,
+ IFLA_BR_MCAST_ACTIVE_V6,
__IFLA_BR_MAX,
};
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 6e337937d0d7..7829d2842851 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1264,7 +1264,9 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
[IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
[IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 },
[IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
+ [IFLA_BR_MCAST_ACTIVE_V4] = { .type = NLA_U8 },
[IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
+ [IFLA_BR_MCAST_ACTIVE_V6] = { .type = NLA_U8 },
[IFLA_BR_VLAN_STATS_PER_PORT] = { .type = NLA_U8 },
[IFLA_BR_MULTI_BOOLOPT] =
NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)),
@@ -1625,7 +1627,9 @@ static size_t br_get_size(const struct net_device *brdev)
nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONSE_INTVL */
nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_STARTUP_QUERY_INTVL */
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_IGMP_VERSION */
+ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ACTIVE_V4 */
nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_MLD_VERSION */
+ nla_total_size(sizeof(u8)) + /* IFLA_BR_MCAST_ACTIVE_V6 */
br_multicast_querier_state_size() + /* IFLA_BR_MCAST_QUERIER_STATE */
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
@@ -1717,12 +1721,16 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
br->multicast_ctx.multicast_startup_query_count) ||
nla_put_u8(skb, IFLA_BR_MCAST_IGMP_VERSION,
br->multicast_ctx.multicast_igmp_version) ||
+ nla_put_u8(skb, IFLA_BR_MCAST_ACTIVE_V4,
+ br->multicast_ctx.ip4_active) ||
br_multicast_dump_querier_state(skb, &br->multicast_ctx,
IFLA_BR_MCAST_QUERIER_STATE))
return -EMSGSIZE;
#if IS_ENABLED(CONFIG_IPV6)
if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION,
- br->multicast_ctx.multicast_mld_version))
+ br->multicast_ctx.multicast_mld_version) ||
+ nla_put_u8(skb, IFLA_BR_MCAST_ACTIVE_V6,
+ br->multicast_ctx.ip6_active))
return -EMSGSIZE;
#endif
clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_last_member_interval);
diff --git a/net/bridge/br_vlan_options.c b/net/bridge/br_vlan_options.c
index 8fa89b04ee94..a97657be51a7 100644
--- a/net/bridge/br_vlan_options.c
+++ b/net/bridge/br_vlan_options.c
@@ -369,6 +369,8 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
!!(v_opts->priv_flags & BR_VLFLAG_GLOBAL_MCAST_ENABLED)) ||
nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION,
v_opts->br_mcast_ctx.multicast_igmp_version) ||
+ nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4,
+ v_opts->br_mcast_ctx.ip4_active) ||
nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT,
v_opts->br_mcast_ctx.multicast_last_member_count) ||
nla_put_u32(skb, BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT,
@@ -423,7 +425,9 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
#if IS_ENABLED(CONFIG_IPV6)
if (nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION,
- v_opts->br_mcast_ctx.multicast_mld_version))
+ v_opts->br_mcast_ctx.multicast_mld_version) ||
+ nla_put_u8(skb, BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6,
+ v_opts->br_mcast_ctx.ip4_active))
goto out_err;
#endif
#endif
@@ -448,7 +452,9 @@ static size_t rtnl_vlan_global_opts_nlmsg_size(const struct net_bridge_vlan *v)
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING */
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION */
+ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4 */
+ nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION */
+ + nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6 */
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT */
+ nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT */
+ nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL */
@@ -630,9 +636,11 @@ static const struct nla_policy br_vlan_db_gpol[BRIDGE_VLANDB_GOPTS_MAX + 1] = {
[BRIDGE_VLANDB_GOPTS_RANGE] = { .type = NLA_U16 },
[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING] = { .type = NLA_U8 },
[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION] = { .type = NLA_U8 },
+ [BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6] = { .type = NLA_U8 },
[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL] = { .type = NLA_U64 },
[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER] = { .type = NLA_U8 },
[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
+ [BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4] = { .type = NLA_U8 },
[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT] = { .type = NLA_U32 },
[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT] = { .type = NLA_U32 },
[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL] = { .type = NLA_U64 },
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index c5a7f41982a5..dee32084be59 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -62,7 +62,7 @@
#include "dev.h"
-#define RTNL_MAX_TYPE 50
+#define RTNL_MAX_TYPE 52
#define RTNL_SLAVE_MAX_TYPE 44
struct rtnl_link {
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state
2025-05-22 19:17 [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink Linus Lüssing
@ 2025-05-22 19:17 ` Linus Lüssing
2025-05-23 13:02 ` Simon Horman
2025-05-22 19:17 ` [PATCH net-next 4/5] net: bridge: switchdev: notify on mcast active changes Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification Linus Lüssing
4 siblings, 1 reply; 12+ messages in thread
From: Linus Lüssing @ 2025-05-22 19:17 UTC (permalink / raw)
To: bridge
Cc: netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork,
Linus Lüssing
To be able to use the upcoming SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE
as a potential replacement for SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED
also check and toggle the active state if multicast snooping is enabled
or disabled. So that MC_ACTIVE not only checks the querier state, but
also if multicast snooping is enabled in general.
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
include/uapi/linux/if_link.h | 6 ++++--
net/bridge/br_multicast.c | 35 +++++++++++++++++++++++++++++++++--
2 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 41f6c461ab32..479d039477cb 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -746,12 +746,14 @@ enum in6_addr_gen_mode {
* @IFLA_BR_MCAST_ACTIVE_V4
* Bridge IPv4 mcast active state, read only.
*
- * 1 if an IGMP querier is present, 0 otherwise.
+ * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an IGMP querier is present,
+ * 0 otherwise.
*
* @IFLA_BR_MCAST_ACTIVE_V6
* Bridge IPv4 mcast active state, read only.
*
- * 1 if an MLD querier is present, 0 otherwise.
+ * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an MLD querier is present,
+ * 0 otherwise.
*/
enum {
IFLA_BR_UNSPEC,
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index b66d2173e321..0bbaa21c1479 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1150,6 +1150,7 @@ static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
*
* The multicast active state is set, per protocol family, if:
*
+ * - multicast snooping is enabled
* - an IGMP/MLD querier is present
* - for own IPv6 MLD querier: an IPv6 address is configured on the bridge
*
@@ -1169,6 +1170,13 @@ static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
lockdep_assert_held_once(&brmctx->br->multicast_lock);
+ if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED))
+ force_inactive = true;
+
+ if (br_opt_get(brmctx->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) &&
+ br_multicast_ctx_vlan_disabled(brmctx))
+ force_inactive = true;
+
ip4_active = !force_inactive;
ip6_active = !force_inactive;
ip4_changed = br_ip4_multicast_check_active(brmctx, &ip4_active);
@@ -1396,6 +1404,22 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx,
return NULL;
}
+static int br_multicast_toggle_enabled(struct net_bridge *br, bool on,
+ struct netlink_ext_ack *extack)
+{
+ int err, old;
+
+ br_opt_toggle(br, BROPT_MULTICAST_ENABLED, on);
+
+ err = br_multicast_update_active(&br->multicast_ctx, extack);
+ if (err && err != -EOPNOTSUPP) {
+ br_opt_toggle(br, BROPT_MULTICAST_ENABLED, old);
+ return err;
+ }
+
+ return 0;
+}
+
struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
struct br_ip *group)
{
@@ -1409,7 +1433,7 @@ struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
if (atomic_read(&br->mdb_hash_tbl.nelems) >= br->hash_max) {
trace_br_mdb_full(br->dev, group);
br_mc_disabled_update(br->dev, false, NULL);
- br_opt_toggle(br, BROPT_MULTICAST_ENABLED, false);
+ br_multicast_toggle_enabled(br, false, NULL);
return ERR_PTR(-E2BIG);
}
@@ -4382,6 +4406,7 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
spin_lock_bh(&br->multicast_lock);
vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED;
+ br_multicast_update_active(&vlan->br_mcast_ctx, NULL);
spin_unlock_bh(&br->multicast_lock);
if (on) {
@@ -4405,6 +4430,7 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
__br_multicast_enable_port_ctx(&vlan->port_mcast_ctx);
else
__br_multicast_disable_port_ctx(&vlan->port_mcast_ctx);
+ br_multicast_update_active(brmctx, NULL);
spin_unlock_bh(&br->multicast_lock);
}
}
@@ -4728,7 +4754,12 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val,
if (err)
goto unlock;
- br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val);
+ err = br_multicast_toggle_enabled(br, !!val, extack);
+ if (err == -EOPNOTSUPP)
+ err = 0;
+ if (err)
+ goto unlock;
+
if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) {
change_snoopers = true;
goto unlock;
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next 4/5] net: bridge: switchdev: notify on mcast active changes
2025-05-22 19:17 [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA Linus Lüssing
` (2 preceding siblings ...)
2025-05-22 19:17 ` [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state Linus Lüssing
@ 2025-05-22 19:17 ` Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification Linus Lüssing
4 siblings, 0 replies; 12+ messages in thread
From: Linus Lüssing @ 2025-05-22 19:17 UTC (permalink / raw)
To: bridge
Cc: netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork,
Linus Lüssing
Let the bridge notify switchdev if the multicast
active state toggles. So that switch drivers can act on it
accordingly, especially to avoid packetloss.
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
Documentation/networking/switchdev.rst | 8 +++----
include/net/switchdev.h | 10 ++++++++
net/bridge/br_multicast.c | 33 ++++++++++++++++++++++++++
3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/Documentation/networking/switchdev.rst b/Documentation/networking/switchdev.rst
index 2966b7122f05..130f7a36fc73 100644
--- a/Documentation/networking/switchdev.rst
+++ b/Documentation/networking/switchdev.rst
@@ -558,7 +558,7 @@ Because IGMP snooping can be turned on/off at runtime, the switchdev driver
must be able to reconfigure the underlying hardware on the fly to honor the
toggling of that option and behave appropriately.
-A switchdev driver can also refuse to support dynamic toggling of the multicast
-snooping knob at runtime and require the destruction of the bridge device(s)
-and creation of a new bridge device(s) with a different multicast snooping
-value.
+A switchdev driver must also be able to react to vanishing or appearing
+IGMP/MLD queriers. If no querier is present then, even if IGMP/MLD snooping
+is enabled, the switch must treat this as if IGMP/MLD snooping were disabled.
+The SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE notification allows to track this.
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 8346b0d29542..abcc34a81e00 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -27,6 +27,7 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL,
SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
+ SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE,
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
SWITCHDEV_ATTR_ID_BRIDGE_MST,
SWITCHDEV_ATTR_ID_MRP_PORT_ROLE,
@@ -43,6 +44,14 @@ struct switchdev_brport_flags {
unsigned long mask;
};
+struct switchdev_mc_active {
+ short vid;
+ u8 ip4:1,
+ ip6:1,
+ ip4_changed:1,
+ ip6_changed:1;
+};
+
struct switchdev_vlan_msti {
u16 vid;
u16 msti;
@@ -64,6 +73,7 @@ struct switchdev_attr {
u16 vlan_protocol; /* BRIDGE_VLAN_PROTOCOL */
bool mst; /* BRIDGE_MST */
bool mc_disabled; /* MC_DISABLED */
+ struct switchdev_mc_active mc_active; /* MC_ACTIVE */
u8 mrp_port_role; /* MRP_PORT_ROLE */
struct switchdev_vlan_msti vlan_msti; /* VLAN_MSTI */
} u;
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 0bbaa21c1479..aec106f9c17d 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1138,6 +1138,27 @@ static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
#endif
}
+static int br_multicast_notify_active(struct net_bridge_mcast *brmctx,
+ bool ip4_active, bool ip6_active,
+ bool ip4_changed, bool ip6_changed,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_attr attr = {
+ .orig_dev = brmctx->br->dev,
+ .id = SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE,
+ .flags = SWITCHDEV_F_DEFER,
+ .u.mc_active = {
+ .vid = brmctx->vlan ? brmctx->vlan->vid : -1,
+ .ip4 = ip4_active,
+ .ip6 = ip6_active,
+ .ip4_changed = ip4_changed,
+ .ip6_changed = ip6_changed,
+ },
+ };
+
+ return switchdev_port_attr_set(brmctx->br->dev, &attr, extack);
+}
+
/**
* __br_multicast_update_active() - update mcast active state
* @brmctx: the bridge multicast context to check
@@ -1159,6 +1180,8 @@ static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
* This function should be called by anything that changes one of the
* above prerequisites.
*
+ * Any multicast active state toggling is further notified to switchdev.
+ *
* Return: 0 on success, a negative value otherwise.
*/
static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
@@ -1182,11 +1205,21 @@ static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
ip4_changed = br_ip4_multicast_check_active(brmctx, &ip4_active);
ip6_changed = br_ip6_multicast_check_active(brmctx, &ip6_active);
+ if (!ip4_changed && !ip6_changed)
+ goto out;
+
+ ret = br_multicast_notify_active(brmctx, ip4_active, ip6_active,
+ ip4_changed, ip6_changed,
+ extack);
+ if (ret && ret != -EOPNOTSUPP)
+ goto out;
+
if (ip4_changed)
brmctx->ip4_active = ip4_active;
if (ip6_changed)
brmctx->ip6_active = ip6_active;
+out:
return ret;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification
2025-05-22 19:17 [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA Linus Lüssing
` (3 preceding siblings ...)
2025-05-22 19:17 ` [PATCH net-next 4/5] net: bridge: switchdev: notify on mcast active changes Linus Lüssing
@ 2025-05-22 19:17 ` Linus Lüssing
2025-05-23 9:24 ` kernel test robot
2025-05-23 9:44 ` kernel test robot
4 siblings, 2 replies; 12+ messages in thread
From: Linus Lüssing @ 2025-05-22 19:17 UTC (permalink / raw)
To: bridge
Cc: netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork,
Linus Lüssing
Add a new "port_mdb_active()" handler to the DSA API. This allows DSA
drivers to receive the multicast active notification from the
bridge. So that switch drivers can act on it accordingly, especially
to avoid packetloss.
The switchdev notifier "handled" attribute is propagated, too, so that a
DSA based switch driver can decide whether it wants to act on the event
for each port individually or only on the first one, on behalf of all
others.
Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
---
Documentation/networking/dsa/dsa.rst | 9 +++++++++
include/net/dsa.h | 5 +++++
net/dsa/port.c | 19 +++++++++++++++++++
net/dsa/port.h | 3 +++
net/dsa/switch.c | 13 +++++++++++++
net/dsa/switch.h | 11 ++++++++++-
net/dsa/user.c | 21 ++++++++++++++++++++-
7 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst
index 7b2e69cd7ef0..0b0be619be04 100644
--- a/Documentation/networking/dsa/dsa.rst
+++ b/Documentation/networking/dsa/dsa.rst
@@ -1025,6 +1025,15 @@ Bridge VLAN filtering
the specified MAC address from the specified VLAN ID if it was mapped into
this port forwarding database.
+- ``port_mdb_active``: bridge layer function invoked when the bridge starts (or
+ stops) to actively apply multicast snooping to multicast payload, i.e. when
+ multicast snooping is enabled and a multicast querier is present on the link
+ for a particular protocol family (or not). A switch should (by default) ensure:
+ To flood multicast packets for the given protocol family if multicast snooping
+ is inactive - to avoid multicast (and consequently also IPv6 unicast, which
+ depends on multicast for NDP) packet loss. And should (by default) avoid
+ forwarding to an active port if there is no listener or multicast router on it.
+
Link aggregation
----------------
diff --git a/include/net/dsa.h b/include/net/dsa.h
index a0a9481c52c2..edc0e6821ba2 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -1080,6 +1080,11 @@ struct dsa_switch_ops {
int (*port_mdb_del)(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db);
+ int (*port_mdb_active)(struct dsa_switch *ds, int port,
+ const struct switchdev_mc_active mc_active,
+ struct netlink_ext_ack *extack,
+ bool handled);
+
/*
* RXNFC
*/
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 5c9d1798e830..a1e692d9122e 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1290,6 +1290,25 @@ int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp,
return dsa_port_host_mdb_del(dp, mdb, db);
}
+int dsa_port_bridge_mdb_active(const struct dsa_port *dp,
+ const struct switchdev_mc_active mc_active,
+ struct netlink_ext_ack *extack,
+ bool handled)
+{
+ struct dsa_switch *ds = dp->ds;
+ struct dsa_notifier_mdb_active_info info = {
+ .dp = dp,
+ .mc_active = mc_active,
+ .extack = extack,
+ .handled = handled,
+ };
+
+ if (!ds->ops->port_mdb_active)
+ return -EOPNOTSUPP;
+
+ return dsa_port_notify(dp, DSA_NOTIFIER_MDB_ACTIVE, &info);
+}
+
int dsa_port_vlan_add(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
diff --git a/net/dsa/port.h b/net/dsa/port.h
index 6bc3291573c0..0e92815e7de2 100644
--- a/net/dsa/port.h
+++ b/net/dsa/port.h
@@ -75,6 +75,9 @@ int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
+int dsa_port_bridge_mdb_active(const struct dsa_port *dp,
+ const struct switchdev_mc_active mc_active,
+ struct netlink_ext_ack *extack, bool handled);
int dsa_port_pre_bridge_flags(const struct dsa_port *dp,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack);
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 3d2feeea897b..5b30dfe4bebd 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -652,6 +652,16 @@ static int dsa_switch_host_mdb_del(struct dsa_switch *ds,
return err;
}
+static int dsa_switch_mdb_active(struct dsa_switch *ds,
+ struct dsa_notifier_mdb_active_info *info)
+{
+ if (!ds->ops->port_mdb_active)
+ return -EOPNOTSUPP;
+
+ return ds->ops->port_mdb_active(ds, info->dp->index, info->mc_active,
+ info->extack, info->handled);
+}
+
/* Port VLANs match on the targeted port and on all DSA ports */
static bool dsa_port_vlan_match(struct dsa_port *dp,
struct dsa_notifier_vlan_info *info)
@@ -1026,6 +1036,9 @@ static int dsa_switch_event(struct notifier_block *nb,
case DSA_NOTIFIER_HOST_MDB_DEL:
err = dsa_switch_host_mdb_del(ds, info);
break;
+ case DSA_NOTIFIER_MDB_ACTIVE:
+ err = dsa_switch_mdb_active(ds, info);
+ break;
case DSA_NOTIFIER_VLAN_ADD:
err = dsa_switch_vlan_add(ds, info);
break;
diff --git a/net/dsa/switch.h b/net/dsa/switch.h
index be0a2749cd97..69a5004e48c8 100644
--- a/net/dsa/switch.h
+++ b/net/dsa/switch.h
@@ -24,6 +24,7 @@ enum {
DSA_NOTIFIER_MDB_DEL,
DSA_NOTIFIER_HOST_MDB_ADD,
DSA_NOTIFIER_HOST_MDB_DEL,
+ DSA_NOTIFIER_MDB_ACTIVE,
DSA_NOTIFIER_VLAN_ADD,
DSA_NOTIFIER_VLAN_DEL,
DSA_NOTIFIER_HOST_VLAN_ADD,
@@ -66,13 +67,21 @@ struct dsa_notifier_lag_fdb_info {
struct dsa_db db;
};
-/* DSA_NOTIFIER_MDB_* */
+/* DSA_NOTIFIER_MDB_{ADD,DEL} */
struct dsa_notifier_mdb_info {
const struct dsa_port *dp;
const struct switchdev_obj_port_mdb *mdb;
struct dsa_db db;
};
+/* DSA_NOTIFIER_MDB_ACTIVE */
+struct dsa_notifier_mdb_active_info {
+ const struct dsa_port *dp;
+ const struct switchdev_mc_active mc_active;
+ struct netlink_ext_ack *extack;
+ int handled;
+};
+
/* DSA_NOTIFIER_LAG_* */
struct dsa_notifier_lag_info {
const struct dsa_port *dp;
diff --git a/net/dsa/user.c b/net/dsa/user.c
index 804dc7dac4f2..231b92d6e7b9 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -603,7 +603,7 @@ static int dsa_user_port_attr_set(struct net_device *dev, const void *ctx,
struct dsa_port *dp = dsa_user_to_port(dev);
int ret;
- if (ctx && ctx != dp)
+ if (ctx && ctx != dp && attr->id != SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE)
return 0;
switch (attr->id) {
@@ -657,6 +657,15 @@ static int dsa_user_port_attr_set(struct net_device *dev, const void *ctx,
ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti);
break;
+ case SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE:
+ const bool *handled = ctx;
+
+ if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
+ return -EOPNOTSUPP;
+
+ ret = dsa_port_bridge_mdb_active(dp, attr->u.mc_active, extack,
+ *handled);
+ break;
default:
ret = -EOPNOTSUPP;
break;
@@ -3758,6 +3767,11 @@ static int dsa_user_switchdev_event(struct notifier_block *unused,
switch (event) {
case SWITCHDEV_PORT_ATTR_SET:
+ struct switchdev_notifier_port_attr_info *item = ptr;
+
+ if (item && item->attr->id == SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE)
+ item->info.ctx = &item->handled;
+
err = switchdev_handle_port_attr_set(dev, ptr,
dsa_user_dev_check,
dsa_user_port_attr_set);
@@ -3796,6 +3810,11 @@ static int dsa_user_switchdev_blocking_event(struct notifier_block *unused,
dsa_user_port_obj_del);
return notifier_from_errno(err);
case SWITCHDEV_PORT_ATTR_SET:
+ struct switchdev_notifier_port_attr_info *item = ptr;
+
+ if (item && item->attr->id == SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE)
+ item->info.ctx = &item->handled;
+
err = switchdev_handle_port_attr_set(dev, ptr,
dsa_user_dev_check,
dsa_user_port_attr_set);
--
2.49.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification
2025-05-22 19:17 ` [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification Linus Lüssing
@ 2025-05-23 9:24 ` kernel test robot
2025-05-23 9:44 ` kernel test robot
1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-05-23 9:24 UTC (permalink / raw)
To: Linus Lüssing, bridge
Cc: llvm, oe-kbuild-all, netdev, openwrt-devel, linux-kernel,
linux-doc, Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera,
Jiri Pirko, Vladimir Oltean, Andrew Lunn, Jonathan Corbet,
Simon Horman, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
David S . Miller, Kuniyuki Iwashima, Stanislav Fomichev,
Xiao Liang, Markus Stockhausen, Jan Hoffmann, Birger Koblitz,
Bjørn Mork, Linus Lüssing
Hi Linus,
kernel test robot noticed the following build warnings:
[auto build test WARNING on net/main]
[also build test WARNING on linus/master v6.15-rc7 next-20250522]
[cannot apply to net-next/main horms-ipvs/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Linus-L-ssing/net-bridge-mcast-explicitly-track-active-state/20250523-040914
base: net/main
patch link: https://lore.kernel.org/r/20250522195952.29265-6-linus.luessing%40c0d3.blue
patch subject: [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification
config: i386-randconfig-001-20250523 (https://download.01.org/0day-ci/archive/20250523/202505231706.fkxIDjje-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250523/202505231706.fkxIDjje-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505231706.fkxIDjje-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> net/dsa/user.c:661:3: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
661 | const bool *handled = ctx;
| ^
net/dsa/user.c:3770:3: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
3770 | struct switchdev_notifier_port_attr_info *item = ptr;
| ^
net/dsa/user.c:3813:3: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
3813 | struct switchdev_notifier_port_attr_info *item = ptr;
| ^
3 warnings generated.
vim +661 net/dsa/user.c
598
599 static int dsa_user_port_attr_set(struct net_device *dev, const void *ctx,
600 const struct switchdev_attr *attr,
601 struct netlink_ext_ack *extack)
602 {
603 struct dsa_port *dp = dsa_user_to_port(dev);
604 int ret;
605
606 if (ctx && ctx != dp && attr->id != SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE)
607 return 0;
608
609 switch (attr->id) {
610 case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
611 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
612 return -EOPNOTSUPP;
613
614 ret = dsa_port_set_state(dp, attr->u.stp_state, true);
615 break;
616 case SWITCHDEV_ATTR_ID_PORT_MST_STATE:
617 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
618 return -EOPNOTSUPP;
619
620 ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack);
621 break;
622 case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
623 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
624 return -EOPNOTSUPP;
625
626 ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
627 extack);
628 break;
629 case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
630 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
631 return -EOPNOTSUPP;
632
633 ret = dsa_port_ageing_time(dp, attr->u.ageing_time);
634 break;
635 case SWITCHDEV_ATTR_ID_BRIDGE_MST:
636 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
637 return -EOPNOTSUPP;
638
639 ret = dsa_port_mst_enable(dp, attr->u.mst, extack);
640 break;
641 case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
642 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
643 return -EOPNOTSUPP;
644
645 ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
646 extack);
647 break;
648 case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
649 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
650 return -EOPNOTSUPP;
651
652 ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack);
653 break;
654 case SWITCHDEV_ATTR_ID_VLAN_MSTI:
655 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
656 return -EOPNOTSUPP;
657
658 ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti);
659 break;
660 case SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE:
> 661 const bool *handled = ctx;
662
663 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
664 return -EOPNOTSUPP;
665
666 ret = dsa_port_bridge_mdb_active(dp, attr->u.mc_active, extack,
667 *handled);
668 break;
669 default:
670 ret = -EOPNOTSUPP;
671 break;
672 }
673
674 return ret;
675 }
676
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state
2025-05-22 19:17 ` [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Linus Lüssing
@ 2025-05-23 9:44 ` kernel test robot
2025-05-25 17:13 ` Ido Schimmel
1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-05-23 9:44 UTC (permalink / raw)
To: Linus Lüssing, bridge
Cc: llvm, oe-kbuild-all, netdev, openwrt-devel, linux-kernel,
linux-doc, Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera,
Jiri Pirko, Vladimir Oltean, Andrew Lunn, Jonathan Corbet,
Simon Horman, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
David S . Miller, Kuniyuki Iwashima, Stanislav Fomichev,
Xiao Liang, Markus Stockhausen, Jan Hoffmann, Birger Koblitz,
Bjørn Mork, Linus Lüssing
Hi Linus,
kernel test robot noticed the following build errors:
[auto build test ERROR on net/main]
[also build test ERROR on linus/master v6.15-rc7 next-20250523]
[cannot apply to net-next/main horms-ipvs/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Linus-L-ssing/net-bridge-mcast-explicitly-track-active-state/20250523-040914
base: net/main
patch link: https://lore.kernel.org/r/20250522195952.29265-2-linus.luessing%40c0d3.blue
patch subject: [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state
config: x86_64-buildonly-randconfig-004-20250523 (https://download.01.org/0day-ci/archive/20250523/202505231710.LnCUsJMF-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250523/202505231710.LnCUsJMF-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505231710.LnCUsJMF-lkp@intel.com/
All errors (new ones prefixed by >>):
>> net/bridge/br_multicast.c:1135:6: error: expected value in expression
1135 | #elif
| ^
>> net/bridge/br_multicast.c:1180:11: error: no member named 'ip6_active' in 'struct net_bridge_mcast'; did you mean 'ip4_active'?
1180 | brmctx->ip6_active = ip6_active;
| ^~~~~~~~~~
| ip4_active
include/trace/events/../../../net/bridge/br_private.h:161:10: note: 'ip4_active' declared here
161 | bool ip4_active;
| ^
2 errors generated.
vim +1135 net/bridge/br_multicast.c
1122
1123 static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
1124 bool *active)
1125 {
1126 #if IS_ENABLED(CONFIG_IPV6)
1127 if (!__br_multicast_querier_exists(brmctx, &brmctx->ip6_other_query,
1128 true))
1129 *active = false;
1130
1131 if (brmctx->ip6_active == *active)
1132 return false;
1133
1134 return true;
> 1135 #elif
1136 *active = false;
1137 return false;
1138 #endif
1139 }
1140
1141 /**
1142 * __br_multicast_update_active() - update mcast active state
1143 * @brmctx: the bridge multicast context to check
1144 * @force_inactive: forcefully deactivate mcast active state
1145 * @extack: netlink extended ACK structure
1146 *
1147 * This (potentially) updates the IPv4/IPv6 multicast active state. And by
1148 * that enables or disables snooping of multicast payload traffic in fast
1149 * path.
1150 *
1151 * The multicast active state is set, per protocol family, if:
1152 *
1153 * - an IGMP/MLD querier is present
1154 * - for own IPv6 MLD querier: an IPv6 address is configured on the bridge
1155 *
1156 * And is unset otherwise.
1157 *
1158 * This function should be called by anything that changes one of the
1159 * above prerequisites.
1160 *
1161 * Return: 0 on success, a negative value otherwise.
1162 */
1163 static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
1164 bool force_inactive,
1165 struct netlink_ext_ack *extack)
1166 {
1167 bool ip4_active, ip6_active, ip4_changed, ip6_changed;
1168 int ret = 0;
1169
1170 lockdep_assert_held_once(&brmctx->br->multicast_lock);
1171
1172 ip4_active = !force_inactive;
1173 ip6_active = !force_inactive;
1174 ip4_changed = br_ip4_multicast_check_active(brmctx, &ip4_active);
1175 ip6_changed = br_ip6_multicast_check_active(brmctx, &ip6_active);
1176
1177 if (ip4_changed)
1178 brmctx->ip4_active = ip4_active;
1179 if (ip6_changed)
> 1180 brmctx->ip6_active = ip6_active;
1181
1182 return ret;
1183 }
1184
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification
2025-05-22 19:17 ` [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification Linus Lüssing
2025-05-23 9:24 ` kernel test robot
@ 2025-05-23 9:44 ` kernel test robot
1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-05-23 9:44 UTC (permalink / raw)
To: Linus Lüssing, bridge
Cc: oe-kbuild-all, netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Simon Horman,
Paolo Abeni, Jakub Kicinski, Eric Dumazet, David S . Miller,
Kuniyuki Iwashima, Stanislav Fomichev, Xiao Liang,
Markus Stockhausen, Jan Hoffmann, Birger Koblitz, Bjørn Mork,
Linus Lüssing
Hi Linus,
kernel test robot noticed the following build errors:
[auto build test ERROR on net/main]
[also build test ERROR on linus/master v6.15-rc7 next-20250523]
[cannot apply to net-next/main horms-ipvs/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Linus-L-ssing/net-bridge-mcast-explicitly-track-active-state/20250523-040914
base: net/main
patch link: https://lore.kernel.org/r/20250522195952.29265-6-linus.luessing%40c0d3.blue
patch subject: [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification
config: s390-randconfig-001-20250523 (https://download.01.org/0day-ci/archive/20250523/202505231753.V7E84RiN-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250523/202505231753.V7E84RiN-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505231753.V7E84RiN-lkp@intel.com/
All errors (new ones prefixed by >>):
net/dsa/user.c: In function 'dsa_user_port_attr_set':
>> net/dsa/user.c:661:3: error: a label can only be part of a statement and a declaration is not a statement
const bool *handled = ctx;
^~~~~
net/dsa/user.c: In function 'dsa_user_switchdev_event':
net/dsa/user.c:3770:3: error: a label can only be part of a statement and a declaration is not a statement
struct switchdev_notifier_port_attr_info *item = ptr;
^~~~~~
net/dsa/user.c: In function 'dsa_user_switchdev_blocking_event':
net/dsa/user.c:3813:3: error: a label can only be part of a statement and a declaration is not a statement
struct switchdev_notifier_port_attr_info *item = ptr;
^~~~~~
vim +661 net/dsa/user.c
598
599 static int dsa_user_port_attr_set(struct net_device *dev, const void *ctx,
600 const struct switchdev_attr *attr,
601 struct netlink_ext_ack *extack)
602 {
603 struct dsa_port *dp = dsa_user_to_port(dev);
604 int ret;
605
606 if (ctx && ctx != dp && attr->id != SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE)
607 return 0;
608
609 switch (attr->id) {
610 case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
611 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
612 return -EOPNOTSUPP;
613
614 ret = dsa_port_set_state(dp, attr->u.stp_state, true);
615 break;
616 case SWITCHDEV_ATTR_ID_PORT_MST_STATE:
617 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
618 return -EOPNOTSUPP;
619
620 ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack);
621 break;
622 case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
623 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
624 return -EOPNOTSUPP;
625
626 ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
627 extack);
628 break;
629 case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
630 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
631 return -EOPNOTSUPP;
632
633 ret = dsa_port_ageing_time(dp, attr->u.ageing_time);
634 break;
635 case SWITCHDEV_ATTR_ID_BRIDGE_MST:
636 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
637 return -EOPNOTSUPP;
638
639 ret = dsa_port_mst_enable(dp, attr->u.mst, extack);
640 break;
641 case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
642 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
643 return -EOPNOTSUPP;
644
645 ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
646 extack);
647 break;
648 case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
649 if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
650 return -EOPNOTSUPP;
651
652 ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack);
653 break;
654 case SWITCHDEV_ATTR_ID_VLAN_MSTI:
655 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
656 return -EOPNOTSUPP;
657
658 ret = dsa_port_vlan_msti(dp, &attr->u.vlan_msti);
659 break;
660 case SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE:
> 661 const bool *handled = ctx;
662
663 if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
664 return -EOPNOTSUPP;
665
666 ret = dsa_port_bridge_mdb_active(dp, attr->u.mc_active, extack,
667 *handled);
668 break;
669 default:
670 ret = -EOPNOTSUPP;
671 break;
672 }
673
674 return ret;
675 }
676
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state
2025-05-22 19:17 ` [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state Linus Lüssing
@ 2025-05-23 13:02 ` Simon Horman
0 siblings, 0 replies; 12+ messages in thread
From: Simon Horman @ 2025-05-23 13:02 UTC (permalink / raw)
To: Linus Lüssing
Cc: bridge, netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ido Schimmel, Ivan Vecera, Jiri Pirko,
Vladimir Oltean, Andrew Lunn, Jonathan Corbet, Paolo Abeni,
Jakub Kicinski, Eric Dumazet, David S . Miller, Kuniyuki Iwashima,
Stanislav Fomichev, Xiao Liang, Markus Stockhausen, Jan Hoffmann,
Birger Koblitz, Bjørn Mork
On Thu, May 22, 2025 at 09:17:05PM +0200, Linus Lüssing wrote:
> To be able to use the upcoming SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE
> as a potential replacement for SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED
> also check and toggle the active state if multicast snooping is enabled
> or disabled. So that MC_ACTIVE not only checks the querier state, but
> also if multicast snooping is enabled in general.
>
> Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
> ---
> include/uapi/linux/if_link.h | 6 ++++--
> net/bridge/br_multicast.c | 35 +++++++++++++++++++++++++++++++++--
> 2 files changed, 37 insertions(+), 4 deletions(-)
>
> diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
> index 41f6c461ab32..479d039477cb 100644
> --- a/include/uapi/linux/if_link.h
> +++ b/include/uapi/linux/if_link.h
> @@ -746,12 +746,14 @@ enum in6_addr_gen_mode {
> * @IFLA_BR_MCAST_ACTIVE_V4
> * Bridge IPv4 mcast active state, read only.
> *
> - * 1 if an IGMP querier is present, 0 otherwise.
> + * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an IGMP querier is present,
> + * 0 otherwise.
> *
> * @IFLA_BR_MCAST_ACTIVE_V6
> * Bridge IPv4 mcast active state, read only.
> *
> - * 1 if an MLD querier is present, 0 otherwise.
> + * 1 if *IFLA_BR_MCAST_SNOOPING* is enabled and an MLD querier is present,
> + * 0 otherwise.
> */
> enum {
> IFLA_BR_UNSPEC,
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index b66d2173e321..0bbaa21c1479 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -1150,6 +1150,7 @@ static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
> *
> * The multicast active state is set, per protocol family, if:
> *
> + * - multicast snooping is enabled
> * - an IGMP/MLD querier is present
> * - for own IPv6 MLD querier: an IPv6 address is configured on the bridge
> *
> @@ -1169,6 +1170,13 @@ static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
>
> lockdep_assert_held_once(&brmctx->br->multicast_lock);
>
> + if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED))
> + force_inactive = true;
> +
> + if (br_opt_get(brmctx->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) &&
> + br_multicast_ctx_vlan_disabled(brmctx))
> + force_inactive = true;
> +
> ip4_active = !force_inactive;
> ip6_active = !force_inactive;
> ip4_changed = br_ip4_multicast_check_active(brmctx, &ip4_active);
> @@ -1396,6 +1404,22 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge_mcast *brmctx,
> return NULL;
> }
>
> +static int br_multicast_toggle_enabled(struct net_bridge *br, bool on,
> + struct netlink_ext_ack *extack)
> +{
> + int err, old;
> +
> + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, on);
> +
> + err = br_multicast_update_active(&br->multicast_ctx, extack);
> + if (err && err != -EOPNOTSUPP) {
> + br_opt_toggle(br, BROPT_MULTICAST_ENABLED, old);
Hi Linus,
Old appears to be used uninitialised here.
Flagged by allmodconfig builds on x86_64 with clang-20.1.4.
...
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state
2025-05-22 19:17 ` [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Linus Lüssing
2025-05-23 9:44 ` kernel test robot
@ 2025-05-25 17:13 ` Ido Schimmel
1 sibling, 0 replies; 12+ messages in thread
From: Ido Schimmel @ 2025-05-25 17:13 UTC (permalink / raw)
To: Linus Lüssing
Cc: bridge, netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ivan Vecera, Jiri Pirko, Vladimir Oltean,
Andrew Lunn, Jonathan Corbet, Simon Horman, Paolo Abeni,
Jakub Kicinski, Eric Dumazet, David S . Miller, Kuniyuki Iwashima,
Stanislav Fomichev, Xiao Liang, Markus Stockhausen, Jan Hoffmann,
Birger Koblitz, Bjørn Mork
On Thu, May 22, 2025 at 09:17:03PM +0200, Linus Lüssing wrote:
> Combining and tracking the active state into new, per protocol family
> variables.
>
> For one thing this slightly reduces the work and checks in fast path
> for multicast payload traffic. For another this is in preparation to
> be able to notify DSA/switchdev on the (in)applicability of multicast
> snooping on multicast payload traffic. Especially on vanishing IGMP/MLD
> queriers.
Adding more code in the control path in order to simplify the data path
makes sense, but IMO this patch is difficult to review and should be
split into smaller patches. At the very least the patch can be split
into a control path patch and a data path patch. The latter simplifies
the data path by making use of the new multicast states maintained by
the control path.
The control path patch can be split into smaller patches where each
patch updates the multicast states from the different places that affect
them:
1. When the delay timer expires.
2. When we stop getting queries.
3. When the bridge gains or losses an IPv6 address.
4. When "mcast_querier" is changed (globally or per-VLAN).
5. When "mcast_snooping" is changed (globally or per-VLAN).
Given the number of patches, consider splitting the offload changes into
a separate patchset. It would probably be merged faster that way.
See more comments below.
>
> Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
> ---
> net/bridge/br_device.c | 5 +-
> net/bridge/br_input.c | 2 +-
> net/bridge/br_multicast.c | 179 +++++++++++++++++++++++++++++++++++---
> net/bridge/br_private.h | 33 ++-----
> 4 files changed, 180 insertions(+), 39 deletions(-)
>
> diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
> index a818fdc22da9..315ed3d33406 100644
> --- a/net/bridge/br_device.c
> +++ b/net/bridge/br_device.c
> @@ -102,7 +102,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
>
> mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
> if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
> - br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst))
> + br_multicast_snooping_active(brmctx, eth_hdr(skb), mdst))
> br_multicast_flood(mdst, skb, brmctx, false, true);
> else
> br_flood(br, skb, BR_PKT_MULTICAST, false, true, vid);
> @@ -168,7 +168,10 @@ static int br_dev_open(struct net_device *dev)
> netdev_update_features(dev);
> netif_start_queue(dev);
> br_stp_enable_bridge(br);
> +
> + spin_lock_bh(&br->multicast_lock);
> br_multicast_open(br);
> + spin_unlock_bh(&br->multicast_lock);
>
> if (br_opt_get(br, BROPT_MULTICAST_ENABLED))
> br_multicast_join_snoopers(br);
> diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
> index 232133a0fd21..0c632655d66c 100644
> --- a/net/bridge/br_input.c
> +++ b/net/bridge/br_input.c
> @@ -187,7 +187,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
> case BR_PKT_MULTICAST:
> mdst = br_mdb_entry_skb_get(brmctx, skb, vid);
> if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
> - br_multicast_querier_exists(brmctx, eth_hdr(skb), mdst)) {
> + br_multicast_snooping_active(brmctx, eth_hdr(skb), mdst)) {
> if ((mdst && mdst->host_joined) ||
> br_multicast_is_router(brmctx, skb)) {
> local_rcv = true;
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index dcbf058de1e3..b66d2173e321 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -1069,6 +1069,125 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
> return skb;
> }
>
> +static bool
> +__br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
> + struct bridge_mcast_other_query *querier,
> + const bool is_ipv6)
Remove the const?
> +{
> + bool own_querier_enabled;
> +
> + if (brmctx->multicast_querier) {
> + if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR))
> + own_querier_enabled = false;
> + else
> + own_querier_enabled = true;
> + } else {
> + own_querier_enabled = false;
> + }
> +
> + return !timer_pending(&querier->delay_timer) &&
> + (own_querier_enabled || timer_pending(&querier->timer));
> +}
> +
> +static bool br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
> + struct ethhdr *eth,
Make this const or just pass the EtherType?
> + const struct net_bridge_mdb_entry *mdb)
> +{
> + switch (eth->h_proto) {
> + case (htons(ETH_P_IP)):
> + return __br_multicast_querier_exists(brmctx,
> + &brmctx->ip4_other_query, false);
> +#if IS_ENABLED(CONFIG_IPV6)
> + case (htons(ETH_P_IPV6)):
> + return __br_multicast_querier_exists(brmctx,
> + &brmctx->ip6_other_query, true);
> +#endif
> + default:
> + return !!mdb && br_group_is_l2(&mdb->addr);
> + }
> +}
> +
> +static bool br_ip4_multicast_check_active(struct net_bridge_mcast *brmctx,
> + bool *active)
> +{
> + if (!__br_multicast_querier_exists(brmctx, &brmctx->ip4_other_query,
> + false))
> + *active = false;
> +
> + if (brmctx->ip4_active == *active)
> + return false;
> +
> + return true;
> +}
> +
> +static int br_ip6_multicast_check_active(struct net_bridge_mcast *brmctx,
> + bool *active)
> +{
> +#if IS_ENABLED(CONFIG_IPV6)
> + if (!__br_multicast_querier_exists(brmctx, &brmctx->ip6_other_query,
> + true))
> + *active = false;
> +
> + if (brmctx->ip6_active == *active)
> + return false;
> +
> + return true;
> +#elif
> + *active = false;
> + return false;
> +#endif
> +}
> +
> +/**
> + * __br_multicast_update_active() - update mcast active state
> + * @brmctx: the bridge multicast context to check
> + * @force_inactive: forcefully deactivate mcast active state
> + * @extack: netlink extended ACK structure
> + *
> + * This (potentially) updates the IPv4/IPv6 multicast active state. And by
> + * that enables or disables snooping of multicast payload traffic in fast
> + * path.
> + *
> + * The multicast active state is set, per protocol family, if:
> + *
> + * - an IGMP/MLD querier is present
> + * - for own IPv6 MLD querier: an IPv6 address is configured on the bridge
> + *
> + * And is unset otherwise.
> + *
> + * This function should be called by anything that changes one of the
> + * above prerequisites.
> + *
> + * Return: 0 on success, a negative value otherwise.
> + */
> +static int __br_multicast_update_active(struct net_bridge_mcast *brmctx,
> + bool force_inactive,
> + struct netlink_ext_ack *extack)
> +{
> + bool ip4_active, ip6_active, ip4_changed, ip6_changed;
> + int ret = 0;
> +
> + lockdep_assert_held_once(&brmctx->br->multicast_lock);
> +
> + ip4_active = !force_inactive;
> + ip6_active = !force_inactive;
> + ip4_changed = br_ip4_multicast_check_active(brmctx, &ip4_active);
> + ip6_changed = br_ip6_multicast_check_active(brmctx, &ip6_active);
ip{4,6}_changed aren't really used in this patch. I suggest adding them
when you need them. In addition, br_ip{4,6}_multicast_check_active() are
quite confusing to me. They return a bool, but also modify a bool
argument. It would be clearer to derive the existing states from
brmctx->ip{4,6}_active and then derive the new states from something
like br_ip{4,6}_multicast_can_activate(brmctx)
> +
> + if (ip4_changed)
> + brmctx->ip4_active = ip4_active;
> + if (ip6_changed)
> + brmctx->ip6_active = ip6_active;
> +
> + return ret;
> +}
> +
> +static int br_multicast_update_active(struct net_bridge_mcast *brmctx,
> + struct netlink_ext_ack *extack)
> +{
> + return __br_multicast_update_active(brmctx, false, extack);
> +}
> +
> #if IS_ENABLED(CONFIG_IPV6)
> static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brmctx,
> struct net_bridge_mcast_port *pmctx,
> @@ -1147,10 +1266,12 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
> &ip6h->daddr, 0, &ip6h->saddr)) {
> kfree_skb(skb);
> br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, false);
> + br_multicast_update_active(brmctx, NULL);
> return NULL;
> }
>
> br_opt_toggle(brmctx->br, BROPT_HAS_IPV6_ADDR, true);
> + br_multicast_update_active(brmctx, NULL);
> ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
>
> hopopt = (u8 *)(ip6h + 1);
> @@ -1762,10 +1883,28 @@ static void br_ip6_multicast_querier_expired(struct timer_list *t)
> }
> #endif
>
> -static void br_multicast_query_delay_expired(struct timer_list *t)
> +static void br_ip4_multicast_query_delay_expired(struct timer_list *t)
> {
> + struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
> + ip4_other_query.delay_timer);
> +
> + spin_lock(&brmctx->br->multicast_lock);
> + br_multicast_update_active(brmctx, NULL);
> + spin_unlock(&brmctx->br->multicast_lock);
> }
>
> +#if IS_ENABLED(CONFIG_IPV6)
> +static void br_ip6_multicast_query_delay_expired(struct timer_list *t)
> +{
> + struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
> + ip6_other_query.delay_timer);
> +
> + spin_lock(&brmctx->br->multicast_lock);
> + br_multicast_update_active(brmctx, NULL);
> + spin_unlock(&brmctx->br->multicast_lock);
> +}
> +#endif
> +
> static void br_multicast_select_own_querier(struct net_bridge_mcast *brmctx,
> struct br_ip *ip,
> struct sk_buff *skb)
> @@ -3981,16 +4120,13 @@ static void br_multicast_query_expired(struct net_bridge_mcast *brmctx,
> struct bridge_mcast_own_query *query,
> struct bridge_mcast_querier *querier)
> {
> - spin_lock(&brmctx->br->multicast_lock);
> if (br_multicast_ctx_vlan_disabled(brmctx))
> - goto out;
> + return;
>
> if (query->startup_sent < brmctx->multicast_startup_query_count)
> query->startup_sent++;
>
> br_multicast_send_query(brmctx, NULL, query);
> -out:
> - spin_unlock(&brmctx->br->multicast_lock);
> }
>
> static void br_ip4_multicast_query_expired(struct timer_list *t)
> @@ -3998,8 +4134,11 @@ static void br_ip4_multicast_query_expired(struct timer_list *t)
> struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
> ip4_own_query.timer);
>
> + spin_lock(&brmctx->br->multicast_lock);
> br_multicast_query_expired(brmctx, &brmctx->ip4_own_query,
> &brmctx->ip4_querier);
> + br_multicast_update_active(brmctx, NULL);
> + spin_unlock(&brmctx->br->multicast_lock);
> }
>
> #if IS_ENABLED(CONFIG_IPV6)
> @@ -4008,8 +4147,11 @@ static void br_ip6_multicast_query_expired(struct timer_list *t)
> struct net_bridge_mcast *brmctx = from_timer(brmctx, t,
> ip6_own_query.timer);
>
> + spin_lock(&brmctx->br->multicast_lock);
> br_multicast_query_expired(brmctx, &brmctx->ip6_own_query,
> &brmctx->ip6_querier);
> + br_multicast_update_active(brmctx, NULL);
It is not clear to me why the new states are updated from
br_ip{4,6}_multicast_query_expired(). These functions are called when
the bridge is the querier and it is time to send a new query.
Did you mean to place these in br_ip{4,6}_multicast_querier_expired()
which are invoked when the other querier expired?
> + spin_unlock(&brmctx->br->multicast_lock);
> }
> #endif
>
> @@ -4044,11 +4186,13 @@ void br_multicast_ctx_init(struct net_bridge *br,
> brmctx->multicast_membership_interval = 260 * HZ;
>
> brmctx->ip4_querier.port_ifidx = 0;
> + brmctx->ip4_active = 0;
> seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock);
> brmctx->multicast_igmp_version = 2;
> #if IS_ENABLED(CONFIG_IPV6)
> brmctx->multicast_mld_version = 1;
> brmctx->ip6_querier.port_ifidx = 0;
> + brmctx->ip6_active = 0;
> seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
> #endif
>
> @@ -4057,7 +4201,7 @@ void br_multicast_ctx_init(struct net_bridge *br,
> timer_setup(&brmctx->ip4_other_query.timer,
> br_ip4_multicast_querier_expired, 0);
> timer_setup(&brmctx->ip4_other_query.delay_timer,
> - br_multicast_query_delay_expired, 0);
> + br_ip4_multicast_query_delay_expired, 0);
> timer_setup(&brmctx->ip4_own_query.timer,
> br_ip4_multicast_query_expired, 0);
> #if IS_ENABLED(CONFIG_IPV6)
> @@ -4066,7 +4210,7 @@ void br_multicast_ctx_init(struct net_bridge *br,
> timer_setup(&brmctx->ip6_other_query.timer,
> br_ip6_multicast_querier_expired, 0);
> timer_setup(&brmctx->ip6_other_query.delay_timer,
> - br_multicast_query_delay_expired, 0);
> + br_ip6_multicast_query_delay_expired, 0);
> timer_setup(&brmctx->ip6_own_query.timer,
> br_ip6_multicast_query_expired, 0);
> #endif
> @@ -4171,6 +4315,8 @@ static void __br_multicast_open(struct net_bridge_mcast *brmctx)
> #if IS_ENABLED(CONFIG_IPV6)
> __br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query);
> #endif
> +
> + br_multicast_update_active(brmctx, NULL);
> }
>
> void br_multicast_open(struct net_bridge *br)
> @@ -4209,6 +4355,10 @@ static void __br_multicast_stop(struct net_bridge_mcast *brmctx)
> timer_delete_sync(&brmctx->ip6_other_query.delay_timer);
> timer_delete_sync(&brmctx->ip6_own_query.timer);
> #endif
> +
> + spin_lock_bh(&brmctx->br->multicast_lock);
> + __br_multicast_update_active(brmctx, true, NULL);
> + spin_unlock_bh(&brmctx->br->multicast_lock);
> }
>
> void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
> @@ -4234,10 +4384,13 @@ void br_multicast_toggle_one_vlan(struct net_bridge_vlan *vlan, bool on)
> vlan->priv_flags ^= BR_VLFLAG_MCAST_ENABLED;
> spin_unlock_bh(&br->multicast_lock);
>
> - if (on)
> + if (on) {
> + spin_lock_bh(&br->multicast_lock);
> __br_multicast_open(&vlan->br_mcast_ctx);
> - else
> + spin_unlock_bh(&br->multicast_lock);
> + } else {
> __br_multicast_stop(&vlan->br_mcast_ctx);
> + }
> } else {
> struct net_bridge_mcast *brmctx;
>
> @@ -4298,10 +4451,13 @@ int br_multicast_toggle_vlan_snooping(struct net_bridge *br, bool on,
> br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on);
>
> /* disable/enable non-vlan mcast contexts based on vlan snooping */
> - if (on)
> + if (on) {
> __br_multicast_stop(&br->multicast_ctx);
> - else
> + } else {
> + spin_lock_bh(&br->multicast_lock);
> __br_multicast_open(&br->multicast_ctx);
> + spin_unlock_bh(&br->multicast_lock);
> + }
> list_for_each_entry(p, &br->port_list, list) {
> if (on)
> br_multicast_disable_port(p);
> @@ -4663,6 +4819,7 @@ int br_multicast_set_querier(struct net_bridge_mcast *brmctx, unsigned long val)
> #endif
>
> unlock:
> + br_multicast_update_active(brmctx, NULL);
> spin_unlock_bh(&brmctx->br->multicast_lock);
>
> return 0;
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index d5b3c5936a79..3d05895a437f 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -158,12 +158,14 @@ struct net_bridge_mcast {
> struct bridge_mcast_other_query ip4_other_query;
> struct bridge_mcast_own_query ip4_own_query;
> struct bridge_mcast_querier ip4_querier;
> + bool ip4_active;
> #if IS_ENABLED(CONFIG_IPV6)
> struct hlist_head ip6_mc_router_list;
> struct timer_list ip6_mc_router_timer;
> struct bridge_mcast_other_query ip6_other_query;
> struct bridge_mcast_own_query ip6_own_query;
> struct bridge_mcast_querier ip6_querier;
> + bool ip6_active;
> #endif /* IS_ENABLED(CONFIG_IPV6) */
> #endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
> };
> @@ -1144,37 +1146,16 @@ br_multicast_is_router(struct net_bridge_mcast *brmctx, struct sk_buff *skb)
> }
>
> static inline bool
> -__br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
> - struct bridge_mcast_other_query *querier,
> - const bool is_ipv6)
> -{
> - bool own_querier_enabled;
> -
> - if (brmctx->multicast_querier) {
> - if (is_ipv6 && !br_opt_get(brmctx->br, BROPT_HAS_IPV6_ADDR))
> - own_querier_enabled = false;
> - else
> - own_querier_enabled = true;
> - } else {
> - own_querier_enabled = false;
> - }
> -
> - return !timer_pending(&querier->delay_timer) &&
> - (own_querier_enabled || timer_pending(&querier->timer));
> -}
> -
> -static inline bool br_multicast_querier_exists(struct net_bridge_mcast *brmctx,
> - struct ethhdr *eth,
> - const struct net_bridge_mdb_entry *mdb)
> +br_multicast_snooping_active(struct net_bridge_mcast *brmctx,
> + struct ethhdr *eth,
> + const struct net_bridge_mdb_entry *mdb)
> {
> switch (eth->h_proto) {
> case (htons(ETH_P_IP)):
> - return __br_multicast_querier_exists(brmctx,
> - &brmctx->ip4_other_query, false);
> + return brmctx->ip4_active;
> #if IS_ENABLED(CONFIG_IPV6)
> case (htons(ETH_P_IPV6)):
> - return __br_multicast_querier_exists(brmctx,
> - &brmctx->ip6_other_query, true);
> + return brmctx->ip6_active;
> #endif
> default:
> return !!mdb && br_group_is_l2(&mdb->addr);
> --
> 2.49.0
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink
2025-05-22 19:17 ` [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink Linus Lüssing
@ 2025-05-25 17:20 ` Ido Schimmel
0 siblings, 0 replies; 12+ messages in thread
From: Ido Schimmel @ 2025-05-25 17:20 UTC (permalink / raw)
To: Linus Lüssing
Cc: bridge, netdev, openwrt-devel, linux-kernel, linux-doc,
Nikolay Aleksandrov, Ivan Vecera, Jiri Pirko, Vladimir Oltean,
Andrew Lunn, Jonathan Corbet, Simon Horman, Paolo Abeni,
Jakub Kicinski, Eric Dumazet, David S . Miller, Kuniyuki Iwashima,
Stanislav Fomichev, Xiao Liang, Markus Stockhausen, Jan Hoffmann,
Birger Koblitz, Bjørn Mork
On Thu, May 22, 2025 at 09:17:04PM +0200, Linus Lüssing wrote:
> diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
> index 318386cc5b0d..41f6c461ab32 100644
> --- a/include/uapi/linux/if_link.h
> +++ b/include/uapi/linux/if_link.h
> @@ -742,6 +742,16 @@ enum in6_addr_gen_mode {
> * @IFLA_BR_FDB_MAX_LEARNED
> * Set the number of max dynamically learned FDB entries for the current
> * bridge.
> + *
> + * @IFLA_BR_MCAST_ACTIVE_V4
> + * Bridge IPv4 mcast active state, read only.
> + *
> + * 1 if an IGMP querier is present, 0 otherwise.
> + *
> + * @IFLA_BR_MCAST_ACTIVE_V6
> + * Bridge IPv4 mcast active state, read only.
> + *
> + * 1 if an MLD querier is present, 0 otherwise.
> */
[...]
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 6e337937d0d7..7829d2842851 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -1264,7 +1264,9 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
> [IFLA_BR_VLAN_STATS_ENABLED] = { .type = NLA_U8 },
> [IFLA_BR_MCAST_STATS_ENABLED] = { .type = NLA_U8 },
> [IFLA_BR_MCAST_IGMP_VERSION] = { .type = NLA_U8 },
> + [IFLA_BR_MCAST_ACTIVE_V4] = { .type = NLA_U8 },
> [IFLA_BR_MCAST_MLD_VERSION] = { .type = NLA_U8 },
> + [IFLA_BR_MCAST_ACTIVE_V6] = { .type = NLA_U8 },
The new attributes should be set to 'NLA_REJECT' if they are meant to be
read only. They can also be removed from the policy, but being explicit
and using 'NLA_REJECT' is better IMO.
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-05-25 17:20 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-22 19:17 [PATCH net-next 0/5] net: bridge: propagate safe mcast snooping to switchdev + DSA Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Linus Lüssing
2025-05-23 9:44 ` kernel test robot
2025-05-25 17:13 ` Ido Schimmel
2025-05-22 19:17 ` [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink Linus Lüssing
2025-05-25 17:20 ` Ido Schimmel
2025-05-22 19:17 ` [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state Linus Lüssing
2025-05-23 13:02 ` Simon Horman
2025-05-22 19:17 ` [PATCH net-next 4/5] net: bridge: switchdev: notify on mcast active changes Linus Lüssing
2025-05-22 19:17 ` [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification Linus Lüssing
2025-05-23 9:24 ` kernel test robot
2025-05-23 9:44 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).