net: dsa: mv88e6xxx: MST Offloading
Allocate a SID in the STU for each MSTID in use by a bridge and handle the mapping of MSTIDs to VLANs using the SID field of each VTU entry. Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
7dc96039b9
commit
acaf4d2e36
|
@ -1667,24 +1667,31 @@ static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_port_fast_age_fid(struct mv88e6xxx_chip *chip, int port,
|
||||||
|
u16 fid)
|
||||||
|
{
|
||||||
|
if (dsa_to_port(chip->ds, port)->lag)
|
||||||
|
/* Hardware is incapable of fast-aging a LAG through a
|
||||||
|
* regular ATU move operation. Until we have something
|
||||||
|
* more fancy in place this is a no-op.
|
||||||
|
*/
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
return mv88e6xxx_g1_atu_remove(chip, fid, port, false);
|
||||||
|
}
|
||||||
|
|
||||||
static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
|
static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
|
||||||
{
|
{
|
||||||
struct mv88e6xxx_chip *chip = ds->priv;
|
struct mv88e6xxx_chip *chip = ds->priv;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (dsa_to_port(ds, port)->lag)
|
|
||||||
/* Hardware is incapable of fast-aging a LAG through a
|
|
||||||
* regular ATU move operation. Until we have something
|
|
||||||
* more fancy in place this is a no-op.
|
|
||||||
*/
|
|
||||||
return;
|
|
||||||
|
|
||||||
mv88e6xxx_reg_lock(chip);
|
mv88e6xxx_reg_lock(chip);
|
||||||
err = mv88e6xxx_g1_atu_remove(chip, 0, port, false);
|
err = mv88e6xxx_port_fast_age_fid(chip, port, 0);
|
||||||
mv88e6xxx_reg_unlock(chip);
|
mv88e6xxx_reg_unlock(chip);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
dev_err(ds->dev, "p%d: failed to flush ATU\n", port);
|
dev_err(chip->ds->dev, "p%d: failed to flush ATU: %d\n",
|
||||||
|
port, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
|
static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
|
||||||
|
@ -1818,6 +1825,160 @@ static int mv88e6xxx_stu_setup(struct mv88e6xxx_chip *chip)
|
||||||
return mv88e6xxx_stu_loadpurge(chip, &stu);
|
return mv88e6xxx_stu_loadpurge(chip, &stu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_sid_get(struct mv88e6xxx_chip *chip, u8 *sid)
|
||||||
|
{
|
||||||
|
DECLARE_BITMAP(busy, MV88E6XXX_N_SID) = { 0 };
|
||||||
|
struct mv88e6xxx_mst *mst;
|
||||||
|
|
||||||
|
__set_bit(0, busy);
|
||||||
|
|
||||||
|
list_for_each_entry(mst, &chip->msts, node)
|
||||||
|
__set_bit(mst->stu.sid, busy);
|
||||||
|
|
||||||
|
*sid = find_first_zero_bit(busy, MV88E6XXX_N_SID);
|
||||||
|
|
||||||
|
return (*sid >= mv88e6xxx_max_sid(chip)) ? -ENOSPC : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_mst_put(struct mv88e6xxx_chip *chip, u8 sid)
|
||||||
|
{
|
||||||
|
struct mv88e6xxx_mst *mst, *tmp;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!sid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(mst, tmp, &chip->msts, node) {
|
||||||
|
if (mst->stu.sid != sid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!refcount_dec_and_test(&mst->refcnt))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mst->stu.valid = false;
|
||||||
|
err = mv88e6xxx_stu_loadpurge(chip, &mst->stu);
|
||||||
|
if (err) {
|
||||||
|
refcount_set(&mst->refcnt, 1);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_del(&mst->node);
|
||||||
|
kfree(mst);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_mst_get(struct mv88e6xxx_chip *chip, struct net_device *br,
|
||||||
|
u16 msti, u8 *sid)
|
||||||
|
{
|
||||||
|
struct mv88e6xxx_mst *mst;
|
||||||
|
int err, i;
|
||||||
|
|
||||||
|
if (!mv88e6xxx_has_stu(chip)) {
|
||||||
|
err = -EOPNOTSUPP;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msti) {
|
||||||
|
*sid = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(mst, &chip->msts, node) {
|
||||||
|
if (mst->br == br && mst->msti == msti) {
|
||||||
|
refcount_inc(&mst->refcnt);
|
||||||
|
*sid = mst->stu.sid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mv88e6xxx_sid_get(chip, sid);
|
||||||
|
if (err)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
mst = kzalloc(sizeof(*mst), GFP_KERNEL);
|
||||||
|
if (!mst) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&mst->node);
|
||||||
|
refcount_set(&mst->refcnt, 1);
|
||||||
|
mst->br = br;
|
||||||
|
mst->msti = msti;
|
||||||
|
mst->stu.valid = true;
|
||||||
|
mst->stu.sid = *sid;
|
||||||
|
|
||||||
|
/* The bridge starts out all ports in the disabled state. But
|
||||||
|
* a STU state of disabled means to go by the port-global
|
||||||
|
* state. So we set all user port's initial state to blocking,
|
||||||
|
* to match the bridge's behavior.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < mv88e6xxx_num_ports(chip); i++)
|
||||||
|
mst->stu.state[i] = dsa_is_user_port(chip->ds, i) ?
|
||||||
|
MV88E6XXX_PORT_CTL0_STATE_BLOCKING :
|
||||||
|
MV88E6XXX_PORT_CTL0_STATE_DISABLED;
|
||||||
|
|
||||||
|
err = mv88e6xxx_stu_loadpurge(chip, &mst->stu);
|
||||||
|
if (err)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
list_add_tail(&mst->node, &chip->msts);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
kfree(mst);
|
||||||
|
err:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_port_mst_state_set(struct dsa_switch *ds, int port,
|
||||||
|
const struct switchdev_mst_state *st)
|
||||||
|
{
|
||||||
|
struct dsa_port *dp = dsa_to_port(ds, port);
|
||||||
|
struct mv88e6xxx_chip *chip = ds->priv;
|
||||||
|
struct mv88e6xxx_mst *mst;
|
||||||
|
u8 state;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!mv88e6xxx_has_stu(chip))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
switch (st->state) {
|
||||||
|
case BR_STATE_DISABLED:
|
||||||
|
case BR_STATE_BLOCKING:
|
||||||
|
case BR_STATE_LISTENING:
|
||||||
|
state = MV88E6XXX_PORT_CTL0_STATE_BLOCKING;
|
||||||
|
break;
|
||||||
|
case BR_STATE_LEARNING:
|
||||||
|
state = MV88E6XXX_PORT_CTL0_STATE_LEARNING;
|
||||||
|
break;
|
||||||
|
case BR_STATE_FORWARDING:
|
||||||
|
state = MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(mst, &chip->msts, node) {
|
||||||
|
if (mst->br == dsa_port_bridge_dev_get(dp) &&
|
||||||
|
mst->msti == st->msti) {
|
||||||
|
if (mst->stu.state[port] == state)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mst->stu.state[port] = state;
|
||||||
|
mv88e6xxx_reg_lock(chip);
|
||||||
|
err = mv88e6xxx_stu_loadpurge(chip, &mst->stu);
|
||||||
|
mv88e6xxx_reg_unlock(chip);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
|
static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
|
||||||
u16 vid)
|
u16 vid)
|
||||||
{
|
{
|
||||||
|
@ -2437,6 +2598,12 @@ static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (!vlan.valid) {
|
||||||
|
err = mv88e6xxx_mst_put(chip, vlan.sid);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false);
|
return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2482,6 +2649,69 @@ unlock:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_port_vlan_fast_age(struct dsa_switch *ds, int port, u16 vid)
|
||||||
|
{
|
||||||
|
struct mv88e6xxx_chip *chip = ds->priv;
|
||||||
|
struct mv88e6xxx_vtu_entry vlan;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mv88e6xxx_reg_lock(chip);
|
||||||
|
|
||||||
|
err = mv88e6xxx_vtu_get(chip, vid, &vlan);
|
||||||
|
if (err)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
err = mv88e6xxx_port_fast_age_fid(chip, port, vlan.fid);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mv88e6xxx_reg_unlock(chip);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mv88e6xxx_vlan_msti_set(struct dsa_switch *ds,
|
||||||
|
struct dsa_bridge bridge,
|
||||||
|
const struct switchdev_vlan_msti *msti)
|
||||||
|
{
|
||||||
|
struct mv88e6xxx_chip *chip = ds->priv;
|
||||||
|
struct mv88e6xxx_vtu_entry vlan;
|
||||||
|
u8 old_sid, new_sid;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mv88e6xxx_reg_lock(chip);
|
||||||
|
|
||||||
|
err = mv88e6xxx_vtu_get(chip, msti->vid, &vlan);
|
||||||
|
if (err)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (!vlan.valid) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_sid = vlan.sid;
|
||||||
|
|
||||||
|
err = mv88e6xxx_mst_get(chip, bridge.dev, msti->msti, &new_sid);
|
||||||
|
if (err)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
if (new_sid != old_sid) {
|
||||||
|
vlan.sid = new_sid;
|
||||||
|
|
||||||
|
err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
|
||||||
|
if (err) {
|
||||||
|
mv88e6xxx_mst_put(chip, new_sid);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mv88e6xxx_mst_put(chip, old_sid);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
mv88e6xxx_reg_unlock(chip);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
|
static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
|
||||||
const unsigned char *addr, u16 vid,
|
const unsigned char *addr, u16 vid,
|
||||||
struct dsa_db db)
|
struct dsa_db db)
|
||||||
|
@ -6008,6 +6238,7 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
|
||||||
mutex_init(&chip->reg_lock);
|
mutex_init(&chip->reg_lock);
|
||||||
INIT_LIST_HEAD(&chip->mdios);
|
INIT_LIST_HEAD(&chip->mdios);
|
||||||
idr_init(&chip->policies);
|
idr_init(&chip->policies);
|
||||||
|
INIT_LIST_HEAD(&chip->msts);
|
||||||
|
|
||||||
return chip;
|
return chip;
|
||||||
}
|
}
|
||||||
|
@ -6540,10 +6771,13 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
|
||||||
.port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags,
|
.port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags,
|
||||||
.port_bridge_flags = mv88e6xxx_port_bridge_flags,
|
.port_bridge_flags = mv88e6xxx_port_bridge_flags,
|
||||||
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
|
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
|
||||||
|
.port_mst_state_set = mv88e6xxx_port_mst_state_set,
|
||||||
.port_fast_age = mv88e6xxx_port_fast_age,
|
.port_fast_age = mv88e6xxx_port_fast_age,
|
||||||
|
.port_vlan_fast_age = mv88e6xxx_port_vlan_fast_age,
|
||||||
.port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
|
.port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
|
||||||
.port_vlan_add = mv88e6xxx_port_vlan_add,
|
.port_vlan_add = mv88e6xxx_port_vlan_add,
|
||||||
.port_vlan_del = mv88e6xxx_port_vlan_del,
|
.port_vlan_del = mv88e6xxx_port_vlan_del,
|
||||||
|
.vlan_msti_set = mv88e6xxx_vlan_msti_set,
|
||||||
.port_fdb_add = mv88e6xxx_port_fdb_add,
|
.port_fdb_add = mv88e6xxx_port_fdb_add,
|
||||||
.port_fdb_del = mv88e6xxx_port_fdb_del,
|
.port_fdb_del = mv88e6xxx_port_fdb_del,
|
||||||
.port_fdb_dump = mv88e6xxx_port_fdb_dump,
|
.port_fdb_dump = mv88e6xxx_port_fdb_dump,
|
||||||
|
|
|
@ -297,6 +297,16 @@ struct mv88e6xxx_region_priv {
|
||||||
enum mv88e6xxx_region_id id;
|
enum mv88e6xxx_region_id id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mv88e6xxx_mst {
|
||||||
|
struct list_head node;
|
||||||
|
|
||||||
|
refcount_t refcnt;
|
||||||
|
struct net_device *br;
|
||||||
|
u16 msti;
|
||||||
|
|
||||||
|
struct mv88e6xxx_stu_entry stu;
|
||||||
|
};
|
||||||
|
|
||||||
struct mv88e6xxx_chip {
|
struct mv88e6xxx_chip {
|
||||||
const struct mv88e6xxx_info *info;
|
const struct mv88e6xxx_info *info;
|
||||||
|
|
||||||
|
@ -397,6 +407,9 @@ struct mv88e6xxx_chip {
|
||||||
|
|
||||||
/* devlink regions */
|
/* devlink regions */
|
||||||
struct devlink_region *regions[_MV88E6XXX_REGION_MAX];
|
struct devlink_region *regions[_MV88E6XXX_REGION_MAX];
|
||||||
|
|
||||||
|
/* Bridge MST to SID mappings */
|
||||||
|
struct list_head msts;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mv88e6xxx_bus_ops {
|
struct mv88e6xxx_bus_ops {
|
||||||
|
|
Loading…
Reference in New Issue