Merge branch 'more-dsa-fixes-for-devres-mdiobus_-alloc-register'
Vladimir Oltean says: ==================== More DSA fixes for devres + mdiobus_{alloc,register} The initial patch series "[net,0/2] Fix mdiobus users with devres" https://patchwork.kernel.org/project/netdevbpf/cover/20210920214209.1733768-1-vladimir.oltean@nxp.com/ fixed some instances where DSA drivers on slow buses (SPI, I2C) trigger a panic (changed since then to a warn) in mdiobus_free. That was due to devres calling mdiobus_free() with no prior mdiobus_unregister(), which again was due to commitac3a68d566
("net: phy: don't abuse devres in devm_mdiobus_register()") by Bartosz Golaszewski. Rafael Richter and Daniel Klauer report yet another variation on that theme, but this time it applies to any DSA switch driver, not just those on buses which have a "->shutdown() calls ->remove() which unregisters children" sequence. Their setup is that of an LX2160A DPAA2 SoC driving a Marvell DSA switch (MDIO). DPAA2 Ethernet drivers probe on the "fsl-mc" bus (drivers/bus/fsl-mc/fsl-mc-bus.c). This bus is meant to be the kernel-side representation of the networking objects kept by the Management Complex (MC) firmware. The fsl-mc bus driver has this pattern: static void fsl_mc_bus_shutdown(struct platform_device *pdev) { fsl_mc_bus_remove(pdev); } which proceeds to remove the children on the bus. Among those children, the dpaa2-eth network driver. When dpaa2-eth is a DSA master, this removal of the master on shutdown trips up the device link created by dsa_master_setup(), and as such, the Marvell switch is also removed. From this point on, readers can revisit the description of commits74b6d7d133
("net: dsa: realtek: register the MDIO bus under devres")5135e96a3d
("net: dsa: don't allocate the slave_mii_bus using devres") since the prerequisites for the BUG_ON in mdiobus_free() have been accomplished if there is a devres mismatch between mdiobus_alloc() and mdiobus_register(). Most DSA drivers have this kind of mismatch, and upon my initial assessment I had not realized the possibility described above, so I didn't fix it. This patch series walks through all drivers and makes them use either fully devres, or no devres. I am aware that there are DSA drivers that are only known to be tested with a single DSA master, so some patches are probably overkill for them. But code is copy-pasted from so many sources without fully understanding the differences, that I think it's better to not leave an in-tree source of inspiration that may lead to subtle breakage if not adapted properly. ==================== Link: https://lore.kernel.org/r/20220207161553.579933-1-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
1335648f0b
|
@ -621,7 +621,7 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)
|
||||||
get_device(&priv->master_mii_bus->dev);
|
get_device(&priv->master_mii_bus->dev);
|
||||||
priv->master_mii_dn = dn;
|
priv->master_mii_dn = dn;
|
||||||
|
|
||||||
priv->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
|
priv->slave_mii_bus = mdiobus_alloc();
|
||||||
if (!priv->slave_mii_bus) {
|
if (!priv->slave_mii_bus) {
|
||||||
of_node_put(dn);
|
of_node_put(dn);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -681,8 +681,10 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mdiobus_register(priv->slave_mii_bus);
|
err = mdiobus_register(priv->slave_mii_bus);
|
||||||
if (err && dn)
|
if (err && dn) {
|
||||||
|
mdiobus_free(priv->slave_mii_bus);
|
||||||
of_node_put(dn);
|
of_node_put(dn);
|
||||||
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -690,6 +692,7 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)
|
||||||
static void bcm_sf2_mdio_unregister(struct bcm_sf2_priv *priv)
|
static void bcm_sf2_mdio_unregister(struct bcm_sf2_priv *priv)
|
||||||
{
|
{
|
||||||
mdiobus_unregister(priv->slave_mii_bus);
|
mdiobus_unregister(priv->slave_mii_bus);
|
||||||
|
mdiobus_free(priv->slave_mii_bus);
|
||||||
of_node_put(priv->master_mii_dn);
|
of_node_put(priv->master_mii_dn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -498,8 +498,9 @@ static int gswip_mdio_rd(struct mii_bus *bus, int addr, int reg)
|
||||||
static int gswip_mdio(struct gswip_priv *priv, struct device_node *mdio_np)
|
static int gswip_mdio(struct gswip_priv *priv, struct device_node *mdio_np)
|
||||||
{
|
{
|
||||||
struct dsa_switch *ds = priv->ds;
|
struct dsa_switch *ds = priv->ds;
|
||||||
|
int err;
|
||||||
|
|
||||||
ds->slave_mii_bus = devm_mdiobus_alloc(priv->dev);
|
ds->slave_mii_bus = mdiobus_alloc();
|
||||||
if (!ds->slave_mii_bus)
|
if (!ds->slave_mii_bus)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -512,7 +513,11 @@ static int gswip_mdio(struct gswip_priv *priv, struct device_node *mdio_np)
|
||||||
ds->slave_mii_bus->parent = priv->dev;
|
ds->slave_mii_bus->parent = priv->dev;
|
||||||
ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
|
ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask;
|
||||||
|
|
||||||
return of_mdiobus_register(ds->slave_mii_bus, mdio_np);
|
err = of_mdiobus_register(ds->slave_mii_bus, mdio_np);
|
||||||
|
if (err)
|
||||||
|
mdiobus_free(ds->slave_mii_bus);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gswip_pce_table_entry_read(struct gswip_priv *priv,
|
static int gswip_pce_table_entry_read(struct gswip_priv *priv,
|
||||||
|
@ -2145,8 +2150,10 @@ disable_switch:
|
||||||
gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB);
|
gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB);
|
||||||
dsa_unregister_switch(priv->ds);
|
dsa_unregister_switch(priv->ds);
|
||||||
mdio_bus:
|
mdio_bus:
|
||||||
if (mdio_np)
|
if (mdio_np) {
|
||||||
mdiobus_unregister(priv->ds->slave_mii_bus);
|
mdiobus_unregister(priv->ds->slave_mii_bus);
|
||||||
|
mdiobus_free(priv->ds->slave_mii_bus);
|
||||||
|
}
|
||||||
put_mdio_node:
|
put_mdio_node:
|
||||||
of_node_put(mdio_np);
|
of_node_put(mdio_np);
|
||||||
for (i = 0; i < priv->num_gphy_fw; i++)
|
for (i = 0; i < priv->num_gphy_fw; i++)
|
||||||
|
@ -2169,6 +2176,7 @@ static int gswip_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
if (priv->ds->slave_mii_bus) {
|
if (priv->ds->slave_mii_bus) {
|
||||||
mdiobus_unregister(priv->ds->slave_mii_bus);
|
mdiobus_unregister(priv->ds->slave_mii_bus);
|
||||||
|
mdiobus_free(priv->ds->slave_mii_bus);
|
||||||
of_node_put(priv->ds->slave_mii_bus->dev.of_node);
|
of_node_put(priv->ds->slave_mii_bus->dev.of_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2074,7 +2074,7 @@ mt7530_setup_mdio(struct mt7530_priv *priv)
|
||||||
if (priv->irq)
|
if (priv->irq)
|
||||||
mt7530_setup_mdio_irq(priv);
|
mt7530_setup_mdio_irq(priv);
|
||||||
|
|
||||||
ret = mdiobus_register(bus);
|
ret = devm_mdiobus_register(dev, bus);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to register MDIO bus: %d\n", ret);
|
dev_err(dev, "failed to register MDIO bus: %d\n", ret);
|
||||||
if (priv->irq)
|
if (priv->irq)
|
||||||
|
|
|
@ -3399,7 +3399,7 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
|
bus = mdiobus_alloc_size(sizeof(*mdio_bus));
|
||||||
if (!bus)
|
if (!bus)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -3424,14 +3424,14 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
|
||||||
if (!external) {
|
if (!external) {
|
||||||
err = mv88e6xxx_g2_irq_mdio_setup(chip, bus);
|
err = mv88e6xxx_g2_irq_mdio_setup(chip, bus);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = of_mdiobus_register(bus, np);
|
err = of_mdiobus_register(bus, np);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
|
dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
|
||||||
mv88e6xxx_g2_irq_mdio_free(chip, bus);
|
mv88e6xxx_g2_irq_mdio_free(chip, bus);
|
||||||
return err;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (external)
|
if (external)
|
||||||
|
@ -3440,6 +3440,10 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
|
||||||
list_add(&mdio_bus->list, &chip->mdios);
|
list_add(&mdio_bus->list, &chip->mdios);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
mdiobus_free(bus);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
|
static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
|
||||||
|
@ -3455,6 +3459,7 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
|
||||||
mv88e6xxx_g2_irq_mdio_free(chip, bus);
|
mv88e6xxx_g2_irq_mdio_free(chip, bus);
|
||||||
|
|
||||||
mdiobus_unregister(bus);
|
mdiobus_unregister(bus);
|
||||||
|
mdiobus_free(bus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1061,7 +1061,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
|
||||||
return PTR_ERR(hw);
|
return PTR_ERR(hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
|
bus = mdiobus_alloc_size(sizeof(*mdio_priv));
|
||||||
if (!bus)
|
if (!bus)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
@ -1081,6 +1081,7 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
|
||||||
rc = mdiobus_register(bus);
|
rc = mdiobus_register(bus);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(dev, "failed to register MDIO bus\n");
|
dev_err(dev, "failed to register MDIO bus\n");
|
||||||
|
mdiobus_free(bus);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1132,6 +1133,7 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
|
||||||
lynx_pcs_destroy(phylink_pcs);
|
lynx_pcs_destroy(phylink_pcs);
|
||||||
}
|
}
|
||||||
mdiobus_unregister(felix->imdio);
|
mdiobus_unregister(felix->imdio);
|
||||||
|
mdiobus_free(felix->imdio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
|
static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
|
||||||
|
|
|
@ -1029,7 +1029,7 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Needed in order to initialize the bus mutex lock */
|
/* Needed in order to initialize the bus mutex lock */
|
||||||
rc = of_mdiobus_register(bus, NULL);
|
rc = devm_of_mdiobus_register(dev, bus, NULL);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
dev_err(dev, "failed to register MDIO bus\n");
|
dev_err(dev, "failed to register MDIO bus\n");
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -1083,7 +1083,8 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
|
||||||
mdio_device_free(mdio_device);
|
mdio_device_free(mdio_device);
|
||||||
lynx_pcs_destroy(phylink_pcs);
|
lynx_pcs_destroy(phylink_pcs);
|
||||||
}
|
}
|
||||||
mdiobus_unregister(felix->imdio);
|
|
||||||
|
/* mdiobus_unregister and mdiobus_free handled by devres */
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct felix_info seville_info_vsc9953 = {
|
static const struct felix_info seville_info_vsc9953 = {
|
||||||
|
|
|
@ -378,7 +378,7 @@ static int ar9331_sw_mbus_init(struct ar9331_sw_priv *priv)
|
||||||
if (!mnp)
|
if (!mnp)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
ret = of_mdiobus_register(mbus, mnp);
|
ret = devm_of_mdiobus_register(dev, mbus, mnp);
|
||||||
of_node_put(mnp);
|
of_node_put(mnp);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1091,7 +1091,6 @@ static void ar9331_sw_remove(struct mdio_device *mdiodev)
|
||||||
}
|
}
|
||||||
|
|
||||||
irq_domain_remove(priv->irqdomain);
|
irq_domain_remove(priv->irqdomain);
|
||||||
mdiobus_unregister(priv->mbus);
|
|
||||||
dsa_unregister_switch(&priv->ds);
|
dsa_unregister_switch(&priv->ds);
|
||||||
|
|
||||||
reset_control_assert(priv->sw_reset);
|
reset_control_assert(priv->sw_reset);
|
||||||
|
|
Loading…
Reference in New Issue