diff options
| author | Tejun Heo <tj@kernel.org> | 2012-07-17 15:39:27 -0400 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2012-07-17 15:39:27 -0400 |
| commit | 6037315269d62bf967286ae2670fdd6b6acedab9 (patch) | |
| tree | c476298b57c0a33aa7fe3c898d62ce17eb11d2ad /kernel | |
| parent | 403c821d452c03be4ced571ac91339a9d3631b17 (diff) | |
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 <tj@kernel.org>
Acked-by: "Rafael J. Wysocki" <rjw@sisk.pl>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/workqueue.c | 65 |
1 files changed, 26 insertions, 39 deletions
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index af512927c607..f7a00697d150 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
| @@ -51,7 +51,6 @@ enum { | |||
| 51 | 51 | ||
| 52 | /* pool flags */ | 52 | /* pool flags */ |
| 53 | POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */ | 53 | POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */ |
| 54 | POOL_MANAGING_WORKERS = 1 << 1, /* managing workers */ | ||
| 55 | 54 | ||
| 56 | /* worker flags */ | 55 | /* worker flags */ |
| 57 | WORKER_STARTED = 1 << 0, /* started */ | 56 | WORKER_STARTED = 1 << 0, /* started */ |
| @@ -155,6 +154,7 @@ struct worker_pool { | |||
| 155 | struct timer_list idle_timer; /* L: worker idle timeout */ | 154 | struct timer_list idle_timer; /* L: worker idle timeout */ |
| 156 | struct timer_list mayday_timer; /* L: SOS timer for workers */ | 155 | struct timer_list mayday_timer; /* L: SOS timer for workers */ |
| 157 | 156 | ||
| 157 | struct mutex manager_mutex; /* mutex manager should hold */ | ||
| 158 | struct ida worker_ida; /* L: for worker IDs */ | 158 | struct ida worker_ida; /* L: for worker IDs */ |
| 159 | struct worker *first_idle; /* L: first idle worker */ | 159 | struct worker *first_idle; /* L: first idle worker */ |
| 160 | }; | 160 | }; |
| @@ -644,7 +644,7 @@ static bool need_to_manage_workers(struct worker_pool *pool) | |||
| 644 | /* Do we have too many workers and should some go away? */ | 644 | /* Do we have too many workers and should some go away? */ |
| 645 | static bool too_many_workers(struct worker_pool *pool) | 645 | static bool too_many_workers(struct worker_pool *pool) |
| 646 | { | 646 | { |
| 647 | bool managing = pool->flags & POOL_MANAGING_WORKERS; | 647 | bool managing = mutex_is_locked(&pool->manager_mutex); |
| 648 | int nr_idle = pool->nr_idle + managing; /* manager is considered idle */ | 648 | int nr_idle = pool->nr_idle + managing; /* manager is considered idle */ |
| 649 | int nr_busy = pool->nr_workers - nr_idle; | 649 | int nr_busy = pool->nr_workers - nr_idle; |
| 650 | 650 | ||
| @@ -1655,14 +1655,12 @@ static bool maybe_destroy_workers(struct worker_pool *pool) | |||
| 1655 | static bool manage_workers(struct worker *worker) | 1655 | static bool manage_workers(struct worker *worker) |
| 1656 | { | 1656 | { |
| 1657 | struct worker_pool *pool = worker->pool; | 1657 | struct worker_pool *pool = worker->pool; |
| 1658 | struct global_cwq *gcwq = pool->gcwq; | ||
| 1659 | bool ret = false; | 1658 | bool ret = false; |
| 1660 | 1659 | ||
| 1661 | if (pool->flags & POOL_MANAGING_WORKERS) | 1660 | if (!mutex_trylock(&pool->manager_mutex)) |
| 1662 | return ret; | 1661 | return ret; |
| 1663 | 1662 | ||
| 1664 | pool->flags &= ~POOL_MANAGE_WORKERS; | 1663 | pool->flags &= ~POOL_MANAGE_WORKERS; |
| 1665 | pool->flags |= POOL_MANAGING_WORKERS; | ||
| 1666 | 1664 | ||
| 1667 | /* | 1665 | /* |
| 1668 | * Destroy and then create so that may_start_working() is true | 1666 | * Destroy and then create so that may_start_working() is true |
| @@ -1671,15 +1669,7 @@ static bool manage_workers(struct worker *worker) | |||
| 1671 | ret |= maybe_destroy_workers(pool); | 1669 | ret |= maybe_destroy_workers(pool); |
| 1672 | ret |= maybe_create_worker(pool); | 1670 | ret |= maybe_create_worker(pool); |
| 1673 | 1671 | ||
| 1674 | pool->flags &= ~POOL_MANAGING_WORKERS; | 1672 | mutex_unlock(&pool->manager_mutex); |
| 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 | |||
| 1683 | return ret; | 1673 | return ret; |
| 1684 | } | 1674 | } |
| 1685 | 1675 | ||
| @@ -3255,6 +3245,24 @@ EXPORT_SYMBOL_GPL(work_busy); | |||
| 3255 | * ----------------> RELEASE -------------- | 3245 | * ----------------> RELEASE -------------- |
| 3256 | */ | 3246 | */ |
| 3257 | 3247 | ||
| 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 | |||
| 3258 | /** | 3266 | /** |
| 3259 | * trustee_wait_event_timeout - timed event wait for trustee | 3267 | * trustee_wait_event_timeout - timed event wait for trustee |
| 3260 | * @cond: condition to wait for | 3268 | * @cond: condition to wait for |
| @@ -3304,16 +3312,6 @@ EXPORT_SYMBOL_GPL(work_busy); | |||
| 3304 | __ret1 < 0 ? -1 : 0; \ | 3312 | __ret1 < 0 ? -1 : 0; \ |
| 3305 | }) | 3313 | }) |
| 3306 | 3314 | ||
| 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 | |||
| 3317 | static bool gcwq_has_idle_workers(struct global_cwq *gcwq) | 3315 | static bool gcwq_has_idle_workers(struct global_cwq *gcwq) |
| 3318 | { | 3316 | { |
| 3319 | struct worker_pool *pool; | 3317 | struct worker_pool *pool; |
| @@ -3336,15 +3334,8 @@ static int __cpuinit trustee_thread(void *__gcwq) | |||
| 3336 | 3334 | ||
| 3337 | BUG_ON(gcwq->cpu != smp_processor_id()); | 3335 | BUG_ON(gcwq->cpu != smp_processor_id()); |
| 3338 | 3336 | ||
| 3337 | gcwq_claim_management(gcwq); | ||
| 3339 | spin_lock_irq(&gcwq->lock); | 3338 | 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); | ||
| 3348 | 3339 | ||
| 3349 | /* | 3340 | /* |
| 3350 | * We've claimed all manager positions. Make all workers unbound | 3341 | * We've claimed all manager positions. Make all workers unbound |
| @@ -3352,12 +3343,9 @@ static int __cpuinit trustee_thread(void *__gcwq) | |||
| 3352 | * ones which are still executing works from before the last CPU | 3343 | * ones which are still executing works from before the last CPU |
| 3353 | * down must be on the cpu. After this, they may become diasporas. | 3344 | * down must be on the cpu. After this, they may become diasporas. |
| 3354 | */ | 3345 | */ |
| 3355 | for_each_worker_pool(pool, gcwq) { | 3346 | for_each_worker_pool(pool, gcwq) |
| 3356 | pool->flags |= POOL_MANAGING_WORKERS; | ||
| 3357 | |||
| 3358 | list_for_each_entry(worker, &pool->idle_list, entry) | 3347 | list_for_each_entry(worker, &pool->idle_list, entry) |
| 3359 | worker->flags |= WORKER_UNBOUND; | 3348 | worker->flags |= WORKER_UNBOUND; |
| 3360 | } | ||
| 3361 | 3349 | ||
| 3362 | for_each_busy_worker(worker, i, pos, gcwq) | 3350 | for_each_busy_worker(worker, i, pos, gcwq) |
| 3363 | worker->flags |= WORKER_UNBOUND; | 3351 | worker->flags |= WORKER_UNBOUND; |
| @@ -3497,9 +3485,7 @@ static int __cpuinit trustee_thread(void *__gcwq) | |||
| 3497 | work_color_to_flags(WORK_NO_COLOR)); | 3485 | work_color_to_flags(WORK_NO_COLOR)); |
| 3498 | } | 3486 | } |
| 3499 | 3487 | ||
| 3500 | /* relinquish manager role */ | 3488 | gcwq_release_management(gcwq); |
| 3501 | for_each_worker_pool(pool, gcwq) | ||
| 3502 | pool->flags &= ~POOL_MANAGING_WORKERS; | ||
| 3503 | 3489 | ||
| 3504 | /* notify completion */ | 3490 | /* notify completion */ |
| 3505 | gcwq->trustee = NULL; | 3491 | gcwq->trustee = NULL; |
| @@ -3894,6 +3880,7 @@ static int __init init_workqueues(void) | |||
| 3894 | setup_timer(&pool->mayday_timer, gcwq_mayday_timeout, | 3880 | setup_timer(&pool->mayday_timer, gcwq_mayday_timeout, |
| 3895 | (unsigned long)pool); | 3881 | (unsigned long)pool); |
| 3896 | 3882 | ||
| 3883 | mutex_init(&pool->manager_mutex); | ||
| 3897 | ida_init(&pool->worker_ida); | 3884 | ida_init(&pool->worker_ida); |
| 3898 | } | 3885 | } |
| 3899 | 3886 | ||
