From nobody Sun Feb 8 16:01:31 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4AAC42222A7; Thu, 22 May 2025 20:07:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944433; cv=none; b=QxEMkHmfXZPXjsj/LsBKHpOUhecrxX14Gy4vq8gupjWkiZ8SJOR4tm5yBgPQ2JnwLfe7EJ8e44oCbLw70/QKBJp+W+5nwaMDq8W8LNtyHYOJkUYn+2heZQwX5i8ZQp3dSaItRmseV2e0mg+K3p1Ep8LjdydUad3jRziyduUfC6M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944433; c=relaxed/simple; bh=xEjEb10uzGTqQiQP3ijydh9JxPOpaufneqCh/lN4bNE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=WQyQGXGAMvOJ9/4EUmTSoyGtwmIx5mvSwb9AhnW7AVmntWl+aqKqe0VeQm/NsMGxTAqtT1PZgYMbQbjX9NnkZwrLCoiAE4/G9DHWIofK808lFBaIysFSqROAu2raVr4BqsKEh9G2YYWij2PcfMJZEuhsjue6jOvX2npVUjTJjeU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id DAEA954D7B3; Thu, 22 May 2025 22:00:10 +0200 (CEST) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, openwrt-devel@lists.openwrt.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, 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 , =?UTF-8?q?Bj=C3=B8rn=20Mork?= , =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next 1/5] net: bridge: mcast: explicitly track active state Date: Thu, 22 May 2025 21:17:03 +0200 Message-ID: <20250522195952.29265-2-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250522195952.29265-1-linus.luessing@c0d3.blue> References: <20250522195952.29265-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 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=C3=BCssing --- 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) =20 mdst =3D 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); =20 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 =3D 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 =3D 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; } =20 +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 =3D false; + else + own_querier_enabled =3D true; + } else { + own_querier_enabled =3D 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 =3D false; + + if (brmctx->ip4_active =3D=3D *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 =3D false; + + if (brmctx->ip6_active =3D=3D *active) + return false; + + return true; +#elif + *active =3D 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 =3D 0; + + lockdep_assert_held_once(&brmctx->br->multicast_lock); + + ip4_active =3D !force_inactive; + ip6_active =3D !force_inactive; + ip4_changed =3D br_ip4_multicast_check_active(brmctx, &ip4_active); + ip6_changed =3D br_ip6_multicast_check_active(brmctx, &ip6_active); + + if (ip4_changed) + brmctx->ip4_active =3D ip4_active; + if (ip6_changed) + brmctx->ip6_active =3D 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_mcas= t *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; } =20 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); =20 hopopt =3D (u8 *)(ip6h + 1); @@ -1762,10 +1883,28 @@ static void br_ip6_multicast_querier_expired(struct= timer_list *t) } #endif =20 -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 =3D 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); } =20 +#if IS_ENABLED(CONFIG_IPV6) +static void br_ip6_multicast_query_delay_expired(struct timer_list *t) +{ + struct net_bridge_mcast *brmctx =3D 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 *brmct= x, struct br_ip *ip, struct sk_buff *skb) @@ -3981,16 +4120,13 @@ static void br_multicast_query_expired(struct net_b= ridge_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; =20 if (query->startup_sent < brmctx->multicast_startup_query_count) query->startup_sent++; =20 br_multicast_send_query(brmctx, NULL, query); -out: - spin_unlock(&brmctx->br->multicast_lock); } =20 static void br_ip4_multicast_query_expired(struct timer_list *t) @@ -3998,8 +4134,11 @@ static void br_ip4_multicast_query_expired(struct ti= mer_list *t) struct net_bridge_mcast *brmctx =3D from_timer(brmctx, t, ip4_own_query.timer); =20 + 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); } =20 #if IS_ENABLED(CONFIG_IPV6) @@ -4008,8 +4147,11 @@ static void br_ip6_multicast_query_expired(struct ti= mer_list *t) struct net_bridge_mcast *brmctx =3D from_timer(brmctx, t, ip6_own_query.timer); =20 + 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 =20 @@ -4044,11 +4186,13 @@ void br_multicast_ctx_init(struct net_bridge *br, brmctx->multicast_membership_interval =3D 260 * HZ; =20 brmctx->ip4_querier.port_ifidx =3D 0; + brmctx->ip4_active =3D 0; seqcount_spinlock_init(&brmctx->ip4_querier.seq, &br->multicast_lock); brmctx->multicast_igmp_version =3D 2; #if IS_ENABLED(CONFIG_IPV6) brmctx->multicast_mld_version =3D 1; brmctx->ip6_querier.port_ifidx =3D 0; + brmctx->ip6_active =3D 0; seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock); #endif =20 @@ -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_mca= st *brmctx) #if IS_ENABLED(CONFIG_IPV6) __br_multicast_open_query(brmctx->br, &brmctx->ip6_own_query); #endif + + br_multicast_update_active(brmctx, NULL); } =20 void br_multicast_open(struct net_bridge *br) @@ -4209,6 +4355,10 @@ static void __br_multicast_stop(struct net_bridge_mc= ast *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); } =20 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 ^=3D BR_VLFLAG_MCAST_ENABLED; spin_unlock_bh(&br->multicast_lock); =20 - 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; =20 @@ -4298,10 +4451,13 @@ int br_multicast_toggle_vlan_snooping(struct net_br= idge *br, bool on, br_opt_toggle(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED, on); =20 /* 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 =20 unlock: + br_multicast_update_active(brmctx, NULL); spin_unlock_bh(&brmctx->br->multicast_lock); =20 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 *brm= ctx, struct sk_buff *skb) } =20 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 =3D false; - else - own_querier_enabled =3D true; - } else { - own_querier_enabled =3D 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 *br= mctx, - 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); --=20 2.49.0 From nobody Sun Feb 8 16:01:31 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6DEF1C5F37; Thu, 22 May 2025 20:07:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944434; cv=none; b=UJ3fLl6vMYC/zBZcHz9NaCDrgJrBS8Ww+Ctq93Hw8QM70xXSCOqGsFMukmcKKDyELxV9JNrnb4jUQq13qwIUNnYQ7P5O0v8N6LmCz1elKUfcW7qD/ahniz7kBrOhVDynHpS5p4I4eE/bogLOOqkcxGLiHG3CzCFoNLayBkYfXlI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944434; c=relaxed/simple; bh=O4tz9mowo94/3T3W6l3LeOmM7TMz65unkbTJRarvkeA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LbF7ezS+Ij9zHqIG/tvvODIcva99Dc6L8KRonzhC21H0l099sTrYDUIwT3EbLRmH9MJngQu2qLaC4mySq3mmNMvl8lVz+PuGAp70KKVRrZzkjlyGuL8ZEt0VD9MDTH+jHTkr1Y+wE58HkCq6d8eNXtcdoNE0um2Wwq+umSBo9g4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 7720154E72C; Thu, 22 May 2025 22:00:12 +0200 (CEST) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, openwrt-devel@lists.openwrt.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, 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 , =?UTF-8?q?Bj=C3=B8rn=20Mork?= , =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next 2/5] net: bridge: mcast: export ip{4,6}_active state to netlink Date: Thu, 22 May 2025 21:17:04 +0200 Message-ID: <20250522195952.29265-3-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250522195952.29265-1-linus.luessing@c0d3.blue> References: <20250522195952.29265-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 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=C3=BCssing --- 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, }; =20 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] =3D { [IFLA_BR_VLAN_STATS_ENABLED] =3D { .type =3D NLA_U8 }, [IFLA_BR_MCAST_STATS_ENABLED] =3D { .type =3D NLA_U8 }, [IFLA_BR_MCAST_IGMP_VERSION] =3D { .type =3D NLA_U8 }, + [IFLA_BR_MCAST_ACTIVE_V4] =3D { .type =3D NLA_U8 }, [IFLA_BR_MCAST_MLD_VERSION] =3D { .type =3D NLA_U8 }, + [IFLA_BR_MCAST_ACTIVE_V6] =3D { .type =3D NLA_U8 }, [IFLA_BR_VLAN_STATS_PER_PORT] =3D { .type =3D NLA_U8 }, [IFLA_BR_MULTI_BOOLOPT] =3D NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)), @@ -1625,7 +1627,9 @@ static size_t br_get_size(const struct net_device *br= dev) nla_total_size_64bit(sizeof(u64)) + /* IFLA_BR_MCAST_QUERY_RESPONS= E_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 =3D jiffies_to_clock_t(br->multicast_ctx.multicast_last_member_i= nterval); 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, =20 #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 st= ruct 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_C= NT */ + nla_total_size(sizeof(u32)) /* BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY= _CNT */ + nla_total_size(sizeof(u64)) /* BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_I= NTVL */ @@ -630,9 +636,11 @@ static const struct nla_policy br_vlan_db_gpol[BRIDGE_= VLANDB_GOPTS_MAX + 1] =3D { [BRIDGE_VLANDB_GOPTS_RANGE] =3D { .type =3D NLA_U16 }, [BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING] =3D { .type =3D NLA_U8 }, [BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION] =3D { .type =3D NLA_U8 }, + [BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V6] =3D { .type =3D NLA_U8 }, [BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL] =3D { .type =3D NLA_U64 }, [BRIDGE_VLANDB_GOPTS_MCAST_QUERIER] =3D { .type =3D NLA_U8 }, [BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION] =3D { .type =3D NLA_U8 }, + [BRIDGE_VLANDB_GOPTS_MCAST_ACTIVE_V4] =3D { .type =3D NLA_U8 }, [BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT] =3D { .type =3D NLA_U32 }, [BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT] =3D { .type =3D NLA_U32 }, [BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL] =3D { .type =3D 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 @@ =20 #include "dev.h" =20 -#define RTNL_MAX_TYPE 50 +#define RTNL_MAX_TYPE 52 #define RTNL_SLAVE_MAX_TYPE 44 =20 struct rtnl_link { --=20 2.49.0 From nobody Sun Feb 8 16:01:31 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A6E451EDA39; Thu, 22 May 2025 20:07:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944432; cv=none; b=meAHmB8EJsdeNRmWKbduvlzUlYYXDlV/lc+ZKmQAqgmqqDrkLDm/OyuQzi0I7YzPW6SRCi14ZGcDVq+kS8PLJI0qYHUuThq1Kfm8SbTZ5GxU+th1xbUQ6TENYGS3iC4GPYWKTLiPdpJzuNRTzSu/PvimBrEFN1SgLeZwf2KmMb4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944432; c=relaxed/simple; bh=lmBoQ/gPJgZcboY70w1KOBKnRRGTwaPAWQ776rYMWU0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=V9t3hDaGpGAqOGE37ciOhEPe28zXLBhCVJNqPM8hVyom3nf9YYHGKa3l13QyPXprt44dtN6QyG2X+iMJv+gV5ExmyZiKmxzzufNgOaepcHZHQqC7thHJwhPzIdXbfCV/CCp9EjaV/Fzis9GNmlOfAOsLWq6/i97/eVzaOOTaJA4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id BF2BC54E72E; Thu, 22 May 2025 22:00:13 +0200 (CEST) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, openwrt-devel@lists.openwrt.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, 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 , =?UTF-8?q?Bj=C3=B8rn=20Mork?= , =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next 3/5] net: bridge: mcast: check if snooping is enabled for active state Date: Thu, 22 May 2025 21:17:05 +0200 Message-ID: <20250522195952.29265-4-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250522195952.29265-1-linus.luessing@c0d3.blue> References: <20250522195952.29265-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 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=C3=BCssing --- 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 prese= nt, + * 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 presen= t, + * 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_b= ridge_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_b= ridge_mcast *brmctx, =20 lockdep_assert_held_once(&brmctx->br->multicast_lock); =20 + if (!br_opt_get(brmctx->br, BROPT_MULTICAST_ENABLED)) + force_inactive =3D true; + + if (br_opt_get(brmctx->br, BROPT_MCAST_VLAN_SNOOPING_ENABLED) && + br_multicast_ctx_vlan_disabled(brmctx)) + force_inactive =3D true; + ip4_active =3D !force_inactive; ip6_active =3D !force_inactive; ip4_changed =3D br_ip4_multicast_check_active(brmctx, &ip4_active); @@ -1396,6 +1404,22 @@ static struct sk_buff *br_multicast_alloc_query(stru= ct net_bridge_mcast *brmctx, return NULL; } =20 +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 =3D br_multicast_update_active(&br->multicast_ctx, extack); + if (err && err !=3D -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(s= truct net_bridge *br, if (atomic_read(&br->mdb_hash_tbl.nelems) >=3D 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); } =20 @@ -4382,6 +4406,7 @@ void br_multicast_toggle_one_vlan(struct net_bridge_v= lan *vlan, bool on) =20 spin_lock_bh(&br->multicast_lock); vlan->priv_flags ^=3D BR_VLFLAG_MCAST_ENABLED; + br_multicast_update_active(&vlan->br_mcast_ctx, NULL); spin_unlock_bh(&br->multicast_lock); =20 if (on) { @@ -4405,6 +4430,7 @@ void br_multicast_toggle_one_vlan(struct net_bridge_v= lan *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, unsig= ned long val, if (err) goto unlock; =20 - br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); + err =3D br_multicast_toggle_enabled(br, !!val, extack); + if (err =3D=3D -EOPNOTSUPP) + err =3D 0; + if (err) + goto unlock; + if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { change_snoopers =3D true; goto unlock; --=20 2.49.0 From nobody Sun Feb 8 16:01:31 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0194E205ABA; Thu, 22 May 2025 20:07:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944432; cv=none; b=TlHqrTWWKcpACAJaBu53SIjDVbJkOiEEz8ieUNZZSI+j09S9ffaDQFIwWlVlwE0rkegcWujtzwftaum7fNXRjY+DpKhOLTmPQpk67wUfLV/ILS0Js/cd+Q2PLjliA3vZrSeT0QkauVsq8UnwJ2VTzRQksLcdIrPiWF8a1g6FYJ8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944432; c=relaxed/simple; bh=MfyhvWwk+ZdZvfOBo/EVjnY5vkPIZsIGtuQ8bygKPT4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jQxxOcY5hL2aNe3Xy6etvslHXtPp1+zzTxpC6eTNswkgC4cIFJ2rX01FTa9Z0aXAUeY96I1PnfdcNRUomwEJHY6YL1kz3y7rOPrDJdfsv3bc9Lwfd4MvkUAPGU2FMYfxWqL0cyCHQBjLw/EHPHDahvRfZY6SuvrZo3t+nggKGHo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id EC8D054E731; Thu, 22 May 2025 22:00:14 +0200 (CEST) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, openwrt-devel@lists.openwrt.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, 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 , =?UTF-8?q?Bj=C3=B8rn=20Mork?= , =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next 4/5] net: bridge: switchdev: notify on mcast active changes Date: Thu, 22 May 2025 21:17:06 +0200 Message-ID: <20250522195952.29265-5-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250522195952.29265-1-linus.luessing@c0d3.blue> References: <20250522195952.29265-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 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=C3=BCssing --- 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/network= ing/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. =20 -A switchdev driver can also refuse to support dynamic toggling of the mult= icast -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 disabl= ed. +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; }; =20 +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 } =20 +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 =3D { + .orig_dev =3D brmctx->br->dev, + .id =3D SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE, + .flags =3D SWITCHDEV_F_DEFER, + .u.mc_active =3D { + .vid =3D brmctx->vlan ? brmctx->vlan->vid : -1, + .ip4 =3D ip4_active, + .ip6 =3D ip6_active, + .ip4_changed =3D ip4_changed, + .ip6_changed =3D 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_b= ridge_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 =3D br_ip4_multicast_check_active(brmctx, &ip4_active); ip6_changed =3D br_ip6_multicast_check_active(brmctx, &ip6_active); =20 + if (!ip4_changed && !ip6_changed) + goto out; + + ret =3D br_multicast_notify_active(brmctx, ip4_active, ip6_active, + ip4_changed, ip6_changed, + extack); + if (ret && ret !=3D -EOPNOTSUPP) + goto out; + if (ip4_changed) brmctx->ip4_active =3D ip4_active; if (ip6_changed) brmctx->ip6_active =3D ip6_active; =20 +out: return ret; } =20 --=20 2.49.0 From nobody Sun Feb 8 16:01:31 2026 Received: from mail.aperture-lab.de (mail.aperture-lab.de [116.203.183.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AEE64148827; Thu, 22 May 2025 20:00:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=116.203.183.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944023; cv=none; b=boQ7ysrKwvbWLveaI/t7lzYQMOv48yxO4WSY9/e8/1+3hREYzk46+jmMB2Uf09VEprGAjOUbkF02TdAS+7MD6/K2yzX0BE1isdOb6DbTK/3YqHIu2u83IDEOe5OfFJd9XmK2x9e4e3XK/RknJE0lFgeMKbU9N3mt0t5Vbl4z90w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747944023; c=relaxed/simple; bh=yLDbXO/gSmkNEDZIFZ98m8PJwn3LOwfLyLA3H+5I8X8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kIVUbEMVU0puFP2RBgE2/6c5kxn6TY+fOPwC880QaBiV/Jli6ReDqgtn7eIQZxLXRNNofomrupUwbFONyfLMCRtJ0FviioLsxRfA7/jjsbsNBBpsktZY0hu6gXfMePsRcamrcO6MB9RxckDz7Z3JJuRvHx22yhvYGWbr5Uzoo5E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue; spf=pass smtp.mailfrom=c0d3.blue; arc=none smtp.client-ip=116.203.183.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=c0d3.blue Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=c0d3.blue Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 31D5E54E733; Thu, 22 May 2025 22:00:16 +0200 (CEST) From: =?UTF-8?q?Linus=20L=C3=BCssing?= To: bridge@lists.linux.dev Cc: netdev@vger.kernel.org, openwrt-devel@lists.openwrt.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, 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 , =?UTF-8?q?Bj=C3=B8rn=20Mork?= , =?UTF-8?q?Linus=20L=C3=BCssing?= Subject: [PATCH net-next 5/5] net: dsa: forward bridge/switchdev mcast active notification Date: Thu, 22 May 2025 21:17:07 +0200 Message-ID: <20250522195952.29265-6-linus.luessing@c0d3.blue> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250522195952.29265-1-linus.luessing@c0d3.blue> References: <20250522195952.29265-1-linus.luessing@c0d3.blue> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 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=C3=BCssing --- 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/networkin= g/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 in= to this port forwarding database. =20 +- ``port_mdb_active``: bridge layer function invoked when the bridge start= s (or + stops) to actively apply multicast snooping to multicast payload, i.e. w= hen + 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 sn= ooping + is inactive - to avoid multicast (and consequently also IPv6 unicast, wh= ich + 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 ---------------- =20 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_po= rt *dp, return dsa_port_host_mdb_del(dp, mdb, db); } =20 +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 =3D dp->ds; + struct dsa_notifier_mdb_active_info info =3D { + .dp =3D dp, + .mc_active =3D mc_active, + .extack =3D extack, + .handled =3D 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 *d= p, 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; } =20 +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 =3D dsa_switch_host_mdb_del(ds, info); break; + case DSA_NOTIFIER_MDB_ACTIVE: + err =3D dsa_switch_mdb_active(ds, info); + break; case DSA_NOTIFIER_VLAN_ADD: err =3D 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; }; =20 -/* 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; }; =20 +/* 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 *de= v, const void *ctx, struct dsa_port *dp =3D dsa_user_to_port(dev); int ret; =20 - if (ctx && ctx !=3D dp) + if (ctx && ctx !=3D dp && attr->id !=3D SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIV= E) return 0; =20 switch (attr->id) { @@ -657,6 +657,15 @@ static int dsa_user_port_attr_set(struct net_device *d= ev, const void *ctx, =20 ret =3D dsa_port_vlan_msti(dp, &attr->u.vlan_msti); break; + case SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE: + const bool *handled =3D ctx; + + if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev)) + return -EOPNOTSUPP; + + ret =3D dsa_port_bridge_mdb_active(dp, attr->u.mc_active, extack, + *handled); + break; default: ret =3D -EOPNOTSUPP; break; @@ -3758,6 +3767,11 @@ static int dsa_user_switchdev_event(struct notifier_= block *unused, =20 switch (event) { case SWITCHDEV_PORT_ATTR_SET: + struct switchdev_notifier_port_attr_info *item =3D ptr; + + if (item && item->attr->id =3D=3D SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE) + item->info.ctx =3D &item->handled; + err =3D 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 =3D ptr; + + if (item && item->attr->id =3D=3D SWITCHDEV_ATTR_ID_BRIDGE_MC_ACTIVE) + item->info.ctx =3D &item->handled; + err =3D switchdev_handle_port_attr_set(dev, ptr, dsa_user_dev_check, dsa_user_port_attr_set); --=20 2.49.0