aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/workqueue.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2012-07-17 15:39:27 -0400
committerTejun Heo <tj@kernel.org>2012-07-17 15:39:27 -0400
commit6037315269d62bf967286ae2670fdd6b6acedab9 (patch)
treec476298b57c0a33aa7fe3c898d62ce17eb11d2ad /kernel/workqueue.c
parent403c821d452c03be4ced571ac91339a9d3631b17 (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.c65
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? */
645static bool too_many_workers(struct worker_pool *pool) 645static 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)
1655static bool manage_workers(struct worker *worker) 1655static 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 */
3249static 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 */
3258static 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
3307static 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
3317static bool gcwq_has_idle_workers(struct global_cwq *gcwq) 3315static 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