提交 60373152 编写于 作者: T Tejun Heo

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: NTejun Heo <tj@kernel.org>
Acked-by: N"Rafael J. Wysocki" <rjw@sisk.pl>
上级 403c821d
...@@ -51,7 +51,6 @@ enum { ...@@ -51,7 +51,6 @@ enum {
/* pool flags */ /* pool flags */
POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */ POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
POOL_MANAGING_WORKERS = 1 << 1, /* managing workers */
/* worker flags */ /* worker flags */
WORKER_STARTED = 1 << 0, /* started */ WORKER_STARTED = 1 << 0, /* started */
...@@ -155,6 +154,7 @@ struct worker_pool { ...@@ -155,6 +154,7 @@ struct worker_pool {
struct timer_list idle_timer; /* L: worker idle timeout */ struct timer_list idle_timer; /* L: worker idle timeout */
struct timer_list mayday_timer; /* L: SOS timer for workers */ struct timer_list mayday_timer; /* L: SOS timer for workers */
struct mutex manager_mutex; /* mutex manager should hold */
struct ida worker_ida; /* L: for worker IDs */ struct ida worker_ida; /* L: for worker IDs */
struct worker *first_idle; /* L: first idle worker */ struct worker *first_idle; /* L: first idle worker */
}; };
...@@ -644,7 +644,7 @@ static bool need_to_manage_workers(struct worker_pool *pool) ...@@ -644,7 +644,7 @@ static bool need_to_manage_workers(struct worker_pool *pool)
/* Do we have too many workers and should some go away? */ /* Do we have too many workers and should some go away? */
static bool too_many_workers(struct worker_pool *pool) static bool too_many_workers(struct worker_pool *pool)
{ {
bool managing = pool->flags & POOL_MANAGING_WORKERS; bool managing = mutex_is_locked(&pool->manager_mutex);
int nr_idle = pool->nr_idle + managing; /* manager is considered idle */ int nr_idle = pool->nr_idle + managing; /* manager is considered idle */
int nr_busy = pool->nr_workers - nr_idle; int nr_busy = pool->nr_workers - nr_idle;
...@@ -1655,14 +1655,12 @@ static bool maybe_destroy_workers(struct worker_pool *pool) ...@@ -1655,14 +1655,12 @@ static bool maybe_destroy_workers(struct worker_pool *pool)
static bool manage_workers(struct worker *worker) static bool manage_workers(struct worker *worker)
{ {
struct worker_pool *pool = worker->pool; struct worker_pool *pool = worker->pool;
struct global_cwq *gcwq = pool->gcwq;
bool ret = false; bool ret = false;
if (pool->flags & POOL_MANAGING_WORKERS) if (!mutex_trylock(&pool->manager_mutex))
return ret; return ret;
pool->flags &= ~POOL_MANAGE_WORKERS; pool->flags &= ~POOL_MANAGE_WORKERS;
pool->flags |= POOL_MANAGING_WORKERS;
/* /*
* Destroy and then create so that may_start_working() is true * Destroy and then create so that may_start_working() is true
...@@ -1671,15 +1669,7 @@ static bool manage_workers(struct worker *worker) ...@@ -1671,15 +1669,7 @@ static bool manage_workers(struct worker *worker)
ret |= maybe_destroy_workers(pool); ret |= maybe_destroy_workers(pool);
ret |= maybe_create_worker(pool); ret |= maybe_create_worker(pool);
pool->flags &= ~POOL_MANAGING_WORKERS; mutex_unlock(&pool->manager_mutex);
/*
* The trustee might be waiting to take over the manager
* position, tell it we're done.
*/
if (unlikely(gcwq->trustee))
wake_up_all(&gcwq->trustee_wait);
return ret; return ret;
} }
...@@ -3255,6 +3245,24 @@ EXPORT_SYMBOL_GPL(work_busy); ...@@ -3255,6 +3245,24 @@ EXPORT_SYMBOL_GPL(work_busy);
* ----------------> RELEASE -------------- * ----------------> RELEASE --------------
*/ */
/* claim manager positions of all pools */
static void gcwq_claim_management(struct global_cwq *gcwq)
{
struct worker_pool *pool;
for_each_worker_pool(pool, gcwq)
mutex_lock_nested(&pool->manager_mutex, pool - gcwq->pools);
}
/* release manager positions */
static void gcwq_release_management(struct global_cwq *gcwq)
{
struct worker_pool *pool;
for_each_worker_pool(pool, gcwq)
mutex_unlock(&pool->manager_mutex);
}
/** /**
* trustee_wait_event_timeout - timed event wait for trustee * trustee_wait_event_timeout - timed event wait for trustee
* @cond: condition to wait for * @cond: condition to wait for
...@@ -3304,16 +3312,6 @@ EXPORT_SYMBOL_GPL(work_busy); ...@@ -3304,16 +3312,6 @@ EXPORT_SYMBOL_GPL(work_busy);
__ret1 < 0 ? -1 : 0; \ __ret1 < 0 ? -1 : 0; \
}) })
static bool gcwq_is_managing_workers(struct global_cwq *gcwq)
{
struct worker_pool *pool;
for_each_worker_pool(pool, gcwq)
if (pool->flags & POOL_MANAGING_WORKERS)
return true;
return false;
}
static bool gcwq_has_idle_workers(struct global_cwq *gcwq) static bool gcwq_has_idle_workers(struct global_cwq *gcwq)
{ {
struct worker_pool *pool; struct worker_pool *pool;
...@@ -3336,15 +3334,8 @@ static int __cpuinit trustee_thread(void *__gcwq) ...@@ -3336,15 +3334,8 @@ static int __cpuinit trustee_thread(void *__gcwq)
BUG_ON(gcwq->cpu != smp_processor_id()); BUG_ON(gcwq->cpu != smp_processor_id());
gcwq_claim_management(gcwq);
spin_lock_irq(&gcwq->lock); spin_lock_irq(&gcwq->lock);
/*
* Claim the manager position and make all workers rogue.
* Trustee must be bound to the target cpu and can't be
* cancelled.
*/
BUG_ON(gcwq->cpu != smp_processor_id());
rc = trustee_wait_event(!gcwq_is_managing_workers(gcwq));
BUG_ON(rc < 0);
/* /*
* We've claimed all manager positions. Make all workers unbound * We've claimed all manager positions. Make all workers unbound
...@@ -3352,12 +3343,9 @@ static int __cpuinit trustee_thread(void *__gcwq) ...@@ -3352,12 +3343,9 @@ static int __cpuinit trustee_thread(void *__gcwq)
* ones which are still executing works from before the last CPU * ones which are still executing works from before the last CPU
* down must be on the cpu. After this, they may become diasporas. * down must be on the cpu. After this, they may become diasporas.
*/ */
for_each_worker_pool(pool, gcwq) { for_each_worker_pool(pool, gcwq)
pool->flags |= POOL_MANAGING_WORKERS;
list_for_each_entry(worker, &pool->idle_list, entry) list_for_each_entry(worker, &pool->idle_list, entry)
worker->flags |= WORKER_UNBOUND; worker->flags |= WORKER_UNBOUND;
}
for_each_busy_worker(worker, i, pos, gcwq) for_each_busy_worker(worker, i, pos, gcwq)
worker->flags |= WORKER_UNBOUND; worker->flags |= WORKER_UNBOUND;
...@@ -3497,9 +3485,7 @@ static int __cpuinit trustee_thread(void *__gcwq) ...@@ -3497,9 +3485,7 @@ static int __cpuinit trustee_thread(void *__gcwq)
work_color_to_flags(WORK_NO_COLOR)); work_color_to_flags(WORK_NO_COLOR));
} }
/* relinquish manager role */ gcwq_release_management(gcwq);
for_each_worker_pool(pool, gcwq)
pool->flags &= ~POOL_MANAGING_WORKERS;
/* notify completion */ /* notify completion */
gcwq->trustee = NULL; gcwq->trustee = NULL;
...@@ -3894,6 +3880,7 @@ static int __init init_workqueues(void) ...@@ -3894,6 +3880,7 @@ static int __init init_workqueues(void)
setup_timer(&pool->mayday_timer, gcwq_mayday_timeout, setup_timer(&pool->mayday_timer, gcwq_mayday_timeout,
(unsigned long)pool); (unsigned long)pool);
mutex_init(&pool->manager_mutex);
ida_init(&pool->worker_ida); ida_init(&pool->worker_ida);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册