]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: dsa: mv88e6xxx: Verify after ATU Load ops
authorJoseph Huang <Joseph.Huang@garmin.com>
Thu, 6 Mar 2025 17:23:05 +0000 (12:23 -0500)
committerJakub Kicinski <kuba@kernel.org>
Sat, 8 Mar 2025 03:36:33 +0000 (19:36 -0800)
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: defb05b9b9b4 ("net: dsa: mv88e6xxx: Add support for fdb_add, fdb_del, and fdb_getnext")
Signed-off-by: Joseph Huang <Joseph.Huang@garmin.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250306172306.3859214-1-Joseph.Huang@garmin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/mv88e6xxx/chip.c

index 68d1e891752b8b367cdb98c1be3800dbd1d2c2c6..5db96ca52505ab0325719a36163c034cda7d3198 100644 (file)
@@ -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,
-                                       const unsigned char *addr, u16 vid,
-                                       u8 state)
+static int mv88e6xxx_port_db_get(struct mv88e6xxx_chip *chip,
+                                const unsigned char *addr, u16 vid,
+                                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_g1_atu_getnext(chip, fid, &entry);
+       err = mv88e6xxx_port_db_get(chip, addr, vid, &fid, &entry);
+       if (err)
+               return false;
+
+       return entry.state && ether_addr_equal(entry.mac, addr);
+}
+
+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;