Commit 3ed018fb authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-pcs-add-helpers-to-xpcs-and-lynx-to-manage-mdiodev'

Russell King says:

====================
net: pcs: add helpers to xpcs and lynx to manage mdiodev

This morning, we have had two instances where the destruction of the
MDIO device associated with XPCS and Lynx has been wrong. Rather than
allowing this pattern of errors to continue, let's make it easier for
driver authors to get this right by adding a helper.

The changes are essentially:

1. Add two new mdio device helpers to manage the underlying struct
   device reference count. Note that the existing mdio_device_free()
   doesn't actually free anything, it merely puts the reference count.

2. Make the existing _create() and _destroy() PCS driver methods
   increment and decrement this refcount using these helpers. This
   results in no overall change, although drivers may hang on to
   the mdio device for a few cycles longer.

3. Add _create_mdiodev() which creates the mdio device before calling
   the existing _create() method. Once the _create() method has
   returned, we put the reference count on the mdio device.

   If _create() was successful, then the reference count taken there
   will "hold" the mdio device for the lifetime of the PCS (in other
   words, until _destroy() is called.) However, if _create() failed,
   then dropping the refcount at this point will free the mdio device.

   This is the exact behaviour we desire.

4. Convert users that create a mdio device and then call the PCS's
   _create() method over to the new _create_mdiodev() method, and
   simplify the cleanup.

We also have DPAA2 and fmem_memac that look up their PCS rather than
creating it. These could also drop their reference count on the MDIO
device immediately after calling lynx_pcs_create(), which would then
mean we wouldn't need lynx_get_mdio_device() and the associated
complexity to put the device in dpaa2_pcs_destroy() and pcs_put().
Note that DPAA2 bypasses the mdio device's abstractions by calling
put_device() directly.
====================

Link: https://lore.kernel.org/r/ZHCGZ8IgAAwr8bla@shell.armlinux.org.ukSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 75455b90 b7d5d043
...@@ -1021,7 +1021,6 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) ...@@ -1021,7 +1021,6 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
for (port = 0; port < felix->info->num_ports; port++) { for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
struct phylink_pcs *phylink_pcs; struct phylink_pcs *phylink_pcs;
struct mdio_device *mdio_device;
if (dsa_is_unused_port(felix->ds, port)) if (dsa_is_unused_port(felix->ds, port))
continue; continue;
...@@ -1029,16 +1028,10 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot) ...@@ -1029,16 +1028,10 @@ static int vsc9959_mdio_bus_alloc(struct ocelot *ocelot)
if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL) if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)
continue; continue;
mdio_device = mdio_device_create(felix->imdio, port); phylink_pcs = lynx_pcs_create_mdiodev(felix->imdio, port);
if (IS_ERR(mdio_device)) if (IS_ERR(phylink_pcs))
continue; continue;
phylink_pcs = lynx_pcs_create(mdio_device);
if (!phylink_pcs) {
mdio_device_free(mdio_device);
continue;
}
felix->pcs[port] = phylink_pcs; felix->pcs[port] = phylink_pcs;
dev_info(dev, "Found PCS at internal MDIO address %d\n", port); dev_info(dev, "Found PCS at internal MDIO address %d\n", port);
...@@ -1054,14 +1047,9 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot) ...@@ -1054,14 +1047,9 @@ static void vsc9959_mdio_bus_free(struct ocelot *ocelot)
for (port = 0; port < ocelot->num_phys_ports; port++) { for (port = 0; port < ocelot->num_phys_ports; port++) {
struct phylink_pcs *phylink_pcs = felix->pcs[port]; struct phylink_pcs *phylink_pcs = felix->pcs[port];
struct mdio_device *mdio_device;
if (!phylink_pcs)
continue;
mdio_device = lynx_get_mdio_device(phylink_pcs); if (phylink_pcs)
mdio_device_free(mdio_device); lynx_pcs_destroy(phylink_pcs);
lynx_pcs_destroy(phylink_pcs);
} }
mdiobus_unregister(felix->imdio); mdiobus_unregister(felix->imdio);
mdiobus_free(felix->imdio); mdiobus_free(felix->imdio);
......
...@@ -912,7 +912,6 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot) ...@@ -912,7 +912,6 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
for (port = 0; port < felix->info->num_ports; port++) { for (port = 0; port < felix->info->num_ports; port++) {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
struct phylink_pcs *phylink_pcs; struct phylink_pcs *phylink_pcs;
struct mdio_device *mdio_device;
int addr = port + 4; int addr = port + 4;
if (dsa_is_unused_port(felix->ds, port)) if (dsa_is_unused_port(felix->ds, port))
...@@ -921,16 +920,10 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot) ...@@ -921,16 +920,10 @@ static int vsc9953_mdio_bus_alloc(struct ocelot *ocelot)
if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL) if (ocelot_port->phy_mode == PHY_INTERFACE_MODE_INTERNAL)
continue; continue;
mdio_device = mdio_device_create(felix->imdio, addr); phylink_pcs = lynx_pcs_create_mdiodev(felix->imdio, addr);
if (IS_ERR(mdio_device)) if (IS_ERR(phylink_pcs))
continue; continue;
phylink_pcs = lynx_pcs_create(mdio_device);
if (!phylink_pcs) {
mdio_device_free(mdio_device);
continue;
}
felix->pcs[port] = phylink_pcs; felix->pcs[port] = phylink_pcs;
dev_info(dev, "Found PCS at internal MDIO address %d\n", addr); dev_info(dev, "Found PCS at internal MDIO address %d\n", addr);
...@@ -946,14 +939,9 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot) ...@@ -946,14 +939,9 @@ static void vsc9953_mdio_bus_free(struct ocelot *ocelot)
for (port = 0; port < ocelot->num_phys_ports; port++) { for (port = 0; port < ocelot->num_phys_ports; port++) {
struct phylink_pcs *phylink_pcs = felix->pcs[port]; struct phylink_pcs *phylink_pcs = felix->pcs[port];
struct mdio_device *mdio_device;
if (!phylink_pcs)
continue;
mdio_device = lynx_get_mdio_device(phylink_pcs); if (phylink_pcs)
mdio_device_free(mdio_device); lynx_pcs_destroy(phylink_pcs);
lynx_pcs_destroy(phylink_pcs);
} }
/* mdiobus_unregister and mdiobus_free handled by devres */ /* mdiobus_unregister and mdiobus_free handled by devres */
......
...@@ -863,7 +863,6 @@ static int enetc_imdio_create(struct enetc_pf *pf) ...@@ -863,7 +863,6 @@ static int enetc_imdio_create(struct enetc_pf *pf)
struct device *dev = &pf->si->pdev->dev; struct device *dev = &pf->si->pdev->dev;
struct enetc_mdio_priv *mdio_priv; struct enetc_mdio_priv *mdio_priv;
struct phylink_pcs *phylink_pcs; struct phylink_pcs *phylink_pcs;
struct mdio_device *mdio_device;
struct mii_bus *bus; struct mii_bus *bus;
int err; int err;
...@@ -889,17 +888,9 @@ static int enetc_imdio_create(struct enetc_pf *pf) ...@@ -889,17 +888,9 @@ static int enetc_imdio_create(struct enetc_pf *pf)
goto free_mdio_bus; goto free_mdio_bus;
} }
mdio_device = mdio_device_create(bus, 0); phylink_pcs = lynx_pcs_create_mdiodev(bus, 0);
if (IS_ERR(mdio_device)) { if (IS_ERR(phylink_pcs)) {
err = PTR_ERR(mdio_device); err = PTR_ERR(phylink_pcs);
dev_err(dev, "cannot create mdio device (%d)\n", err);
goto unregister_mdiobus;
}
phylink_pcs = lynx_pcs_create(mdio_device);
if (!phylink_pcs) {
mdio_device_free(mdio_device);
err = -ENOMEM;
dev_err(dev, "cannot create lynx pcs (%d)\n", err); dev_err(dev, "cannot create lynx pcs (%d)\n", err);
goto unregister_mdiobus; goto unregister_mdiobus;
} }
...@@ -918,13 +909,8 @@ static int enetc_imdio_create(struct enetc_pf *pf) ...@@ -918,13 +909,8 @@ static int enetc_imdio_create(struct enetc_pf *pf)
static void enetc_imdio_remove(struct enetc_pf *pf) static void enetc_imdio_remove(struct enetc_pf *pf)
{ {
struct mdio_device *mdio_device; if (pf->pcs)
if (pf->pcs) {
mdio_device = lynx_get_mdio_device(pf->pcs);
mdio_device_free(mdio_device);
lynx_pcs_destroy(pf->pcs); lynx_pcs_destroy(pf->pcs);
}
if (pf->imdio) { if (pf->imdio) {
mdiobus_unregister(pf->imdio); mdiobus_unregister(pf->imdio);
mdiobus_free(pf->imdio); mdiobus_free(pf->imdio);
......
...@@ -491,7 +491,6 @@ int stmmac_mdio_reset(struct mii_bus *bus) ...@@ -491,7 +491,6 @@ int stmmac_mdio_reset(struct mii_bus *bus)
int stmmac_xpcs_setup(struct mii_bus *bus) int stmmac_xpcs_setup(struct mii_bus *bus)
{ {
struct net_device *ndev = bus->priv; struct net_device *ndev = bus->priv;
struct mdio_device *mdiodev;
struct stmmac_priv *priv; struct stmmac_priv *priv;
struct dw_xpcs *xpcs; struct dw_xpcs *xpcs;
int mode, addr; int mode, addr;
...@@ -501,16 +500,10 @@ int stmmac_xpcs_setup(struct mii_bus *bus) ...@@ -501,16 +500,10 @@ int stmmac_xpcs_setup(struct mii_bus *bus)
/* Try to probe the XPCS by scanning all addresses. */ /* Try to probe the XPCS by scanning all addresses. */
for (addr = 0; addr < PHY_MAX_ADDR; addr++) { for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
mdiodev = mdio_device_create(bus, addr); xpcs = xpcs_create_mdiodev(bus, addr, mode);
if (IS_ERR(mdiodev)) if (IS_ERR(xpcs))
continue; continue;
xpcs = xpcs_create(mdiodev, mode);
if (IS_ERR_OR_NULL(xpcs)) {
mdio_device_free(mdiodev);
continue;
}
priv->hw->xpcs = xpcs; priv->hw->xpcs = xpcs;
break; break;
} }
...@@ -669,10 +662,8 @@ int stmmac_mdio_unregister(struct net_device *ndev) ...@@ -669,10 +662,8 @@ int stmmac_mdio_unregister(struct net_device *ndev)
if (!priv->mii) if (!priv->mii)
return 0; return 0;
if (priv->hw->xpcs) { if (priv->hw->xpcs)
mdio_device_free(priv->hw->xpcs->mdiodev);
xpcs_destroy(priv->hw->xpcs); xpcs_destroy(priv->hw->xpcs);
}
mdiobus_unregister(priv->mii); mdiobus_unregister(priv->mii);
priv->mii->priv = NULL; priv->mii->priv = NULL;
......
...@@ -323,6 +323,7 @@ struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio) ...@@ -323,6 +323,7 @@ struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
if (!lynx) if (!lynx)
return NULL; return NULL;
mdio_device_get(mdio);
lynx->mdio = mdio; lynx->mdio = mdio;
lynx->pcs.ops = &lynx_pcs_phylink_ops; lynx->pcs.ops = &lynx_pcs_phylink_ops;
lynx->pcs.poll = true; lynx->pcs.poll = true;
...@@ -331,10 +332,40 @@ struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio) ...@@ -331,10 +332,40 @@ struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
} }
EXPORT_SYMBOL(lynx_pcs_create); EXPORT_SYMBOL(lynx_pcs_create);
struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr)
{
struct mdio_device *mdio;
struct phylink_pcs *pcs;
mdio = mdio_device_create(bus, addr);
if (IS_ERR(mdio))
return ERR_CAST(mdio);
pcs = lynx_pcs_create(mdio);
/* Convert failure to create the PCS to an error pointer, so this
* function has a consistent return value strategy.
*/
if (!pcs)
pcs = ERR_PTR(-ENOMEM);
/* lynx_create() has taken a refcount on the mdiodev if it was
* successful. If lynx_create() fails, this will free the mdio
* device here. In any case, we don't need to hold our reference
* anymore, and putting it here will allow mdio_device_put() in
* lynx_destroy() to automatically free the mdio device.
*/
mdio_device_put(mdio);
return pcs;
}
EXPORT_SYMBOL(lynx_pcs_create_mdiodev);
void lynx_pcs_destroy(struct phylink_pcs *pcs) void lynx_pcs_destroy(struct phylink_pcs *pcs)
{ {
struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
mdio_device_put(lynx->mdio);
kfree(lynx); kfree(lynx);
} }
EXPORT_SYMBOL(lynx_pcs_destroy); EXPORT_SYMBOL(lynx_pcs_destroy);
......
...@@ -1235,6 +1235,7 @@ struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, ...@@ -1235,6 +1235,7 @@ struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
if (!xpcs) if (!xpcs)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
mdio_device_get(mdiodev);
xpcs->mdiodev = mdiodev; xpcs->mdiodev = mdiodev;
xpcs_id = xpcs_get_id(xpcs); xpcs_id = xpcs_get_id(xpcs);
...@@ -1267,6 +1268,7 @@ struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, ...@@ -1267,6 +1268,7 @@ struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
ret = -ENODEV; ret = -ENODEV;
out: out:
mdio_device_put(mdiodev);
kfree(xpcs); kfree(xpcs);
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -1275,8 +1277,34 @@ EXPORT_SYMBOL_GPL(xpcs_create); ...@@ -1275,8 +1277,34 @@ EXPORT_SYMBOL_GPL(xpcs_create);
void xpcs_destroy(struct dw_xpcs *xpcs) void xpcs_destroy(struct dw_xpcs *xpcs)
{ {
if (xpcs)
mdio_device_put(xpcs->mdiodev);
kfree(xpcs); kfree(xpcs);
} }
EXPORT_SYMBOL_GPL(xpcs_destroy); EXPORT_SYMBOL_GPL(xpcs_destroy);
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
phy_interface_t interface)
{
struct mdio_device *mdiodev;
struct dw_xpcs *xpcs;
mdiodev = mdio_device_create(bus, addr);
if (IS_ERR(mdiodev))
return ERR_CAST(mdiodev);
xpcs = xpcs_create(mdiodev, interface);
/* xpcs_create() has taken a refcount on the mdiodev if it was
* successful. If xpcs_create() fails, this will free the mdio
* device here. In any case, we don't need to hold our reference
* anymore, and putting it here will allow mdio_device_put() in
* xpcs_destroy() to automatically free the mdio device.
*/
mdio_device_put(mdiodev);
return xpcs;
}
EXPORT_SYMBOL_GPL(xpcs_create_mdiodev);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -106,6 +106,16 @@ int mdio_driver_register(struct mdio_driver *drv); ...@@ -106,6 +106,16 @@ int mdio_driver_register(struct mdio_driver *drv);
void mdio_driver_unregister(struct mdio_driver *drv); void mdio_driver_unregister(struct mdio_driver *drv);
int mdio_device_bus_match(struct device *dev, struct device_driver *drv); int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
static inline void mdio_device_get(struct mdio_device *mdiodev)
{
get_device(&mdiodev->dev);
}
static inline void mdio_device_put(struct mdio_device *mdiodev)
{
mdio_device_free(mdiodev);
}
static inline bool mdio_phy_id_is_c45(int phy_id) static inline bool mdio_phy_id_is_c45(int phy_id)
{ {
return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK); return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs); struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs);
struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio); struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio);
struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr);
void lynx_pcs_destroy(struct phylink_pcs *pcs); void lynx_pcs_destroy(struct phylink_pcs *pcs);
......
...@@ -37,6 +37,8 @@ int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, ...@@ -37,6 +37,8 @@ int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
int enable); int enable);
struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
phy_interface_t interface); phy_interface_t interface);
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr,
phy_interface_t interface);
void xpcs_destroy(struct dw_xpcs *xpcs); void xpcs_destroy(struct dw_xpcs *xpcs);
#endif /* __LINUX_PCS_XPCS_H */ #endif /* __LINUX_PCS_XPCS_H */
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