* [PATCH net v3] net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue
@ 2026-01-27 8:02 Kevin Hao
2026-01-28 3:08 ` Jakub Kicinski
0 siblings, 1 reply; 4+ messages in thread
From: Kevin Hao @ 2026-01-27 8:02 UTC (permalink / raw)
To: netdev
Cc: Kevin Hao, stable, Siddharth Vadapalli, Roger Quadros,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Vladimir Oltean, Kuniyuki Iwashima, linux-omap
Commit 1767bb2d47b7 ("ipv6: mcast: Don't hold RTNL for
IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.") removed the RTNL lock for
IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP operations. However, this
change triggered the following call trace on my BeagleBone Black board:
WARNING: net/8021q/vlan_core.c:236 at vlan_for_each+0x120/0x124, CPU#0: rpcbind/496
RTNL: assertion failed at net/8021q/vlan_core.c (236)
Modules linked in:
CPU: 0 UID: 997 PID: 496 Comm: rpcbind Not tainted 6.19.0-rc6-next-20260122-yocto-standard+ #8 PREEMPT
Hardware name: Generic AM33XX (Flattened Device Tree)
Call trace:
unwind_backtrace from show_stack+0x28/0x2c
show_stack from dump_stack_lvl+0x30/0x38
dump_stack_lvl from __warn+0xb8/0x11c
__warn from warn_slowpath_fmt+0x130/0x194
warn_slowpath_fmt from vlan_for_each+0x120/0x124
vlan_for_each from cpsw_add_mc_addr+0x54/0xd8
cpsw_add_mc_addr from __hw_addr_ref_sync_dev+0xc4/0xec
__hw_addr_ref_sync_dev from __dev_mc_add+0x78/0x88
__dev_mc_add from igmp6_group_added+0x84/0xec
igmp6_group_added from __ipv6_dev_mc_inc+0x1fc/0x2f0
__ipv6_dev_mc_inc from __ipv6_sock_mc_join+0x124/0x1b4
__ipv6_sock_mc_join from do_ipv6_setsockopt+0x84c/0x1168
do_ipv6_setsockopt from ipv6_setsockopt+0x88/0xc8
ipv6_setsockopt from do_sock_setsockopt+0xe8/0x19c
do_sock_setsockopt from __sys_setsockopt+0x84/0xac
__sys_setsockopt from ret_fast_syscall+0x0/0x5
This trace occurs because vlan_for_each() is called within
cpsw_ndo_set_rx_mode(), which expects the RTNL lock to be held.
Since modifying vlan_for_each() to operate without the RTNL lock is not
straightforward, and because ndo_set_rx_mode() is invoked both with and
without the RTNL lock across different code paths, simply adding
rtnl_lock() in cpsw_ndo_set_rx_mode() is not a viable solution.
To resolve this issue, we opt to execute the actual processing within
a work queue, following the approach used by the icssg-prueth driver.
Fixes: 1767bb2d47b7 ("ipv6: mcast: Don't hold RTNL for IPV6_ADD_MEMBERSHIP and MCAST_JOIN_GROUP.")
Signed-off-by: Kevin Hao <haokexin@gmail.com>
Cc: stable@vger.kernel.org
---
Changes in v3:
- Resolve the deadlock issue identified in the AI review [2]
by moving the netif_running() check under the RTNL lock and removing the
cancel_work_sync() call in cpsw_ndo_stop().
- Link to v2: https://lore.kernel.org/r/20260125-bbb-v2-1-1547ffabc9d3@gmail.com
Changes in v2:
- Addresses the issue identified in the AI review [1]:
- Adds a netif_running() check in cpsw_ndo_set_rx_mode_work()
- Cancels the rx_mode_work in cpsw_ndo_stop()
- Link to v1: https://lore.kernel.org/r/20260123-bbb-v1-1-176b0b71834d@gmail.com
[1] https://netdev-ai.bots.linux.dev/ai-review.html?id=bd885e1e-1aed-4755-ad60-7150737ad0f5
[2] https://netdev-ai.bots.linux.dev/ai-review.html?id=c9fc3cf8-a06c-4cb8-b26b-910e775951a0
---
Please note that the cpsw driver also has the same issue. If this resolution
is acceptable, I will create another patch to fix the issue in cpsw.
Cc: Siddharth Vadapalli <s-vadapalli@ti.com>
Cc: Roger Quadros <rogerq@kernel.org>
Cc: Andrew Lunn <andrew+netdev@lunn.ch>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Vladimir Oltean <vladimir.oltean@nxp.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>
Cc: linux-omap@vger.kernel.org
---
drivers/net/ethernet/ti/cpsw_new.c | 34 ++++++++++++++++++++++++++++++++--
drivers/net/ethernet/ti/cpsw_priv.h | 2 ++
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c
index ab88d4c02cbde76207f89cf433e2b383dcde6a83..838615c9a5ec3c9226c2aff6325ecda13ba9729f 100644
--- a/drivers/net/ethernet/ti/cpsw_new.c
+++ b/drivers/net/ethernet/ti/cpsw_new.c
@@ -248,15 +248,25 @@ 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)) {
+ rtnl_unlock();
+ return;
+ }
+
+ 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);
+ netif_addr_unlock_bh(ndev);
+ rtnl_unlock();
return;
}
@@ -270,6 +280,16 @@ 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);
+ netif_addr_unlock_bh(ndev);
+ rtnl_unlock();
+}
+
+static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
+{
+ struct cpsw_priv *priv = netdev_priv(ndev);
+ struct cpsw_common *cpsw = priv->cpsw;
+
+ queue_work(cpsw->cmd_wq, &priv->rx_mode_work);
}
static unsigned int cpsw_rxbuf_total_len(unsigned int len)
@@ -1398,6 +1418,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);
@@ -1976,6 +1997,13 @@ static int cpsw_probe(struct platform_device *pdev)
}
cpsw_split_res(cpsw);
+ cpsw->cmd_wq = create_singlethread_workqueue("cpsw_cmd_wq");
+ if (!cpsw->cmd_wq) {
+ dev_err(dev, "error initializing workqueue\n");
+ ret = -ENOMEM;
+ goto clean_cpts;
+ }
+
/* setup netdevs */
ret = cpsw_create_ports(cpsw);
if (ret)
@@ -2042,6 +2070,7 @@ static int cpsw_probe(struct platform_device *pdev)
clean_unregister_notifiers:
cpsw_unregister_notifiers(cpsw);
clean_unregister_netdev:
+ destroy_workqueue(cpsw->cmd_wq);
cpsw_unregister_ports(cpsw);
clean_cpts:
cpts_release(cpsw->cpts);
@@ -2068,6 +2097,7 @@ static void cpsw_remove(struct platform_device *pdev)
return;
}
+ destroy_workqueue(cpsw->cmd_wq);
cpsw_unregister_notifiers(cpsw);
cpsw_unregister_devlink(cpsw);
cpsw_unregister_ports(cpsw);
diff --git a/drivers/net/ethernet/ti/cpsw_priv.h b/drivers/net/ethernet/ti/cpsw_priv.h
index 91add8925e235c6cf5542fde11f3383b9234c872..8cdf4bff198fcc05436ff381a7e4326b3e3c27b1 100644
--- a/drivers/net/ethernet/ti/cpsw_priv.h
+++ b/drivers/net/ethernet/ti/cpsw_priv.h
@@ -362,6 +362,7 @@ struct cpsw_common {
struct net_device *hw_bridge_dev;
bool ale_bypass;
u8 base_mac[ETH_ALEN];
+ struct workqueue_struct *cmd_wq;
};
struct cpsw_ale_ratelimit {
@@ -391,6 +392,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)
---
base-commit: 615aad0f61e0c7a898184a394dc895c610100d4f
change-id: 20260123-bbb-dc3675f671d0
Best regards,
--
Kevin Hao <haokexin@gmail.com>
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net v3] net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue
2026-01-27 8:02 [PATCH net v3] net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue Kevin Hao
@ 2026-01-28 3:08 ` Jakub Kicinski
2026-01-29 6:32 ` Kevin Hao
0 siblings, 1 reply; 4+ messages in thread
From: Jakub Kicinski @ 2026-01-28 3:08 UTC (permalink / raw)
To: Kevin Hao
Cc: netdev, stable, Siddharth Vadapalli, Roger Quadros, Andrew Lunn,
David S. Miller, Eric Dumazet, Paolo Abeni, Vladimir Oltean,
Kuniyuki Iwashima, linux-omap
On Tue, 27 Jan 2026 16:02:07 +0800 Kevin Hao wrote:
> To resolve this issue, we opt to execute the actual processing within
> a work queue, following the approach used by the icssg-prueth driver.
Code looks good now, but why are you creating a workqueue for this one
work? Can't you use the system wq and just cancel it sync where you had
the wq destroy?
BTW you're fixing drivers/net/ethernet/ti/cpsw_new.c I think
drivers/net/ethernet/ti/cpsw.c has an identical bug, no?
--
pw-bot: cr
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net v3] net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue
2026-01-28 3:08 ` Jakub Kicinski
@ 2026-01-29 6:32 ` Kevin Hao
2026-01-30 2:31 ` Kevin Hao
0 siblings, 1 reply; 4+ messages in thread
From: Kevin Hao @ 2026-01-29 6:32 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, stable, Siddharth Vadapalli, Roger Quadros, Andrew Lunn,
David S. Miller, Eric Dumazet, Paolo Abeni, Vladimir Oltean,
Kuniyuki Iwashima, linux-omap
[-- Attachment #1: Type: text/plain, Size: 1471 bytes --]
On Tue, Jan 27, 2026 at 07:08:36PM -0800, Jakub Kicinski wrote:
> On Tue, 27 Jan 2026 16:02:07 +0800 Kevin Hao wrote:
> > To resolve this issue, we opt to execute the actual processing within
> > a work queue, following the approach used by the icssg-prueth driver.
>
> Code looks good now, but why are you creating a workqueue for this one
> work? Can't you use the system wq and just cancel it sync where you had
> the wq destroy?
This implementation was adapted from the icssg-prueth driver. After reviewing
the git history, I found no explicit rationale for using a dedicated workqueue.
In my opinion, if we were to use the system wq and rely on cancel_work_sync()
before unregister_netdev(), a race condition could arise between these two calls.
Specifically, cpsw_ndo_set_rx_mode_work() might be scheduled during this interval
and run after the net device is unregistered, leading to a use-after-free bug.
While reviewing the code, I noticed that in the current implementation, we may
need to move the destroy_workqueue() call after unregister_netdev(). Otherwise,
there is a risk of encountering a use-after-free bug related to the dedicated workqueue.
>
> BTW you're fixing drivers/net/ethernet/ti/cpsw_new.c I think
> drivers/net/ethernet/ti/cpsw.c has an identical bug, no?
Yes, as noted in the patch comment area, I plan to address the same issue in
drivers/net/ethernet/ti/cpsw.c once this patch is approved.
Thanks,
Kevin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net v3] net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue
2026-01-29 6:32 ` Kevin Hao
@ 2026-01-30 2:31 ` Kevin Hao
0 siblings, 0 replies; 4+ messages in thread
From: Kevin Hao @ 2026-01-30 2:31 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, stable, Siddharth Vadapalli, Roger Quadros, Andrew Lunn,
David S. Miller, Eric Dumazet, Paolo Abeni, Vladimir Oltean,
Kuniyuki Iwashima, linux-omap
[-- Attachment #1: Type: text/plain, Size: 1203 bytes --]
On Thu, Jan 29, 2026 at 02:32:44PM +0800, Kevin Hao wrote:
> On Tue, Jan 27, 2026 at 07:08:36PM -0800, Jakub Kicinski wrote:
> > On Tue, 27 Jan 2026 16:02:07 +0800 Kevin Hao wrote:
> > > To resolve this issue, we opt to execute the actual processing within
> > > a work queue, following the approach used by the icssg-prueth driver.
> >
> > Code looks good now, but why are you creating a workqueue for this one
> > work? Can't you use the system wq and just cancel it sync where you had
> > the wq destroy?
>
> This implementation was adapted from the icssg-prueth driver. After reviewing
> the git history, I found no explicit rationale for using a dedicated workqueue.
> In my opinion, if we were to use the system wq and rely on cancel_work_sync()
> before unregister_netdev(), a race condition could arise between these two calls.
> Specifically, cpsw_ndo_set_rx_mode_work() might be scheduled during this interval
> and run after the net device is unregistered, leading to a use-after-free bug.
We can use disable_work_sync() instead of cancel_work_sync() to avoid potential
use-after-free issues. I will send a v4 patch to remove this dedicated workqueue.
Thanks,
Kevin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-01-30 2:31 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-27 8:02 [PATCH net v3] net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue Kevin Hao
2026-01-28 3:08 ` Jakub Kicinski
2026-01-29 6:32 ` Kevin Hao
2026-01-30 2:31 ` Kevin Hao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox