Commit e22bee78 authored by Tejun Heo's avatar Tejun Heo

workqueue: implement concurrency managed dynamic worker pool

Instead of creating a worker for each cwq and putting it into the
shared pool, manage per-cpu workers dynamically.

Works aren't supposed to be cpu cycle hogs and maintaining just enough
concurrency to prevent work processing from stalling due to lack of
processing context is optimal.  gcwq keeps the number of concurrent
active workers to minimum but no less.  As long as there's one or more
running workers on the cpu, no new worker is scheduled so that works
can be processed in batch as much as possible but when the last
running worker blocks, gcwq immediately schedules new worker so that
the cpu doesn't sit idle while there are works to be processed.

gcwq always keeps at least single idle worker around.  When a new
worker is necessary and the worker is the last idle one, the worker
assumes the role of "manager" and manages the worker pool -
ie. creates another worker.  Forward-progress is guaranteed by having
dedicated rescue workers for workqueues which may be necessary while
creating a new worker.  When the manager is having problem creating a
new worker, mayday timer activates and rescue workers are summoned to
the cpu and execute works which might be necessary to create new
workers.

Trustee is expanded to serve the role of manager while a CPU is being
taken down and stays down.  As no new works are supposed to be queued
on a dead cpu, it just needs to drain all the existing ones.  Trustee
continues to try to create new workers and summon rescuers as long as
there are pending works.  If the CPU is brought back up while the
trustee is still trying to drain the gcwq from the previous offlining,
the trustee will kill all idles ones and tell workers which are still
busy to rebind to the cpu, and pass control over to gcwq which assumes
the manager role as necessary.

Concurrency managed worker pool reduces the number of workers
drastically.  Only workers which are necessary to keep the processing
going are created and kept.  Also, it reduces cache footprint by
avoiding unnecessarily switching contexts between different workers.

Please note that this patch does not increase max_active of any
workqueue.  All workqueues can still only process one work per cpu.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
parent d302f017
...@@ -226,6 +226,7 @@ enum { ...@@ -226,6 +226,7 @@ enum {
WQ_FREEZEABLE = 1 << 0, /* freeze during suspend */ WQ_FREEZEABLE = 1 << 0, /* freeze during suspend */
WQ_SINGLE_CPU = 1 << 1, /* only single cpu at a time */ WQ_SINGLE_CPU = 1 << 1, /* only single cpu at a time */
WQ_NON_REENTRANT = 1 << 2, /* guarantee non-reentrance */ WQ_NON_REENTRANT = 1 << 2, /* guarantee non-reentrance */
WQ_RESCUER = 1 << 3, /* has an rescue worker */
}; };
extern struct workqueue_struct * extern struct workqueue_struct *
...@@ -252,11 +253,12 @@ __create_workqueue_key(const char *name, unsigned int flags, int max_active, ...@@ -252,11 +253,12 @@ __create_workqueue_key(const char *name, unsigned int flags, int max_active,
#endif #endif
#define create_workqueue(name) \ #define create_workqueue(name) \
__create_workqueue((name), 0, 1) __create_workqueue((name), WQ_RESCUER, 1)
#define create_freezeable_workqueue(name) \ #define create_freezeable_workqueue(name) \
__create_workqueue((name), WQ_FREEZEABLE | WQ_SINGLE_CPU, 1) __create_workqueue((name), \
WQ_FREEZEABLE | WQ_SINGLE_CPU | WQ_RESCUER, 1)
#define create_singlethread_workqueue(name) \ #define create_singlethread_workqueue(name) \
__create_workqueue((name), WQ_SINGLE_CPU, 1) __create_workqueue((name), WQ_SINGLE_CPU | WQ_RESCUER, 1)
extern void destroy_workqueue(struct workqueue_struct *wq); extern void destroy_workqueue(struct workqueue_struct *wq);
......
This diff is collapsed.
...@@ -4,13 +4,6 @@ ...@@ -4,13 +4,6 @@
* Scheduler hooks for concurrency managed workqueue. Only to be * Scheduler hooks for concurrency managed workqueue. Only to be
* included from sched.c and workqueue.c. * included from sched.c and workqueue.c.
*/ */
static inline void wq_worker_waking_up(struct task_struct *task, void wq_worker_waking_up(struct task_struct *task, unsigned int cpu);
unsigned int cpu) struct task_struct *wq_worker_sleeping(struct task_struct *task,
{ unsigned int cpu);
}
static inline struct task_struct *wq_worker_sleeping(struct task_struct *task,
unsigned int cpu)
{
return NULL;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment