mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [Patch net-next 0/3] Add support for mdb offload failure notification
@ 2025-03-18 22:42 Joseph Huang
  2025-03-18 22:42 ` [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag Joseph Huang
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Joseph Huang @ 2025-03-18 22:42 UTC (permalink / raw)
  To: netdev
  Cc: Joseph Huang, Joseph Huang, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Roopa Prabhu,
	Nikolay Aleksandrov, Simon Horman, linux-kernel, bridge

Currently the bridge does not provide real-time feedback to user space
on whether or not an attempt to offload an mdb entry was successful.

This patch set adds support to notify user space about successful and
failed offload attempts, and the behavior is controlled by a new knob
mdb_notify_on_flag_change:

0 - the bridge will not notify user space about MDB flag change
1 - the bridge will notify user space about flag change if either
    MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
2 - the bridge will notify user space about flag change only if
    MDB_PG_FLAGS_OFFLOAD_FAILED has changed

The default value is 0.

A break-down of the patches in the series:

Patch 1 adds offload failed flag to indicate that the offload attempt
has failed. The flag is reflected in netlink mdb entry flags.

Patch 2 adds the knob mdb_notify_on_flag_change, and notify user space
accordingly in br_switchdev_mdb_complete() when the result is known.

Patch 3 adds netlink interface to manipulate mdb_notify_on_flag_change
knob.

This patch set was inspired by the patch series "Add support for route
offload failure notifications" discussed here:
https://lore.kernel.org/all/20210207082258.3872086-1-idosch@idosch.org/

Joseph Huang (3):
  net: bridge: mcast: Add offload failed mdb flag
  net: bridge: mcast: Notify on offload flag change
  net: bridge: Add notify on flag change netlink i/f

 include/uapi/linux/if_bridge.h |  9 +++++----
 include/uapi/linux/if_link.h   | 14 ++++++++++++++
 net/bridge/br_mdb.c            | 30 +++++++++++++++++++++++++-----
 net/bridge/br_multicast.c      | 25 +++++++++++++++++++++++++
 net/bridge/br_netlink.c        | 21 +++++++++++++++++++++
 net/bridge/br_private.h        | 26 +++++++++++++++++++++-----
 net/bridge/br_switchdev.c      | 31 ++++++++++++++++++++++++++-----
 7 files changed, 137 insertions(+), 19 deletions(-)

-- 
2.49.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag
  2025-03-18 22:42 [Patch net-next 0/3] Add support for mdb offload failure notification Joseph Huang
@ 2025-03-18 22:42 ` Joseph Huang
  2025-03-21  8:19   ` Nikolay Aleksandrov
  2025-03-18 22:42 ` [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change Joseph Huang
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: Joseph Huang @ 2025-03-18 22:42 UTC (permalink / raw)
  To: netdev
  Cc: Joseph Huang, Joseph Huang, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Roopa Prabhu,
	Nikolay Aleksandrov, Simon Horman, linux-kernel, bridge

Add MDB_FLAGS_OFFLOAD_FAILED and MDB_PG_FLAGS_OFFLOAD_FAILED to indicate
that an attempt to offload the MDB entry to switchdev has failed.

Signed-off-by: Joseph Huang <Joseph.Huang@garmin.com>
---
 include/uapi/linux/if_bridge.h |  9 +++++----
 net/bridge/br_mdb.c            |  2 ++
 net/bridge/br_private.h        | 11 ++++++-----
 net/bridge/br_switchdev.c      | 10 +++++-----
 4 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index a5b743a2f775..f2a6de424f3f 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -699,10 +699,11 @@ struct br_mdb_entry {
 #define MDB_TEMPORARY 0
 #define MDB_PERMANENT 1
 	__u8 state;
-#define MDB_FLAGS_OFFLOAD	(1 << 0)
-#define MDB_FLAGS_FAST_LEAVE	(1 << 1)
-#define MDB_FLAGS_STAR_EXCL	(1 << 2)
-#define MDB_FLAGS_BLOCKED	(1 << 3)
+#define MDB_FLAGS_OFFLOAD		(1 << 0)
+#define MDB_FLAGS_FAST_LEAVE		(1 << 1)
+#define MDB_FLAGS_STAR_EXCL		(1 << 2)
+#define MDB_FLAGS_BLOCKED		(1 << 3)
+#define MDB_FLAGS_OFFLOAD_FAILED	(1 << 4)
 	__u8 flags;
 	__u16 vid;
 	struct {
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index 1a52a0bca086..0639691cd19b 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -144,6 +144,8 @@ static void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags)
 		e->flags |= MDB_FLAGS_STAR_EXCL;
 	if (flags & MDB_PG_FLAGS_BLOCKED)
 		e->flags |= MDB_FLAGS_BLOCKED;
+	if (flags & MDB_PG_FLAGS_OFFLOAD_FAILED)
+		e->flags |= MDB_FLAGS_OFFLOAD_FAILED;
 }
 
 static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip,
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 1054b8a88edc..cd6b4e91e7d6 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -306,11 +306,12 @@ struct net_bridge_fdb_flush_desc {
 	u16				vlan_id;
 };
 
-#define MDB_PG_FLAGS_PERMANENT	BIT(0)
-#define MDB_PG_FLAGS_OFFLOAD	BIT(1)
-#define MDB_PG_FLAGS_FAST_LEAVE	BIT(2)
-#define MDB_PG_FLAGS_STAR_EXCL	BIT(3)
-#define MDB_PG_FLAGS_BLOCKED	BIT(4)
+#define MDB_PG_FLAGS_PERMANENT		BIT(0)
+#define MDB_PG_FLAGS_OFFLOAD		BIT(1)
+#define MDB_PG_FLAGS_FAST_LEAVE		BIT(2)
+#define MDB_PG_FLAGS_STAR_EXCL		BIT(3)
+#define MDB_PG_FLAGS_BLOCKED		BIT(4)
+#define MDB_PG_FLAGS_OFFLOAD_FAILED	BIT(5)
 
 #define PG_SRC_ENT_LIMIT	32
 
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 7b41ee8740cb..68dccc2ff7b1 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -505,9 +505,6 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
 	struct net_bridge_port *port = data->port;
 	struct net_bridge *br = port->br;
 
-	if (err)
-		goto err;
-
 	spin_lock_bh(&br->multicast_lock);
 	mp = br_mdb_ip_get(br, &data->ip);
 	if (!mp)
@@ -516,11 +513,14 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
 	     pp = &p->next) {
 		if (p->key.port != port)
 			continue;
-		p->flags |= MDB_PG_FLAGS_OFFLOAD;
+
+		if (err)
+			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
+		else
+			p->flags |= MDB_PG_FLAGS_OFFLOAD;
 	}
 out:
 	spin_unlock_bh(&br->multicast_lock);
-err:
 	kfree(priv);
 }
 
-- 
2.49.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change
  2025-03-18 22:42 [Patch net-next 0/3] Add support for mdb offload failure notification Joseph Huang
  2025-03-18 22:42 ` [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag Joseph Huang
@ 2025-03-18 22:42 ` Joseph Huang
  2025-03-21  8:47   ` Nikolay Aleksandrov
  2025-03-18 22:42 ` [Patch net-next 3/3] net: bridge: Add notify on flag change netlink i/f Joseph Huang
  2025-03-20  6:17 ` [Patch net-next 0/3] Add support for mdb offload failure notification Nikolay Aleksandrov
  3 siblings, 1 reply; 16+ messages in thread
From: Joseph Huang @ 2025-03-18 22:42 UTC (permalink / raw)
  To: netdev
  Cc: Joseph Huang, Joseph Huang, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Roopa Prabhu,
	Nikolay Aleksandrov, Simon Horman, linux-kernel, bridge

Notify user space on offload flag(s) change.

This behavior is controlled by the new knob mdb_notify_on_flag_change:

0 - the bridge will not notify user space about MDB flag change
1 - the bridge will notify user space about flag change if either
    MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
2 - the bridge will notify user space about flag change only if
    MDB_PG_FLAGS_OFFLOAD_FAILED has changed

The default value is 0.

Signed-off-by: Joseph Huang <Joseph.Huang@garmin.com>
---
 net/bridge/br_mdb.c       | 28 +++++++++++++++++++++++-----
 net/bridge/br_multicast.c | 25 +++++++++++++++++++++++++
 net/bridge/br_private.h   | 15 +++++++++++++++
 net/bridge/br_switchdev.c | 25 +++++++++++++++++++++++--
 4 files changed, 86 insertions(+), 7 deletions(-)

diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index 0639691cd19b..d206b5a160f3 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -519,16 +519,17 @@ static size_t rtnl_mdb_nlmsg_size(const struct net_bridge_port_group *pg)
 	       rtnl_mdb_nlmsg_pg_size(pg);
 }
 
-void br_mdb_notify(struct net_device *dev,
-		   struct net_bridge_mdb_entry *mp,
-		   struct net_bridge_port_group *pg,
-		   int type)
+static void _br_mdb_notify(struct net_device *dev,
+			   struct net_bridge_mdb_entry *mp,
+			   struct net_bridge_port_group *pg,
+			   int type, bool notify_switchdev)
 {
 	struct net *net = dev_net(dev);
 	struct sk_buff *skb;
 	int err = -ENOBUFS;
 
-	br_switchdev_mdb_notify(dev, mp, pg, type);
+	if (notify_switchdev)
+		br_switchdev_mdb_notify(dev, mp, pg, type);
 
 	skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
 	if (!skb)
@@ -546,6 +547,23 @@ void br_mdb_notify(struct net_device *dev,
 	rtnl_set_sk_err(net, RTNLGRP_MDB, err);
 }
 
+void br_mdb_notify(struct net_device *dev,
+		   struct net_bridge_mdb_entry *mp,
+		   struct net_bridge_port_group *pg,
+		   int type)
+{
+	_br_mdb_notify(dev, mp, pg, type, true);
+}
+
+#ifdef CONFIG_NET_SWITCHDEV
+void br_mdb_flag_change_notify(struct net_device *dev,
+			       struct net_bridge_mdb_entry *mp,
+			       struct net_bridge_port_group *pg)
+{
+	_br_mdb_notify(dev, mp, pg, RTM_NEWMDB, false);
+}
+#endif
+
 static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
 				   struct net_device *dev,
 				   int ifindex, u16 vid, u32 pid,
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index b2ae0d2434d2..8d583caecd40 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -4051,6 +4051,10 @@ void br_multicast_ctx_init(struct net_bridge *br,
 	brmctx->ip6_querier.port_ifidx = 0;
 	seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
 #endif
+#ifdef CONFIG_NET_SWITCHDEV
+	brmctx->multicast_mdb_notify_on_flag_change =
+		MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE;
+#endif
 
 	timer_setup(&brmctx->ip4_mc_router_timer,
 		    br_ip4_multicast_local_router_expired, 0);
@@ -4708,6 +4712,27 @@ int br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
 }
 #endif
 
+#ifdef CONFIG_NET_SWITCHDEV
+int br_multicast_set_mdb_notify_on_flag_change(struct net_bridge_mcast *brmctx,
+					       u8 val)
+{
+	switch (val) {
+	case MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE:
+	case MDB_NOTIFY_ON_FLAG_CHANGE_BOTH:
+	case MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_bh(&brmctx->br->multicast_lock);
+	brmctx->multicast_mdb_notify_on_flag_change = val;
+	spin_unlock_bh(&brmctx->br->multicast_lock);
+
+	return 0;
+}
+#endif
+
 void br_multicast_set_query_intvl(struct net_bridge_mcast *brmctx,
 				  unsigned long val)
 {
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index cd6b4e91e7d6..8e8de5d54ae3 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -132,6 +132,10 @@ struct net_bridge_mcast_port {
 #endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
 };
 
+#define MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE	0
+#define MDB_NOTIFY_ON_FLAG_CHANGE_BOTH		1
+#define MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY	2
+
 /* net_bridge_mcast must be always defined due to forwarding stubs */
 struct net_bridge_mcast {
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
@@ -146,6 +150,9 @@ struct net_bridge_mcast {
 	u8				multicast_router;
 #if IS_ENABLED(CONFIG_IPV6)
 	u8				multicast_mld_version;
+#endif
+#ifdef CONFIG_NET_SWITCHDEV
+	u8				multicast_mdb_notify_on_flag_change;
 #endif
 	unsigned long			multicast_last_member_interval;
 	unsigned long			multicast_membership_interval;
@@ -988,6 +995,10 @@ int br_multicast_set_igmp_version(struct net_bridge_mcast *brmctx,
 int br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
 				 unsigned long val);
 #endif
+#ifdef CONFIG_NET_SWITCHDEV
+int br_multicast_set_mdb_notify_on_flag_change(struct net_bridge_mcast *brmctx,
+					       u8 val);
+#endif
 struct net_bridge_mdb_entry *
 br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
 struct net_bridge_mdb_entry *
@@ -1004,6 +1015,10 @@ int br_mdb_hash_init(struct net_bridge *br);
 void br_mdb_hash_fini(struct net_bridge *br);
 void br_mdb_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
 		   struct net_bridge_port_group *pg, int type);
+#ifdef CONFIG_NET_SWITCHDEV
+void br_mdb_flag_change_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
+			       struct net_bridge_port_group *pg);
+#endif
 void br_rtr_notify(struct net_device *dev, struct net_bridge_mcast_port *pmctx,
 		   int type);
 void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 68dccc2ff7b1..5b09cfcdf3f3 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -504,20 +504,41 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
 	struct net_bridge_mdb_entry *mp;
 	struct net_bridge_port *port = data->port;
 	struct net_bridge *br = port->br;
+	bool offload_changed = false;
+	bool failed_changed = false;
+	u8 notify;
 
 	spin_lock_bh(&br->multicast_lock);
 	mp = br_mdb_ip_get(br, &data->ip);
 	if (!mp)
 		goto out;
+
+	notify = br->multicast_ctx.multicast_mdb_notify_on_flag_change;
+
 	for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
 	     pp = &p->next) {
 		if (p->key.port != port)
 			continue;
 
-		if (err)
+		if (err) {
+			if (!(p->flags & MDB_PG_FLAGS_OFFLOAD_FAILED))
+				failed_changed = true;
 			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
-		else
+		} else {
+			if (!(p->flags & MDB_PG_FLAGS_OFFLOAD))
+				offload_changed = true;
 			p->flags |= MDB_PG_FLAGS_OFFLOAD;
+		}
+
+		if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE ||
+		    (!offload_changed && !failed_changed))
+			continue;
+
+		if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY &&
+		    !failed_changed)
+			continue;
+
+		br_mdb_flag_change_notify(br->dev, mp, p);
 	}
 out:
 	spin_unlock_bh(&br->multicast_lock);
-- 
2.49.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Patch net-next 3/3] net: bridge: Add notify on flag change netlink i/f
  2025-03-18 22:42 [Patch net-next 0/3] Add support for mdb offload failure notification Joseph Huang
  2025-03-18 22:42 ` [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag Joseph Huang
  2025-03-18 22:42 ` [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change Joseph Huang
@ 2025-03-18 22:42 ` Joseph Huang
  2025-03-20  6:17 ` [Patch net-next 0/3] Add support for mdb offload failure notification Nikolay Aleksandrov
  3 siblings, 0 replies; 16+ messages in thread
From: Joseph Huang @ 2025-03-18 22:42 UTC (permalink / raw)
  To: netdev
  Cc: Joseph Huang, Joseph Huang, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Roopa Prabhu,
	Nikolay Aleksandrov, Simon Horman, linux-kernel, bridge

Add netlink interface to manipulate the mdb_notify_on_flag_change knob.

Signed-off-by: Joseph Huang <Joseph.Huang@garmin.com>
---
 include/uapi/linux/if_link.h | 14 ++++++++++++++
 net/bridge/br_netlink.c      | 21 +++++++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index bfe880fbbb24..8fa830599972 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -741,6 +741,19 @@ 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_MDB_NOTIFY_ON_FLAG_CHANGE
+ *   Set how the bridge shall notify user space about MDB flag change via
+ *   RTM_NEWMDB netlink message.
+ *   The valid values are:
+ *
+ *     * 0 - the bridge will not notify user space about MDB flag change
+ *     * 1 - the bridge will notify user space about flag change if either
+ *           MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
+ *     * 2 - the bridge will notify user space about flag change only if
+ *           MDB_PG_FLAGS_OFFLOAD_FAILED has changed
+ *
+ *   The default value is 0.
  */
 enum {
 	IFLA_BR_UNSPEC,
@@ -793,6 +806,7 @@ enum {
 	IFLA_BR_MCAST_QUERIER_STATE,
 	IFLA_BR_FDB_N_LEARNED,
 	IFLA_BR_FDB_MAX_LEARNED,
+	IFLA_BR_MDB_NOTIFY_ON_FLAG_CHANGE,
 	__IFLA_BR_MAX,
 };
 
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 3e0f47203f2a..e87d39b148d8 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1270,6 +1270,7 @@ static const struct nla_policy br_policy[IFLA_BR_MAX + 1] = {
 		NLA_POLICY_EXACT_LEN(sizeof(struct br_boolopt_multi)),
 	[IFLA_BR_FDB_N_LEARNED] = { .type = NLA_REJECT },
 	[IFLA_BR_FDB_MAX_LEARNED] = { .type = NLA_U32 },
+	[IFLA_BR_MDB_NOTIFY_ON_FLAG_CHANGE] = { .type = NLA_U8 },
 };
 
 static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
@@ -1514,6 +1515,18 @@ static int br_changelink(struct net_device *brdev, struct nlattr *tb[],
 			return err;
 	}
 #endif
+
+#ifdef CONFIG_NET_SWITCHDEV
+	if (data[IFLA_BR_MDB_NOTIFY_ON_FLAG_CHANGE]) {
+		__u8 val;
+
+		val = nla_get_u8(data[IFLA_BR_MDB_NOTIFY_ON_FLAG_CHANGE]);
+		err = br_multicast_set_mdb_notify_on_flag_change(&br->multicast_ctx,
+								 val);
+		if (err)
+			return err;
+	}
+#endif
 #endif
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 	if (data[IFLA_BR_NF_CALL_IPTABLES]) {
@@ -1625,6 +1638,9 @@ static size_t br_get_size(const struct net_device *brdev)
 	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MCAST_IGMP_VERSION */
 	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MCAST_MLD_VERSION */
 	       br_multicast_querier_state_size() + /* IFLA_BR_MCAST_QUERIER_STATE */
+#ifdef CONFIG_NET_SWITCHDEV
+	       nla_total_size(sizeof(u8)) +	/* IFLA_BR_MDB_NOTIFY_ON_FLAG_CHANGE */
+#endif
 #endif
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 	       nla_total_size(sizeof(u8)) +     /* IFLA_BR_NF_CALL_IPTABLES */
@@ -1722,6 +1738,11 @@ static int br_fill_info(struct sk_buff *skb, const struct net_device *brdev)
 	if (nla_put_u8(skb, IFLA_BR_MCAST_MLD_VERSION,
 		       br->multicast_ctx.multicast_mld_version))
 		return -EMSGSIZE;
+#endif
+#ifdef CONFIG_NET_SWITCHDEV
+	if (nla_put_u8(skb, IFLA_BR_MDB_NOTIFY_ON_FLAG_CHANGE,
+		       br->multicast_ctx.multicast_mdb_notify_on_flag_change))
+		return -EMSGSIZE;
 #endif
 	clockval = jiffies_to_clock_t(br->multicast_ctx.multicast_last_member_interval);
 	if (nla_put_u64_64bit(skb, IFLA_BR_MCAST_LAST_MEMBER_INTVL, clockval,
-- 
2.49.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 0/3] Add support for mdb offload failure notification
  2025-03-18 22:42 [Patch net-next 0/3] Add support for mdb offload failure notification Joseph Huang
                   ` (2 preceding siblings ...)
  2025-03-18 22:42 ` [Patch net-next 3/3] net: bridge: Add notify on flag change netlink i/f Joseph Huang
@ 2025-03-20  6:17 ` Nikolay Aleksandrov
  2025-03-20 21:14   ` Joseph Huang
  3 siblings, 1 reply; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-03-20  6:17 UTC (permalink / raw)
  To: Joseph Huang, netdev
  Cc: Joseph Huang, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Roopa Prabhu, Simon Horman,
	linux-kernel, bridge

On 3/19/25 00:42, Joseph Huang wrote:
> Currently the bridge does not provide real-time feedback to user space
> on whether or not an attempt to offload an mdb entry was successful.
> 
> This patch set adds support to notify user space about successful and
> failed offload attempts, and the behavior is controlled by a new knob
> mdb_notify_on_flag_change:
> 
> 0 - the bridge will not notify user space about MDB flag change
> 1 - the bridge will notify user space about flag change if either
>     MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
> 2 - the bridge will notify user space about flag change only if
>     MDB_PG_FLAGS_OFFLOAD_FAILED has changed
> 
> The default value is 0.
> 
> A break-down of the patches in the series:
> 
> Patch 1 adds offload failed flag to indicate that the offload attempt
> has failed. The flag is reflected in netlink mdb entry flags.
> 
> Patch 2 adds the knob mdb_notify_on_flag_change, and notify user space
> accordingly in br_switchdev_mdb_complete() when the result is known.
> 
> Patch 3 adds netlink interface to manipulate mdb_notify_on_flag_change
> knob.
> 
> This patch set was inspired by the patch series "Add support for route
> offload failure notifications" discussed here:
> https://lore.kernel.org/all/20210207082258.3872086-1-idosch@idosch.org/
> 
> Joseph Huang (3):
>   net: bridge: mcast: Add offload failed mdb flag
>   net: bridge: mcast: Notify on offload flag change
>   net: bridge: Add notify on flag change netlink i/f
> 
>  include/uapi/linux/if_bridge.h |  9 +++++----
>  include/uapi/linux/if_link.h   | 14 ++++++++++++++
>  net/bridge/br_mdb.c            | 30 +++++++++++++++++++++++++-----
>  net/bridge/br_multicast.c      | 25 +++++++++++++++++++++++++
>  net/bridge/br_netlink.c        | 21 +++++++++++++++++++++
>  net/bridge/br_private.h        | 26 +++++++++++++++++++++-----
>  net/bridge/br_switchdev.c      | 31 ++++++++++++++++++++++++++-----
>  7 files changed, 137 insertions(+), 19 deletions(-)
> 

Hi,
Could you please share more about the motivation - why do you need this and
what will be using it? Also why do you need an option with 3 different modes
instead of just an on/off switch for these notifications?

Thanks,
 Nik


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 0/3] Add support for mdb offload failure notification
  2025-03-20  6:17 ` [Patch net-next 0/3] Add support for mdb offload failure notification Nikolay Aleksandrov
@ 2025-03-20 21:14   ` Joseph Huang
  2025-03-21  8:47     ` Nikolay Aleksandrov
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph Huang @ 2025-03-20 21:14 UTC (permalink / raw)
  To: Nikolay Aleksandrov, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/20/2025 2:17 AM, Nikolay Aleksandrov wrote:
> On 3/19/25 00:42, Joseph Huang wrote:
>> Currently the bridge does not provide real-time feedback to user space
>> on whether or not an attempt to offload an mdb entry was successful.
>>
>> This patch set adds support to notify user space about successful and
>> failed offload attempts, and the behavior is controlled by a new knob
>> mdb_notify_on_flag_change:
>>
>> 0 - the bridge will not notify user space about MDB flag change
>> 1 - the bridge will notify user space about flag change if either
>>      MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
>> 2 - the bridge will notify user space about flag change only if
>>      MDB_PG_FLAGS_OFFLOAD_FAILED has changed
>>
>> The default value is 0.
>>
>> A break-down of the patches in the series:
>>
>> Patch 1 adds offload failed flag to indicate that the offload attempt
>> has failed. The flag is reflected in netlink mdb entry flags.
>>
>> Patch 2 adds the knob mdb_notify_on_flag_change, and notify user space
>> accordingly in br_switchdev_mdb_complete() when the result is known.
>>
>> Patch 3 adds netlink interface to manipulate mdb_notify_on_flag_change
>> knob.
>>
>> This patch set was inspired by the patch series "Add support for route
>> offload failure notifications" discussed here:
>> https://lore.kernel.org/all/20210207082258.3872086-1-idosch@idosch.org/
>>
>> Joseph Huang (3):
>>    net: bridge: mcast: Add offload failed mdb flag
>>    net: bridge: mcast: Notify on offload flag change
>>    net: bridge: Add notify on flag change netlink i/f
>>
>>   include/uapi/linux/if_bridge.h |  9 +++++----
>>   include/uapi/linux/if_link.h   | 14 ++++++++++++++
>>   net/bridge/br_mdb.c            | 30 +++++++++++++++++++++++++-----
>>   net/bridge/br_multicast.c      | 25 +++++++++++++++++++++++++
>>   net/bridge/br_netlink.c        | 21 +++++++++++++++++++++
>>   net/bridge/br_private.h        | 26 +++++++++++++++++++++-----
>>   net/bridge/br_switchdev.c      | 31 ++++++++++++++++++++++++++-----
>>   7 files changed, 137 insertions(+), 19 deletions(-)
>>
> 
> Hi,
> Could you please share more about the motivation - why do you need this and
> what will be using it? 

Hi Nik,

The API for a user space application to join a multicast group is 
write-only (and really best-efforts only), meaning that after an 
application calls setsockopt(), the application has no way to know 
whether the operation actually succeeded or not. Normally for soft 
bridges this is not an issue; however for switchdev-backed bridges, due 
to limited hardware resources, the failure rate is meaningfully higher.

With this patch set, the user space application will now get a 
notification about a failed attempt to join a multicast group. The user 
space application can then have the opportunity to mitigate the failure 
[1][2].

> Also why do you need an option with 3 different modes
> instead of just an on/off switch for these notifications?
> 
> Thanks,
>   Nik
> 

Some user space application might be interested in both successful and 
failed offload attempts (for example the application might want to keep 
an mdb database which is perfectly in sync with the hardware), while 
some other user space application might only be interested in failed 
attempts (so that it can retry the operation or choose a different group 
for example).

This knob is modeled after fib_notify_on_flag_change knob on route 
offload failure notification (see 
https://lore.kernel.org/all/20210207082258.3872086-4-idosch@idosch.org/). 
The rationale is that "Separate value (read: 2) is added for such 
notifications because there are less of them, so they do not impact 
performance and some users will find them more important."

Thanks,
Joseph

--

[1] 
https://datatracker.ietf.org/doc/draft-ietf-pim-zeroconf-mcast-addr-alloc-ps/, 
section 2, the last paragraph
[2] 
https://datatracker.ietf.org/doc/draft-ietf-pim-ipv6-zeroconf-assignment/, 
section 2.1, the first paragraph

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag
  2025-03-18 22:42 ` [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag Joseph Huang
@ 2025-03-21  8:19   ` Nikolay Aleksandrov
  2025-03-26 22:38     ` Joseph Huang
  0 siblings, 1 reply; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-03-21  8:19 UTC (permalink / raw)
  To: Joseph Huang, netdev
  Cc: Joseph Huang, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Roopa Prabhu, Simon Horman,
	linux-kernel, bridge

On 3/19/25 00:42, Joseph Huang wrote:
> Add MDB_FLAGS_OFFLOAD_FAILED and MDB_PG_FLAGS_OFFLOAD_FAILED to indicate
> that an attempt to offload the MDB entry to switchdev has failed.
> 
> Signed-off-by: Joseph Huang <Joseph.Huang@garmin.com>
> ---
>  include/uapi/linux/if_bridge.h |  9 +++++----
>  net/bridge/br_mdb.c            |  2 ++
>  net/bridge/br_private.h        | 11 ++++++-----
>  net/bridge/br_switchdev.c      | 10 +++++-----
>  4 files changed, 18 insertions(+), 14 deletions(-)
> 
> diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
> index a5b743a2f775..f2a6de424f3f 100644
> --- a/include/uapi/linux/if_bridge.h
> +++ b/include/uapi/linux/if_bridge.h
> @@ -699,10 +699,11 @@ struct br_mdb_entry {
>  #define MDB_TEMPORARY 0
>  #define MDB_PERMANENT 1
>  	__u8 state;
> -#define MDB_FLAGS_OFFLOAD	(1 << 0)
> -#define MDB_FLAGS_FAST_LEAVE	(1 << 1)
> -#define MDB_FLAGS_STAR_EXCL	(1 << 2)
> -#define MDB_FLAGS_BLOCKED	(1 << 3)
> +#define MDB_FLAGS_OFFLOAD		(1 << 0)
> +#define MDB_FLAGS_FAST_LEAVE		(1 << 1)
> +#define MDB_FLAGS_STAR_EXCL		(1 << 2)
> +#define MDB_FLAGS_BLOCKED		(1 << 3)
> +#define MDB_FLAGS_OFFLOAD_FAILED	(1 << 4)
>  	__u8 flags;
>  	__u16 vid;
>  	struct {
> diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
> index 1a52a0bca086..0639691cd19b 100644
> --- a/net/bridge/br_mdb.c
> +++ b/net/bridge/br_mdb.c
> @@ -144,6 +144,8 @@ static void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags)
>  		e->flags |= MDB_FLAGS_STAR_EXCL;
>  	if (flags & MDB_PG_FLAGS_BLOCKED)
>  		e->flags |= MDB_FLAGS_BLOCKED;
> +	if (flags & MDB_PG_FLAGS_OFFLOAD_FAILED)
> +		e->flags |= MDB_FLAGS_OFFLOAD_FAILED;
>  }
>  
>  static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip,
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 1054b8a88edc..cd6b4e91e7d6 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -306,11 +306,12 @@ struct net_bridge_fdb_flush_desc {
>  	u16				vlan_id;
>  };
>  
> -#define MDB_PG_FLAGS_PERMANENT	BIT(0)
> -#define MDB_PG_FLAGS_OFFLOAD	BIT(1)
> -#define MDB_PG_FLAGS_FAST_LEAVE	BIT(2)
> -#define MDB_PG_FLAGS_STAR_EXCL	BIT(3)
> -#define MDB_PG_FLAGS_BLOCKED	BIT(4)
> +#define MDB_PG_FLAGS_PERMANENT		BIT(0)
> +#define MDB_PG_FLAGS_OFFLOAD		BIT(1)
> +#define MDB_PG_FLAGS_FAST_LEAVE		BIT(2)
> +#define MDB_PG_FLAGS_STAR_EXCL		BIT(3)
> +#define MDB_PG_FLAGS_BLOCKED		BIT(4)
> +#define MDB_PG_FLAGS_OFFLOAD_FAILED	BIT(5)
>  
>  #define PG_SRC_ENT_LIMIT	32
>  
> diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
> index 7b41ee8740cb..68dccc2ff7b1 100644
> --- a/net/bridge/br_switchdev.c
> +++ b/net/bridge/br_switchdev.c
> @@ -505,9 +505,6 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>  	struct net_bridge_port *port = data->port;
>  	struct net_bridge *br = port->br;
>  
> -	if (err)
> -		goto err;
> -
>  	spin_lock_bh(&br->multicast_lock);
>  	mp = br_mdb_ip_get(br, &data->ip);
>  	if (!mp)
> @@ -516,11 +513,14 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>  	     pp = &p->next) {
>  		if (p->key.port != port)
>  			continue;
> -		p->flags |= MDB_PG_FLAGS_OFFLOAD;
> +
> +		if (err)
> +			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
> +		else
> +			p->flags |= MDB_PG_FLAGS_OFFLOAD;

These two should be mutually exclusive, either it's offloaded or it failed an offload,
shouldn't be possible to have both set. I'd recommend adding some helper that takes
care of that.

>  	}
>  out:
>  	spin_unlock_bh(&br->multicast_lock);
> -err:
>  	kfree(priv);
>  }
>  


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 0/3] Add support for mdb offload failure notification
  2025-03-20 21:14   ` Joseph Huang
@ 2025-03-21  8:47     ` Nikolay Aleksandrov
  0 siblings, 0 replies; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-03-21  8:47 UTC (permalink / raw)
  To: Joseph Huang, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/20/25 23:14, Joseph Huang wrote:
> On 3/20/2025 2:17 AM, Nikolay Aleksandrov wrote:
>> On 3/19/25 00:42, Joseph Huang wrote:
>>> Currently the bridge does not provide real-time feedback to user space
>>> on whether or not an attempt to offload an mdb entry was successful.
>>>
>>> This patch set adds support to notify user space about successful and
>>> failed offload attempts, and the behavior is controlled by a new knob
>>> mdb_notify_on_flag_change:
>>>
>>> 0 - the bridge will not notify user space about MDB flag change
>>> 1 - the bridge will notify user space about flag change if either
>>>      MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
>>> 2 - the bridge will notify user space about flag change only if
>>>      MDB_PG_FLAGS_OFFLOAD_FAILED has changed
>>>
>>> The default value is 0.
>>>
>>> A break-down of the patches in the series:
>>>
>>> Patch 1 adds offload failed flag to indicate that the offload attempt
>>> has failed. The flag is reflected in netlink mdb entry flags.
>>>
>>> Patch 2 adds the knob mdb_notify_on_flag_change, and notify user space
>>> accordingly in br_switchdev_mdb_complete() when the result is known.
>>>
>>> Patch 3 adds netlink interface to manipulate mdb_notify_on_flag_change
>>> knob.
>>>
>>> This patch set was inspired by the patch series "Add support for route
>>> offload failure notifications" discussed here:
>>> https://lore.kernel.org/all/20210207082258.3872086-1-idosch@idosch.org/
>>>
>>> Joseph Huang (3):
>>>    net: bridge: mcast: Add offload failed mdb flag
>>>    net: bridge: mcast: Notify on offload flag change
>>>    net: bridge: Add notify on flag change netlink i/f
>>>
>>>   include/uapi/linux/if_bridge.h |  9 +++++----
>>>   include/uapi/linux/if_link.h   | 14 ++++++++++++++
>>>   net/bridge/br_mdb.c            | 30 +++++++++++++++++++++++++-----
>>>   net/bridge/br_multicast.c      | 25 +++++++++++++++++++++++++
>>>   net/bridge/br_netlink.c        | 21 +++++++++++++++++++++
>>>   net/bridge/br_private.h        | 26 +++++++++++++++++++++-----
>>>   net/bridge/br_switchdev.c      | 31 ++++++++++++++++++++++++++-----
>>>   7 files changed, 137 insertions(+), 19 deletions(-)
>>>
>>
>> Hi,
>> Could you please share more about the motivation - why do you need this and
>> what will be using it? 
> 
> Hi Nik,
> 
> The API for a user space application to join a multicast group is write-only (and really best-efforts only), meaning that after an application calls setsockopt(), the application has no way to know whether the operation actually succeeded or not. Normally for soft bridges this is not an issue; however for switchdev-backed bridges, due to limited hardware resources, the failure rate is meaningfully higher.
> 
> With this patch set, the user space application will now get a notification about a failed attempt to join a multicast group. The user space application can then have the opportunity to mitigate the failure [1][2].
> 

Thanks for the pointers.

>> Also why do you need an option with 3 different modes
>> instead of just an on/off switch for these notifications?
>>
>> Thanks,
>>   Nik
>>
> 
> Some user space application might be interested in both successful and failed offload attempts (for example the application might want to keep an mdb database which is perfectly in sync with the hardware), while some other user space application might only be interested in failed attempts (so that it can retry the operation or choose a different group for example).
> 
> This knob is modeled after fib_notify_on_flag_change knob on route offload failure notification (see https://lore.kernel.org/all/20210207082258.3872086-4-idosch@idosch.org/). The rationale is that "Separate value (read: 2) is added for such notifications because there are less of them, so they do not impact performance and some users will find them more important."
> 
> Thanks,
> Joseph
> 

Can we please not add features that don't have actual users? It seems you're interested
in failed attempts, so you can just add a bridge boolopt on/off switch to notify about
those events, if anyone becomes interested in all then we can extend it. Also it can
have a more specific name like mdb_offload_fail_notification instead, saying that we
notify on mdb flags change is misleading because there are more flags which can change.

Also please drop all of the switchdev ifdefs and just always have this option available
it will actually be used only with switchdev enabled so setting it in other
cases is a noop, these flags will never be seen anyway.
 
Cheers,
 Nik



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change
  2025-03-18 22:42 ` [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change Joseph Huang
@ 2025-03-21  8:47   ` Nikolay Aleksandrov
  2025-03-31 20:11     ` Joseph Huang
  0 siblings, 1 reply; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-03-21  8:47 UTC (permalink / raw)
  To: Joseph Huang, netdev
  Cc: Joseph Huang, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Roopa Prabhu, Simon Horman,
	linux-kernel, bridge

On 3/19/25 00:42, Joseph Huang wrote:
> Notify user space on offload flag(s) change.
> 
> This behavior is controlled by the new knob mdb_notify_on_flag_change:
> 
> 0 - the bridge will not notify user space about MDB flag change
> 1 - the bridge will notify user space about flag change if either
>     MDB_PG_FLAGS_OFFLOAD or MDB_PG_FLAGS_OFFLOAD_FAILED has changed
> 2 - the bridge will notify user space about flag change only if
>     MDB_PG_FLAGS_OFFLOAD_FAILED has changed
> 
> The default value is 0.
> 
> Signed-off-by: Joseph Huang <Joseph.Huang@garmin.com>
> ---
>  net/bridge/br_mdb.c       | 28 +++++++++++++++++++++++-----
>  net/bridge/br_multicast.c | 25 +++++++++++++++++++++++++
>  net/bridge/br_private.h   | 15 +++++++++++++++
>  net/bridge/br_switchdev.c | 25 +++++++++++++++++++++++--
>  4 files changed, 86 insertions(+), 7 deletions(-)
> 
> diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
> index 0639691cd19b..d206b5a160f3 100644
> --- a/net/bridge/br_mdb.c
> +++ b/net/bridge/br_mdb.c
> @@ -519,16 +519,17 @@ static size_t rtnl_mdb_nlmsg_size(const struct net_bridge_port_group *pg)
>  	       rtnl_mdb_nlmsg_pg_size(pg);
>  }
>  
> -void br_mdb_notify(struct net_device *dev,
> -		   struct net_bridge_mdb_entry *mp,
> -		   struct net_bridge_port_group *pg,
> -		   int type)
> +static void _br_mdb_notify(struct net_device *dev,
> +			   struct net_bridge_mdb_entry *mp,
> +			   struct net_bridge_port_group *pg,
> +			   int type, bool notify_switchdev)

please use double underscore "__"

>  {
>  	struct net *net = dev_net(dev);
>  	struct sk_buff *skb;
>  	int err = -ENOBUFS;
>  
> -	br_switchdev_mdb_notify(dev, mp, pg, type);
> +	if (notify_switchdev)
> +		br_switchdev_mdb_notify(dev, mp, pg, type);
>  
>  	skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
>  	if (!skb)
> @@ -546,6 +547,23 @@ void br_mdb_notify(struct net_device *dev,
>  	rtnl_set_sk_err(net, RTNLGRP_MDB, err);
>  }
>  
> +void br_mdb_notify(struct net_device *dev,
> +		   struct net_bridge_mdb_entry *mp,
> +		   struct net_bridge_port_group *pg,
> +		   int type)
> +{
> +	_br_mdb_notify(dev, mp, pg, type, true);
> +}
> +
> +#ifdef CONFIG_NET_SWITCHDEV
> +void br_mdb_flag_change_notify(struct net_device *dev,
> +			       struct net_bridge_mdb_entry *mp,
> +			       struct net_bridge_port_group *pg)
> +{
> +	_br_mdb_notify(dev, mp, pg, RTM_NEWMDB, false);
> +}
> +#endif
> +
>  static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
>  				   struct net_device *dev,
>  				   int ifindex, u16 vid, u32 pid,
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index b2ae0d2434d2..8d583caecd40 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -4051,6 +4051,10 @@ void br_multicast_ctx_init(struct net_bridge *br,
>  	brmctx->ip6_querier.port_ifidx = 0;
>  	seqcount_spinlock_init(&brmctx->ip6_querier.seq, &br->multicast_lock);
>  #endif
> +#ifdef CONFIG_NET_SWITCHDEV
> +	brmctx->multicast_mdb_notify_on_flag_change =
> +		MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE;
> +#endif
>  
>  	timer_setup(&brmctx->ip4_mc_router_timer,
>  		    br_ip4_multicast_local_router_expired, 0);
> @@ -4708,6 +4712,27 @@ int br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
>  }
>  #endif
>  
> +#ifdef CONFIG_NET_SWITCHDEV
> +int br_multicast_set_mdb_notify_on_flag_change(struct net_bridge_mcast *brmctx,
> +					       u8 val)
> +{
> +	switch (val) {
> +	case MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE:
> +	case MDB_NOTIFY_ON_FLAG_CHANGE_BOTH:
> +	case MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

Please use NLA_POLICY_MAX() instead.

> +
> +	spin_lock_bh(&brmctx->br->multicast_lock);
> +	brmctx->multicast_mdb_notify_on_flag_change = val;
> +	spin_unlock_bh(&brmctx->br->multicast_lock);
> +
> +	return 0;
> +}
> +#endif
> +
>  void br_multicast_set_query_intvl(struct net_bridge_mcast *brmctx,
>  				  unsigned long val)
>  {
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index cd6b4e91e7d6..8e8de5d54ae3 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -132,6 +132,10 @@ struct net_bridge_mcast_port {
>  #endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
>  };
>  
> +#define MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE	0
> +#define MDB_NOTIFY_ON_FLAG_CHANGE_BOTH		1
> +#define MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY	2

This should be an enum and it is also uAPI, you should move it to 
include/uapi/linux/if_bridge.h

> +
>  /* net_bridge_mcast must be always defined due to forwarding stubs */
>  struct net_bridge_mcast {
>  #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
> @@ -146,6 +150,9 @@ struct net_bridge_mcast {
>  	u8				multicast_router;
>  #if IS_ENABLED(CONFIG_IPV6)
>  	u8				multicast_mld_version;
> +#endif
> +#ifdef CONFIG_NET_SWITCHDEV
> +	u8				multicast_mdb_notify_on_flag_change;
>  #endif
>  	unsigned long			multicast_last_member_interval;
>  	unsigned long			multicast_membership_interval;
> @@ -988,6 +995,10 @@ int br_multicast_set_igmp_version(struct net_bridge_mcast *brmctx,
>  int br_multicast_set_mld_version(struct net_bridge_mcast *brmctx,
>  				 unsigned long val);
>  #endif
> +#ifdef CONFIG_NET_SWITCHDEV
> +int br_multicast_set_mdb_notify_on_flag_change(struct net_bridge_mcast *brmctx,
> +					       u8 val);
> +#endif
>  struct net_bridge_mdb_entry *
>  br_mdb_ip_get(struct net_bridge *br, struct br_ip *dst);
>  struct net_bridge_mdb_entry *
> @@ -1004,6 +1015,10 @@ int br_mdb_hash_init(struct net_bridge *br);
>  void br_mdb_hash_fini(struct net_bridge *br);
>  void br_mdb_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
>  		   struct net_bridge_port_group *pg, int type);
> +#ifdef CONFIG_NET_SWITCHDEV
> +void br_mdb_flag_change_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
> +			       struct net_bridge_port_group *pg);
> +#endif
>  void br_rtr_notify(struct net_device *dev, struct net_bridge_mcast_port *pmctx,
>  		   int type);
>  void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
> diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
> index 68dccc2ff7b1..5b09cfcdf3f3 100644
> --- a/net/bridge/br_switchdev.c
> +++ b/net/bridge/br_switchdev.c
> @@ -504,20 +504,41 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>  	struct net_bridge_mdb_entry *mp;
>  	struct net_bridge_port *port = data->port;
>  	struct net_bridge *br = port->br;
> +	bool offload_changed = false;
> +	bool failed_changed = false;
> +	u8 notify;
>  
>  	spin_lock_bh(&br->multicast_lock);
>  	mp = br_mdb_ip_get(br, &data->ip);
>  	if (!mp)
>  		goto out;
> +
> +	notify = br->multicast_ctx.multicast_mdb_notify_on_flag_change;

let's not waste cycles if there was an error and notify == 0, please keep the original
code path and avoid walking over the group ports.

> +
>  	for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
>  	     pp = &p->next) {
>  		if (p->key.port != port)
>  			continue;
>  
> -		if (err)
> +		if (err) {
> +			if (!(p->flags & MDB_PG_FLAGS_OFFLOAD_FAILED))
> +				failed_changed = true;
>  			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
> -		else
> +		} else {
> +			if (!(p->flags & MDB_PG_FLAGS_OFFLOAD))
> +				offload_changed = true;
>  			p->flags |= MDB_PG_FLAGS_OFFLOAD;
> +		}
> +
> +		if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE ||
> +		    (!offload_changed && !failed_changed))
> +			continue;
> +
> +		if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY &&
> +		    !failed_changed)
> +			continue;
> +
> +		br_mdb_flag_change_notify(br->dev, mp, p);

This looks like a mess.. First you need to manage these flags properly as I wrote in my
other reply, they must be mutually exclusive and you can do this in a helper. Also
please read the old flags in the beginning, then check what flags changed, make a mask
what flags are for notifications (again can come from a helper, it can be generated when
the option changes so you don't compute it every time) and decide what to do if any of
those flags changed.
Note you have to keep proper flags state regardless of the notify option.

>  	}
>  out:
>  	spin_unlock_bh(&br->multicast_lock);


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag
  2025-03-21  8:19   ` Nikolay Aleksandrov
@ 2025-03-26 22:38     ` Joseph Huang
  2025-03-27 22:52       ` Nikolay Aleksandrov
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph Huang @ 2025-03-26 22:38 UTC (permalink / raw)
  To: Nikolay Aleksandrov, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/21/2025 4:19 AM, Nikolay Aleksandrov wrote:
>> @@ -516,11 +513,14 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>>   	     pp = &p->next) {
>>   		if (p->key.port != port)
>>   			continue;
>> -		p->flags |= MDB_PG_FLAGS_OFFLOAD;
>> +
>> +		if (err)
>> +			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>> +		else
>> +			p->flags |= MDB_PG_FLAGS_OFFLOAD;
> 
> These two should be mutually exclusive, either it's offloaded or it failed an offload,
> shouldn't be possible to have both set. I'd recommend adding some helper that takes
> care of that.

It is true that these two are mutually exclusive, but strictly speaking 
there are four types of entries:

1. Entries which are not offload-able (i.e., the ports are not backed by 
switchdev)
2. Entries which are being offloaded, but results yet unknown
3. Entries which are successfully offloaded, and
4. Entries which failed to be offloaded

Even if we ignore the ones which are being offloaded (type 2 is 
transient), we still need two flags, otherwise we won't be able to tell 
type 1 from type 4 entries.

If we need two flags anyway, having separate flags for type 3 and type 4 
simplifies the logic.

Or did I misunderstood your comments?

Thanks,
Joseph

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag
  2025-03-26 22:38     ` Joseph Huang
@ 2025-03-27 22:52       ` Nikolay Aleksandrov
  2025-03-28 15:53         ` Joseph Huang
  0 siblings, 1 reply; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-03-27 22:52 UTC (permalink / raw)
  To: Joseph Huang, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/27/25 00:38, Joseph Huang wrote:
> On 3/21/2025 4:19 AM, Nikolay Aleksandrov wrote:
>>> @@ -516,11 +513,14 @@ static void br_switchdev_mdb_complete(struct 
>>> net_device *dev, int err, void *pri
>>>            pp = &p->next) {
>>>           if (p->key.port != port)
>>>               continue;
>>> -        p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>> +
>>> +        if (err)
>>> +            p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>>> +        else
>>> +            p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>
>> These two should be mutually exclusive, either it's offloaded or it 
>> failed an offload,
>> shouldn't be possible to have both set. I'd recommend adding some 
>> helper that takes
>> care of that.
> 
> It is true that these two are mutually exclusive, but strictly speaking 
> there are four types of entries:
> 
> 1. Entries which are not offload-able (i.e., the ports are not backed by 
> switchdev)
> 2. Entries which are being offloaded, but results yet unknown
> 3. Entries which are successfully offloaded, and
> 4. Entries which failed to be offloaded
> 
> Even if we ignore the ones which are being offloaded (type 2 is 
> transient), we still need two flags, otherwise we won't be able to tell 
> type 1 from type 4 entries.
> 
> If we need two flags anyway, having separate flags for type 3 and type 4 
> simplifies the logic.
> 
> Or did I misunderstood your comments?
> 
> Thanks,
> Joseph

I think you misunderstood me, I don't mind having the two flags. :)
My point is that they must be managed correctly and shouldn't be allowed
to be set simultaneously.

Cheers,
  Nik


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag
  2025-03-27 22:52       ` Nikolay Aleksandrov
@ 2025-03-28 15:53         ` Joseph Huang
  2025-03-31 12:51           ` Nikolay Aleksandrov
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph Huang @ 2025-03-28 15:53 UTC (permalink / raw)
  To: Nikolay Aleksandrov, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/27/2025 6:52 PM, Nikolay Aleksandrov wrote:
> On 3/27/25 00:38, Joseph Huang wrote:
>> On 3/21/2025 4:19 AM, Nikolay Aleksandrov wrote:
>>>> @@ -516,11 +513,14 @@ static void br_switchdev_mdb_complete(struct 
>>>> net_device *dev, int err, void *pri
>>>>            pp = &p->next) {
>>>>           if (p->key.port != port)
>>>>               continue;
>>>> -        p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>>> +
>>>> +        if (err)
>>>> +            p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>>>> +        else
>>>> +            p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>>
>>> These two should be mutually exclusive, either it's offloaded or it 
>>> failed an offload,
>>> shouldn't be possible to have both set. I'd recommend adding some 
>>> helper that takes
>>> care of that.
>>
>> It is true that these two are mutually exclusive, but strictly 
>> speaking there are four types of entries:
>>
>> 1. Entries which are not offload-able (i.e., the ports are not backed 
>> by switchdev)
>> 2. Entries which are being offloaded, but results yet unknown
>> 3. Entries which are successfully offloaded, and
>> 4. Entries which failed to be offloaded
>>
>> Even if we ignore the ones which are being offloaded (type 2 is 
>> transient), we still need two flags, otherwise we won't be able to 
>> tell type 1 from type 4 entries.
>>
>> If we need two flags anyway, having separate flags for type 3 and type 
>> 4 simplifies the logic.
>>
>> Or did I misunderstood your comments?
>>
>> Thanks,
>> Joseph
> 
> I think you misunderstood me, I don't mind having the two flags. :)

Got it. Thanks.

> My point is that they must be managed correctly and shouldn't be allowed
> to be set simultaneously.
> 
> Cheers,
>   Nik
> 

Helper function like this?

+static void set_mdb_pg_offload_flags(bool err, u8 *flags)
+{
+	*flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
+	*flags |= (err ? MDB_PG_FLAGS_OFFLOAD_FAILED : MDB_PG_FLAGS_OFFLOAD);
+}

and then from the call site

-		p->flags |= MDB_PG_FLAGS_OFFLOAD;
+		set_mdb_pg_offload_flags(err, &p->flags);

?

Or simply clearing the flags in-line:

-		p->flags |= MDB_PG_FLAGS_OFFLOAD;
+		p->flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
+
+		if (err)
+			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
+		else
+			p->flags |= MDB_PG_FLAGS_OFFLOAD;

?

Thanks,
Joseph

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag
  2025-03-28 15:53         ` Joseph Huang
@ 2025-03-31 12:51           ` Nikolay Aleksandrov
  0 siblings, 0 replies; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-03-31 12:51 UTC (permalink / raw)
  To: Joseph Huang, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/28/25 17:53, Joseph Huang wrote:
> On 3/27/2025 6:52 PM, Nikolay Aleksandrov wrote:
>> On 3/27/25 00:38, Joseph Huang wrote:
>>> On 3/21/2025 4:19 AM, Nikolay Aleksandrov wrote:
>>>>> @@ -516,11 +513,14 @@ static void br_switchdev_mdb_complete(struct 
>>>>> net_device *dev, int err, void *pri
>>>>>            pp = &p->next) {
>>>>>           if (p->key.port != port)
>>>>>               continue;
>>>>> -        p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>>>> +
>>>>> +        if (err)
>>>>> +            p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>>>>> +        else
>>>>> +            p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>>>
>>>> These two should be mutually exclusive, either it's offloaded or it 
>>>> failed an offload,
>>>> shouldn't be possible to have both set. I'd recommend adding some 
>>>> helper that takes
>>>> care of that.
>>>
>>> It is true that these two are mutually exclusive, but strictly 
>>> speaking there are four types of entries:
>>>
>>> 1. Entries which are not offload-able (i.e., the ports are not backed 
>>> by switchdev)
>>> 2. Entries which are being offloaded, but results yet unknown
>>> 3. Entries which are successfully offloaded, and
>>> 4. Entries which failed to be offloaded
>>>
>>> Even if we ignore the ones which are being offloaded (type 2 is 
>>> transient), we still need two flags, otherwise we won't be able to 
>>> tell type 1 from type 4 entries.
>>>
>>> If we need two flags anyway, having separate flags for type 3 and 
>>> type 4 simplifies the logic.
>>>
>>> Or did I misunderstood your comments?
>>>
>>> Thanks,
>>> Joseph
>>
>> I think you misunderstood me, I don't mind having the two flags. :)
> 
> Got it. Thanks.
> 
>> My point is that they must be managed correctly and shouldn't be allowed
>> to be set simultaneously.
>>
>> Cheers,
>>   Nik
>>
> 
> Helper function like this?
> 
> +static void set_mdb_pg_offload_flags(bool err, u8 *flags)
> +{
> +    *flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
> +    *flags |= (err ? MDB_PG_FLAGS_OFFLOAD_FAILED : MDB_PG_FLAGS_OFFLOAD);
> +}

This could work, but I have to see how it aligns with the rest of the
code to be able to answer well. Also why not just pass the pg?
Please also choose another helper name, e.g.
br_multicast_set_pg_offload_flags() or something in these lines. You
can check br_private.h for other helpers to get an idea.

> 
> and then from the call site
> 
> -        p->flags |= MDB_PG_FLAGS_OFFLOAD;
> +        set_mdb_pg_offload_flags(err, &p->flags);
> 
> ?
> 
> Or simply clearing the flags in-line:
> 
> -        p->flags |= MDB_PG_FLAGS_OFFLOAD;
> +        p->flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
> +
> +        if (err)
> +            p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
> +        else
> +            p->flags |= MDB_PG_FLAGS_OFFLOAD;
> 
> ?

I'd prefer using a  helper. Thanks.

> 
> Thanks,
> Joseph

Cheers,
  Nik


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change
  2025-03-21  8:47   ` Nikolay Aleksandrov
@ 2025-03-31 20:11     ` Joseph Huang
  2025-04-01 12:49       ` Nikolay Aleksandrov
  0 siblings, 1 reply; 16+ messages in thread
From: Joseph Huang @ 2025-03-31 20:11 UTC (permalink / raw)
  To: Nikolay Aleksandrov, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/21/2025 4:47 AM, Nikolay Aleksandrov wrote:
>> diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
>> index 68dccc2ff7b1..5b09cfcdf3f3 100644
>> --- a/net/bridge/br_switchdev.c
>> +++ b/net/bridge/br_switchdev.c
>> @@ -504,20 +504,41 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>>   	struct net_bridge_mdb_entry *mp;
>>   	struct net_bridge_port *port = data->port;
>>   	struct net_bridge *br = port->br;
>> +	bool offload_changed = false;
>> +	bool failed_changed = false;
>> +	u8 notify;
>>   
>>   	spin_lock_bh(&br->multicast_lock);
>>   	mp = br_mdb_ip_get(br, &data->ip);
>>   	if (!mp)
>>   		goto out;
>> +
>> +	notify = br->multicast_ctx.multicast_mdb_notify_on_flag_change;
> 
> let's not waste cycles if there was an error and notify == 0, please keep the original
> code path and avoid walking over the group ports.

But we do want to keep the error flag so that the error shows up in 
'bridge mdb show', right? Notify should only affect the real-time 
notifications, and not the error status itself.

> 
>> +
>>   	for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
>>   	     pp = &p->next) {
>>   		if (p->key.port != port)
>>   			continue;
>>   
>> -		if (err)
>> +		if (err) {
>> +			if (!(p->flags & MDB_PG_FLAGS_OFFLOAD_FAILED))
>> +				failed_changed = true;
>>   			p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>> -		else
>> +		} else {
>> +			if (!(p->flags & MDB_PG_FLAGS_OFFLOAD))
>> +				offload_changed = true;
>>   			p->flags |= MDB_PG_FLAGS_OFFLOAD;
>> +		}
>> +
>> +		if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE ||
>> +		    (!offload_changed && !failed_changed))
>> +			continue;
>> +
>> +		if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY &&
>> +		    !failed_changed)
>> +			continue;
>> +
>> +		br_mdb_flag_change_notify(br->dev, mp, p);
> 
> This looks like a mess.. First you need to manage these flags properly as I wrote in my
> other reply, they must be mutually exclusive and you can do this in a helper. Also
> please read the old flags in the beginning, then check what flags changed, make a mask
> what flags are for notifications (again can come from a helper, it can be generated when
> the option changes so you don't compute it every time) and decide what to do if any of
> those flags changed.
> Note you have to keep proper flags state regardless of the notify option.
> 
>>   	}
>>   out:
>>   	spin_unlock_bh(&br->multicast_lock);
> 

How does this look:

--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -496,6 +496,21 @@ struct br_switchdev_mdb_complete_info {
         struct br_ip ip;
  };

+static void br_multicast_set_pg_offload_flags(int err,
+                                             struct 
net_bridge_port_group *p)
+{
+       p->flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
+       p->flags |= (err ? MDB_PG_FLAGS_OFFLOAD_FAILED : 
MDB_PG_FLAGS_OFFLOAD);
+}
+
+static bool br_multicast_should_notify(struct net_bridge *br,
+                                      u8 old_flags, u8 new_flags)
+{
+       return (br_boolopt_get(br, 
BR_BOOLOPT_FAILED_OFFLOAD_NOTIFICATION) &&
+               ((old_flags & MDB_PG_FLAGS_OFFLOAD_FAILED) !=
+               (new_flags & MDB_PG_FLAGS_OFFLOAD_FAILED)));
+}
+
  static void br_switchdev_mdb_complete(struct net_device *dev, int err, 
void *priv)
  {
         struct br_switchdev_mdb_complete_info *data = priv;
@@ -504,23 +519,25 @@ static void br_switchdev_mdb_complete(struct 
net_device *dev, int err, void *pri
         struct net_bridge_mdb_entry *mp;
         struct net_bridge_port *port = data->port;
         struct net_bridge *br = port->br;
-
-       if (err)
-               goto err;
+       u8 old_flags;

         spin_lock_bh(&br->multicast_lock);
         mp = br_mdb_ip_get(br, &data->ip);
         if (!mp)
                 goto out;
         for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
              pp = &p->next) {
                 if (p->key.port != port)
                         continue;
-               p->flags |= MDB_PG_FLAGS_OFFLOAD;
+
+               old_flags = p->flags;
+               br_multicast_set_pg_offload_flags(err, p);
+               if (br_multicast_should_notify(br, old_flags, p->flags))
+                       br_mdb_flag_change_notify(br->dev, mp, p);
         }
  out:
         spin_unlock_bh(&br->multicast_lock);
-err:
         kfree(priv);
  }

Thanks,
Joseph

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change
  2025-03-31 20:11     ` Joseph Huang
@ 2025-04-01 12:49       ` Nikolay Aleksandrov
  2025-04-01 13:37         ` Nikolay Aleksandrov
  0 siblings, 1 reply; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-04-01 12:49 UTC (permalink / raw)
  To: Joseph Huang, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 3/31/25 23:11, Joseph Huang wrote:
> On 3/21/2025 4:47 AM, Nikolay Aleksandrov wrote:
>>> diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
>>> index 68dccc2ff7b1..5b09cfcdf3f3 100644
>>> --- a/net/bridge/br_switchdev.c
>>> +++ b/net/bridge/br_switchdev.c
>>> @@ -504,20 +504,41 @@ static void br_switchdev_mdb_complete(struct 
>>> net_device *dev, int err, void *pri
>>>       struct net_bridge_mdb_entry *mp;
>>>       struct net_bridge_port *port = data->port;
>>>       struct net_bridge *br = port->br;
>>> +    bool offload_changed = false;
>>> +    bool failed_changed = false;
>>> +    u8 notify;
>>>       spin_lock_bh(&br->multicast_lock);
>>>       mp = br_mdb_ip_get(br, &data->ip);
>>>       if (!mp)
>>>           goto out;
>>> +
>>> +    notify = br->multicast_ctx.multicast_mdb_notify_on_flag_change;
>>
>> let's not waste cycles if there was an error and notify == 0, please 
>> keep the original
>> code path and avoid walking over the group ports.
> 
> But we do want to keep the error flag so that the error shows up in 
> 'bridge mdb show', right? Notify should only affect the real-time 
> notifications, and not the error status itself.
> 

Fair enough, sounds good.

>>
>>> +
>>>       for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
>>>            pp = &p->next) {
>>>           if (p->key.port != port)
>>>               continue;
>>> -        if (err)
>>> +        if (err) {
>>> +            if (!(p->flags & MDB_PG_FLAGS_OFFLOAD_FAILED))
>>> +                failed_changed = true;
>>>               p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>>> -        else
>>> +        } else {
>>> +            if (!(p->flags & MDB_PG_FLAGS_OFFLOAD))
>>> +                offload_changed = true;
>>>               p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>> +        }
>>> +
>>> +        if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE ||
>>> +            (!offload_changed && !failed_changed))
>>> +            continue;
>>> +
>>> +        if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY &&
>>> +            !failed_changed)
>>> +            continue;
>>> +
>>> +        br_mdb_flag_change_notify(br->dev, mp, p);
>>
>> This looks like a mess.. First you need to manage these flags properly 
>> as I wrote in my
>> other reply, they must be mutually exclusive and you can do this in a 
>> helper. Also
>> please read the old flags in the beginning, then check what flags 
>> changed, make a mask
>> what flags are for notifications (again can come from a helper, it can 
>> be generated when
>> the option changes so you don't compute it every time) and decide what 
>> to do if any of
>> those flags changed.
>> Note you have to keep proper flags state regardless of the notify option.
>>
>>>       }
>>>   out:
>>>       spin_unlock_bh(&br->multicast_lock);
>>
> 
> How does this look:
> 
> --- a/net/bridge/br_switchdev.c
> +++ b/net/bridge/br_switchdev.c
> @@ -496,6 +496,21 @@ struct br_switchdev_mdb_complete_info {
>          struct br_ip ip;
>   };
>

#define MDB_NOTIFY_FLAGS MDB_PG_FLAGS_OFFLOAD_FAILED

> +static void br_multicast_set_pg_offload_flags(int err,
> +                                             struct 
> net_bridge_port_group *p)

swap these two arguments please, since we don't use err you can probably
rename it to "failed" and make it a bool

alternatively if you prefer maybe rename it to
br_multicast_set_pg_offload_flag() and pass the correct flag from the
caller
e.g. br_multicast_set_pg_offload_flag(pg, err ?
MDB_PG_FLAGS_OFFLOAD_FAILED :  MDB_PG_FLAGS_OFFLOAD)

I don't mind either way.

> +{
> +       p->flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
> +       p->flags |= (err ? MDB_PG_FLAGS_OFFLOAD_FAILED : 
> MDB_PG_FLAGS_OFFLOAD);
> +}
> +
> +static bool br_multicast_should_notify(struct net_bridge *br,

hmm perhaps br_mdb_should_notify() to be more specific? I don't mind the
current name, just a thought.

also const br

> +                                      u8 old_flags, u8 new_flags)

u8 changed_flags should suffice

> +{
> +       return (br_boolopt_get(br, 
> BR_BOOLOPT_FAILED_OFFLOAD_NOTIFICATION) &&
> +               ((old_flags & MDB_PG_FLAGS_OFFLOAD_FAILED) !=
> +               (new_flags & MDB_PG_FLAGS_OFFLOAD_FAILED)));

if (changed_flags & MDB_NOTIFY_FLAGS)

also no need for the extra () around the whole statement

> +}
> +

both of these helpers should go into br_private.h

>   static void br_switchdev_mdb_complete(struct net_device *dev, int err, 
> void *priv)
>   {
>          struct br_switchdev_mdb_complete_info *data = priv;
> @@ -504,23 +519,25 @@ static void br_switchdev_mdb_complete(struct 
> net_device *dev, int err, void *pri
>          struct net_bridge_mdb_entry *mp;
>          struct net_bridge_port *port = data->port;
>          struct net_bridge *br = port->br;
> -
> -       if (err)
> -               goto err;
> +       u8 old_flags;
> 
>          spin_lock_bh(&br->multicast_lock);
>          mp = br_mdb_ip_get(br, &data->ip);
>          if (!mp)
>                  goto out;
>          for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
>               pp = &p->next) {
>                  if (p->key.port != port)
>                          continue;
> -               p->flags |= MDB_PG_FLAGS_OFFLOAD;
> +
> +               old_flags = p->flags;
> +               br_multicast_set_pg_offload_flags(err, p);
> +               if (br_multicast_should_notify(br, old_flags, p->flags))

and here it would become:
br_multicast_should_notify(br, old_flags ^ p->flags)

> +                       br_mdb_flag_change_notify(br->dev, mp, p);
>          }
>   out:
>          spin_unlock_bh(&br->multicast_lock);
> -err:
>          kfree(priv);
>   }
> 
> Thanks,
> Joseph

Cheers,
  Nik


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change
  2025-04-01 12:49       ` Nikolay Aleksandrov
@ 2025-04-01 13:37         ` Nikolay Aleksandrov
  0 siblings, 0 replies; 16+ messages in thread
From: Nikolay Aleksandrov @ 2025-04-01 13:37 UTC (permalink / raw)
  To: Joseph Huang, Joseph Huang, netdev
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roopa Prabhu, Simon Horman, linux-kernel, bridge

On 4/1/25 15:49, Nikolay Aleksandrov wrote:
> On 3/31/25 23:11, Joseph Huang wrote:
>> On 3/21/2025 4:47 AM, Nikolay Aleksandrov wrote:
>>>> diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
>>>> index 68dccc2ff7b1..5b09cfcdf3f3 100644
>>>> --- a/net/bridge/br_switchdev.c
>>>> +++ b/net/bridge/br_switchdev.c
>>>> @@ -504,20 +504,41 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>>>>       struct net_bridge_mdb_entry *mp;
>>>>       struct net_bridge_port *port = data->port;
>>>>       struct net_bridge *br = port->br;
>>>> +    bool offload_changed = false;
>>>> +    bool failed_changed = false;
>>>> +    u8 notify;
>>>>       spin_lock_bh(&br->multicast_lock);
>>>>       mp = br_mdb_ip_get(br, &data->ip);
>>>>       if (!mp)
>>>>           goto out;
>>>> +
>>>> +    notify = br->multicast_ctx.multicast_mdb_notify_on_flag_change;
>>>
>>> let's not waste cycles if there was an error and notify == 0, please keep the original
>>> code path and avoid walking over the group ports.
>>
>> But we do want to keep the error flag so that the error shows up in 'bridge mdb show', right? Notify should only affect the real-time notifications, and not the error status itself.
>>
> 
> Fair enough, sounds good.
> 
>>>
>>>> +
>>>>       for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
>>>>            pp = &p->next) {
>>>>           if (p->key.port != port)
>>>>               continue;
>>>> -        if (err)
>>>> +        if (err) {
>>>> +            if (!(p->flags & MDB_PG_FLAGS_OFFLOAD_FAILED))
>>>> +                failed_changed = true;
>>>>               p->flags |= MDB_PG_FLAGS_OFFLOAD_FAILED;
>>>> -        else
>>>> +        } else {
>>>> +            if (!(p->flags & MDB_PG_FLAGS_OFFLOAD))
>>>> +                offload_changed = true;
>>>>               p->flags |= MDB_PG_FLAGS_OFFLOAD;
>>>> +        }
>>>> +
>>>> +        if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_DISABLE ||
>>>> +            (!offload_changed && !failed_changed))
>>>> +            continue;
>>>> +
>>>> +        if (notify == MDB_NOTIFY_ON_FLAG_CHANGE_FAIL_ONLY &&
>>>> +            !failed_changed)
>>>> +            continue;
>>>> +
>>>> +        br_mdb_flag_change_notify(br->dev, mp, p);
>>>
>>> This looks like a mess.. First you need to manage these flags properly as I wrote in my
>>> other reply, they must be mutually exclusive and you can do this in a helper. Also
>>> please read the old flags in the beginning, then check what flags changed, make a mask
>>> what flags are for notifications (again can come from a helper, it can be generated when
>>> the option changes so you don't compute it every time) and decide what to do if any of
>>> those flags changed.
>>> Note you have to keep proper flags state regardless of the notify option.
>>>
>>>>       }
>>>>   out:
>>>>       spin_unlock_bh(&br->multicast_lock);
>>>
>>
>> How does this look:
>>
>> --- a/net/bridge/br_switchdev.c
>> +++ b/net/bridge/br_switchdev.c
>> @@ -496,6 +496,21 @@ struct br_switchdev_mdb_complete_info {
>>          struct br_ip ip;
>>   };
>>
> 
> #define MDB_NOTIFY_FLAGS MDB_PG_FLAGS_OFFLOAD_FAILED
> 

pardon me, you can drop this define as the flag is guarded by a specific
option so we don't always notify when we see it, you can check for it
explicitly below in changed_flags below...

>> +static void br_multicast_set_pg_offload_flags(int err,
>> +                                             struct net_bridge_port_group *p)
> 
> swap these two arguments please, since we don't use err you can probably
> rename it to "failed" and make it a bool
> 
> alternatively if you prefer maybe rename it to
> br_multicast_set_pg_offload_flag() and pass the correct flag from the
> caller
> e.g. br_multicast_set_pg_offload_flag(pg, err ?
> MDB_PG_FLAGS_OFFLOAD_FAILED :  MDB_PG_FLAGS_OFFLOAD)
> 
> I don't mind either way.
> 
>> +{
>> +       p->flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
>> +       p->flags |= (err ? MDB_PG_FLAGS_OFFLOAD_FAILED : MDB_PG_FLAGS_OFFLOAD);
>> +}
>> +
>> +static bool br_multicast_should_notify(struct net_bridge *br,
> 
> hmm perhaps br_mdb_should_notify() to be more specific? I don't mind the
> current name, just a thought.
> 
> also const br
> 
>> +                                      u8 old_flags, u8 new_flags)
> 
> u8 changed_flags should suffice
> 
>> +{
>> +       return (br_boolopt_get(br, BR_BOOLOPT_FAILED_OFFLOAD_NOTIFICATION) &&
>> +               ((old_flags & MDB_PG_FLAGS_OFFLOAD_FAILED) !=
>> +               (new_flags & MDB_PG_FLAGS_OFFLOAD_FAILED)));
> 
> if (changed_flags & MDB_NOTIFY_FLAGS)

... here just do an explicit check for the offload flag in changed_flags
     instead of using a define, it is guarded by a specific option so it's ok
> 
> also no need for the extra () around the whole statement
> 
>> +}
>> +
> 
> both of these helpers should go into br_private.h
> 
>>   static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *priv)
>>   {
>>          struct br_switchdev_mdb_complete_info *data = priv;
>> @@ -504,23 +519,25 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
>>          struct net_bridge_mdb_entry *mp;
>>          struct net_bridge_port *port = data->port;
>>          struct net_bridge *br = port->br;
>> -
>> -       if (err)
>> -               goto err;
>> +       u8 old_flags;
>>
>>          spin_lock_bh(&br->multicast_lock);
>>          mp = br_mdb_ip_get(br, &data->ip);
>>          if (!mp)
>>                  goto out;
>>          for (pp = &mp->ports; (p = mlock_dereference(*pp, br)) != NULL;
>>               pp = &p->next) {
>>                  if (p->key.port != port)
>>                          continue;
>> -               p->flags |= MDB_PG_FLAGS_OFFLOAD;
>> +
>> +               old_flags = p->flags;
>> +               br_multicast_set_pg_offload_flags(err, p);
>> +               if (br_multicast_should_notify(br, old_flags, p->flags))
> 
> and here it would become:
> br_multicast_should_notify(br, old_flags ^ p->flags)
> 
>> +                       br_mdb_flag_change_notify(br->dev, mp, p);
>>          }
>>   out:
>>          spin_unlock_bh(&br->multicast_lock);
>> -err:
>>          kfree(priv);
>>   }
>>
>> Thanks,
>> Joseph
> 
> Cheers,
>   Nik
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2025-04-01 13:37 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-18 22:42 [Patch net-next 0/3] Add support for mdb offload failure notification Joseph Huang
2025-03-18 22:42 ` [Patch net-next 1/3] net: bridge: mcast: Add offload failed mdb flag Joseph Huang
2025-03-21  8:19   ` Nikolay Aleksandrov
2025-03-26 22:38     ` Joseph Huang
2025-03-27 22:52       ` Nikolay Aleksandrov
2025-03-28 15:53         ` Joseph Huang
2025-03-31 12:51           ` Nikolay Aleksandrov
2025-03-18 22:42 ` [Patch net-next 2/3] net: bridge: mcast: Notify on offload flag change Joseph Huang
2025-03-21  8:47   ` Nikolay Aleksandrov
2025-03-31 20:11     ` Joseph Huang
2025-04-01 12:49       ` Nikolay Aleksandrov
2025-04-01 13:37         ` Nikolay Aleksandrov
2025-03-18 22:42 ` [Patch net-next 3/3] net: bridge: Add notify on flag change netlink i/f Joseph Huang
2025-03-20  6:17 ` [Patch net-next 0/3] Add support for mdb offload failure notification Nikolay Aleksandrov
2025-03-20 21:14   ` Joseph Huang
2025-03-21  8:47     ` Nikolay Aleksandrov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®