From: Marco Crivellari <hidden> Date: 2026-07-06 13:40:42
Hello,
Currently the code uses the per-cpu workqueue system_long_wq to schedule
long running works.
Unbound works could benefit from scheduler task placement, to optimize
performance and power consumption. Another good reason to have this unbound,
is the "queue_delayed_work()" function, used to enqueue the work item.
More details on this will follow in the next section.
Recently, a new unbound workqueue specific for long running work has been
added:
c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
~~~ Details about queue_delayed_work ~~~
system_long_wq is a per-cpu workqueue and it is used as a parameter of
queue_delayed_work(). This function schedule an item that it will later
be enqueued (once the timer will fire). __queue_delayed_work() does the job
receiving as "cpu" WORK_CPU_UNBOUND:
if (housekeeping_enabled(HK_TYPE_TIMER)) {
// [....]
} else {
if (likely(cpu == WORK_CPU_UNBOUND))
add_timer_global(timer);
else
add_timer_on(timer, cpu);
}
The timer is global, so can fire everywhere, and the work item will be
enqueued where the timer fired.
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change the
workqueue with the new system_dfl_long_wq, so that the used workqueue is
now unbound and can benefit from scheduler task placement.
Thanks!
---
Changes in v2:
- rebased on v7.2-rc2
- dropped the RFC prefix, kept Ack and review tags
Link to v1: https://lore.kernel.org/all/20260511092846.120141-1-marco.crivellari@suse.com/
Marco Crivellari (5):
ibmvnic: Move long delayed work on system_dfl_long_wq
net: ti: icssg-stats: Move long delayed work on system_dfl_long_wq
net: thunderbolt: Move long delayed work on system_dfl_long_wq
net: usb: pegasus: Move long delayed work on system_dfl_long_wq
net: usb: r8152: Move long delayed work on system_dfl_long_wq
drivers/net/ethernet/ibm/ibmvnic.c | 4 ++--
drivers/net/ethernet/ti/icssg/icssg_stats.c | 2 +-
drivers/net/thunderbolt/main.c | 7 ++++---
drivers/net/usb/pegasus.c | 9 +++++----
drivers/net/usb/r8152.c | 7 ++++---
5 files changed, 16 insertions(+), 13 deletions(-)
--
2.54.0
From: Marco Crivellari <hidden> Date: 2026-07-06 13:40:46
Currently the code enqueue work items using {queue|mod}_delayed_work(),
using system_long_wq. This workqueue should be used when long works are
expected and it is a per-cpu workqueue.
The function(s) end up calling __queue_delayed_work(), which set a global
timer that could fire anywhere, enqueuing the work where the timer fired.
Unbound works could benefit from scheduler task placement, to optimize
performance and power consumption. Long work shouldn't stick to a single
CPU.
Recently, a new unbound workqueue specific for long running work has
been added:
c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change
system_long_wq with system_dfl_long_wq so that the work may benefit from
scheduler task placement.
Cc: Mika Westerberg <westeri@kernel.org>
Cc: Yehezkel Bernat <YehezkelShB@gmail.com>
Signed-off-by: Marco Crivellari <redacted>
Acked-by: Mika Westerberg <westeri@kernel.org>
---
drivers/net/thunderbolt/main.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
From: Marco Crivellari <hidden> Date: 2026-07-06 13:40:47
Currently the code enqueue work items using {queue|mod}_delayed_work(),
using system_long_wq. This workqueue should be used when long works are
expected and it is a per-cpu workqueue.
The function(s) end up calling __queue_delayed_work(), which set a global
timer that could fire anywhere, enqueuing the work where the timer fired.
Unbound works could benefit from scheduler task placement, to optimize
performance and power consumption. Long work shouldn't stick to a single
CPU.
Recently, a new unbound workqueue specific for long running work has
been added:
c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change
system_long_wq with system_dfl_long_wq so that the work may benefit from
scheduler task placement.
Cc: Petko Manolov <petkan@nucleusys.com>
Cc: linux-usb@vger.kernel.org
Signed-off-by: Marco Crivellari <redacted>
---
drivers/net/usb/pegasus.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
From: Marco Crivellari <hidden> Date: 2026-07-06 13:40:48
Currently the code enqueue work items using {queue|mod}_delayed_work(),
using system_long_wq. This workqueue should be used when long works are
expected and it is a per-cpu workqueue.
The function(s) end up calling __queue_delayed_work(), which set a global
timer that could fire anywhere, enqueuing the work where the timer fired.
Unbound works could benefit from scheduler task placement, to optimize
performance and power consumption. Long work shouldn't stick to a single
CPU.
Recently, a new unbound workqueue specific for long running work has
been added:
c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change
system_long_wq with system_dfl_long_wq so that the work may benefit from
scheduler task placement.
Cc: Ethan Nelson-Moore <redacted>
Cc: linux-usb@vger.kernel.org
Signed-off-by: Marco Crivellari <redacted>
---
drivers/net/usb/r8152.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
@@ -7072,7 +7072,8 @@ static void rtl_hw_phy_work_func_t(struct work_struct *work)/* Delay execution in case request_firmware() is not ready yet.*/-queue_delayed_work(system_long_wq,&tp->hw_phy_work,HZ*10);+queue_delayed_work(system_dfl_long_wq,&tp->hw_phy_work,+HZ*10);gotoignore_once;}
@@ -8840,7 +8841,7 @@ static int rtl8152_reset_resume(struct usb_interface *intf)clear_bit(SELECTIVE_SUSPEND,&tp->flags);rtl_reset_ocp_base(tp);tp->rtl_ops.init(tp);-queue_delayed_work(system_long_wq,&tp->hw_phy_work,0);+queue_delayed_work(system_dfl_long_wq,&tp->hw_phy_work,0);set_ethernet_addr(tp,true);returnrtl8152_resume(intf);}
@@ -10295,7 +10296,7 @@ static int rtl8152_probe_once(struct usb_interface *intf,/* Retry in case request_firmware() is not ready yet. */tp->rtl_fw.retry=true;#endif-queue_delayed_work(system_long_wq,&tp->hw_phy_work,0);+queue_delayed_work(system_dfl_long_wq,&tp->hw_phy_work,0);set_ethernet_addr(tp,false);usb_set_intfdata(intf,tp);
From: Marco Crivellari <hidden> Date: 2026-07-06 13:40:51
Currently the code enqueue work items using {queue|mod}_delayed_work(),
using system_long_wq. This workqueue should be used when long works are
expected and it is a per-cpu workqueue.
The function(s) end up calling __queue_delayed_work(), which set a global
timer that could fire anywhere, enqueuing the work where the timer fired.
Unbound works could benefit from scheduler task placement, to optimize
performance and power consumption. Long work shouldn't stick to a single
CPU.
Recently, a new unbound workqueue specific for long running work has
been added:
c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change
system_long_wq with system_dfl_long_wq so that the work may benefit from
scheduler task placement.
Cc: Haren Myneni <haren@linux.ibm.com>
Cc: Rick Lindsley <ricklind@linux.ibm.com>
Cc: Nick Child <nnac123@linux.ibm.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Marco Crivellari <redacted>
---
drivers/net/ethernet/ibm/ibmvnic.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
From: Marco Crivellari <hidden> Date: 2026-07-06 13:40:57
Currently the code enqueue work items using {queue|mod}_delayed_work(),
using system_long_wq. This workqueue should be used when long works are
expected and it is a per-cpu workqueue.
The function(s) end up calling __queue_delayed_work(), which set a global
timer that could fire anywhere, enqueuing the work where the timer fired.
Unbound works could benefit from scheduler task placement, to optimize
performance and power consumption. Long work shouldn't stick to a single
CPU.
Recently, a new unbound workqueue specific for long running work has
been added:
c116737e972e ("workqueue: Add system_dfl_long_wq for long unbound works")
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change
system_long_wq with system_dfl_long_wq so that the work may benefit from
scheduler task placement.
Cc: MD Danish Anwar <danishanwar@ti.com>
Cc: Roger Quadros <rogerq@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org
Signed-off-by: Marco Crivellari <redacted>
Reviewed-by: Richard Cheng <redacted>
---
drivers/net/ethernet/ti/icssg/icssg_stats.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Simon Horman <horms@kernel.org> Date: 2026-07-11 16:46:39
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
Simon says: While I think that the pre-existing issue flagged below is
orthogonal to this patch, I wonder if you could respond
to the question regarding scope raised in the first
issue flagged below.
---
ibmvnic: Move long delayed work on system_dfl_long_wq
This commit changes the code to enqueue delayed reset work items using
system_dfl_long_wq instead of the per-cpu system_long_wq. The intent is to
allow these unbound, long-running works to benefit from scheduler task
placement.
[ ... ]
Since the workqueue work doesn't rely on per-cpu variables, there is no
obvious reason that justify the use of a per-cpu workqueue. So change
system_long_wq with system_dfl_long_wq so that the work may benefit from
scheduler task placement.
[Severity: Medium]
Will this leave the primary reset path bound to the per-cpu system_long_wq?
While this patch partially migrates delayed reset works to system_dfl_long_wq,
the initial reset work enqueued in ibmvnic_reset() is still bound to the
per-CPU system_long_wq:
drivers/net/ethernet/ibm/ibmvnic.c:ibmvnic_reset() {
...
rwi->reset_reason = reason;
list_add_tail(&rwi->list, &adapter->rwi_list);
netdev_dbg(adapter->netdev, "Scheduling reset (reason %s)\n",
reset_reason_to_string(reason));
queue_work(system_long_wq, &adapter->ibmvnic_reset);
...
}
Should queue_work(system_long_wq, ...) also be migrated to ensure the
reset workload fully benefits from scheduler placement as intended?
[Severity: High]
This is a pre-existing issue, but can this plain spin_lock() cause a
deadlock against the tasklet context?
The workqueue handler __ibmvnic_reset() acquires adapter->rwi_lock using a
plain spin_lock() without disabling softirqs.
If a hardware interrupt fires on the same CPU while this lock is held, local
softirqs are processed on return, invoking ibmvnic_tasklet().
If the tasklet handles CRQ events via ibmvnic_handle_crq() and triggers a
reset (e.g., IBMVNIC_DEVICE_FAILOVER):
ibmvnic_reset()
spin_lock_irqsave(&adapter->rwi_lock, flags);
Will the tasklet spin forever trying to acquire rwi_lock since the preempted
__ibmvnic_reset() already holds it?
if (!list_empty(&adapter->rwi_list)) {
if (test_and_set_bit_lock(0, &adapter->resetting)) {
- queue_delayed_work(system_long_wq,
+ queue_delayed_work(system_dfl_long_wq,
&adapter->ibmvnic_delayed_reset,
IBMVNIC_RESET_DELAY);
} else {
From: Simon Horman <horms@kernel.org> Date: 2026-07-11 16:47:43
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
net: ti: icssg-stats: Move long delayed work on system_dfl_long_wq
This commit changes the workqueue used for hardware statistics from the
per-CPU system_long_wq to the unbound system_dfl_long_wq. The goal is to
benefit from scheduler task placement since the work doesn't rely on
per-CPU variables.
[Severity: Low]
This refactoring appears incomplete. While this changes the delayed work
re-queuing, shouldn't the initial queue_work() calls also use
system_dfl_long_wq?
For example, in drivers/net/ethernet/ti/icssg/icssg_prueth.c:emac_ndo_open():
queue_work(system_long_wq, &emac->stats_work.work);
And similarly in drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c:emac_ndo_open():
queue_work(system_long_wq, &emac->stats_work.work);
From: Marco Crivellari <hidden> Date: 2026-07-15 10:28:16
Hi,
On Sat, Jul 11, 2026 at 6:46 PM Simon Horman [off-list ref] wrote:
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
Simon says: While I think that the pre-existing issue flagged below is
orthogonal to this patch, I wonder if you could respond
to the question regarding scope raised in the first
issue flagged below.
[...]
[Severity: Medium]
Will this leave the primary reset path bound to the per-cpu system_long_wq?
While this patch partially migrates delayed reset works to system_dfl_long_wq,
the initial reset work enqueued in ibmvnic_reset() is still bound to the
per-CPU system_long_wq:
I will double check the code but converting that workqueue seems doable.
Thanks!
--
Marco Crivellari
SUSE Labs