@@ -1570,7 +1570,7 @@ static int owl_emac_probe(struct platform_device *pdev)
ret = devm_register_netdev(dev, netdev);
if (ret) {
netif_napi_del(&priv->napi);
- phy_disconnect(netdev->phydev);
+ phy_disconnect_rtnl(netdev->phydev);
return ret;
}
@@ -1582,7 +1582,7 @@ static void owl_emac_remove(struct platform_device *pdev)
struct owl_emac_priv *priv = platform_get_drvdata(pdev);
netif_napi_del(&priv->napi);
- phy_disconnect(priv->netdev->phydev);
+ phy_disconnect_rtnl(priv->netdev->phydev);
cancel_work_sync(&priv->mac_reset_task);
}
@@ -1224,7 +1224,7 @@ static struct notifier_block adin1110_netdevice_nb = {
static void adin1110_disconnect_phy(void *data)
{
- phy_disconnect(data);
+ phy_disconnect_rtnl(data);
}
static int adin1110_port_set_forwarding_state(struct adin1110_port_priv *port_priv)
@@ -3294,7 +3294,7 @@ static void et131x_pci_remove(struct pci_dev *pdev)
unregister_netdev(netdev);
netif_napi_del(&adapter->napi);
- phy_disconnect(netdev->phydev);
+ phy_disconnect_rtnl(netdev->phydev);
mdiobus_unregister(adapter->mii_bus);
mdiobus_free(adapter->mii_bus);
@@ -4028,7 +4028,7 @@ static int et131x_pci_setup(struct pci_dev *pdev,
return rc;
err_phy_disconnect:
- phy_disconnect(netdev->phydev);
+ phy_disconnect_rtnl(netdev->phydev);
err_mdio_unregister:
mdiobus_unregister(adapter->mii_bus);
err_mdio_free:
@@ -805,7 +805,7 @@ static void xgbe_phy_free_phy_device(struct xgbe_prv_data *pdata)
struct xgbe_phy_data *phy_data = pdata->phy_data;
if (phy_data->phydev) {
- phy_detach(phy_data->phydev);
+ phy_detach_rtnl(phy_data->phydev);
phy_device_remove(phy_data->phydev);
phy_device_free(phy_data->phydev);
phy_data->phydev = NULL;
@@ -87,7 +87,7 @@ void xge_mdio_remove(struct net_device *ndev)
struct mii_bus *mdio_bus = pdata->mdio_bus;
if (ndev->phydev)
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
if (mdio_bus->state == MDIOBUS_REGISTERED)
mdiobus_unregister(mdio_bus);
@@ -973,7 +973,7 @@ void xgene_enet_phy_disconnect(struct xgene_enet_pdata *pdata)
struct net_device *ndev = pdata->ndev;
if (ndev->phydev)
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
}
void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata)
@@ -981,7 +981,7 @@ void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata)
struct net_device *ndev = pdata->ndev;
if (ndev->phydev)
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
mdiobus_unregister(pdata->mdio_bus);
mdiobus_free(pdata->mdio_bus);
@@ -1000,7 +1000,7 @@ int arc_emac_probe(struct net_device *ndev, int interface)
out_netif_api:
netif_napi_del(&priv->napi);
- phy_disconnect(phydev);
+ phy_disconnect_rtnl(phydev);
out_mdio:
arc_mdio_remove(priv);
out_clken:
@@ -1017,7 +1017,7 @@ void arc_emac_remove(struct net_device *ndev)
{
struct arc_emac_priv *priv = netdev_priv(ndev);
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
arc_mdio_remove(priv);
unregister_netdev(ndev);
netif_napi_del(&priv->napi);
@@ -1097,7 +1097,7 @@ static int ax88796c_probe(struct spi_device *spi)
return 0;
err_phy_dis:
- phy_disconnect(ax_local->phydev);
+ phy_disconnect_rtnl(ax_local->phydev);
err:
return ret;
}
@@ -1107,7 +1107,7 @@ static void ax88796c_remove(struct spi_device *spi)
struct ax88796c_device *ax_local = dev_get_drvdata(&spi->dev);
struct net_device *ndev = ax_local->ndev;
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
netif_info(ax_local, probe, ndev, "removing network device %s %s\n",
dev_driver_string(&spi->dev),
@@ -2314,7 +2314,7 @@ static void b44_unregister_phy_one(struct b44 *bp)
struct net_device *dev = bp->dev;
struct mii_bus *mii_bus = bp->mii_bus;
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
mdiobus_unregister(mii_bus);
mdiobus_free(mii_bus);
}
@@ -1548,15 +1548,15 @@ int bgmac_enet_probe(struct bgmac *bgmac)
err = register_netdev(bgmac->net_dev);
if (err) {
dev_err(bgmac->dev, "Cannot register net device\n");
- goto err_phy_disconnect;
+ goto err_phy_disconnect_rtnl;
}
netif_carrier_off(net_dev);
return 0;
-err_phy_disconnect:
- phy_disconnect(net_dev->phydev);
+err_phy_disconnect_rtnl:
+ phy_disconnect_rtnl(net_dev->phydev);
err_dma_free:
bgmac_dma_free(bgmac);
err_out:
@@ -1568,7 +1568,7 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe);
void bgmac_enet_remove(struct bgmac *bgmac)
{
unregister_netdev(bgmac->net_dev);
- phy_disconnect(bgmac->net_dev->phydev);
+ phy_disconnect_rtnl(bgmac->net_dev->phydev);
netif_napi_del(&bgmac->napi);
bgmac_dma_free(bgmac);
}
@@ -4256,8 +4256,11 @@ static int bcmgenet_resume(struct device *d)
return 0;
out_clk_disable:
- if (priv->internal_phy)
+ if (priv->internal_phy) {
+ rtnl_lock();
bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
+ rtnl_unlock();
+ }
clk_disable_unprepare(priv->clk);
return ret;
}
@@ -4325,11 +4328,13 @@ static int bcmgenet_suspend_noirq(struct device *d)
if (!netif_running(dev))
return 0;
+ rtnl_lock();
/* Prepare the device for Wake-on-LAN and switch to the slow clock */
if (device_may_wakeup(d) && priv->wolopts)
ret = bcmgenet_power_down(priv, GENET_POWER_WOL_MAGIC);
else if (priv->internal_phy)
ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE);
+ rtnl_unlock();
/* Let the framework handle resumption and leave the clocks on */
if (ret)
@@ -397,7 +397,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
*/
ret = bcmgenet_mii_config(dev, true);
if (ret) {
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
return ret;
}
@@ -2116,7 +2116,7 @@ static int tg3_phy_init(struct tg3 *tp)
phy_support_asym_pause(phydev);
break;
default:
- phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
+ phy_disconnect_rtnl(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
return -EINVAL;
}
@@ -2161,7 +2161,7 @@ static void tg3_phy_stop(struct tg3 *tp)
static void tg3_phy_fini(struct tg3 *tp)
{
if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) {
- phy_disconnect(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
+ phy_disconnect_rtnl(mdiobus_get_phy(tp->mdio_bus, tp->phy_addr));
tp->phy_flags &= ~TG3_PHYFLG_IS_CONNECTED;
}
}
@@ -1183,7 +1183,7 @@ static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
(lmac->lmac_type != BGX_MODE_XLAUI) &&
(lmac->lmac_type != BGX_MODE_40G_KR) &&
(lmac->lmac_type != BGX_MODE_10G_KR) && lmac->phydev)
- phy_disconnect(lmac->phydev);
+ phy_disconnect_rtnl(lmac->phydev);
lmac->phydev = NULL;
}
@@ -393,7 +393,7 @@ static int gmac_setup_phy(struct net_device *netdev)
break;
default:
netdev_err(netdev, "Unsupported MII interface\n");
- phy_disconnect(phy);
+ phy_disconnect_rtnl(phy);
netdev->phydev = NULL;
return -EINVAL;
}
@@ -2344,7 +2344,7 @@ static irqreturn_t gemini_port_irq(int irq, void *data)
static void gemini_port_remove(struct gemini_ethernet_port *port)
{
if (port->netdev) {
- phy_disconnect(port->netdev->phydev);
+ phy_disconnect_rtnl(port->netdev->phydev);
unregister_netdev(port->netdev);
}
clk_disable_unprepare(port->pclk);
@@ -1215,7 +1215,7 @@ static int dm9051_probe(struct spi_device *spi)
ret = devm_register_netdev(dev, ndev);
if (ret) {
- phy_disconnect(db->phydev);
+ phy_disconnect_rtnl(db->phydev);
return dev_err_probe(dev, ret, "device register failed");
}
@@ -1228,7 +1228,7 @@ static void dm9051_drv_remove(struct spi_device *spi)
struct net_device *ndev = dev_get_drvdata(dev);
struct board_info *db = to_dm9051_board(ndev);
- phy_disconnect(db->phydev);
+ phy_disconnect_rtnl(db->phydev);
}
static const struct of_device_id dm9051_match_table[] = {
@@ -852,7 +852,7 @@ static void dnet_remove(struct platform_device *pdev)
if (dev) {
bp = netdev_priv(dev);
if (dev->phydev)
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
mdiobus_unregister(bp->mii_bus);
mdiobus_free(bp->mii_bus);
unregister_netdev(dev);
@@ -1261,7 +1261,7 @@ static void ethoc_remove(struct platform_device *pdev)
if (netdev) {
netif_napi_del(&priv->napi);
- phy_disconnect(netdev->phydev);
+ phy_disconnect_rtnl(netdev->phydev);
if (priv->mdio) {
mdiobus_unregister(priv->mdio);
@@ -1734,7 +1734,7 @@ static void ftgmac100_phy_disconnect(struct net_device *netdev)
if (!netdev->phydev)
return;
- phy_disconnect(netdev->phydev);
+ phy_disconnect_rtnl(netdev->phydev);
if (of_phy_is_fixed_link(priv->dev->of_node))
of_phy_deregister_fixed_link(priv->dev->of_node);
@@ -434,10 +434,10 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
mac->phylink = phylink;
rtnl_lock();
- err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
+ err = phylink_fwnode_phy_connect_rtnl(mac->phylink, dpmac_node, 0);
rtnl_unlock();
if (err) {
- netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
+ netdev_err(net_dev, "phylink_fwnode_phy_connect_rtnl() = %d\n", err);
goto err_phylink_destroy;
}
@@ -194,7 +194,7 @@ static void hbg_phy_adjust_link(struct net_device *netdev)
static void hbg_phy_disconnect(void *data)
{
- phy_disconnect((struct phy_device *)data);
+ phy_disconnect_rtnl((struct phy_device *)data);
}
static int hbg_phy_connect(struct hbg_priv *priv)
@@ -1027,7 +1027,7 @@ static void hip04_remove(struct platform_device *pdev)
struct device *d = &pdev->dev;
if (priv->phy)
- phy_disconnect(priv->phy);
+ phy_disconnect_rtnl(priv->phy);
hip04_free_ring(ndev, d);
unregister_netdev(ndev);
@@ -884,7 +884,7 @@ static int hisi_femac_drv_probe(struct platform_device *pdev)
out_disconnect_phy:
netif_napi_del(&priv->napi);
- phy_disconnect(phy);
+ phy_disconnect_rtnl(phy);
out_disable_clk:
clk_disable_unprepare(priv->clk);
out_free_netdev:
@@ -901,7 +901,7 @@ static void hisi_femac_drv_remove(struct platform_device *pdev)
netif_napi_del(&priv->napi);
unregister_netdev(ndev);
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
clk_disable_unprepare(priv->clk);
free_netdev(ndev);
}
@@ -2406,7 +2406,7 @@ static void hns_nic_dev_remove(struct platform_device *pdev)
priv->ring_data = NULL;
if (ndev->phydev)
- phy_disconnect(ndev->phydev);
+ phy_disconnect_rtnl(ndev->phydev);
if (!IS_ERR_OR_NULL(priv->ae_handle))
hnae_put_handle(priv->ae_handle);
@@ -248,7 +248,7 @@ void hclge_mac_disconnect_phy(struct hnae3_handle *handle)
if (!phydev)
return;
- phy_disconnect(phydev);
+ phy_disconnect_rtnl(phydev);
}
void hclge_mac_start_phy(struct hclge_dev *hdev)
@@ -424,7 +424,7 @@ ltq_etop_mdio_cleanup(struct net_device *dev)
{
struct ltq_etop_priv *priv = netdev_priv(dev);
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
mdiobus_unregister(priv->mii_bus);
mdiobus_free(priv->mii_bus);
}
@@ -3277,7 +3277,7 @@ static void mv643xx_eth_remove(struct platform_device *pdev)
unregister_netdev(mp->dev);
if (dev->phydev)
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
cancel_work_sync(&mp->tx_timeout_task);
if (!IS_ERR(mp->clk))
@@ -1535,7 +1535,7 @@ static void pxa168_eth_remove(struct platform_device *pdev)
pep->htpr = NULL;
}
if (dev->phydev)
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
mdiobus_unregister(pep->smi_bus);
mdiobus_free(pep->smi_bus);
@@ -480,7 +480,7 @@ static int mlxbf_gige_probe(struct platform_device *pdev)
err = register_netdev(netdev);
if (err) {
dev_err(&pdev->dev, "Failed to register netdev\n");
- phy_disconnect(phydev);
+ phy_disconnect_rtnl(phydev);
goto out;
}
@@ -496,7 +496,7 @@ static void mlxbf_gige_remove(struct platform_device *pdev)
struct mlxbf_gige *priv = platform_get_drvdata(pdev);
unregister_netdev(priv->netdev);
- phy_disconnect(priv->netdev->phydev);
+ phy_disconnect_rtnl(priv->netdev->phydev);
mlxbf_gige_mdio_remove(priv);
platform_set_drvdata(pdev, NULL);
}
@@ -579,7 +579,7 @@ static int oa_tc6_phy_init(struct oa_tc6 *tc6)
static void oa_tc6_phy_exit(struct oa_tc6 *tc6)
{
- phy_disconnect(tc6->phydev);
+ phy_disconnect_rtnl(tc6->phydev);
oa_tc6_mdiobus_unregister(tc6);
}
@@ -1159,12 +1159,12 @@ static int r6040_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
err = register_netdev(dev);
if (err) {
dev_err(&pdev->dev, "Failed to register net device\n");
- goto err_out_phy_disconnect;
+ goto err_out_phy_disconnect_rtnl;
}
return 0;
-err_out_phy_disconnect:
- phy_disconnect(dev->phydev);
+err_out_phy_disconnect_rtnl:
+ phy_disconnect_rtnl(dev->phydev);
err_out_mdio_unregister:
mdiobus_unregister(lp->mii_bus);
err_out_mdio:
@@ -1188,7 +1188,7 @@ static void r6040_remove_one(struct pci_dev *pdev)
struct r6040_private *lp = netdev_priv(dev);
unregister_netdev(dev);
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
mdiobus_unregister(lp->mii_bus);
mdiobus_free(lp->mii_bus);
netif_napi_del(&lp->napi);
@@ -1478,7 +1478,7 @@ static int rswitch_phy_device_init(struct rswitch_device *rdev)
static void rswitch_phy_device_deinit(struct rswitch_device *rdev)
{
if (rdev->ndev->phydev)
- phy_disconnect(rdev->ndev->phydev);
+ phy_disconnect_rtnl(rdev->ndev->phydev);
}
static int rswitch_serdes_set_params(struct rswitch_device *rdev)
@@ -930,7 +930,7 @@ static int rtsn_phy_init(struct rtsn_private *priv)
static void rtsn_phy_deinit(struct rtsn_private *priv)
{
- phy_disconnect(priv->ndev->phydev);
+ phy_disconnect_rtnl(priv->ndev->phydev);
priv->ndev->phydev = NULL;
}
@@ -1958,7 +1958,7 @@ static int prueth_probe(struct platform_device *pdev)
if (!prueth->registered_netdevs[i])
continue;
if (prueth->emac[i]->ndev->phydev) {
- phy_disconnect(prueth->emac[i]->ndev->phydev);
+ phy_disconnect_rtnl(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
}
unregister_netdev(prueth->registered_netdevs[i]);
@@ -2017,7 +2017,7 @@ static void prueth_remove(struct platform_device *pdev)
if (!prueth->registered_netdevs[i])
continue;
phy_stop(prueth->emac[i]->ndev->phydev);
- phy_disconnect(prueth->emac[i]->ndev->phydev);
+ phy_disconnect_rtnl(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
unregister_netdev(prueth->registered_netdevs[i]);
}
@@ -1125,7 +1125,7 @@ static int prueth_probe(struct platform_device *pdev)
continue;
if (prueth->emac[i]->ndev->phydev) {
- phy_disconnect(prueth->emac[i]->ndev->phydev);
+ phy_disconnect_rtnl(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
}
unregister_netdev(prueth->registered_netdevs[i]);
@@ -1187,7 +1187,7 @@ static void prueth_remove(struct platform_device *pdev)
if (!prueth->registered_netdevs[i])
continue;
phy_stop(prueth->emac[i]->ndev->phydev);
- phy_disconnect(prueth->emac[i]->ndev->phydev);
+ phy_disconnect_rtnl(prueth->emac[i]->ndev->phydev);
prueth->emac[i]->ndev->phydev = NULL;
unregister_netdev(prueth->registered_netdevs[i]);
}
@@ -856,7 +856,7 @@ static void tc35815_remove_one(struct pci_dev *pdev)
struct net_device *dev = pci_get_drvdata(pdev);
struct tc35815_local *lp = netdev_priv(dev);
- phy_disconnect(dev->phydev);
+ phy_disconnect_rtnl(dev->phydev);
mdiobus_unregister(lp->mii_bus);
mdiobus_free(lp->mii_bus);
unregister_netdev(dev);
@@ -297,7 +297,7 @@ static int txgbe_phylink_init(struct txgbe *txgbe)
if (wx->phydev) {
int ret;
- ret = phylink_connect_phy(phylink, wx->phydev);
+ ret = phylink_connect_phy_rtnl(phylink, wx->phydev);
if (ret) {
phylink_destroy(phylink);
return ret;
@@ -1583,7 +1583,7 @@ static int ixp4xx_eth_probe(struct platform_device *pdev)
return 0;
err_phy_dis:
- phy_disconnect(phydev);
+ phy_disconnect_rtnl(phydev);
err_free_mem:
npe_port_tab[NPE_ID(port->id)] = NULL;
npe_release(port->npe);
@@ -1597,7 +1597,7 @@ static void ixp4xx_eth_remove(struct platform_device *pdev)
struct port *port = netdev_priv(ndev);
unregister_netdev(ndev);
- phy_disconnect(phydev);
+ phy_disconnect_rtnl(phydev);
ixp4xx_mdio_remove();
npe_port_tab[NPE_ID(port->id)] = NULL;
npe_release(port->npe);
@@ -1339,6 +1339,22 @@ void phy_disconnect(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_disconnect);
+/**
+ * phy_disconnect_rtnl - disable interrupts, stop state machine, and detach a PHY
+ * device
+ * @phydev: target phy_device struct
+ *
+ * This is a wrapper around phy_disconnect that takes the rtnl semaphore.
+ */
+void phy_disconnect_rtnl(struct phy_device *phydev)
+{
+ if (rtnl_lock_killable())
+ return;
+ phy_disconnect(phydev);
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(phy_disconnect_rtnl);
+
/**
* phy_poll_reset - Safely wait until a PHY reset has properly completed
* @phydev: The PHY device to poll
@@ -1767,8 +1783,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
return err;
error:
- /* phy_detach() does all of the cleanup below */
- phy_detach(phydev);
+ /* phy_detach_rtnl() does all of the cleanup below */
+ phy_detach_rtnl(phydev);
return err;
error_module_put:
@@ -1899,6 +1915,24 @@ void phy_detach(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_detach);
+/**
+ * phy_detach_rtnl - detach a PHY device from its network device
+ * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
+ *
+ * This is a wrapper around phy_detach that takes the rtnl semaphore.
+ */
+void phy_detach_rtnl(struct phy_device *phydev)
+{
+ if (rtnl_lock_killable())
+ return;
+ phy_detach(phydev);
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(phy_detach_rtnl);
+
int phy_suspend(struct phy_device *phydev)
{
struct net_device *netdev = phydev->attached_dev;
@@ -2159,6 +2159,17 @@ static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
return phy_attach_direct(pl->netdev, phy, flags, interface);
}
+static int phylink_preconnect_phy(struct phylink *pl, struct phy_device *phy)
+{
+ /* Use PHY device/driver interface */
+ if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
+ pl->link_interface = phy->interface;
+ pl->link_config.interface = pl->link_interface;
+ }
+
+ return phylink_attach_phy(pl, phy, pl->link_interface);
+}
+
/**
* phylink_connect_phy() - connect a PHY to the phylink instance
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2178,14 +2189,8 @@ int phylink_connect_phy(struct phylink *pl, struct phy_device *phy)
{
int ret;
- /* Use PHY device/driver interface */
- if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
- pl->link_interface = phy->interface;
- pl->link_config.interface = pl->link_interface;
- }
-
- ret = phylink_attach_phy(pl, phy, pl->link_interface);
- if (ret < 0)
+ ret = phylink_preconnect_phy(pl, phy);
+ if (ret)
return ret;
ret = phylink_bringup_phy(pl, phy, pl->link_config.interface);
@@ -2196,6 +2201,40 @@ int phylink_connect_phy(struct phylink *pl, struct phy_device *phy)
}
EXPORT_SYMBOL_GPL(phylink_connect_phy);
+/**
+ * phylink_connect_phy_rtnl() - connect a PHY to the phylink instance
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @phy: a pointer to a &struct phy_device.
+ *
+ * Connect @phy to the phylink instance specified by @pl by calling
+ * phy_attach_direct(). Configure the @phy according to the MAC driver's
+ * capabilities, start the PHYLIB state machine and enable any interrupts
+ * that the PHY supports.
+ *
+ * This updates the phylink's ethtool supported and advertising link mode
+ * masks.
+ *
+ * This is a similar to phylink_connect_phy but is called without the hold
+ * of rtnlock. It then use phy_detach_rtnl that takes the rtnl semaphore.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int phylink_connect_phy_rtnl(struct phylink *pl, struct phy_device *phy)
+{
+ int ret;
+
+ ret = phylink_preconnect_phy(pl, phy);
+ if (ret)
+ return ret;
+
+ ret = phylink_bringup_phy(pl, phy, pl->link_config.interface);
+ if (ret)
+ phy_detach_rtnl(phy);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_connect_phy_rtnl);
+
/**
* phylink_of_phy_connect() - connect the PHY specified in the DT mode.
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2215,20 +2254,10 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn,
}
EXPORT_SYMBOL_GPL(phylink_of_phy_connect);
-/**
- * phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode.
- * @pl: a pointer to a &struct phylink returned from phylink_create()
- * @fwnode: a pointer to a &struct fwnode_handle.
- * @flags: PHY-specific flags to communicate to the PHY device driver
- *
- * Connect the phy specified @fwnode to the phylink instance specified
- * by @pl.
- *
- * Returns 0 on success or a negative errno.
- */
-int phylink_fwnode_phy_connect(struct phylink *pl,
- const struct fwnode_handle *fwnode,
- u32 flags)
+static struct phy_device *
+phylink_fwnode_phy_preconnect(struct phylink *pl,
+ const struct fwnode_handle *fwnode,
+ u32 flags)
{
struct fwnode_handle *phy_fwnode;
struct phy_device *phy_dev;
@@ -2238,20 +2267,20 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
(pl->cfg_link_an_mode == MLO_AN_INBAND &&
phy_interface_mode_is_8023z(pl->link_interface)))
- return 0;
+ return NULL;
phy_fwnode = fwnode_get_phy_node(fwnode);
if (IS_ERR(phy_fwnode)) {
if (pl->cfg_link_an_mode == MLO_AN_PHY)
- return -ENODEV;
- return 0;
+ return ERR_PTR(-ENODEV);
+ return NULL;
}
phy_dev = fwnode_phy_find_device(phy_fwnode);
/* We're done with the phy_node handle */
fwnode_handle_put(phy_fwnode);
if (!phy_dev)
- return -ENODEV;
+ return ERR_PTR(-ENODEV);
/* Use PHY device/driver interface */
if (pl->link_interface == PHY_INTERFACE_MODE_NA) {
@@ -2266,7 +2295,38 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
pl->link_interface);
phy_device_free(phy_dev);
if (ret)
- return ret;
+ return ERR_PTR(ret);
+
+ ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
+ if (ret) {
+ phy_detach(phy_dev);
+ return ERR_PTR(ret);
+ }
+
+ return phy_dev;
+}
+
+/**
+ * phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode.
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @fwnode: a pointer to a &struct fwnode_handle.
+ * @flags: PHY-specific flags to communicate to the PHY device driver
+ *
+ * Connect the phy specified @fwnode to the phylink instance specified
+ * by @pl.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int phylink_fwnode_phy_connect(struct phylink *pl,
+ const struct fwnode_handle *fwnode,
+ u32 flags)
+{
+ struct phy_device *phy_dev;
+ int ret;
+
+ phy_dev = phylink_fwnode_phy_preconnect(pl, fwnode, flags);
+ if (IS_ERR_OR_NULL(phy_dev))
+ return PTR_ERR(phy_dev);
ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
if (ret)
@@ -2276,6 +2336,40 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
}
EXPORT_SYMBOL_GPL(phylink_fwnode_phy_connect);
+/**
+ * phylink_fwnode_phy_connect() - connect the PHY specified in the fwnode.
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @fwnode: a pointer to a &struct fwnode_handle.
+ * @flags: PHY-specific flags to communicate to the PHY device driver
+ *
+ * Connect the phy specified @fwnode to the phylink instance specified
+ * by @pl.
+ *
+ * This is a similar to phylink_fwnode_phy_connect but is called without
+ * the hold of rtnlock. It then use phy_detach_rtnl that takes the rtnl
+ * semaphore.
+ *
+ * Returns 0 on success or a negative errno.
+ */
+int phylink_fwnode_phy_connect_rtnl(struct phylink *pl,
+ const struct fwnode_handle *fwnode,
+ u32 flags)
+{
+ struct phy_device *phy_dev;
+ int ret;
+
+ phy_dev = phylink_fwnode_phy_preconnect(pl, fwnode, flags);
+ if (IS_ERR_OR_NULL(phy_dev))
+ return PTR_ERR(phy_dev);
+
+ ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
+ if (ret)
+ phy_detach_rtnl(phy_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phylink_fwnode_phy_connect_rtnl);
+
/**
* phylink_disconnect_phy() - disconnect any PHY attached to the phylink
* instance.
@@ -707,7 +707,7 @@ static int ax88772_init_phy(struct usbnet *dev)
return -ENODEV;
}
- ret = phylink_connect_phy(priv->phylink, priv->phydev);
+ ret = phylink_connect_phy_rtnl(priv->phylink, priv->phydev);
if (ret) {
netdev_err(dev->net, "Could not connect PHY\n");
return ret;
@@ -248,7 +248,7 @@ static int ax88172a_stop(struct usbnet *dev)
netdev_info(dev->net, "Disconnecting from phy %s\n",
priv->phy_name);
phy_stop(priv->phydev);
- phy_disconnect(priv->phydev);
+ phy_disconnect_rtnl(priv->phydev);
}
return 0;
@@ -1263,7 +1263,7 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
{
struct smsc95xx_priv *pdata = dev->driver_priv;
- phy_disconnect(dev->net->phydev);
+ phy_disconnect_rtnl(dev->net->phydev);
mdiobus_unregister(pdata->mdiobus);
mdiobus_free(pdata->mdiobus);
irq_dispose_mapping(irq_find_mapping(pdata->irqdomain, PHY_HWIRQ));
@@ -1798,7 +1798,9 @@ struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
void (*handler)(struct net_device *),
phy_interface_t interface);
void phy_disconnect(struct phy_device *phydev);
+void phy_disconnect_rtnl(struct phy_device *phydev);
void phy_detach(struct phy_device *phydev);
+void phy_detach_rtnl(struct phy_device *phydev);
void phy_start(struct phy_device *phydev);
void phy_stop(struct phy_device *phydev);
int phy_config_aneg(struct phy_device *phydev);
@@ -699,10 +699,14 @@ void phylink_destroy(struct phylink *);
bool phylink_expects_phy(struct phylink *pl);
int phylink_connect_phy(struct phylink *, struct phy_device *);
+int phylink_connect_phy_rtnl(struct phylink *pl, struct phy_device *phy);
int phylink_of_phy_connect(struct phylink *, struct device_node *, u32 flags);
int phylink_fwnode_phy_connect(struct phylink *pl,
const struct fwnode_handle *fwnode,
u32 flags);
+int phylink_fwnode_phy_connect_rtnl(struct phylink *pl,
+ const struct fwnode_handle *fwnode,
+ u32 flags);
void phylink_disconnect_phy(struct phylink *);
int phylink_set_fixed_link(struct phylink *,
const struct phylink_link_state *);
@@ -90,6 +90,7 @@ int rtnl_lock_killable(void)
{
return mutex_lock_killable(&rtnl_mutex);
}
+EXPORT_SYMBOL_GPL(rtnl_lock_killable);
static struct sk_buff *defer_kfree_skb_list;
void rtnl_kfree_skbs(struct sk_buff *head, struct sk_buff *tail)
@@ -2650,7 +2650,7 @@ static int dsa_user_phy_connect(struct net_device *user_dev, int addr,
user_dev->phydev->dev_flags |= flags;
- return phylink_connect_phy(dp->pl, user_dev->phydev);
+ return phylink_connect_phy_rtnl(dp->pl, user_dev->phydev);
}
static int dsa_user_phy_setup(struct net_device *user_dev)
phy_detach needs the rtnl lock to be held. It should have been added before to avoid this massive change among lots of net drivers but there was no clear evidence of such needs at that time. This imply a lock change in this API. Add phy_detach_rtnl, phy_diconnect_rtnl, phylink_connect_phy_rtnl and phylink_fwnode_phy_connect_rtnl helpers to take the lock before calling their respective function. The RTNL requirement was identified when adding rtnl_dereference() calls for hardware timestamping support [1], but applies to other features as well [2]. [1] https://lore.kernel.org/all/20241212-feature_ptp_netnext-v21-3-2c282a941518@bootlin.com/ [2] https://lore.kernel.org/netdev/Z6OdkdI2ss19FyVT@shell.armlinux.org.uk/ Signed-off-by: Kory Maincent <kory.maincent@bootlin.com> --- drivers/net/ethernet/actions/owl-emac.c | 4 +- drivers/net/ethernet/adi/adin1110.c | 2 +- drivers/net/ethernet/agere/et131x.c | 4 +- drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c | 2 +- drivers/net/ethernet/apm/xgene-v2/mdio.c | 2 +- .../net/ethernet/apm/xgene/xgene_enet_hw.c | 4 +- drivers/net/ethernet/arc/emac_main.c | 4 +- drivers/net/ethernet/asix/ax88796c_main.c | 4 +- drivers/net/ethernet/broadcom/b44.c | 2 +- drivers/net/ethernet/broadcom/bgmac.c | 8 +- .../net/ethernet/broadcom/genet/bcmgenet.c | 7 +- drivers/net/ethernet/broadcom/genet/bcmmii.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 4 +- .../net/ethernet/cavium/thunder/thunder_bgx.c | 2 +- drivers/net/ethernet/cortina/gemini.c | 4 +- drivers/net/ethernet/davicom/dm9051.c | 4 +- drivers/net/ethernet/dnet.c | 2 +- drivers/net/ethernet/ethoc.c | 2 +- drivers/net/ethernet/faraday/ftgmac100.c | 2 +- .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 4 +- .../net/ethernet/hisilicon/hibmcge/hbg_mdio.c | 2 +- drivers/net/ethernet/hisilicon/hip04_eth.c | 2 +- drivers/net/ethernet/hisilicon/hisi_femac.c | 4 +- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 2 +- .../hisilicon/hns3/hns3pf/hclge_mdio.c | 2 +- drivers/net/ethernet/lantiq_etop.c | 2 +- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- .../mellanox/mlxbf_gige/mlxbf_gige_main.c | 4 +- drivers/net/ethernet/oa_tc6.c | 2 +- drivers/net/ethernet/rdc/r6040.c | 8 +- drivers/net/ethernet/renesas/rswitch.c | 2 +- drivers/net/ethernet/renesas/rtsn.c | 2 +- drivers/net/ethernet/ti/icssg/icssg_prueth.c | 4 +- .../net/ethernet/ti/icssg/icssg_prueth_sr1.c | 4 +- drivers/net/ethernet/toshiba/tc35815.c | 2 +- .../net/ethernet/wangxun/txgbe/txgbe_phy.c | 2 +- drivers/net/ethernet/xscale/ixp4xx_eth.c | 4 +- drivers/net/phy/phy_device.c | 38 ++++- drivers/net/phy/phylink.c | 148 ++++++++++++++---- drivers/net/usb/asix_devices.c | 2 +- drivers/net/usb/ax88172a.c | 2 +- drivers/net/usb/smsc95xx.c | 2 +- include/linux/phy.h | 2 + include/linux/phylink.h | 4 + net/core/rtnetlink.c | 1 + net/dsa/user.c | 2 +- 47 files changed, 231 insertions(+), 91 deletions(-)