Merge branch 'net-cpsw-execute-ndo_set_rx_mode-callback-in-a-work-queue'

Kevin Hao says:

====================
net: cpsw: Execute ndo_set_rx_mode callback in a work queue

These two patches resolve an RTNL assertion call trace issue
in both the legacy and new cpsw drivers.
====================

Link: https://patch.msgid.link/20260203-bbb-v5-0-ea0ea217a85c@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-02-03 19:45:53 -08:00
commit 7576bd9017
3 changed files with 65 additions and 11 deletions

View File

@ -305,12 +305,19 @@ static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
return 0;
}
static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
static void cpsw_ndo_set_rx_mode_work(struct work_struct *work)
{
struct cpsw_priv *priv = netdev_priv(ndev);
struct cpsw_priv *priv = container_of(work, struct cpsw_priv, rx_mode_work);
struct cpsw_common *cpsw = priv->cpsw;
struct net_device *ndev = priv->ndev;
int slave_port = -1;
rtnl_lock();
if (!netif_running(ndev))
goto unlock_rtnl;
netif_addr_lock_bh(ndev);
if (cpsw->data.dual_emac)
slave_port = priv->emac_port + 1;
@ -318,7 +325,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
/* Enable promiscuous mode */
cpsw_set_promiscious(ndev, true);
cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, slave_port);
return;
goto unlock_addr;
} else {
/* Disable promiscuous mode */
cpsw_set_promiscious(ndev, false);
@ -331,6 +338,18 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
/* add/remove mcast address either for real netdev or for vlan */
__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
cpsw_del_mc_addr);
unlock_addr:
netif_addr_unlock_bh(ndev);
unlock_rtnl:
rtnl_unlock();
}
static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
{
struct cpsw_priv *priv = netdev_priv(ndev);
schedule_work(&priv->rx_mode_work);
}
static unsigned int cpsw_rxbuf_total_len(unsigned int len)
@ -1472,6 +1491,7 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
priv_sl2->ndev = ndev;
priv_sl2->dev = &ndev->dev;
priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
INIT_WORK(&priv_sl2->rx_mode_work, cpsw_ndo_set_rx_mode_work);
if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
@ -1653,6 +1673,7 @@ static int cpsw_probe(struct platform_device *pdev)
priv->dev = dev;
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
priv->emac_port = 0;
INIT_WORK(&priv->rx_mode_work, cpsw_ndo_set_rx_mode_work);
if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
@ -1758,6 +1779,8 @@ static int cpsw_probe(struct platform_device *pdev)
static void cpsw_remove(struct platform_device *pdev)
{
struct cpsw_common *cpsw = platform_get_drvdata(pdev);
struct net_device *ndev;
struct cpsw_priv *priv;
int i, ret;
ret = pm_runtime_resume_and_get(&pdev->dev);
@ -1770,9 +1793,15 @@ static void cpsw_remove(struct platform_device *pdev)
return;
}
for (i = 0; i < cpsw->data.slaves; i++)
if (cpsw->slaves[i].ndev)
unregister_netdev(cpsw->slaves[i].ndev);
for (i = 0; i < cpsw->data.slaves; i++) {
ndev = cpsw->slaves[i].ndev;
if (!ndev)
continue;
priv = netdev_priv(ndev);
unregister_netdev(ndev);
disable_work_sync(&priv->rx_mode_work);
}
cpts_release(cpsw->cpts);
cpdma_ctlr_destroy(cpsw->dma);

View File

@ -248,16 +248,22 @@ static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
return 0;
}
static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
static void cpsw_ndo_set_rx_mode_work(struct work_struct *work)
{
struct cpsw_priv *priv = netdev_priv(ndev);
struct cpsw_priv *priv = container_of(work, struct cpsw_priv, rx_mode_work);
struct cpsw_common *cpsw = priv->cpsw;
struct net_device *ndev = priv->ndev;
rtnl_lock();
if (!netif_running(ndev))
goto unlock_rtnl;
netif_addr_lock_bh(ndev);
if (ndev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
cpsw_set_promiscious(ndev, true);
cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, priv->emac_port);
return;
goto unlock_addr;
}
/* Disable promiscuous mode */
@ -270,6 +276,18 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
/* add/remove mcast address either for real netdev or for vlan */
__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
cpsw_del_mc_addr);
unlock_addr:
netif_addr_unlock_bh(ndev);
unlock_rtnl:
rtnl_unlock();
}
static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
{
struct cpsw_priv *priv = netdev_priv(ndev);
schedule_work(&priv->rx_mode_work);
}
static unsigned int cpsw_rxbuf_total_len(unsigned int len)
@ -1398,6 +1416,7 @@ static int cpsw_create_ports(struct cpsw_common *cpsw)
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
priv->emac_port = i + 1;
priv->tx_packet_min = CPSW_MIN_PACKET_SIZE;
INIT_WORK(&priv->rx_mode_work, cpsw_ndo_set_rx_mode_work);
if (is_valid_ether_addr(slave_data->mac_addr)) {
ether_addr_copy(priv->mac_addr, slave_data->mac_addr);
@ -1447,13 +1466,18 @@ static int cpsw_create_ports(struct cpsw_common *cpsw)
static void cpsw_unregister_ports(struct cpsw_common *cpsw)
{
struct net_device *ndev;
struct cpsw_priv *priv;
int i = 0;
for (i = 0; i < cpsw->data.slaves; i++) {
if (!cpsw->slaves[i].ndev)
ndev = cpsw->slaves[i].ndev;
if (!ndev)
continue;
unregister_netdev(cpsw->slaves[i].ndev);
priv = netdev_priv(ndev);
unregister_netdev(ndev);
disable_work_sync(&priv->rx_mode_work);
}
}

View File

@ -391,6 +391,7 @@ struct cpsw_priv {
u32 tx_packet_min;
struct cpsw_ale_ratelimit ale_bc_ratelimit;
struct cpsw_ale_ratelimit ale_mc_ratelimit;
struct work_struct rx_mode_work;
};
#define ndev_to_cpsw(ndev) (((struct cpsw_priv *)netdev_priv(ndev))->cpsw)