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/workqueue.c | |
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/workqueue.c')
-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 | ||