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>
This commit is contained in:
parent
403c821d45
commit
6037315269
1 changed files with 26 additions and 39 deletions
|
@ -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 {
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
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);
|
||||||
* ----------------> 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);
|
||||||
__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)
|
||||||
|
|
||||||
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)
|
||||||
* 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)
|
||||||
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)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue