[PATCH v2 net-next 04/10] net: bridge: mst: Notify switchdev drivers of VLAN MSTI migrations

From: Tobias Waldekranz
Date: Tue Mar 01 2022 - 05:04:12 EST


Whenever a VLAN moves to a new MSTI, send a switchdev notification so
that switchdevs can...

...either refuse the migration if the hardware does not support
offloading of MST...

..or track a bridge's VID to MSTI mapping when offloading is
supported.

Signed-off-by: Tobias Waldekranz <tobias@xxxxxxxxxxxxxx>
---
include/net/switchdev.h | 10 +++++++
net/bridge/br_mst.c | 15 +++++++++++
net/bridge/br_switchdev.c | 57 +++++++++++++++++++++++++++++++++++++++
3 files changed, 82 insertions(+)

diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 3e424d40fae3..39e57aa5005a 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -28,6 +28,7 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED,
SWITCHDEV_ATTR_ID_BRIDGE_MROUTER,
SWITCHDEV_ATTR_ID_MRP_PORT_ROLE,
+ SWITCHDEV_ATTR_ID_VLAN_MSTI,
};

struct switchdev_brport_flags {
@@ -35,6 +36,14 @@ struct switchdev_brport_flags {
unsigned long mask;
};

+struct switchdev_vlan_attr {
+ u16 vid;
+
+ union {
+ u16 msti;
+ };
+};
+
struct switchdev_attr {
struct net_device *orig_dev;
enum switchdev_attr_id id;
@@ -50,6 +59,7 @@ struct switchdev_attr {
u16 vlan_protocol; /* BRIDGE_VLAN_PROTOCOL */
bool mc_disabled; /* MC_DISABLED */
u8 mrp_port_role; /* MRP_PORT_ROLE */
+ struct switchdev_vlan_attr vlan_attr; /* VLAN_* */
} u;
};

diff --git a/net/bridge/br_mst.c b/net/bridge/br_mst.c
index 8dea8e7257fd..aba603675165 100644
--- a/net/bridge/br_mst.c
+++ b/net/bridge/br_mst.c
@@ -7,6 +7,7 @@
*/

#include <linux/kernel.h>
+#include <net/switchdev.h>

#include "br_private.h"

@@ -65,9 +66,23 @@ static void br_mst_vlan_sync_state(struct net_bridge_vlan *pv, u16 msti)

int br_mst_vlan_set_msti(struct net_bridge_vlan *mv, u16 msti)
{
+ struct switchdev_attr attr = {
+ .id = SWITCHDEV_ATTR_ID_VLAN_MSTI,
+ .flags = SWITCHDEV_F_DEFER,
+ .orig_dev = mv->br->dev,
+ .u.vlan_attr = {
+ .vid = mv->vid,
+ .msti = msti,
+ },
+ };
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *pv;
struct net_bridge_port *p;
+ int err;
+
+ err = switchdev_port_attr_set(mv->br->dev, &attr, NULL);
+ if (err && err != -EOPNOTSUPP)
+ return err;

mv->msti = msti;

diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 6f6a70121a5e..160d7659f88a 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -428,6 +428,57 @@ static int br_switchdev_vlan_replay(struct net_device *br_dev,
return 0;
}

+static int br_switchdev_mst_replay(struct net_device *br_dev,
+ const void *ctx, bool adding,
+ struct notifier_block *nb,
+ struct netlink_ext_ack *extack)
+{
+ struct switchdev_notifier_port_attr_info attr_info = {
+ .info = {
+ .dev = br_dev,
+ .extack = extack,
+ .ctx = ctx,
+ },
+ };
+ struct net_bridge *br = netdev_priv(br_dev);
+ struct net_bridge_vlan_group *vg;
+ struct net_bridge_vlan *v;
+ int err;
+
+ ASSERT_RTNL();
+
+ if (!nb)
+ return 0;
+
+ if (!netif_is_bridge_master(br_dev))
+ return -EINVAL;
+
+ vg = br_vlan_group(br);
+
+ list_for_each_entry(v, &vg->vlan_list, vlist) {
+ struct switchdev_attr attr = {
+ .id = SWITCHDEV_ATTR_ID_VLAN_MSTI,
+ .flags = SWITCHDEV_F_DEFER,
+ .orig_dev = br_dev,
+ .u.vlan_attr = {
+ .vid = v->vid,
+ .msti = v->msti,
+ }
+ };
+
+ if (!v->msti)
+ continue;
+
+ attr_info.attr = &attr;
+ err = nb->notifier_call(nb, SWITCHDEV_PORT_ATTR_SET, &attr_info);
+ err = notifier_to_errno(err);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
struct br_switchdev_mdb_complete_info {
struct net_bridge_port *port;
@@ -695,6 +746,10 @@ static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx,
if (err && err != -EOPNOTSUPP)
return err;

+ err = br_switchdev_mst_replay(br_dev, ctx, true, blocking_nb, extack);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
err = br_switchdev_mdb_replay(br_dev, dev, ctx, true, blocking_nb,
extack);
if (err && err != -EOPNOTSUPP)
@@ -719,6 +774,8 @@ static void nbp_switchdev_unsync_objs(struct net_bridge_port *p,

br_switchdev_mdb_replay(br_dev, dev, ctx, false, blocking_nb, NULL);

+ br_switchdev_mst_replay(br_dev, ctx, false, blocking_nb, NULL);
+
br_switchdev_vlan_replay(br_dev, ctx, false, blocking_nb, NULL);
}

--
2.25.1