Commit dc5340c3 authored by Joseph Huang's avatar Joseph Huang Committed by Jakub Kicinski
Browse files

net: dsa: mv88e6xxx: Verify after ATU Load ops



ATU Load operations could fail silently if there's not enough space
on the device to hold the new entry. When this happens, the symptom
depends on the unknown flood settings. If unknown multicast flood is
disabled, the multicast packets are dropped when the ATU table is
full. If unknown multicast flood is enabled, the multicast packets
will be flooded to all ports. Either way, IGMP snooping is broken
when the ATU Load operation fails silently.

Do a Read-After-Write verification after each fdb/mdb add operation
to make sure that the operation was really successful, and return
-ENOSPC otherwise.

Fixes: defb05b9 ("net: dsa: mv88e6xxx: Add support for fdb_add, fdb_del, and fdb_getnext")
Signed-off-by: default avatarJoseph Huang <Joseph.Huang@garmin.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250306172306.3859214-1-Joseph.Huang@garmin.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent d749d901
Loading
Loading
Loading
Loading
+48 −11
Original line number Diff line number Diff line
@@ -2208,13 +2208,11 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
	return err;
}

static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
static int mv88e6xxx_port_db_get(struct mv88e6xxx_chip *chip,
				 const unsigned char *addr, u16 vid,
					u8 state)
				 u16 *fid, struct mv88e6xxx_atu_entry *entry)
{
	struct mv88e6xxx_atu_entry entry;
	struct mv88e6xxx_vtu_entry vlan;
	u16 fid;
	int err;

	/* Ports have two private address databases: one for when the port is
@@ -2225,7 +2223,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
	 * VLAN ID into the port's database used for VLAN-unaware bridging.
	 */
	if (vid == 0) {
		fid = MV88E6XXX_FID_BRIDGED;
		*fid = MV88E6XXX_FID_BRIDGED;
	} else {
		err = mv88e6xxx_vtu_get(chip, vid, &vlan);
		if (err)
@@ -2235,14 +2233,39 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
		if (!vlan.valid)
			return -EOPNOTSUPP;

		fid = vlan.fid;
		*fid = vlan.fid;
	}

	entry.state = 0;
	ether_addr_copy(entry.mac, addr);
	eth_addr_dec(entry.mac);
	entry->state = 0;
	ether_addr_copy(entry->mac, addr);
	eth_addr_dec(entry->mac);

	return mv88e6xxx_g1_atu_getnext(chip, *fid, entry);
}

static bool mv88e6xxx_port_db_find(struct mv88e6xxx_chip *chip,
				   const unsigned char *addr, u16 vid)
{
	struct mv88e6xxx_atu_entry entry;
	u16 fid;
	int err;

	err = mv88e6xxx_port_db_get(chip, addr, vid, &fid, &entry);
	if (err)
		return false;

	return entry.state && ether_addr_equal(entry.mac, addr);
}

	err = mv88e6xxx_g1_atu_getnext(chip, fid, &entry);
static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
					const unsigned char *addr, u16 vid,
					u8 state)
{
	struct mv88e6xxx_atu_entry entry;
	u16 fid;
	int err;

	err = mv88e6xxx_port_db_get(chip, addr, vid, &fid, &entry);
	if (err)
		return err;

@@ -2846,6 +2869,13 @@ static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid,
					   MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
	if (err)
		goto out;

	if (!mv88e6xxx_port_db_find(chip, addr, vid))
		err = -ENOSPC;

out:
	mv88e6xxx_reg_unlock(chip);

	return err;
@@ -6614,6 +6644,13 @@ static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
	mv88e6xxx_reg_lock(chip);
	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
					   MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC);
	if (err)
		goto out;

	if (!mv88e6xxx_port_db_find(chip, mdb->addr, mdb->vid))
		err = -ENOSPC;

out:
	mv88e6xxx_reg_unlock(chip);

	return err;