Skip to content

Commit 6037315

Browse files
committed
workqueue: use mutex for global_cwq manager exclusion
POOL_MANAGING_WORKERS is used to ensure that at most one worker takes the manager role at any given time on a given global_cwq. Trustee later hitched on it to assume manager adding blocking wait for the bit. As trustee already needed a custom wait mechanism, waiting for MANAGING_WORKERS was rolled into the same mechanism. Trustee is scheduled to be removed. This patch separates out MANAGING_WORKERS wait into per-pool mutex. Workers use mutex_trylock() to test for manager role and trustee uses mutex_lock() to claim manager roles. gcwq_claim/release_management() helpers are added to grab and release manager roles of all pools on a global_cwq. gcwq_claim_management() always grabs pool manager mutexes in ascending pool index order and uses pool index as lockdep subclass. Signed-off-by: Tejun Heo <[email protected]> Acked-by: "Rafael J. Wysocki" <[email protected]>
1 parent 403c821 commit 6037315

File tree

1 file changed

+26
-39
lines changed

1 file changed

+26
-39
lines changed

kernel/workqueue.c

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ enum {
5151

5252
/* pool flags */
5353
POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
54-
POOL_MANAGING_WORKERS = 1 << 1, /* managing workers */
5554

5655
/* worker flags */
5756
WORKER_STARTED = 1 << 0, /* started */
@@ -155,6 +154,7 @@ struct worker_pool {
155154
struct timer_list idle_timer; /* L: worker idle timeout */
156155
struct timer_list mayday_timer; /* L: SOS timer for workers */
157156

157+
struct mutex manager_mutex; /* mutex manager should hold */
158158
struct ida worker_ida; /* L: for worker IDs */
159159
struct worker *first_idle; /* L: first idle worker */
160160
};
@@ -644,7 +644,7 @@ static bool need_to_manage_workers(struct worker_pool *pool)
644644
/* Do we have too many workers and should some go away? */
645645
static bool too_many_workers(struct worker_pool *pool)
646646
{
647-
bool managing = pool->flags & POOL_MANAGING_WORKERS;
647+
bool managing = mutex_is_locked(&pool->manager_mutex);
648648
int nr_idle = pool->nr_idle + managing; /* manager is considered idle */
649649
int nr_busy = pool->nr_workers - nr_idle;
650650

@@ -1655,14 +1655,12 @@ static bool maybe_destroy_workers(struct worker_pool *pool)
16551655
static bool manage_workers(struct worker *worker)
16561656
{
16571657
struct worker_pool *pool = worker->pool;
1658-
struct global_cwq *gcwq = pool->gcwq;
16591658
bool ret = false;
16601659

1661-
if (pool->flags & POOL_MANAGING_WORKERS)
1660+
if (!mutex_trylock(&pool->manager_mutex))
16621661
return ret;
16631662

16641663
pool->flags &= ~POOL_MANAGE_WORKERS;
1665-
pool->flags |= POOL_MANAGING_WORKERS;
16661664

16671665
/*
16681666
* Destroy and then create so that may_start_working() is true
@@ -1671,15 +1669,7 @@ static bool manage_workers(struct worker *worker)
16711669
ret |= maybe_destroy_workers(pool);
16721670
ret |= maybe_create_worker(pool);
16731671

1674-
pool->flags &= ~POOL_MANAGING_WORKERS;
1675-
1676-
/*
1677-
* The trustee might be waiting to take over the manager
1678-
* position, tell it we're done.
1679-
*/
1680-
if (unlikely(gcwq->trustee))
1681-
wake_up_all(&gcwq->trustee_wait);
1682-
1672+
mutex_unlock(&pool->manager_mutex);
16831673
return ret;
16841674
}
16851675

@@ -3255,6 +3245,24 @@ EXPORT_SYMBOL_GPL(work_busy);
32553245
* ----------------> RELEASE --------------
32563246
*/
32573247

3248+
/* claim manager positions of all pools */
3249+
static void gcwq_claim_management(struct global_cwq *gcwq)
3250+
{
3251+
struct worker_pool *pool;
3252+
3253+
for_each_worker_pool(pool, gcwq)
3254+
mutex_lock_nested(&pool->manager_mutex, pool - gcwq->pools);
3255+
}
3256+
3257+
/* release manager positions */
3258+
static void gcwq_release_management(struct global_cwq *gcwq)
3259+
{
3260+
struct worker_pool *pool;
3261+
3262+
for_each_worker_pool(pool, gcwq)
3263+
mutex_unlock(&pool->manager_mutex);
3264+
}
3265+
32583266
/**
32593267
* trustee_wait_event_timeout - timed event wait for trustee
32603268
* @cond: condition to wait for
@@ -3304,16 +3312,6 @@ EXPORT_SYMBOL_GPL(work_busy);
33043312
__ret1 < 0 ? -1 : 0; \
33053313
})
33063314

3307-
static bool gcwq_is_managing_workers(struct global_cwq *gcwq)
3308-
{
3309-
struct worker_pool *pool;
3310-
3311-
for_each_worker_pool(pool, gcwq)
3312-
if (pool->flags & POOL_MANAGING_WORKERS)
3313-
return true;
3314-
return false;
3315-
}
3316-
33173315
static bool gcwq_has_idle_workers(struct global_cwq *gcwq)
33183316
{
33193317
struct worker_pool *pool;
@@ -3336,28 +3334,18 @@ static int __cpuinit trustee_thread(void *__gcwq)
33363334

33373335
BUG_ON(gcwq->cpu != smp_processor_id());
33383336

3337+
gcwq_claim_management(gcwq);
33393338
spin_lock_irq(&gcwq->lock);
3340-
/*
3341-
* Claim the manager position and make all workers rogue.
3342-
* Trustee must be bound to the target cpu and can't be
3343-
* cancelled.
3344-
*/
3345-
BUG_ON(gcwq->cpu != smp_processor_id());
3346-
rc = trustee_wait_event(!gcwq_is_managing_workers(gcwq));
3347-
BUG_ON(rc < 0);
33483339

33493340
/*
33503341
* We've claimed all manager positions. Make all workers unbound
33513342
* and set DISASSOCIATED. Before this, all workers except for the
33523343
* ones which are still executing works from before the last CPU
33533344
* down must be on the cpu. After this, they may become diasporas.
33543345
*/
3355-
for_each_worker_pool(pool, gcwq) {
3356-
pool->flags |= POOL_MANAGING_WORKERS;
3357-
3346+
for_each_worker_pool(pool, gcwq)
33583347
list_for_each_entry(worker, &pool->idle_list, entry)
33593348
worker->flags |= WORKER_UNBOUND;
3360-
}
33613349

33623350
for_each_busy_worker(worker, i, pos, gcwq)
33633351
worker->flags |= WORKER_UNBOUND;
@@ -3497,9 +3485,7 @@ static int __cpuinit trustee_thread(void *__gcwq)
34973485
work_color_to_flags(WORK_NO_COLOR));
34983486
}
34993487

3500-
/* relinquish manager role */
3501-
for_each_worker_pool(pool, gcwq)
3502-
pool->flags &= ~POOL_MANAGING_WORKERS;
3488+
gcwq_release_management(gcwq);
35033489

35043490
/* notify completion */
35053491
gcwq->trustee = NULL;
@@ -3894,6 +3880,7 @@ static int __init init_workqueues(void)
38943880
setup_timer(&pool->mayday_timer, gcwq_mayday_timeout,
38953881
(unsigned long)pool);
38963882

3883+
mutex_init(&pool->manager_mutex);
38973884
ida_init(&pool->worker_ida);
38983885
}
38993886

0 commit comments

Comments
 (0)