History

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.

Breno Leitao committed 4 months ago

Commit d070f291 parent 1ad5dce1 file · +4 −1Browse files

Showing the changes to kernel/workqueue.c (1 of 1 file). Show all

Unified · Split

kernel/workqueue.c+4−1
@@ -2302,6 +2302,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, · 2301 unmodified lines
23022302 {
23032303 struct pool_workqueue *pwq;
23042304 struct worker_pool *last_pool, *pool;
2305+ struct task_struct *wake_task = NULL;
23052306 unsigned int work_flags;
23062307 unsigned int req_cpu = cpu;
23072308
@@ -2424,7 +2425,7 @@ retry: · 116 unmodified lines
24242425
24252426 trace_workqueue_activate_work(work);
24262427 insert_work(pwq, work, &pool->worklist, work_flags);
2427− kick_pool(pool);
2428+ kick_pool_pick(pool, &wake_task);
24282429 } else {
24292430 work_flags |= WORK_STRUCT_INACTIVE;
24302431 insert_work(pwq, work, &pwq->inactive_works, work_flags);
@@ -2432,6 +2433,8 @@ retry: · 1 unmodified line
24322433
24332434 out:
24342435 raw_spin_unlock(&pool->lock);
2436+ if (wake_task)
2437+ wake_up_process(wake_task);
24352438 rcu_read_unlock();
24362439 }
24372440