diff mbox series

[net-next,5/5] net: dsa: forward bridge/switchdev mcast active notification

Message ID 20250522195952.29265-6-linus.luessing@c0d3.blue
State New
Headers show
Series net: bridge: propagate safe mcast snooping to switchdev + DSA | expand

Commit Message

Linus Lüssing May 22, 2025, 7:17 p.m. UTC
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(-)

Comments

kernel test robot May 23, 2025, 9:24 a.m. UTC | #1
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
kernel test robot May 23, 2025, 9:44 a.m. UTC | #2
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
diff mbox series

Patch

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);