Commit dbf14d15 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'add-support-for-mdb-offload-failure-notification'

Joseph Huang says:

====================
Add support for mdb offload failure notification

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 failed offload
attempts, and is controlled by a new knob mdb_offload_fail_notification.

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 new bridge bool option mdb_offload_fail_notification.

Patch 3 notifies user space when the result is known, controlled by
mdb_offload_fail_notification setting.
====================

Link: https://patch.msgid.link/20250411150323.1117797-1-Joseph.Huang@garmin.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 1450e452 c428d43d
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -703,6 +703,7 @@ struct br_mdb_entry {
#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 {
@@ -830,6 +831,7 @@ enum br_boolopt_id {
	BR_BOOLOPT_NO_LL_LEARN,
	BR_BOOLOPT_MCAST_VLAN_SNOOPING,
	BR_BOOLOPT_MST_ENABLE,
	BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
	BR_BOOLOPT_MAX
};

+5 −0
Original line number Diff line number Diff line
@@ -284,6 +284,9 @@ int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
	case BR_BOOLOPT_MST_ENABLE:
		err = br_mst_set_enabled(br, on, extack);
		break;
	case BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION:
		br_opt_toggle(br, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION, on);
		break;
	default:
		/* shouldn't be called with unsupported options */
		WARN_ON(1);
@@ -302,6 +305,8 @@ int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
		return br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED);
	case BR_BOOLOPT_MST_ENABLE:
		return br_opt_get(br, BROPT_MST_ENABLED);
	case BR_BOOLOPT_MDB_OFFLOAD_FAIL_NOTIFICATION:
		return br_opt_get(br, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION);
	default:
		/* shouldn't be called with unsupported options */
		WARN_ON(1);
+23 −5
Original line number Diff line number Diff line
@@ -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,
@@ -517,15 +519,16 @@ 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,
static void __br_mdb_notify(struct net_device *dev,
			    struct net_bridge_mdb_entry *mp,
			    struct net_bridge_port_group *pg,
		   int type)
			    int type, bool notify_switchdev)
{
	struct net *net = dev_net(dev);
	struct sk_buff *skb;
	int err = -ENOBUFS;

	if (notify_switchdev)
		br_switchdev_mdb_notify(dev, mp, pg, type);

	skb = nlmsg_new(rtnl_mdb_nlmsg_size(pg), GFP_ATOMIC);
@@ -544,6 +547,21 @@ 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);
}

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

static int nlmsg_populate_rtr_fill(struct sk_buff *skb,
				   struct net_device *dev,
				   int ifindex, u16 vid, u32 pid,
+25 −5
Original line number Diff line number Diff line
@@ -311,6 +311,7 @@ struct net_bridge_fdb_flush_desc {
#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

@@ -483,6 +484,7 @@ enum net_bridge_opts {
	BROPT_VLAN_BRIDGE_BINDING,
	BROPT_MCAST_VLAN_SNOOPING_ENABLED,
	BROPT_MST_ENABLED,
	BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION,
};

struct net_bridge {
@@ -1002,6 +1004,8 @@ 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);
void br_mdb_flag_change_notify(struct net_device *dev, struct net_bridge_mdb_entry *mp,
			       struct net_bridge_port_group *pg);
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,
@@ -1342,6 +1346,22 @@ br_multicast_ctx_matches_vlan_snooping(const struct net_bridge_mcast *brmctx)

	return !!(vlan_snooping_enabled == br_multicast_ctx_is_vlan(brmctx));
}

static inline void
br_multicast_set_pg_offload_flags(struct net_bridge_port_group *p,
				  bool offloaded)
{
	p->flags &= ~(MDB_PG_FLAGS_OFFLOAD | MDB_PG_FLAGS_OFFLOAD_FAILED);
	p->flags |= (offloaded ? MDB_PG_FLAGS_OFFLOAD :
		MDB_PG_FLAGS_OFFLOAD_FAILED);
}

static inline bool
br_mdb_should_notify(const struct net_bridge *br, u8 changed_flags)
{
	return br_opt_get(br, BROPT_MDB_OFFLOAD_FAIL_NOTIFICATION) &&
		(changed_flags & MDB_PG_FLAGS_OFFLOAD_FAILED);
}
#else
static inline int br_multicast_rcv(struct net_bridge_mcast **brmctx,
				   struct net_bridge_mcast_port **pmctx,
+9 −4
Original line number Diff line number Diff line
@@ -504,9 +504,10 @@ 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;
	u8 old_flags;

	if (err)
		goto err;
	if (err == -EOPNOTSUPP)
		goto out_free;

	spin_lock_bh(&br->multicast_lock);
	mp = br_mdb_ip_get(br, &data->ip);
@@ -516,11 +517,15 @@ 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;

		old_flags = p->flags;
		br_multicast_set_pg_offload_flags(p, !err);
		if (br_mdb_should_notify(br, old_flags ^ p->flags))
			br_mdb_flag_change_notify(br->dev, mp, p);
	}
out:
	spin_unlock_bh(&br->multicast_lock);
err:
out_free:
	kfree(priv);
}