lkml.org 
[lkml]   [2022]   [Mar]   [14]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [PATCH v3 net-next 14/14] net: dsa: mv88e6xxx: MST Offloading
On Mon, Mar 14, 2022 at 10:52:31AM +0100, Tobias Waldekranz wrote:
> 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>
> ---
> drivers/net/dsa/mv88e6xxx/chip.c | 251 ++++++++++++++++++++++++++++++-
> drivers/net/dsa/mv88e6xxx/chip.h | 13 ++
> 2 files changed, 257 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
> index c14a62aa6a6c..c23dbf37aeec 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.c
> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
> @@ -1667,24 +1667,32 @@ static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
> return 0;
> }
>
> -static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
> +static void mv88e6xxx_port_fast_age_fid(struct mv88e6xxx_chip *chip, int port,
> + u16 fid)
> {
> - struct mv88e6xxx_chip *chip = ds->priv;
> int err;
>
> - if (dsa_to_port(ds, port)->lag)
> + 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;
>
> - mv88e6xxx_reg_lock(chip);
> - err = mv88e6xxx_g1_atu_remove(chip, 0, port, false);
> - mv88e6xxx_reg_unlock(chip);
> + err = mv88e6xxx_g1_atu_remove(chip, fid, port, false);
>
> 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 (FID %u)\n",
> + port, fid);
> +}
> +
> +static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
> +
> + mv88e6xxx_reg_lock(chip);
> + mv88e6xxx_port_fast_age_fid(chip, port, 0);
> + mv88e6xxx_reg_unlock(chip);
> }
>
> static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
> @@ -1818,6 +1826,159 @@ static int mv88e6xxx_stu_setup(struct mv88e6xxx_chip *chip)
> 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);

__set_bit

> +
> + list_for_each_entry(mst, &chip->msts, node) {
> + set_bit(mst->stu.sid, busy);
> + }

Up to you, but parentheses are generally not used for single-line blocks.

> +
> + *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;

Very minor nitpick: since mv88e6xxx_mst_put already checks this, could
you drop the "!sid" check from callers?

> +
> + 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)

Should we bother with a refcount_set(&mst->refcount, 1) on error?

> + 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,
> u16 vid)
> {
> @@ -2437,6 +2598,12 @@ static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip,
> if (err)
> return err;
>
> + if (!vlan.valid && vlan.sid) {
> + err = mv88e6xxx_mst_put(chip, vlan.sid);
> + if (err)
> + return err;
> + }
> +
> return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false);
> }
>
> @@ -2482,6 +2649,72 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
> return err;
> }
>
> +static void 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;
> +
> + mv88e6xxx_port_fast_age_fid(chip, port, vlan.fid);
> +
> +unlock:
> + mv88e6xxx_reg_unlock(chip);
> +
> + if (err)
> + dev_err(ds->dev, "p%d: failed to flush ATU in VID %u\n",
> + port, vid);

This error message actually corresponds to an mv88e6xxx_vtu_get() error,
so the message is kind of incorrect. mv88e6xxx_port_fast_age_fid(),
whose error code isn't propagated here, has its own "failed to flush ATU"
error message.

> +}

Otherwise this looks pretty good.

\
 
 \ /
  Last update: 2022-03-14 17:28    [W:0.269 / U:0.044 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site