Merge branch 'fixed-phy-phydev-leaks'

Johan Hovold says:

====================
net: fix fixed-link phydev leaks

This series fixes failures to deregister and free fixed-link phydevs
that have been registered using the of_phy_register_fixed_link()
interface.

All but two drivers currently fail to do this and this series fixes most
of them with the exception of a staging driver and the stmmac drivers
which will be fixed by follow-on patches.

Included are also a couple of fixes for related of-node leaks.

Note that all patches except the of_mdio one have been compile-tested
only.

Also note that the series is against net due to dependencies not yet in
net-next.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2016-11-29 23:17:04 -05:00
commit f2ebf2a6ca
18 changed files with 152 additions and 54 deletions

View File

@ -819,6 +819,8 @@ static int init_phy(struct net_device *dev)
if (!phydev) {
netdev_err(dev, "Could not find the PHY\n");
if (fixed_link)
of_phy_deregister_fixed_link(priv->device->of_node);
return -ENODEV;
}
@ -1545,10 +1547,15 @@ err_free_netdev:
static int altera_tse_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct altera_tse_private *priv = netdev_priv(ndev);
if (ndev->phydev)
if (ndev->phydev) {
phy_disconnect(ndev->phydev);
if (of_phy_is_fixed_link(priv->device->of_node))
of_phy_deregister_fixed_link(priv->device->of_node);
}
platform_set_drvdata(pdev, NULL);
altera_tse_mdio_destroy(ndev);
unregister_netdev(ndev);

View File

@ -1466,12 +1466,12 @@ static int nb8800_probe(struct platform_device *pdev)
ret = nb8800_hw_init(dev);
if (ret)
goto err_free_bus;
goto err_deregister_fixed_link;
if (ops && ops->init) {
ret = ops->init(dev);
if (ret)
goto err_free_bus;
goto err_deregister_fixed_link;
}
dev->netdev_ops = &nb8800_netdev_ops;
@ -1504,6 +1504,9 @@ static int nb8800_probe(struct platform_device *pdev)
err_free_dma:
nb8800_dma_free(dev);
err_deregister_fixed_link:
if (of_phy_is_fixed_link(pdev->dev.of_node))
of_phy_deregister_fixed_link(pdev->dev.of_node);
err_free_bus:
of_node_put(priv->phy_node);
mdiobus_unregister(bus);
@ -1521,6 +1524,8 @@ static int nb8800_remove(struct platform_device *pdev)
struct nb8800_priv *priv = netdev_priv(ndev);
unregister_netdev(ndev);
if (of_phy_is_fixed_link(pdev->dev.of_node))
of_phy_deregister_fixed_link(pdev->dev.of_node);
of_node_put(priv->phy_node);
mdiobus_unregister(priv->mii_bus);

View File

@ -1755,13 +1755,13 @@ static int bcm_sysport_probe(struct platform_device *pdev)
if (priv->irq0 <= 0 || priv->irq1 <= 0) {
dev_err(&pdev->dev, "invalid interrupts\n");
ret = -EINVAL;
goto err;
goto err_free_netdev;
}
priv->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(priv->base)) {
ret = PTR_ERR(priv->base);
goto err;
goto err_free_netdev;
}
priv->netdev = dev;
@ -1779,7 +1779,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
ret = of_phy_register_fixed_link(dn);
if (ret) {
dev_err(&pdev->dev, "failed to register fixed PHY\n");
goto err;
goto err_free_netdev;
}
priv->phy_dn = dn;
@ -1821,7 +1821,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
ret = register_netdev(dev);
if (ret) {
dev_err(&pdev->dev, "failed to register net_device\n");
goto err;
goto err_deregister_fixed_link;
}
priv->rev = topctrl_readl(priv, REV_CNTL) & REV_MASK;
@ -1832,7 +1832,11 @@ static int bcm_sysport_probe(struct platform_device *pdev)
priv->base, priv->irq0, priv->irq1, txq, rxq);
return 0;
err:
err_deregister_fixed_link:
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
err_free_netdev:
free_netdev(dev);
return ret;
}
@ -1840,11 +1844,14 @@ err:
static int bcm_sysport_remove(struct platform_device *pdev)
{
struct net_device *dev = dev_get_drvdata(&pdev->dev);
struct device_node *dn = pdev->dev.of_node;
/* Not much to do, ndo_close has been called
* and we use managed allocations
*/
unregister_netdev(dev);
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
free_netdev(dev);
dev_set_drvdata(&pdev->dev, NULL);

View File

@ -627,6 +627,7 @@ static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
int bcmgenet_mii_init(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
int ret;
ret = bcmgenet_mii_alloc(priv);
@ -640,6 +641,8 @@ int bcmgenet_mii_init(struct net_device *dev)
return 0;
out:
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
of_node_put(priv->phy_dn);
mdiobus_unregister(priv->mii_bus);
mdiobus_free(priv->mii_bus);
@ -649,7 +652,10 @@ out:
void bcmgenet_mii_exit(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct device_node *dn = priv->pdev->dev.of_node;
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
of_node_put(priv->phy_dn);
mdiobus_unregister(priv->mii_bus);
mdiobus_free(priv->mii_bus);

View File

@ -3475,6 +3475,8 @@ failed_regulator:
failed_clk_ipg:
fec_enet_clk_enable(ndev, false);
failed_clk:
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
failed_phy:
of_node_put(phy_node);
failed_ioremap:
@ -3488,6 +3490,7 @@ fec_drv_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *np = pdev->dev.of_node;
cancel_work_sync(&fep->tx_timeout_work);
fec_ptp_stop(pdev);
@ -3495,6 +3498,8 @@ fec_drv_remove(struct platform_device *pdev)
fec_enet_mii_remove(fep);
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(fep->phy_node);
free_netdev(ndev);

View File

@ -980,7 +980,7 @@ static int fs_enet_probe(struct platform_device *ofdev)
err = clk_prepare_enable(clk);
if (err) {
ret = err;
goto out_free_fpi;
goto out_deregister_fixed_link;
}
fpi->clk_per = clk;
}
@ -1061,6 +1061,9 @@ out_put:
of_node_put(fpi->phy_node);
if (fpi->clk_per)
clk_disable_unprepare(fpi->clk_per);
out_deregister_fixed_link:
if (of_phy_is_fixed_link(ofdev->dev.of_node))
of_phy_deregister_fixed_link(ofdev->dev.of_node);
out_free_fpi:
kfree(fpi);
return ret;
@ -1079,6 +1082,8 @@ static int fs_enet_remove(struct platform_device *ofdev)
of_node_put(fep->fpi->phy_node);
if (fep->fpi->clk_per)
clk_disable_unprepare(fep->fpi->clk_per);
if (of_phy_is_fixed_link(ofdev->dev.of_node))
of_phy_deregister_fixed_link(ofdev->dev.of_node);
free_netdev(ndev);
return 0;
}

View File

@ -1312,6 +1312,7 @@ static void gfar_init_addr_hash_table(struct gfar_private *priv)
*/
static int gfar_probe(struct platform_device *ofdev)
{
struct device_node *np = ofdev->dev.of_node;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
int err = 0, i;
@ -1462,6 +1463,8 @@ static int gfar_probe(struct platform_device *ofdev)
return 0;
register_fail:
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
unmap_group_regs(priv);
gfar_free_rx_queues(priv);
gfar_free_tx_queues(priv);
@ -1474,11 +1477,16 @@ register_fail:
static int gfar_remove(struct platform_device *ofdev)
{
struct gfar_private *priv = platform_get_drvdata(ofdev);
struct device_node *np = ofdev->dev.of_node;
of_node_put(priv->phy_node);
of_node_put(priv->tbi_node);
unregister_netdev(priv->ndev);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
unmap_group_regs(priv);
gfar_free_rx_queues(priv);
gfar_free_tx_queues(priv);

View File

@ -3868,9 +3868,8 @@ static int ucc_geth_probe(struct platform_device* ofdev)
dev = alloc_etherdev(sizeof(*ugeth));
if (dev == NULL) {
of_node_put(ug_info->tbi_node);
of_node_put(ug_info->phy_node);
return -ENOMEM;
err = -ENOMEM;
goto err_deregister_fixed_link;
}
ugeth = netdev_priv(dev);
@ -3907,10 +3906,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
if (netif_msg_probe(ugeth))
pr_err("%s: Cannot register net device, aborting\n",
dev->name);
free_netdev(dev);
of_node_put(ug_info->tbi_node);
of_node_put(ug_info->phy_node);
return err;
goto err_free_netdev;
}
mac_addr = of_get_mac_address(np);
@ -3923,16 +3919,29 @@ static int ucc_geth_probe(struct platform_device* ofdev)
ugeth->node = np;
return 0;
err_free_netdev:
free_netdev(dev);
err_deregister_fixed_link:
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(ug_info->tbi_node);
of_node_put(ug_info->phy_node);
return err;
}
static int ucc_geth_remove(struct platform_device* ofdev)
{
struct net_device *dev = platform_get_drvdata(ofdev);
struct ucc_geth_private *ugeth = netdev_priv(dev);
struct device_node *np = ofdev->dev.of_node;
unregister_netdev(dev);
free_netdev(dev);
ucc_geth_memclean(ugeth);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(ugeth->ug_info->tbi_node);
of_node_put(ugeth->ug_info->phy_node);

View File

@ -4191,6 +4191,8 @@ err_clk:
clk_disable_unprepare(pp->clk);
err_put_phy_node:
of_node_put(phy_node);
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
err_free_irq:
irq_dispose_mapping(dev->irq);
err_free_netdev:
@ -4202,6 +4204,7 @@ err_free_netdev:
static int mvneta_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct device_node *dn = pdev->dev.of_node;
struct mvneta_port *pp = netdev_priv(dev);
unregister_netdev(dev);
@ -4209,6 +4212,8 @@ static int mvneta_remove(struct platform_device *pdev)
clk_disable_unprepare(pp->clk);
free_percpu(pp->ports);
free_percpu(pp->stats);
if (of_phy_is_fixed_link(dn))
of_phy_deregister_fixed_link(dn);
irq_dispose_mapping(dev->irq);
of_node_put(pp->phy_node);
free_netdev(dev);

View File

@ -318,6 +318,8 @@ static int mtk_phy_connect(struct net_device *dev)
return 0;
err_phy:
if (of_phy_is_fixed_link(mac->of_node))
of_phy_deregister_fixed_link(mac->of_node);
of_node_put(np);
dev_err(eth->dev, "%s: invalid phy\n", __func__);
return -EINVAL;
@ -1923,6 +1925,8 @@ static void mtk_uninit(struct net_device *dev)
struct mtk_eth *eth = mac->hw;
phy_disconnect(dev->phydev);
if (of_phy_is_fixed_link(mac->of_node))
of_phy_deregister_fixed_link(mac->of_node);
mtk_irq_disable(eth, MTK_QDMA_INT_MASK, ~0);
mtk_irq_disable(eth, MTK_PDMA_INT_MASK, ~0);
}

View File

@ -1008,7 +1008,8 @@ static int ravb_phy_init(struct net_device *ndev)
of_node_put(pn);
if (!phydev) {
netdev_err(ndev, "failed to connect PHY\n");
return -ENOENT;
err = -ENOENT;
goto err_deregister_fixed_link;
}
/* This driver only support 10/100Mbit speeds on Gen3
@ -1020,8 +1021,7 @@ static int ravb_phy_init(struct net_device *ndev)
err = phy_set_max_speed(phydev, SPEED_100);
if (err) {
netdev_err(ndev, "failed to limit PHY to 100Mbit/s\n");
phy_disconnect(phydev);
return err;
goto err_phy_disconnect;
}
netdev_info(ndev, "limited PHY to 100Mbit/s\n");
@ -1033,6 +1033,14 @@ static int ravb_phy_init(struct net_device *ndev)
phy_attached_info(phydev);
return 0;
err_phy_disconnect:
phy_disconnect(phydev);
err_deregister_fixed_link:
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
return err;
}
/* PHY control start function */
@ -1634,6 +1642,7 @@ static void ravb_set_rx_mode(struct net_device *ndev)
/* Device close function for Ethernet AVB */
static int ravb_close(struct net_device *ndev)
{
struct device_node *np = ndev->dev.parent->of_node;
struct ravb_private *priv = netdev_priv(ndev);
struct ravb_tstamp_skb *ts_skb, *ts_skb2;
@ -1663,6 +1672,8 @@ static int ravb_close(struct net_device *ndev)
if (ndev->phydev) {
phy_stop(ndev->phydev);
phy_disconnect(ndev->phydev);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
}
if (priv->chip_id != RCAR_GEN2) {

View File

@ -2881,7 +2881,7 @@ static int dwceqos_probe(struct platform_device *pdev)
ret = of_get_phy_mode(lp->pdev->dev.of_node);
if (ret < 0) {
dev_err(&lp->pdev->dev, "error in getting phy i/f\n");
goto err_out_clk_dis_phy;
goto err_out_deregister_fixed_link;
}
lp->phy_interface = ret;
@ -2889,14 +2889,14 @@ static int dwceqos_probe(struct platform_device *pdev)
ret = dwceqos_mii_init(lp);
if (ret) {
dev_err(&lp->pdev->dev, "error in dwceqos_mii_init\n");
goto err_out_clk_dis_phy;
goto err_out_deregister_fixed_link;
}
ret = dwceqos_mii_probe(ndev);
if (ret != 0) {
netdev_err(ndev, "mii_probe fail.\n");
ret = -ENXIO;
goto err_out_clk_dis_phy;
goto err_out_deregister_fixed_link;
}
dwceqos_set_umac_addr(lp, lp->ndev->dev_addr, 0);
@ -2914,7 +2914,7 @@ static int dwceqos_probe(struct platform_device *pdev)
if (ret) {
dev_err(&lp->pdev->dev, "Unable to retrieve DT, error %d\n",
ret);
goto err_out_clk_dis_phy;
goto err_out_deregister_fixed_link;
}
dev_info(&lp->pdev->dev, "pdev->id %d, baseaddr 0x%08lx, irq %d\n",
pdev->id, ndev->base_addr, ndev->irq);
@ -2924,7 +2924,7 @@ static int dwceqos_probe(struct platform_device *pdev)
if (ret) {
dev_err(&lp->pdev->dev, "Unable to request IRQ %d, error %d\n",
ndev->irq, ret);
goto err_out_clk_dis_phy;
goto err_out_deregister_fixed_link;
}
if (netif_msg_probe(lp))
@ -2935,11 +2935,14 @@ static int dwceqos_probe(struct platform_device *pdev)
ret = register_netdev(ndev);
if (ret) {
dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
goto err_out_clk_dis_phy;
goto err_out_deregister_fixed_link;
}
return 0;
err_out_deregister_fixed_link:
if (of_phy_is_fixed_link(pdev->dev.of_node))
of_phy_deregister_fixed_link(pdev->dev.of_node);
err_out_clk_dis_phy:
clk_disable_unprepare(lp->phy_ref_clk);
err_out_clk_dis_aper:
@ -2959,8 +2962,11 @@ static int dwceqos_remove(struct platform_device *pdev)
if (ndev) {
lp = netdev_priv(ndev);
if (ndev->phydev)
if (ndev->phydev) {
phy_disconnect(ndev->phydev);
if (of_phy_is_fixed_link(pdev->dev.of_node))
of_phy_deregister_fixed_link(pdev->dev.of_node);
}
mdiobus_unregister(lp->mii_bus);
mdiobus_free(lp->mii_bus);

View File

@ -2459,20 +2459,8 @@ static void cpsw_remove_dt(struct platform_device *pdev)
if (strcmp(slave_node->name, "slave"))
continue;
if (of_phy_is_fixed_link(slave_node)) {
struct phy_device *phydev;
phydev = of_phy_find_device(slave_node);
if (phydev) {
fixed_phy_unregister(phydev);
/* Put references taken by
* of_phy_find_device() and
* of_phy_register_fixed_link().
*/
phy_device_free(phydev);
phy_device_free(phydev);
}
}
if (of_phy_is_fixed_link(slave_node))
of_phy_deregister_fixed_link(slave_node);
of_node_put(slave_data->phy_node);

View File

@ -1767,6 +1767,7 @@ static int davinci_emac_try_get_mac(struct platform_device *pdev,
*/
static int davinci_emac_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int rc = 0;
struct resource *res, *res_ctrl;
struct net_device *ndev;
@ -1805,7 +1806,7 @@ static int davinci_emac_probe(struct platform_device *pdev)
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
rc = -ENODEV;
goto no_pdata;
goto err_free_netdev;
}
/* MAC addr and PHY mask , RMII enable info from platform_data */
@ -1941,6 +1942,10 @@ no_cpdma_chan:
cpdma_chan_destroy(priv->rxchan);
cpdma_ctlr_destroy(priv->dma);
no_pdata:
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
of_node_put(priv->phy_node);
err_free_netdev:
free_netdev(ndev);
return rc;
}
@ -1956,6 +1961,7 @@ static int davinci_emac_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct emac_priv *priv = netdev_priv(ndev);
struct device_node *np = pdev->dev.of_node;
dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n");
@ -1968,6 +1974,8 @@ static int davinci_emac_remove(struct platform_device *pdev)
unregister_netdev(ndev);
of_node_put(priv->phy_node);
pm_runtime_disable(&pdev->dev);
if (of_phy_is_fixed_link(np))
of_phy_deregister_fixed_link(np);
free_netdev(ndev);
return 0;

View File

@ -490,3 +490,18 @@ int of_phy_register_fixed_link(struct device_node *np)
return -ENODEV;
}
EXPORT_SYMBOL(of_phy_register_fixed_link);
void of_phy_deregister_fixed_link(struct device_node *np)
{
struct phy_device *phydev;
phydev = of_phy_find_device(np);
if (!phydev)
return;
fixed_phy_unregister(phydev);
put_device(&phydev->mdio.dev); /* of_phy_find_device() */
phy_device_free(phydev); /* fixed_phy_register() */
}
EXPORT_SYMBOL(of_phy_deregister_fixed_link);

View File

@ -29,6 +29,7 @@ struct phy_device *of_phy_attach(struct net_device *dev,
extern struct mii_bus *of_mdio_find_bus(struct device_node *mdio_np);
extern int of_mdio_parse_addr(struct device *dev, const struct device_node *np);
extern int of_phy_register_fixed_link(struct device_node *np);
extern void of_phy_deregister_fixed_link(struct device_node *np);
extern bool of_phy_is_fixed_link(struct device_node *np);
#else /* CONFIG_OF */
@ -83,6 +84,9 @@ static inline int of_phy_register_fixed_link(struct device_node *np)
{
return -ENOSYS;
}
static inline void of_phy_deregister_fixed_link(struct device_node *np)
{
}
static inline bool of_phy_is_fixed_link(struct device_node *np)
{
return false;

View File

@ -506,16 +506,8 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
void dsa_cpu_dsa_destroy(struct device_node *port_dn)
{
struct phy_device *phydev;
if (of_phy_is_fixed_link(port_dn)) {
phydev = of_phy_find_device(port_dn);
if (phydev) {
fixed_phy_unregister(phydev);
put_device(&phydev->mdio.dev);
phy_device_free(phydev);
}
}
if (of_phy_is_fixed_link(port_dn))
of_phy_deregister_fixed_link(port_dn);
}
static void dsa_switch_destroy(struct dsa_switch *ds)

View File

@ -1125,7 +1125,7 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
p->phy_interface = mode;
phy_dn = of_parse_phandle(port_dn, "phy-handle", 0);
if (of_phy_is_fixed_link(port_dn)) {
if (!phy_dn && of_phy_is_fixed_link(port_dn)) {
/* In the case of a fixed PHY, the DT node associated
* to the fixed PHY is the Port DT node
*/
@ -1135,7 +1135,7 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
return ret;
}
phy_is_fixed = true;
phy_dn = port_dn;
phy_dn = of_node_get(port_dn);
}
if (ds->ops->get_phy_flags)
@ -1154,6 +1154,7 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
ret = dsa_slave_phy_connect(p, slave_dev, phy_id);
if (ret) {
netdev_err(slave_dev, "failed to connect to phy%d: %d\n", phy_id, ret);
of_node_put(phy_dn);
return ret;
}
} else {
@ -1162,6 +1163,8 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
phy_flags,
p->phy_interface);
}
of_node_put(phy_dn);
}
if (p->phy && phy_is_fixed)
@ -1174,6 +1177,8 @@ static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
ret = dsa_slave_phy_connect(p, slave_dev, p->port);
if (ret) {
netdev_err(slave_dev, "failed to connect to port %d: %d\n", p->port, ret);
if (phy_is_fixed)
of_phy_deregister_fixed_link(port_dn);
return ret;
}
}
@ -1289,10 +1294,18 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
void dsa_slave_destroy(struct net_device *slave_dev)
{
struct dsa_slave_priv *p = netdev_priv(slave_dev);
struct dsa_switch *ds = p->parent;
struct device_node *port_dn;
port_dn = ds->ports[p->port].dn;
netif_carrier_off(slave_dev);
if (p->phy)
if (p->phy) {
phy_disconnect(p->phy);
if (of_phy_is_fixed_link(port_dn))
of_phy_deregister_fixed_link(port_dn);
}
unregister_netdev(slave_dev);
free_netdev(slave_dev);
}