* [PATCH v4 1/3] workqueue: split kick_pool() into kick_pool_pick() + wake_up_q()
2026-06-24 11:47 [PATCH v4 0/3] workqueue: Shrink the lock time Breno Leitao
@ 2026-06-24 11:47 ` Breno Leitao
2026-06-24 18:46 ` Tejun Heo
2026-06-24 11:47 ` [PATCH v4 2/3] workqueue: defer the worker wakeup outside pool->lock in __queue_work() Breno Leitao
` (2 subsequent siblings)
3 siblings, 1 reply; 7+ messages in thread
From: Breno Leitao @ 2026-06-24 11:47 UTC (permalink / raw)
To: Tejun Heo, Lai Jiangshan, bigeasy
Cc: linux-kernel, marco.crivellari, frederic, bigeasy, Hillf Danton,
Breno Leitao, kernel-team, kmagar, psuriset, david.dai
Factor the worker selection out of kick_pool() into kick_pool_pick(),
which picks and claims the worker under pool->lock but, instead of waking
it, queues it on a caller-provided wake_q via wake_q_add(). The caller
issues the wakeup later with wake_up_q(). wake_q_add() is safe under the
lock (cmpxchg + get_task_struct()); only wake_up_q() takes rq->lock.
kick_pool() becomes a thin wrapper using a local wake_q, so all existing
callers keep waking under pool->lock.
Pure refactor, no functional change.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
kernel/workqueue.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 78f25afb4a9d6..fd3b5bc78df9e 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -52,6 +52,7 @@
#include <linux/uaccess.h>
#include <linux/sched/isolation.h>
#include <linux/sched/debug.h>
+#include <linux/sched/wake_q.h>
#include <linux/nmi.h>
#include <linux/kvm_para.h>
#include <linux/delay.h>
@@ -1258,13 +1259,17 @@ static void kick_bh_pool(struct worker_pool *pool)
}
/**
- * kick_pool - wake up an idle worker if necessary
+ * kick_pool_pick - select and claim an idle worker, deferring the wakeup
* @pool: pool to kick
+ * @wakeq: wake_q to queue the selected worker on
*
- * @pool may have pending work items. Wake up worker if necessary. Returns
- * whether a worker was woken up.
+ * Like kick_pool() but queues the picked worker on @wakeq (wake_q_add())
+ * instead of waking it, so the caller can wake_up_q(@wakeq) after dropping
+ * pool->lock. Returns whether a worker was selected.
+ *
+ * Must be called with @pool->lock held.
*/
-static bool kick_pool(struct worker_pool *pool)
+static bool kick_pool_pick(struct worker_pool *pool, struct wake_q_head *wakeq)
{
struct worker *worker = first_idle_worker(pool);
struct task_struct *p;
@@ -1310,10 +1315,27 @@ static bool kick_pool(struct worker_pool *pool)
}
}
#endif
- wake_up_process(p);
+ wake_q_add(wakeq, p);
return true;
}
+/**
+ * kick_pool - wake up an idle worker if necessary
+ * @pool: pool to kick
+ *
+ * @pool may have pending work items. Wake up worker if necessary. Returns
+ * whether a worker was woken up.
+ */
+static bool kick_pool(struct worker_pool *pool)
+{
+ DEFINE_WAKE_Q(wakeq);
+ bool kicked;
+
+ kicked = kick_pool_pick(pool, &wakeq);
+ wake_up_q(&wakeq);
+ return kicked;
+}
+
#ifdef CONFIG_WQ_CPU_INTENSIVE_REPORT
/*
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v4 1/3] workqueue: split kick_pool() into kick_pool_pick() + wake_up_q()
2026-06-24 11:47 ` [PATCH v4 1/3] workqueue: split kick_pool() into kick_pool_pick() + wake_up_q() Breno Leitao
@ 2026-06-24 18:46 ` Tejun Heo
2026-06-25 16:51 ` Breno Leitao
0 siblings, 1 reply; 7+ messages in thread
From: Tejun Heo @ 2026-06-24 18:46 UTC (permalink / raw)
To: Breno Leitao
Cc: Lai Jiangshan, bigeasy, linux-kernel, marco.crivellari, frederic,
Hillf Danton, kernel-team, kmagar, psuriset, david.dai
On Wed, Jun 24, 2026 at 04:47:39AM -0700, Breno Leitao wrote:
> + wake_q_add(wakeq, p);
This is two extra atomic ops for every work item scheduling. This isn't
necessarily a deal braker but is this the only way to do this? Can't you
just stash the task pointer, extend irq disabled region and wake under rcu
protection?
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 1/3] workqueue: split kick_pool() into kick_pool_pick() + wake_up_q()
2026-06-24 18:46 ` Tejun Heo
@ 2026-06-25 16:51 ` Breno Leitao
0 siblings, 0 replies; 7+ messages in thread
From: Breno Leitao @ 2026-06-25 16:51 UTC (permalink / raw)
To: Tejun Heo
Cc: Lai Jiangshan, bigeasy, linux-kernel, marco.crivellari, frederic,
Hillf Danton, kernel-team, kmagar, psuriset, david.dai
On Wed, Jun 24, 2026 at 08:46:59AM -1000, Tejun Heo wrote:
> On Wed, Jun 24, 2026 at 04:47:39AM -0700, Breno Leitao wrote:
> > + wake_q_add(wakeq, p);
>
> This is two extra atomic ops for every work item scheduling. This isn't
> necessarily a deal braker but is this the only way to do this?
>
> Can't you just stash the task pointer, extend irq disabled region and wake under rcu
> protection?
Agreed, that's a better approach. Thanks for the suggestion.
I have a working PoC implementing this. I'll clean it up and post an
updated series.
Thanks,
--breno
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/3] workqueue: defer the worker wakeup outside pool->lock in __queue_work()
2026-06-24 11:47 [PATCH v4 0/3] workqueue: Shrink the lock time Breno Leitao
2026-06-24 11:47 ` [PATCH v4 1/3] workqueue: split kick_pool() into kick_pool_pick() + wake_up_q() Breno Leitao
@ 2026-06-24 11:47 ` Breno Leitao
2026-06-24 11:47 ` [PATCH v4 3/3] workqueue: defer the worker wakeup outside pool->lock in process_one_work() Breno Leitao
2026-06-24 15:30 ` [PATCH v4 0/3] workqueue: Shrink the lock time Sebastian Andrzej Siewior
3 siblings, 0 replies; 7+ messages in thread
From: Breno Leitao @ 2026-06-24 11:47 UTC (permalink / raw)
To: Tejun Heo, Lai Jiangshan, bigeasy
Cc: linux-kernel, marco.crivellari, frederic, bigeasy, Hillf Danton,
Breno Leitao, kernel-team, kmagar, psuriset, david.dai
__queue_work() is the enqueue hot path: it inserts the work item and
calls kick_pool() while holding pool->lock. kick_pool() ends in a
wakeup, which takes the target task's rq->lock, so rq->lock nests under
pool->lock on every enqueue that wakes a worker on a contended unbound
pool.
Use kick_pool_pick() to select and claim the worker under pool->lock,
queue it on an on-stack wake_q, and issue the wakeup with wake_up_q()
right after dropping the lock. Worker selection, wake_cpu setup and
claiming the worker off pool->idle_list still happen under the lock;
only the rq->lock acquisition moves out.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
kernel/workqueue.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index fd3b5bc78df9e..972f783f98281 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -2299,6 +2299,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq,
{
struct pool_workqueue *pwq;
struct worker_pool *last_pool, *pool;
+ DEFINE_WAKE_Q(wakeq);
unsigned int work_flags;
unsigned int req_cpu = cpu;
@@ -2421,7 +2422,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq,
trace_workqueue_activate_work(work);
insert_work(pwq, work, &pool->worklist, work_flags);
- kick_pool(pool);
+ kick_pool_pick(pool, &wakeq);
} else {
work_flags |= WORK_STRUCT_INACTIVE;
insert_work(pwq, work, &pwq->inactive_works, work_flags);
@@ -2429,6 +2430,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq,
out:
raw_spin_unlock(&pool->lock);
+ wake_up_q(&wakeq);
rcu_read_unlock();
}
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v4 3/3] workqueue: defer the worker wakeup outside pool->lock in process_one_work()
2026-06-24 11:47 [PATCH v4 0/3] workqueue: Shrink the lock time Breno Leitao
2026-06-24 11:47 ` [PATCH v4 1/3] workqueue: split kick_pool() into kick_pool_pick() + wake_up_q() Breno Leitao
2026-06-24 11:47 ` [PATCH v4 2/3] workqueue: defer the worker wakeup outside pool->lock in __queue_work() Breno Leitao
@ 2026-06-24 11:47 ` Breno Leitao
2026-06-24 15:30 ` [PATCH v4 0/3] workqueue: Shrink the lock time Sebastian Andrzej Siewior
3 siblings, 0 replies; 7+ messages in thread
From: Breno Leitao @ 2026-06-24 11:47 UTC (permalink / raw)
To: Tejun Heo, Lai Jiangshan, bigeasy
Cc: linux-kernel, marco.crivellari, frederic, bigeasy, Hillf Danton,
Breno Leitao, kernel-team, kmagar, psuriset, david.dai
process_one_work() kicks the pool to chain execution of the remaining
work items on WORKER_NOT_RUNNING pools (the UNBOUND and CPU_INTENSIVE
ones), calling kick_pool() while holding pool->lock. As in the enqueue
path, the wakeup pulls the target rq->lock in under pool->lock.
Use kick_pool_pick() to select and claim the worker under pool->lock and
issue the wakeup with wake_up_q() after the lock is dropped.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
kernel/workqueue.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 972f783f98281..ab62af99852ce 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3251,6 +3251,7 @@ __acquires(&pool->lock)
{
struct pool_workqueue *pwq = get_work_pwq(work);
struct worker_pool *pool = worker->pool;
+ DEFINE_WAKE_Q(wakeq);
unsigned long work_data;
int lockdep_start_depth, rcu_start_depth;
bool bh_draining = pool->flags & POOL_BH_DRAINING;
@@ -3305,7 +3306,7 @@ __acquires(&pool->lock)
* chain execution of the pending work items for WORKER_NOT_RUNNING
* workers such as the UNBOUND and CPU_INTENSIVE ones.
*/
- kick_pool(pool);
+ kick_pool_pick(pool, &wakeq);
/*
* Record the last pool and clear PENDING which should be the last
@@ -3317,6 +3318,7 @@ __acquires(&pool->lock)
pwq->stats[PWQ_STAT_STARTED]++;
raw_spin_unlock_irq(&pool->lock);
+ wake_up_q(&wakeq);
rcu_start_depth = rcu_preempt_depth();
lockdep_start_depth = lockdep_depth(current);
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v4 0/3] workqueue: Shrink the lock time
2026-06-24 11:47 [PATCH v4 0/3] workqueue: Shrink the lock time Breno Leitao
` (2 preceding siblings ...)
2026-06-24 11:47 ` [PATCH v4 3/3] workqueue: defer the worker wakeup outside pool->lock in process_one_work() Breno Leitao
@ 2026-06-24 15:30 ` Sebastian Andrzej Siewior
3 siblings, 0 replies; 7+ messages in thread
From: Sebastian Andrzej Siewior @ 2026-06-24 15:30 UTC (permalink / raw)
To: Breno Leitao
Cc: Tejun Heo, Lai Jiangshan, linux-kernel, marco.crivellari,
frederic, Hillf Danton, kernel-team, kmagar, psuriset, david.dai
On 2026-06-24 04:47:38 [-0700], Breno Leitao wrote:
> The goal of this patchset is to decrease the time spent under the
> workqueue pool->lock.
…
> Signed-off-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Sebastian
^ permalink raw reply [flat|nested] 7+ messages in thread