net: dsa: mv88e6xxx: Export STU as devlink region
authorTobias Waldekranz <tobias@waldekranz.com>
Wed, 16 Mar 2022 15:08:56 +0000 (16:08 +0100)
committerJakub Kicinski <kuba@kernel.org>
Thu, 17 Mar 2022 23:49:59 +0000 (16:49 -0700)
Export the raw STU data in a devlink region so that it can be
inspected from userspace and compared to the current bridge
configuration.

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/dsa/mv88e6xxx/chip.h
drivers/net/dsa/mv88e6xxx/devlink.c

index be654be..6d4daa2 100644 (file)
@@ -287,6 +287,7 @@ enum mv88e6xxx_region_id {
        MV88E6XXX_REGION_GLOBAL2,
        MV88E6XXX_REGION_ATU,
        MV88E6XXX_REGION_VTU,
+       MV88E6XXX_REGION_STU,
        MV88E6XXX_REGION_PVT,
 
        _MV88E6XXX_REGION_MAX,
index 3810683..1266eab 100644 (file)
@@ -503,6 +503,85 @@ static int mv88e6xxx_region_vtu_snapshot(struct devlink *dl,
        return 0;
 }
 
+/**
+ * struct mv88e6xxx_devlink_stu_entry - Devlink STU entry
+ * @sid:   Global1/3:   SID, unknown filters and learning.
+ * @vid:   Global1/6:   Valid bit.
+ * @data:  Global1/7-9: Membership data and priority override.
+ * @resvd: Reserved. In case we forgot something.
+ *
+ * The STU entry format varies between chipset generations. Peridot
+ * and Amethyst packs the STU data into Global1/7-8. Older silicon
+ * spreads the information across all three VTU data registers -
+ * inheriting the layout of even older hardware that had no STU at
+ * all. Since this is a low-level debug interface, copy all data
+ * verbatim and defer parsing to the consumer.
+ */
+struct mv88e6xxx_devlink_stu_entry {
+       u16 sid;
+       u16 vid;
+       u16 data[3];
+       u16 resvd;
+};
+
+static int mv88e6xxx_region_stu_snapshot(struct devlink *dl,
+                                        const struct devlink_region_ops *ops,
+                                        struct netlink_ext_ack *extack,
+                                        u8 **data)
+{
+       struct mv88e6xxx_devlink_stu_entry *table, *entry;
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+       struct mv88e6xxx_chip *chip = ds->priv;
+       struct mv88e6xxx_stu_entry stu;
+       int err;
+
+       table = kcalloc(mv88e6xxx_max_sid(chip) + 1,
+                       sizeof(struct mv88e6xxx_devlink_stu_entry),
+                       GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       entry = table;
+       stu.sid = mv88e6xxx_max_sid(chip);
+       stu.valid = false;
+
+       mv88e6xxx_reg_lock(chip);
+
+       do {
+               err = mv88e6xxx_g1_stu_getnext(chip, &stu);
+               if (err)
+                       break;
+
+               if (!stu.valid)
+                       break;
+
+               err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
+                                               &entry->sid);
+               err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
+                                               &entry->vid);
+               err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
+                                               &entry->data[0]);
+               err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
+                                               &entry->data[1]);
+               err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
+                                               &entry->data[2]);
+               if (err)
+                       break;
+
+               entry++;
+       } while (stu.sid < mv88e6xxx_max_sid(chip));
+
+       mv88e6xxx_reg_unlock(chip);
+
+       if (err) {
+               kfree(table);
+               return err;
+       }
+
+       *data = (u8 *)table;
+       return 0;
+}
+
 static int mv88e6xxx_region_pvt_snapshot(struct devlink *dl,
                                         const struct devlink_region_ops *ops,
                                         struct netlink_ext_ack *extack,
@@ -605,6 +684,12 @@ static struct devlink_region_ops mv88e6xxx_region_vtu_ops = {
        .destructor = kfree,
 };
 
+static struct devlink_region_ops mv88e6xxx_region_stu_ops = {
+       .name = "stu",
+       .snapshot = mv88e6xxx_region_stu_snapshot,
+       .destructor = kfree,
+};
+
 static struct devlink_region_ops mv88e6xxx_region_pvt_ops = {
        .name = "pvt",
        .snapshot = mv88e6xxx_region_pvt_snapshot,
@@ -640,6 +725,11 @@ static struct mv88e6xxx_region mv88e6xxx_regions[] = {
                .ops = &mv88e6xxx_region_vtu_ops
          /* calculated at runtime */
        },
+       [MV88E6XXX_REGION_STU] = {
+               .ops = &mv88e6xxx_region_stu_ops,
+               .cond = mv88e6xxx_has_stu,
+         /* calculated at runtime */
+       },
        [MV88E6XXX_REGION_PVT] = {
                .ops = &mv88e6xxx_region_pvt_ops,
                .size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16),
@@ -706,6 +796,10 @@ int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
                        size = (mv88e6xxx_max_vid(chip) + 1) *
                                sizeof(struct mv88e6xxx_devlink_vtu_entry);
                        break;
+               case MV88E6XXX_REGION_STU:
+                       size = (mv88e6xxx_max_sid(chip) + 1) *
+                               sizeof(struct mv88e6xxx_devlink_stu_entry);
+                       break;
                }
 
                region = dsa_devlink_region_create(ds, ops, 1, size);