diff options
| author | Breno Leitao <leitao@debian.org> | 2026-06-26 02:57:54 -0700 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-06-29 08:07:10 -1000 |
| commit | d070f2916ae918c4dddadce6160576c070efcd3e (patch) | |
| tree | 20a9f1cfd3716a88fe9df11bccd25627204f29bf /kernel/workqueue.c | |
| parent | 1ad5dcee7c819031cf02eaf5e1e03728d0ffeb09 (diff) | |
workqueue: defer the worker wakeup outside pool->lock in __queue_work()
__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 and
issue the wakeup with wake_up_process() right after dropping the lock.
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'kernel/workqueue.c')
| -rw-r--r-- | kernel/workqueue.c | 5 |
1 files changed, 4 insertions, 1 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 49770093e785..2d41000c918f 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2302,6 +2302,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, { struct pool_workqueue *pwq; struct worker_pool *last_pool, *pool; + struct task_struct *wake_task = NULL; unsigned int work_flags; unsigned int req_cpu = cpu; @@ -2424,7 +2425,7 @@ retry: trace_workqueue_activate_work(work); insert_work(pwq, work, &pool->worklist, work_flags); - kick_pool(pool); + kick_pool_pick(pool, &wake_task); } else { work_flags |= WORK_STRUCT_INACTIVE; insert_work(pwq, work, &pwq->inactive_works, work_flags); @@ -2432,6 +2433,8 @@ retry: out: raw_spin_unlock(&pool->lock); + if (wake_task) + wake_up_process(wake_task); rcu_read_unlock(); } |
