[openwrt/openwrt] realtek: dsa: Add MSTI to HW MST ID mapping

LEDE Commits lede-commits at lists.infradead.org
Sat Nov 15 07:22:14 PST 2025


hauke pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/44976dcac4687bfec84a5317c53d9b14ffc78735

commit 44976dcac4687bfec84a5317c53d9b14ffc78735
Author: Sven Eckelmann <se at simonwunderlich.de>
AuthorDate: Mon Oct 27 17:19:24 2025 +0100

    realtek: dsa: Add MSTI to HW MST ID mapping
    
    The MSTI range is 0..4095 but the HW range is only supporting a lower
    range - for example 0..63 for RTL930x. But the HW doesn't really need to
    know the actual MSTI. It is therefore possible to use a mapping from MSTI
    to HW slot to allow a larger range of MSTIs.
    
    Since the CIST (MSTI 0) is always needed, the mapping data structure is
    skipping this entry and is always keeping the HW slot 0 for CIST.
    
    This doesn't increase the total number of MSTIs a HW supports.
    
    Suggested-by: Jonas Gorski <jonas.gorski at gmail.com>
    Signed-off-by: Sven Eckelmann <se at simonwunderlich.de>
    Link: https://github.com/openwrt/openwrt/pull/20421
    Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
---
 .../files-6.12/drivers/net/dsa/rtl83xx/common.c    |   6 +
 .../files-6.12/drivers/net/dsa/rtl83xx/dsa.c       | 243 ++++++++++++++++++++-
 .../files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h   |  14 ++
 3 files changed, 257 insertions(+), 6 deletions(-)

diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/common.c
index dfb4c2f24c..98559c7c36 100644
--- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/common.c
+++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/common.c
@@ -1542,6 +1542,12 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
 		return err;
 	}
 
+	priv->msts = devm_kcalloc(priv->dev,
+				  priv->n_mst - 1, sizeof(struct rtldsa_mst),
+				  GFP_KERNEL);
+	if (!priv->msts)
+		return -ENOMEM;
+
 	priv->wq = create_singlethread_workqueue("rtl83xx");
 	if (!priv->wq) {
 		dev_err(dev, "Error creating workqueue: %d\n", err);
diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c
index cc29fd1ba6..160e29af80 100644
--- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c
+++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c
@@ -1453,6 +1453,217 @@ static int rtl83xx_set_ageing_time(struct dsa_switch *ds, unsigned int msec)
 	return 0;
 }
 
+/**
+ * rtldsa_mst_init() - Initialize newly "allocated" MST HW slot
+ * @priv: private data of rtldsa switch
+ * @mst_slot: MST slot of MSTI
+ */
+static void rtldsa_mst_init(struct rtl838x_switch_priv *priv, u16 mst_slot)
+			    __must_hold(&priv->reg_mutex)
+{
+	struct dsa_port *dp;
+	unsigned int port;
+	u8 state;
+
+	dsa_switch_for_each_user_port(dp, priv->ds) {
+		if (dp->bridge)
+			state = BR_STATE_DISABLED;
+		else
+			state = dp->stp_state;
+
+		port = dp->index;
+
+		rtldsa_port_xstp_state_set(priv, port, state, mst_slot);
+	}
+}
+
+/**
+ * rtldsa_mst_find() - Find HW MST slot for MSTI (without reference counting)
+ * @priv: private data of rtldsa switch
+ * @msti: MSTI to search
+ *
+ * Return: found HW slot (unmodified reference count) or negative encoded error value
+ */
+static int rtldsa_mst_find(struct rtl838x_switch_priv *priv, u16 msti)
+			   __must_hold(&priv->reg_mutex)
+{
+	unsigned int i;
+
+	/* CIST is always mapped to 0 */
+	if (msti == 0)
+		return 0;
+
+	if (msti > 4095)
+		return -EINVAL;
+
+	/* search for existing entry */
+	for (i = 0; i < priv->n_mst - 1; i++) {
+		if (priv->msts[i].msti != msti)
+			continue;
+
+		return i + 1;
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * rtldsa_mst_get() - Get (or allocate) HW MST slot for MSTI
+ * @priv: private data of rtldsa switch
+ * @msti: MSTI for which a HW slot is needed
+ *
+ * Return: allocated slot (with increased reference count) or negative encoded error value
+ */
+static int rtldsa_mst_get(struct rtl838x_switch_priv *priv, u16 msti)
+			  __must_hold(&priv->reg_mutex)
+{
+	unsigned int i;
+	int ret;
+
+	ret = rtldsa_mst_find(priv, msti);
+
+	/* CIST doesn't need reference counting */
+	if (ret == 0)
+		return ret;
+
+	/* valid HW slot was found - refcount needs to be adjusted */
+	if (ret > 0) {
+		u16 index = ret - 1;
+
+		kref_get(&priv->msts[index].refcount);
+		return ret;
+	}
+
+	/* any error except "no entry found" cannot be handled */
+	if (ret != -ENOENT)
+		return ret;
+
+	/* search for free slot */
+	for (i = 0; i < priv->n_mst - 1; i++) {
+		if (priv->msts[i].msti != 0)
+			continue;
+
+		kref_init(&priv->msts[i].refcount);
+		priv->msts[i].msti = msti;
+
+		rtldsa_mst_init(priv, i + 1);
+		return i + 1;
+	}
+
+	return -ENOSPC;
+}
+
+/**
+ * rtldsa_mst_recycle_slot() - Try to recycle old MST slot in case of -ENOSPC of rtldsa_mst_get()
+ * @priv: private data of rtldsa switch
+ * @msti: MSTI for which a HW slot is needed
+ * @old_mst_slot: old mst slot which will be released "soon"
+ *
+ * If a VLAN should be moved from one MSTI to another one, it is possible that there are currently
+ * not enough slots still available to perform a get+put operation. But if this slot is used
+ * by a single VLAN anyway, it is not needed to really allocate a new slow - reassigning it to
+ * the new MSTI is good enough.
+ *
+ * This is only allowed when holding the reg_mutex over both calls rtldsa_mst_get() and
+ * rtldsa_mst_recycle(). After a rtldsa_mst_recycle() call, rtldsa_mst_put_slot() must no longer
+ * be called for @old_mst_slot.
+ *
+ * Return: allocated slot (with increased reference count) or negative encoded error value
+ */
+static int rtldsa_mst_recycle_slot(struct rtl838x_switch_priv *priv, u16 msti, u16 old_mst_slot)
+				   __must_hold(&priv->reg_mutex)
+{
+	u16 index;
+
+	/* CIST is always mapped to 0 */
+	if (msti == 0)
+		return 0;
+
+	if (old_mst_slot == 0)
+		return -ENOSPC;
+
+	if (msti > 4095)
+		return -EINVAL;
+
+	if (old_mst_slot >= priv->n_mst)
+		return -EINVAL;
+
+	index = old_mst_slot - 1;
+
+	/* this slot is unused - should not happen because rtldsa_mst_get() searches for it */
+	if (priv->msts[index].msti == 0)
+		return -EINVAL;
+
+	/* it is only allowed to swap when no other VLAN is using this MST slot */
+	if (kref_read(&priv->msts[index].refcount) != 1)
+		return -ENOSPC;
+
+	priv->msts[index].msti = msti;
+	return old_mst_slot;
+}
+
+static void rtldsa_mst_release_slot(struct kref *ref)
+{
+	struct rtldsa_mst *slot = container_of(ref, struct rtldsa_mst, refcount);
+
+	slot->msti = 0;
+}
+
+/**
+ * rtldsa_mst_put_slot() - Decrement VLAN use counter for MST slot
+ * @priv: private data of rtldsa switch
+ * @mst_slot: MST slot which should be put
+ *
+ * Return: false when MST slot reference counter was only decreased or an invalid @mst_slot was
+ * given, true when @mst_slot is now unused
+ */
+static bool rtldsa_mst_put_slot(struct rtl838x_switch_priv *priv, u16 mst_slot)
+			        __must_hold(&priv->reg_mutex)
+{
+	unsigned int index;
+
+	/* CIST is always mapped to 0 and cannot be put */
+	if (mst_slot == 0)
+		return 0;
+
+	if (mst_slot >= priv->n_mst)
+		return 0;
+
+	index = mst_slot - 1;
+
+	/* this slot is unused and must not release a reference */
+	if (priv->msts[index].msti == 0)
+		return 0;
+
+	return kref_put(&priv->msts[index].refcount, rtldsa_mst_release_slot);
+}
+
+/**
+ * rtldsa_mst_replace() - Get HW slot for @msti and drop old HW slot
+ * @priv: private data of rtldsa switch
+ * @msti: MSTI for which a HW slot is needed
+ * @old_mst_slot: old mst slot which will no longer be assigned to VLAN
+ *
+ * Return: allocated slot (with increased reference count) or negative encoded error value
+ */
+static int rtldsa_mst_replace(struct rtl838x_switch_priv *priv, u16 msti, u16 old_mst_slot)
+			      __must_hold(&priv->reg_mutex)
+{
+	int mst_slot_new;
+
+	mst_slot_new = rtldsa_mst_get(priv, msti);
+	if (mst_slot_new == -ENOSPC)
+		return rtldsa_mst_recycle_slot(priv, msti, old_mst_slot);
+
+	/* directly return errors and don't free old slot */
+	if (mst_slot_new < 0)
+		return mst_slot_new;
+
+	rtldsa_mst_put_slot(priv, old_mst_slot);
+
+	return mst_slot_new;
+}
+
 static void rtldsa_update_port_member(struct rtl838x_switch_priv *priv, int port,
 				      const struct net_device *bridge_dev, bool join)
 				      __must_hold(&priv->reg_mutex)
@@ -1681,9 +1892,17 @@ static int rtldsa_port_mst_state_set(struct dsa_switch *ds, int port,
 				     const struct switchdev_mst_state *st)
 {
 	struct rtl838x_switch_priv *priv = ds->priv;
+	int mst_slot;
 
 	mutex_lock(&priv->reg_mutex);
-	rtldsa_port_xstp_state_set(priv, port, st->state, st->msti);
+
+	mst_slot = rtldsa_mst_find(priv, st->msti);
+	if (mst_slot < 0) {
+		mutex_unlock(&priv->reg_mutex);
+		return mst_slot;
+	}
+
+	rtldsa_port_xstp_state_set(priv, port, st->state, mst_slot);
 	mutex_unlock(&priv->reg_mutex);
 
 	return 0;
@@ -1860,9 +2079,13 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
 	info.member_ports &= (~BIT_ULL(port));
 
 	/* VLANs without members are set back (implicitly) to CIST by DSA */
-	if (!info.member_ports)
+	if (!info.member_ports) {
+		u16 mst = info.fid;
 		info.fid = 0;
 
+		rtldsa_mst_put_slot(priv, mst);
+	}
+
 	priv->r->vlan_set_untagged(vlan->vid, info.untagged_ports);
 	pr_debug("Untagged ports, VLAN %d: %llx\n", vlan->vid, info.untagged_ports);
 
@@ -1894,12 +2117,20 @@ static int rtldsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
 {
 	struct rtl838x_switch_priv *priv = ds->priv;
 	struct rtl838x_vlan_info info;
-	int mst_slot = msti->msti;
-
-	if (mst_slot >= priv->n_mst)
-		return -EINVAL;
+	u16 mst_slot_old;
+	int mst_slot;
 
 	priv->r->vlan_tables_read(msti->vid, &info);
+	mst_slot_old = info.fid;
+
+	/* find HW slot for MSTI */
+	mutex_lock(&priv->reg_mutex);
+	mst_slot = rtldsa_mst_replace(priv, msti->msti, mst_slot_old);
+	mutex_unlock(&priv->reg_mutex);
+
+	if (mst_slot < 0)
+		return mst_slot;
+
 	info.fid = mst_slot;
 	priv->r->vlan_set_tagged(msti->vid, &info);
 
diff --git a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h
index c2c166d545..52c0819dc0 100644
--- a/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h
+++ b/target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/rtl838x.h
@@ -781,6 +781,14 @@ struct rtl838x_vlan_info {
 	int l2_tunnel_list_id;
 };
 
+struct rtldsa_mst {
+	/** @msti: MSTI mapped to this slot. 0 == unused */
+	u16 msti;
+
+	/** @refcount: number of vlans currently using this msti, undefined when unused */
+	struct kref refcount;
+};
+
 enum l2_entry_type {
 	L2_INVALID = 0,
 	L2_UNICAST = 1,
@@ -1251,6 +1259,12 @@ struct rtl838x_switch_priv {
 	struct rtl838x_l3_intf *interfaces[MAX_INTERFACES];
 	u16 intf_mtus[MAX_INTF_MTUS];
 	int intf_mtu_count[MAX_INTF_MTUS];
+
+	/**
+	 * @msts: MSTI to HW MST slot allocations. index 0 is for HW slot 1 because CIST is
+	 * not stored in @msts
+	 */
+	struct rtldsa_mst *msts;
 	struct delayed_work counters_work;
 };
 




More information about the lede-commits mailing list