mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-01-28 03:37:17 +01:00
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@gmail.com> Signed-off-by: Sven Eckelmann <se@simonwunderlich.de> Link: https://github.com/openwrt/openwrt/pull/20421 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
parent
6c15c5d5eb
commit
44976dcac4
3 changed files with 257 additions and 6 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue