Skip to content

Commit

Permalink
net: dsa: tear down devlink port regions when tearing down the devlin…
Browse files Browse the repository at this point in the history
…k port on error

Commit 86f8b1c ("net: dsa: Do not make user port errors fatal")
decided it was fine to ignore errors on certain ports that fail to
probe, and go on with the ports that do probe fine.

Commit fb6ec87 ("net: dsa: Fix type was not set for devlink port")
noticed that devlink_port_type_eth_set(dlp, dp->slave); does not get
called, and devlink notices after a timeout of 3600 seconds and prints a
WARN_ON. So it went ahead to unregister the devlink port. And because
there exists an UNUSED port flavour, we actually re-register the devlink
port as UNUSED.

Commit 08156ba ("net: dsa: Add devlink port regions support to
DSA") added devlink port regions, which are set up by the driver and not
by DSA.

When we trigger the devlink port deregistration and reregistration as
unused, devlink now prints another WARN_ON, from here:

devlink_port_unregister:
	WARN_ON(!list_empty(&devlink_port->region_list));

So the port still has regions, which makes sense, because they were set
up by the driver, and the driver doesn't know we're unregistering the
devlink port.

Somebody needs to tear them down, and optionally (actually it would be
nice, to be consistent) set them up again for the new devlink port.

But DSA's layering stays in our way quite badly here.

The options I've considered are:

1. Introduce a function in devlink to just change a port's type and
   flavour. No dice, devlink keeps a lot of state, it really wants the
   port to not be registered when you set its parameters, so changing
   anything can only be done by destroying what we currently have and
   recreating it.

2. Make DSA cache the parameters passed to dsa_devlink_port_region_create,
   and the region returned, keep those in a list, then when the devlink
   port unregister needs to take place, the existing devlink regions are
   destroyed by DSA, and we replay the creation of new regions using the
   cached parameters. Problem: mv88e6xxx keeps the region pointers in
   chip->ports[port].region, and these will remain stale after DSA frees
   them. There are many things DSA can do, but updating mv88e6xxx's
   private pointers is not one of them.

3. Just let the driver do it (i.e. introduce a very specific method
   called ds->ops->port_reinit_as_unused, which unregisters its devlink
   port devlink regions, then the old devlink port, then registers the
   new one, then the devlink port regions for it). While it does work,
   as opposed to the others, it's pretty horrible from an API
   perspective and we can do better.

4. Introduce a new pair of methods, ->port_setup and ->port_teardown,
   which in the case of mv88e6xxx must register and unregister the
   devlink port regions. Call these 2 methods when the port must be
   reinitialized as unused.

Naturally, I went for the 4th approach.

Fixes: 08156ba ("net: dsa: Add devlink port regions support to DSA")
Signed-off-by: Vladimir Oltean <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
vladimiroltean authored and davem330 committed Sep 19, 2021
1 parent fdb4758 commit fd292c1
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 73 deletions.
16 changes: 14 additions & 2 deletions drivers/net/dsa/mv88e6xxx/chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -3071,7 +3071,7 @@ static void mv88e6xxx_teardown(struct dsa_switch *ds)
{
mv88e6xxx_teardown_devlink_params(ds);
dsa_devlink_resources_unregister(ds);
mv88e6xxx_teardown_devlink_regions(ds);
mv88e6xxx_teardown_devlink_regions_global(ds);
}

static int mv88e6xxx_setup(struct dsa_switch *ds)
Expand Down Expand Up @@ -3215,7 +3215,7 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
if (err)
goto out_resources;

err = mv88e6xxx_setup_devlink_regions(ds);
err = mv88e6xxx_setup_devlink_regions_global(ds);
if (err)
goto out_params;

Expand All @@ -3229,6 +3229,16 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
return err;
}

static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port)
{
return mv88e6xxx_setup_devlink_regions_port(ds, port);
}

static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port)
{
mv88e6xxx_teardown_devlink_regions_port(ds, port);
}

/* prod_id for switch families which do not have a PHY model number */
static const u16 family_prod_id_table[] = {
[MV88E6XXX_FAMILY_6341] = MV88E6XXX_PORT_SWITCH_ID_PROD_6341,
Expand Down Expand Up @@ -6116,6 +6126,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.change_tag_protocol = mv88e6xxx_change_tag_protocol,
.setup = mv88e6xxx_setup,
.teardown = mv88e6xxx_teardown,
.port_setup = mv88e6xxx_port_setup,
.port_teardown = mv88e6xxx_port_teardown,
.phylink_validate = mv88e6xxx_validate,
.phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state,
.phylink_mac_config = mv88e6xxx_mac_config,
Expand Down
73 changes: 9 additions & 64 deletions drivers/net/dsa/mv88e6xxx/devlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -647,26 +647,25 @@ static struct mv88e6xxx_region mv88e6xxx_regions[] = {
},
};

static void
mv88e6xxx_teardown_devlink_regions_global(struct mv88e6xxx_chip *chip)
void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds)
{
struct mv88e6xxx_chip *chip = ds->priv;
int i;

for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++)
dsa_devlink_region_destroy(chip->regions[i]);
}

static void
mv88e6xxx_teardown_devlink_regions_port(struct mv88e6xxx_chip *chip,
int port)
void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_chip *chip = ds->priv;

dsa_devlink_region_destroy(chip->ports[port].region);
}

static int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds,
struct mv88e6xxx_chip *chip,
int port)
int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct devlink_region *region;

region = dsa_devlink_port_region_create(ds,
Expand All @@ -681,40 +680,10 @@ static int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds,
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,
struct mv88e6xxx_chip *chip)
int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
{
bool (*cond)(struct mv88e6xxx_chip *chip);
struct mv88e6xxx_chip *chip = ds->priv;
struct devlink_region_ops *ops;
struct devlink_region *region;
u64 size;
Expand Down Expand Up @@ -753,30 +722,6 @@ static int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds,
return PTR_ERR(region);
}

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 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,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
Expand Down
6 changes: 4 additions & 2 deletions drivers/net/dsa/mv88e6xxx/devlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx);
int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id,
struct devlink_param_gset_ctx *ctx);
int mv88e6xxx_setup_devlink_regions(struct dsa_switch *ds);
void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds);
int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds);
void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds);
int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port);
void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port);

int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
struct devlink_info_req *req,
Expand Down
8 changes: 8 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -585,8 +585,16 @@ struct dsa_switch_ops {
int (*change_tag_protocol)(struct dsa_switch *ds, int port,
enum dsa_tag_protocol proto);

/* Optional switch-wide initialization and destruction methods */
int (*setup)(struct dsa_switch *ds);
void (*teardown)(struct dsa_switch *ds);

/* Per-port initialization and destruction methods. Mandatory if the
* driver registers devlink port regions, optional otherwise.
*/
int (*port_setup)(struct dsa_switch *ds, int port);
void (*port_teardown)(struct dsa_switch *ds, int port);

u32 (*get_phy_flags)(struct dsa_switch *ds, int port);

/*
Expand Down
51 changes: 46 additions & 5 deletions net/dsa/dsa2.c
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ static int dsa_port_setup(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
bool dsa_port_link_registered = false;
struct dsa_switch *ds = dp->ds;
bool dsa_port_enabled = false;
int err = 0;

Expand All @@ -438,6 +439,12 @@ static int dsa_port_setup(struct dsa_port *dp)
INIT_LIST_HEAD(&dp->fdbs);
INIT_LIST_HEAD(&dp->mdbs);

if (ds->ops->port_setup) {
err = ds->ops->port_setup(ds, dp->index);
if (err)
return err;
}

switch (dp->type) {
case DSA_PORT_TYPE_UNUSED:
dsa_port_disable(dp);
Expand Down Expand Up @@ -480,8 +487,11 @@ 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)
if (err) {
if (ds->ops->port_teardown)
ds->ops->port_teardown(ds, dp->index);
return err;
}

dp->setup = true;

Expand Down Expand Up @@ -533,11 +543,15 @@ static int dsa_port_devlink_setup(struct dsa_port *dp)
static void dsa_port_teardown(struct dsa_port *dp)
{
struct devlink_port *dlp = &dp->devlink_port;
struct dsa_switch *ds = dp->ds;
struct dsa_mac_addr *a, *tmp;

if (!dp->setup)
return;

if (ds->ops->port_teardown)
ds->ops->port_teardown(ds, dp->index);

devlink_port_type_clear(dlp);

switch (dp->type) {
Expand Down Expand Up @@ -581,6 +595,36 @@ static void dsa_port_devlink_teardown(struct dsa_port *dp)
dp->devlink_port_setup = false;
}

/* Destroy the current devlink port, and create a new one which has the UNUSED
* flavour. At this point, any call to ds->ops->port_setup has been already
* balanced out by a call to ds->ops->port_teardown, so we know that any
* devlink port regions the driver had are now unregistered. We then call its
* ds->ops->port_setup again, in order for the driver to re-create them on the
* new devlink port.
*/
static int dsa_port_reinit_as_unused(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
int err;

dsa_port_devlink_teardown(dp);
dp->type = DSA_PORT_TYPE_UNUSED;
err = dsa_port_devlink_setup(dp);
if (err)
return err;

if (ds->ops->port_setup) {
/* On error, leave the devlink port registered,
* dsa_switch_teardown will clean it up later.
*/
err = ds->ops->port_setup(ds, dp->index);
if (err)
return err;
}

return 0;
}

static int dsa_devlink_info_get(struct devlink *dl,
struct devlink_info_req *req,
struct netlink_ext_ack *extack)
Expand Down Expand Up @@ -938,12 +982,9 @@ static int dsa_tree_setup_switches(struct dsa_switch_tree *dst)
list_for_each_entry(dp, &dst->ports, list) {
err = dsa_port_setup(dp);
if (err) {
dsa_port_devlink_teardown(dp);
dp->type = DSA_PORT_TYPE_UNUSED;
err = dsa_port_devlink_setup(dp);
err = dsa_port_reinit_as_unused(dp);
if (err)
goto teardown;
continue;
}
}

Expand Down

0 comments on commit fd292c1

Please sign in to comment.