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
2302{2302{
2303 struct pool_workqueue *pwq;2303 struct pool_workqueue *pwq;
2304 struct worker_pool *last_pool, *pool;2304 struct worker_pool *last_pool, *pool;
2305 struct task_struct *wake_task = NULL;
2305 unsigned int work_flags;2306 unsigned int work_flags;
2306 unsigned int req_cpu = cpu;2307 unsigned int req_cpu = cpu;
2307 2308
@@ -2424,7 +2425,7 @@ retry: · 116 unmodified lines
2424 2425
2425 trace_workqueue_activate_work(work);2426 trace_workqueue_activate_work(work);
2426 insert_work(pwq, work, &pool->worklist, work_flags);2427 insert_work(pwq, work, &pool->worklist, work_flags);
2427 kick_pool(pool);2428 kick_pool_pick(pool, &wake_task);
2428 } else {2429 } else {
2429 work_flags |= WORK_STRUCT_INACTIVE;2430 work_flags |= WORK_STRUCT_INACTIVE;
2430 insert_work(pwq, work, &pwq->inactive_works, work_flags);2431 insert_work(pwq, work, &pwq->inactive_works, work_flags);
@@ -2432,6 +2433,8 @@ retry: · 1 unmodified line
2432 2433
2433out:2434out:
2434 raw_spin_unlock(&pool->lock);2435 raw_spin_unlock(&pool->lock);
2436 if (wake_task)
2437 wake_up_process(wake_task);
2435 rcu_read_unlock();2438 rcu_read_unlock();
2436}2439}
2437 2440