Commit 7dc96039 authored by Tobias Waldekranz's avatar Tobias Waldekranz Committed by Jakub Kicinski
Browse files

net: dsa: mv88e6xxx: Export STU as devlink region



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: default avatarTobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 49c98c1d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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,
+94 −0
Original line number Diff line number Diff line
@@ -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);