Map the MDB_FLAGS_STREAM_RESERVED flag to MC_STATIC_AVB_NRL when adding or removing MDB entries to the ATU. The presence or absence of this flag must match any existing ATU entries for the destination address, as the ATU entry type applies to all destination ports. A port in enhanced mode, requested by BR_FILTER_STREAM_RESERVED, will only admit frames with priorities associated with AVB traffic classes for these destinations. Note: MC_STATIC_AVB_NRL entries persist independently of the MQPRIO mode. If port ingress rate limiting (PIRL) support is ever added, this should be revisited. Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.c | 101 ++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 23 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index db79302c2b84d..ef3cb1cca134d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2269,6 +2269,35 @@ static bool mv88e6xxx_port_db_find(struct mv88e6xxx_chip *chip, return entry.state && ether_addr_equal(entry.mac, addr); } +static int mv88e6xxx_port_db_loadpurge_entry(struct mv88e6xxx_chip *chip, + int port, u16 fid, + const unsigned char *addr, + struct mv88e6xxx_atu_entry *entry, + u8 state) +{ + /* Initialize a fresh ATU entry if it isn't found */ + if (!entry->state || !ether_addr_equal(entry->mac, addr)) { + memset(entry, 0, sizeof(*entry)); + ether_addr_copy(entry->mac, addr); + } + + /* Purge the ATU entry only if no port is using it anymore */ + if (!state) { + entry->portvec &= ~BIT(port); + if (!entry->portvec) + entry->state = 0; + } else { + if (state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC) + entry->portvec = BIT(port); + else + entry->portvec |= BIT(port); + + entry->state = state; + } + + return mv88e6xxx_g1_atu_loadpurge(chip, fid, entry); +} + static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, const unsigned char *addr, u16 vid, u8 state) @@ -2281,27 +2310,8 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, if (err) return err; - /* Initialize a fresh ATU entry if it isn't found */ - if (!entry.state || !ether_addr_equal(entry.mac, addr)) { - memset(&entry, 0, sizeof(entry)); - ether_addr_copy(entry.mac, addr); - } - - /* Purge the ATU entry only if no port is using it anymore */ - if (!state) { - entry.portvec &= ~BIT(port); - if (!entry.portvec) - entry.state = 0; - } else { - if (state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC) - entry.portvec = BIT(port); - else - entry.portvec |= BIT(port); - - entry.state = state; - } - - return mv88e6xxx_g1_atu_loadpurge(chip, fid, &entry); + return mv88e6xxx_port_db_loadpurge_entry(chip, port, fid, addr, &entry, + state); } static int mv88e6xxx_policy_apply(struct mv88e6xxx_chip *chip, int port, @@ -6781,16 +6791,61 @@ static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, return err; } +static bool mv88e6xxx_atu_mc_entry_changed(const struct mv88e6xxx_atu_entry *existing, + int port, + const struct switchdev_obj_port_mdb *mdb, + u8 state) +{ + if (!ether_addr_equal(existing->mac, mdb->addr)) + return false; + + if (existing->state != MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC && + existing->state != MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL) + return false; + + if (existing->state == state) + return false; + + if (!(existing->portvec & ~BIT(port))) + return false; + + return true; +} + static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { struct mv88e6xxx_chip *chip = ds->priv; + struct mv88e6xxx_atu_entry existing; + u8 state; + u16 fid; int err; mv88e6xxx_reg_lock(chip); - err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, - MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC); + + /* Note that AVB_NRL entries persist independently of the MQPRIO mode; + * as ingress rate control is not used by this driver, this is safe. + */ + if ((mdb->flags & SWITCHDEV_MDB_F_STREAM_RESERVED) && chip->info->qav) + state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL; + else + state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC; + + err = mv88e6xxx_port_db_get(chip, mdb->addr, mdb->vid, &fid, &existing); + if (err) + goto out; + + if (mv88e6xxx_atu_mc_entry_changed(&existing, port, mdb, state)) { + dev_info_ratelimited(chip->dev, + "p%d: cannot offload MDB %pM: stream-reserved flag conflicts with existing entry\n", + port, mdb->addr); + err = -EINVAL; + goto out; + } + + err = mv88e6xxx_port_db_loadpurge_entry(chip, port, fid, mdb->addr, + &existing, state); if (err) goto out; -- 2.43.0