Commit d3a6987d authored by David S. Miller's avatar David S. Miller

Merge branch 'mv88e6xxx-Add-per-port-devlink-regions'

Andrew Lunn says:

====================
mv88e6xxx: Add per port devlink regions

This patchset extends devlink regions to support per port regions, and
them makes use of them to support the ports of the mv88e6xxx switches.

root@rap:~# devlink region show
mdio_bus/gpio-0:00/global1: size 64 snapshot []
mdio_bus/gpio-0:00/global2: size 64 snapshot []
mdio_bus/gpio-0:00/atu: size 49152 snapshot []
mdio_bus/gpio-0:00/0/port: size 64 snapshot []
mdio_bus/gpio-0:00/1/port: size 64 snapshot []
mdio_bus/gpio-0:00/2/port: size 64 snapshot []
mdio_bus/gpio-0:00/3/port: size 64 snapshot []
mdio_bus/gpio-0:00/4/port: size 64 snapshot []
mdio_bus/gpio-0:00/5/port: size 64 snapshot []
mdio_bus/gpio-0:00/6/port: size 64 snapshot []
mdio_bus/gpio-0:00/7/port: size 64 snapshot []
mdio_bus/gpio-0:00/8/port: size 64 snapshot []
mdio_bus/gpio-0:00/9/port: size 64 snapshot []
mdio_bus/gpio-0:00/10/port: size 64 snapshot []

root@rap:~# devlink region new mdio_bus/gpio-0:00/1/port snapshot 42
root@rap:~# devlink region dump mdio_bus/gpio-0:00/1/port snapshot 42
0000000000000000 4f 1e 3e 20 00 01 01 39 3f 05 00 00 fd 07 00 00
0000000000000010 80 00 01 00 00 00 00 00 00 00 00 00 00 00 00 91
0000000000000020 00 00 00 00 00 00 00 00 00 00 00 00 22 00 00 00
0000000000000030 07 3e 00 00 00 00 00 80 00 00 00 00 00 00 5b 00

In order to support all ports of the switch, a new devlink flavour has
been added for unused ports:

mdio_bus/gpio-0:00/0: type notset flavour unused splittable false
mdio_bus/gpio-0:00/1: type notset flavour cpu port 1 splittable false
mdio_bus/gpio-0:00/2: type eth netdev red flavour physical port 2 splittable fae
mdio_bus/gpio-0:00/3: type eth netdev blue flavour physical port 3 splittable fe
mdio_bus/gpio-0:00/4: type eth netdev green flavour physical port 4 splittable e
mdio_bus/gpio-0:00/5: type notset flavour unused splittable false
mdio_bus/gpio-0:00/6: type notset flavour unused splittable false
mdio_bus/gpio-0:00/7: type notset flavour unused splittable false
mdio_bus/gpio-0:00/8: type eth netdev waic0 flavour physical port 8 splittable e
mdio_bus/gpio-0:00/9: type notset flavour unused splittable false
mdio_bus/gpio-0:00/10: type notset flavour unused splittable false

The DSA core now creates the devlink port instances earlier, so that
the driver setup function can make use of them.

v3:
Whitespace cleanup
Added justification for devlink unused flavour
Added Tested-by, Reviewed-by:
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 321e921d b71a8d60
......@@ -415,6 +415,36 @@ static int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
return err;
}
static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port,
const struct devlink_port_region_ops *ops,
struct netlink_ext_ack *extack,
u8 **data)
{
struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port);
int port = dsa_devlink_port_to_port(devlink_port);
struct mv88e6xxx_chip *chip = ds->priv;
u16 *registers;
int i, err;
registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
if (!registers)
return -ENOMEM;
mv88e6xxx_reg_lock(chip);
for (i = 0; i < 32; i++) {
err = mv88e6xxx_port_read(chip, port, i, &registers[i]);
if (err) {
kfree(registers);
goto out;
}
}
*data = (u8 *)registers;
out:
mv88e6xxx_reg_unlock(chip);
return err;
}
static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = {
.id = MV88E6XXX_REGION_GLOBAL1,
};
......@@ -443,6 +473,12 @@ static struct devlink_region_ops mv88e6xxx_region_atu_ops = {
.destructor = kfree,
};
static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = {
.name = "port",
.snapshot = mv88e6xxx_region_port_snapshot,
.destructor = kfree,
};
struct mv88e6xxx_region {
struct devlink_region_ops *ops;
u64 size;
......@@ -471,11 +507,59 @@ mv88e6xxx_teardown_devlink_regions_global(struct mv88e6xxx_chip *chip)
dsa_devlink_region_destroy(chip->regions[i]);
}
void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds)
static void
mv88e6xxx_teardown_devlink_regions_port(struct mv88e6xxx_chip *chip,
int port)
{
struct mv88e6xxx_chip *chip = ds->priv;
dsa_devlink_region_destroy(chip->ports[port].region);
}
mv88e6xxx_teardown_devlink_regions_global(chip);
static int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds,
struct mv88e6xxx_chip *chip,
int port)
{
struct devlink_region *region;
region = dsa_devlink_port_region_create(ds,
port,
&mv88e6xxx_region_port_ops, 1,
32 * sizeof(u16));
if (IS_ERR(region))
return PTR_ERR(region);
chip->ports[port].region = region;
return 0;
}
static void
mv88e6xxx_teardown_devlink_regions_ports(struct mv88e6xxx_chip *chip)
{
int port;
for (port = 0; port < mv88e6xxx_num_ports(chip); port++)
mv88e6xxx_teardown_devlink_regions_port(chip, port);
}
static int mv88e6xxx_setup_devlink_regions_ports(struct dsa_switch *ds,
struct mv88e6xxx_chip *chip)
{
int port;
int err;
for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
err = mv88e6xxx_setup_devlink_regions_port(ds, chip, port);
if (err)
goto out;
}
return 0;
out:
while (port-- > 0)
mv88e6xxx_teardown_devlink_regions_port(chip, port);
return err;
}
static int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds,
......@@ -511,8 +595,25 @@ static int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds,
int mv88e6xxx_setup_devlink_regions(struct dsa_switch *ds)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;
err = mv88e6xxx_setup_devlink_regions_global(ds, chip);
if (err)
return err;
err = mv88e6xxx_setup_devlink_regions_ports(ds, chip);
if (err)
mv88e6xxx_teardown_devlink_regions_global(chip);
return mv88e6xxx_setup_devlink_regions_global(ds, chip);
return err;
}
void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds)
{
struct mv88e6xxx_chip *chip = ds->priv;
mv88e6xxx_teardown_devlink_regions_ports(chip);
mv88e6xxx_teardown_devlink_regions_global(chip);
}
int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
......
......@@ -110,6 +110,7 @@ struct devlink_port_attrs {
struct devlink_port {
struct list_head list;
struct list_head param_list;
struct list_head region_list;
struct devlink *devlink;
unsigned int index;
bool registered;
......@@ -591,6 +592,26 @@ struct devlink_region_ops {
void *priv;
};
/**
* struct devlink_port_region_ops - Region operations for a port
* @name: region name
* @destructor: callback used to free snapshot memory when deleting
* @snapshot: callback to request an immediate snapshot. On success,
* the data variable must be updated to point to the snapshot data.
* The function will be called while the devlink instance lock is
* held.
* @priv: Pointer to driver private data for the region operation
*/
struct devlink_port_region_ops {
const char *name;
void (*destructor)(const void *data);
int (*snapshot)(struct devlink_port *port,
const struct devlink_port_region_ops *ops,
struct netlink_ext_ack *extack,
u8 **data);
void *priv;
};
struct devlink_fmsg;
struct devlink_health_reporter;
......@@ -1445,7 +1466,13 @@ struct devlink_region *
devlink_region_create(struct devlink *devlink,
const struct devlink_region_ops *ops,
u32 region_max_snapshots, u64 region_size);
struct devlink_region *
devlink_port_region_create(struct devlink_port *port,
const struct devlink_port_region_ops *ops,
u32 region_max_snapshots, u64 region_size);
void devlink_region_destroy(struct devlink_region *region);
void devlink_port_region_destroy(struct devlink_region *region);
int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id);
void devlink_region_snapshot_id_put(struct devlink *devlink, u32 id);
int devlink_region_snapshot_create(struct devlink_region *region,
......
......@@ -215,6 +215,7 @@ struct dsa_port {
u8 stp_state;
struct net_device *bridge_dev;
struct devlink_port devlink_port;
bool devlink_port_setup;
struct phylink *pl;
struct phylink_config pl_config;
......@@ -680,6 +681,11 @@ struct devlink_region *
dsa_devlink_region_create(struct dsa_switch *ds,
const struct devlink_region_ops *ops,
u32 region_max_snapshots, u64 region_size);
struct devlink_region *
dsa_devlink_port_region_create(struct dsa_switch *ds,
int port,
const struct devlink_port_region_ops *ops,
u32 region_max_snapshots, u64 region_size);
void dsa_devlink_region_destroy(struct devlink_region *region);
struct dsa_port *dsa_port_from_netdev(struct net_device *netdev);
......@@ -695,6 +701,20 @@ static inline struct dsa_switch *dsa_devlink_to_ds(struct devlink *dl)
return dl_priv->ds;
}
static inline
struct dsa_switch *dsa_devlink_port_to_ds(struct devlink_port *port)
{
struct devlink *dl = port->devlink;
struct dsa_devlink_priv *dl_priv = devlink_priv(dl);
return dl_priv->ds;
}
static inline int dsa_devlink_port_to_port(struct devlink_port *port)
{
return port->index;
}
struct dsa_switch_driver {
struct list_head list;
const struct dsa_switch_ops *ops;
......
......@@ -197,6 +197,9 @@ enum devlink_port_flavour {
* port that faces the PCI VF.
*/
DEVLINK_PORT_FLAVOUR_VIRTUAL, /* Any virtual port facing the user. */
DEVLINK_PORT_FLAVOUR_UNUSED, /* Port which exists in the switch, but
* is not used in any way.
*/
};
enum devlink_param_cmode {
......
This diff is collapsed.
......@@ -423,6 +423,20 @@ dsa_devlink_region_create(struct dsa_switch *ds,
}
EXPORT_SYMBOL_GPL(dsa_devlink_region_create);
struct devlink_region *
dsa_devlink_port_region_create(struct dsa_switch *ds,
int port,
const struct devlink_port_region_ops *ops,
u32 region_max_snapshots, u64 region_size)
{
struct dsa_port *dp = dsa_to_port(ds, port);
return devlink_port_region_create(&dp->devlink_port, ops,
region_max_snapshots,
region_size);
}
EXPORT_SYMBOL_GPL(dsa_devlink_port_region_create);
void dsa_devlink_region_destroy(struct devlink_region *region)
{
devlink_region_destroy(region);
......
......@@ -251,22 +251,11 @@ static void dsa_tree_teardown_default_cpu(struct dsa_switch_tree *dst)
static int dsa_port_setup(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
struct dsa_switch_tree *dst = ds->dst;
const unsigned char *id = (const unsigned char *)&dst->index;
const unsigned char len = sizeof(dst->index);
struct devlink_port *dlp = &dp->devlink_port;
bool dsa_port_link_registered = false;
bool devlink_port_registered = false;
struct devlink_port_attrs attrs = {};
struct devlink *dl = ds->devlink;
bool dsa_port_enabled = false;
int err = 0;
attrs.phys.port_number = dp->index;
memcpy(attrs.switch_id.id, id, len);
attrs.switch_id.id_len = len;
if (dp->setup)
return 0;
......@@ -275,14 +264,6 @@ static int dsa_port_setup(struct dsa_port *dp)
dsa_port_disable(dp);
break;
case DSA_PORT_TYPE_CPU:
memset(dlp, 0, sizeof(*dlp));
attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err)
break;
devlink_port_registered = true;
err = dsa_port_link_register_of(dp);
if (err)
break;
......@@ -295,14 +276,6 @@ static int dsa_port_setup(struct dsa_port *dp)
break;
case DSA_PORT_TYPE_DSA:
memset(dlp, 0, sizeof(*dlp));
attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err)
break;
devlink_port_registered = true;
err = dsa_port_link_register_of(dp);
if (err)
break;
......@@ -315,14 +288,6 @@ static int dsa_port_setup(struct dsa_port *dp)
break;
case DSA_PORT_TYPE_USER:
memset(dlp, 0, sizeof(*dlp));
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (err)
break;
devlink_port_registered = true;
dp->mac = of_get_mac_address(dp->dn);
err = dsa_slave_create(dp);
if (err)
......@@ -336,8 +301,6 @@ static int dsa_port_setup(struct dsa_port *dp)
dsa_port_disable(dp);
if (err && dsa_port_link_registered)
dsa_port_link_unregister_of(dp);
if (err && devlink_port_registered)
devlink_port_unregister(dlp);
if (err)
return err;
......@@ -346,10 +309,50 @@ static int dsa_port_setup(struct dsa_port *dp)
return 0;
}
static void dsa_port_teardown(struct dsa_port *dp)
static int dsa_port_devlink_setup(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch_tree *dst = dp->ds->dst;
struct devlink_port_attrs attrs = {};
struct devlink *dl = dp->ds->devlink;
const unsigned char *id;
unsigned char len;
int err;
id = (const unsigned char *)&dst->index;
len = sizeof(dst->index);
attrs.phys.port_number = dp->index;
memcpy(attrs.switch_id.id, id, len);
attrs.switch_id.id_len = len;
memset(dlp, 0, sizeof(*dlp));
switch (dp->type) {
case DSA_PORT_TYPE_UNUSED:
attrs.flavour = DEVLINK_PORT_FLAVOUR_UNUSED;
break;
case DSA_PORT_TYPE_CPU:
attrs.flavour = DEVLINK_PORT_FLAVOUR_CPU;
break;
case DSA_PORT_TYPE_DSA:
attrs.flavour = DEVLINK_PORT_FLAVOUR_DSA;
break;
case DSA_PORT_TYPE_USER:
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
break;
}
devlink_port_attrs_set(dlp, &attrs);
err = devlink_port_register(dl, dlp, dp->index);
if (!err)
dp->devlink_port_setup = true;
return err;
}
static void dsa_port_teardown(struct dsa_port *dp)
{
if (!dp->setup)
return;
......@@ -359,16 +362,13 @@ static void dsa_port_teardown(struct dsa_port *dp)
case DSA_PORT_TYPE_CPU:
dsa_port_disable(dp);
dsa_tag_driver_put(dp->tag_ops);
devlink_port_unregister(dlp);
dsa_port_link_unregister_of(dp);
break;
case DSA_PORT_TYPE_DSA:
dsa_port_disable(dp);
devlink_port_unregister(dlp);
dsa_port_link_unregister_of(dp);
break;
case DSA_PORT_TYPE_USER:
devlink_port_unregister(dlp);
if (dp->slave) {
dsa_slave_destroy(dp->slave);
dp->slave = NULL;
......@@ -379,6 +379,15 @@ static void dsa_port_teardown(struct dsa_port *dp)
dp->setup = false;
}
static void dsa_port_devlink_teardown(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
if (dp->devlink_port_setup)
devlink_port_unregister(dlp);
dp->devlink_port_setup = false;
}
static int dsa_devlink_info_get(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
......@@ -398,6 +407,7 @@ static const struct devlink_ops dsa_devlink_ops = {
static int dsa_switch_setup(struct dsa_switch *ds)
{
struct dsa_devlink_priv *dl_priv;
struct dsa_port *dp;
int err;
if (ds->setup)
......@@ -423,9 +433,20 @@ static int dsa_switch_setup(struct dsa_switch *ds)
if (err)
goto free_devlink;
/* Setup devlink port instances now, so that the switch
* setup() can register regions etc, against the ports
*/
list_for_each_entry(dp, &ds->dst->ports, list) {
if (dp->ds == ds) {
err = dsa_port_devlink_setup(dp);
if (err)
goto unregister_devlink_ports;
}
}
err = dsa_switch_register_notifier(ds);
if (err)
goto unregister_devlink;
goto unregister_devlink_ports;
err = ds->ops->setup(ds);
if (err < 0)
......@@ -453,7 +474,10 @@ static int dsa_switch_setup(struct dsa_switch *ds)
unregister_notifier:
dsa_switch_unregister_notifier(ds);
unregister_devlink:
unregister_devlink_ports:
list_for_each_entry(dp, &ds->dst->ports, list)
if (dp->ds == ds)
dsa_port_devlink_teardown(dp);
devlink_unregister(ds->devlink);
free_devlink:
devlink_free(ds->devlink);
......@@ -464,6 +488,8 @@ static int dsa_switch_setup(struct dsa_switch *ds)
static void dsa_switch_teardown(struct dsa_switch *ds)
{
struct dsa_port *dp;
if (!ds->setup)
return;
......@@ -476,6 +502,9 @@ static void dsa_switch_teardown(struct dsa_switch *ds)
ds->ops->teardown(ds);
if (ds->devlink) {
list_for_each_entry(dp, &ds->dst->ports, list)
if (dp->ds == ds)
dsa_port_devlink_teardown(dp);
devlink_unregister(ds->devlink);
devlink_free(ds->devlink);
ds->devlink = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment