workqueue: use enum value to set array size of pools in gcwq
PATCH 1/5 workqueue: use enum value to set array size of pools in gcwq
Commit 3270476a6c0ce322354df8679652f060d66526dc ('workqueue: reimplement WQ_HIGHPRI using a separate worker_pool') introduce separate worker_pool for HIGHPRI. Although there is NR_WORKER_POOLS enum value which represent size of pools, definition of worker_pool in gcwq doesn't use it. Using it makes code robust and prevent future mistakes.
So change code to use this enum value.
kernel/workqueue.c +2 -1
@@ -183,7 +183,8 @@ struct global_cwq {struct hlist_head busy_hash[BUSY_WORKER_HASH_SIZE ];/* L: hash of busy workers */ -struct worker_pool pools[2 ];/* normal and highpri pools */ +struct worker_pool pools[NR_WORKER_POOLS ]; +/* normal and highpri pools */ wait_queue_head_t rebind_hold;/* rebind hold wait */ }____cacheline_aligned_in_smp ;
Signature · 1 line
1.7.9.5
PATCH 2/5 workqueue: change value of lcpu in queue_delayed_work_on()
We assign cpu id into work struct in queue_delayed_work_on(). In current implementation, when work is come in first time, current running cpu id is assigned.
If we do queue_delayed_work_on() with CPU A on CPU B, __queue_work() invoked in delayed_work_timer_fn() go into sub-optimal path in case of WQ_NON_REENTRANT.
Change it to cpu argument is prevent to go into sub-optimal path.
kernel/workqueue.c +2 -0
@@ -1158,6 +1158,8 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,if (gcwq && gcwq->cpu !=WORK_CPU_UNBOUND ) lcpu = gcwq->cpu; +else if (cpu >=0 ) + lcpu = cpu;else lcpu =raw_smp_processor_id (); }else
Signature · 1 line
1.7.9.5
Tejun Heo
maintainerRe: [PATCH 2/5] workqueue: change value of lcpu in queue_delayed_work_on()
Hello,
Quoted 7 lines · Joonsoo Kim
We assign cpu id into work struct in
queue_delayed_work_on(). In current implementation, when work is come in first time, current running cpu id is assigned.
If we doqueue_delayed_work_on()with CPU A on CPU B,__queue_work()invoked indelayed_work_timer_fn()go into sub-optimal path in case ofWQ_NON_REENTRANT.
Change it to cpu argument is prevent to go into sub-optimal path.
Which part is suboptimal? Also, what if @cpu is WQ_UNBOUND?
Thanks.
Signature · 1 line
tejun
Re: [PATCH 2/5] workqueue: change value of lcpu in queue_delayed_work_on()
2012/8/14 Tejun Heo <[email protected]>:
Quoted 11 lines
Hello,
Quoted 7 lines · Joonsoo Kim
We assign cpu id into work struct in
queue_delayed_work_on(). In current implementation, when work is come in first time, current running cpu id is assigned.
If we doqueue_delayed_work_on()with CPU A on CPU B,__queue_work()invoked indelayed_work_timer_fn()go into sub-optimal path in case ofWQ_NON_REENTRANT.
Change it to cpu argument is prevent to go into sub-optimal path.
Quoted 1 line
Which part is suboptimal? Also, what if @cpu is
WQ_UNBOUND?
Hi.
I think a following scenario.
wq = WQ_NON_REENTRANT.queue_delayed_work_on(CPU B) is invoked in CPU A, so lcpu = CPU A, cpu = CPU B.
In this case, we call add_time_on(CPU B), then delayed_work_timer_fn() is invoked on CPU B.delayed_work_timer_fn() calls __queue_work(), then following comparisons return true!
gcwq = get_gcwq(cpu);
if (wq->flags & WQ_NON_REENTRANT &&
(last_gcwq = get_work_gcwq(work)) && last_gcwq != gcwq) {I thinks that if we assign cpu to lcpu, above comparisons return false, so save some overheads.
Is there any missing part?
And, do u mean @cpu is WORK_CPU_UNBOUND?
Thanks.
Tejun Heo
maintainerRe: [PATCH 2/5] workqueue: change value of lcpu in queue_delayed_work_on()
Hello,
Quoted 8 lines · JoonSoo Kim
wq =
WQ_NON_REENTRANT.queue_delayed_work_on(CPU B)is invoked in CPU A, so lcpu = CPU A, cpu = CPU B.In this case, we call
add_time_on(CPU B), thendelayed_work_timer_fn()is invoked on CPU B.delayed_work_timer_fn()calls__queue_work(), then following comparisons return true!
Quoted 7 lines
gcwq = get_gcwq(cpu); if (wq->flags & WQ_NON_REENTRANT && (last_gcwq = get_work_gcwq(work)) && last_gcwq != gcwq) {I thinks that if we assign cpu to lcpu, above comparisons return false, so save some overheads.
Is there any missing part?
Nope, that sound correct to me.
Quoted 1 line
And, do u mean @cpu is
WORK_CPU_UNBOUND?
@cpu could be WORK_CPU_UNBOUND at that point. The timer will be added to local CPU but @work->data would be pointing to WORK_CPU_UNBOUND, again triggering the condition. Given that @cpu being WORK_CPU_UNBOUND is far more common than an actual CPU number, the patch would actually increase spurious nrt lookups. The right thing to do is probably setting cpu to raw_smp_processor_id() beforehand.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 2/5] workqueue: change value of lcpu in queue_delayed_work_on()
Quoted 8 lines
Quoted 1 line
And, do u mean @cpu is
WORK_CPU_UNBOUND?@cpu could be
WORK_CPU_UNBOUNDat that point. The timer will be added to local CPU but @work->data would be pointing toWORK_CPU_UNBOUND, again triggering the condition. Given that @cpu beingWORK_CPU_UNBOUNDis far more common than an actual CPU number, the patch would actually increase spurious nrt lookups. The right thing to do is probably setting cpu toraw_smp_processor_id()beforehand.
I got your point.
Thanks for kind illustration.
Following is a alternative implementation for this.
I thinks this is too rare case, so it doesn't help in any real workload. But how do you thinks?
@@ -1156,7 +1156,9 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
if (!(wq->flags & WQ_UNBOUND)) {
struct global_cwq *gcwq = get_work_gcwq(work);- if (gcwq && gcwq->cpu != WORK_CPU_UNBOUND)
+ if (!gcwq)
+ lcpu = cpu;
+ else if (gcwq->cpu != WORK_CPU_UNBOUND)
lcpu = gcwq->cpu;
else
lcpu = raw_smp_processor_id();Tejun Heo
maintainerRe: [PATCH 2/5] workqueue: change value of lcpu in queue_delayed_work_on()
Hello,
Quoted 27 lines · JoonSoo Kim
Quoted 8 lines
Quoted 1 line
And, do u mean @cpu is
WORK_CPU_UNBOUND?@cpu could be
WORK_CPU_UNBOUNDat that point. The timer will be added to local CPU but @work->data would be pointing toWORK_CPU_UNBOUND, again triggering the condition. Given that @cpu beingWORK_CPU_UNBOUNDis far more common than an actual CPU number, the patch would actually increase spurious nrt lookups. The right thing to do is probably setting cpu toraw_smp_processor_id()beforehand.I got your point.
Thanks for kind illustration.Following is a alternative implementation for this.
I thinks this is too rare case, so it doesn't help in any real workload. But how do you thinks?(quoted diff) +3
-1@@ -1156,7 +1156,9 @@ int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, if (!(wq->flags & WQ_UNBOUND)) { struct global_cwq *gcwq = get_work_gcwq(work); - if (gcwq && gcwq->cpu != WORK_CPU_UNBOUND) + if (!gcwq) + lcpu = cpu; + else if (gcwq->cpu != WORK_CPU_UNBOUND) lcpu = gcwq->cpu; else
Quoted 1 line
lcpu = raw_smp_processor_id();
Why not just do
if (cpu == WORK_CPU_UNBOUND)
cpu = raw_smp_processor_id();
if (!(wq->flags...) {
...
if (gcwq && gcwq->cpu != WORK_CPU_UNBOUND)
lcpu = gcwq->cpu;
else
lcpu = cpu;
}
...
add_timer_on(timer, cpu);Also, can you please base the patches on top of the following git branch?
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git for-3.7
Thanks.
Signature · 1 line
tejun
Re: [PATCH 2/5] workqueue: change value of lcpu in queue_delayed_work_on()
Quoted 15 lines
Why not just do
if (cpu == WORK_CPU_UNBOUND) cpu = raw_smp_processor_id(); if (!(wq->flags...) { ... if (gcwq && gcwq->cpu != WORK_CPU_UNBOUND) lcpu = gcwq->cpu; else lcpu = cpu; } ... add_timer_on(timer, cpu);
I re-look at code deeply and find that dwork->cpu is related to tracing information.
If we do above change, tracing information is bias.
Quoted 4 lines
Also, can you please base the patches on top of the following git branch?
git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.gitfor-3.7
Sure!
PATCH 3/5 workqueue: introduce system_highpri_wq
In reply to Joonsoo KimCommit 3270476a6c0ce322354df8679652f060d66526dc ('workqueue: reimplement WQ_HIGHPRI using a separate worker_pool') introduce separate worker pool for HIGHPRI. When we handle busyworkers for gcwq, it can be normal worker or highpri worker. But, we don't consider this difference in rebind_workers(), we use just system_wq for highpri worker. It makes mismatch between cwq->pool and worker->pool.
It doesn't make error in current implementation, but possible in the future. Now, we introduce system_highpri_wq to use proper cwq for highpri workers in rebind_workers(). Following patch fix this issue properly.
kernel/workqueue.c +5 -2
@@ -269,12 +269,14 @@ struct workqueue_struct { };struct workqueue_struct *system_wq__read_mostly ; +struct workqueue_struct *system_highpri_wq__read_mostly ;struct workqueue_struct *system_long_wq__read_mostly ;struct workqueue_struct *system_nrt_wq__read_mostly ;struct workqueue_struct *system_unbound_wq__read_mostly ;struct workqueue_struct *system_freezable_wq__read_mostly ;struct workqueue_struct *system_nrt_freezable_wq__read_mostly ;EXPORT_SYMBOL_GPL (system_wq); +EXPORT_SYMBOL_GPL (system_highpri_wq);EXPORT_SYMBOL_GPL (system_long_wq);EXPORT_SYMBOL_GPL (system_nrt_wq);EXPORT_SYMBOL_GPL (system_unbound_wq);
@@ -3749,6 +3751,7 @@ static int __init init_workqueues(void) } system_wq =alloc_workqueue ("events" ,0 ,0 ); + system_highpri_wq =alloc_workqueue ("events_highpri" ,0 ,0 ); system_long_wq =alloc_workqueue ("events_long" ,0 ,0 ); system_nrt_wq =alloc_workqueue ("events_nrt" ,WQ_NON_REENTRANT ,0 ); system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,
@@ -3757,8 +3760,8 @@ static int __init init_workqueues(void) WQ_FREEZABLE, 0); system_nrt_freezable_wq =alloc_workqueue ("events_nrt_freezable" ,WQ_NON_REENTRANT |WQ_FREEZABLE ,0 ); -BUG_ON (!system_wq || !system_long_wq || !system_nrt_wq || - !system_unbound_wq || !system_freezable_wq || +BUG_ON (!system_wq || !system_highpri_wq || !system_long_wq || + !system_nrt_wq || !system_unbound_wq || !system_freezable_wq || !system_nrt_freezable_wq);return 0 ; }
Signature · 1 line
1.7.9.5
PATCH 4/5 workqueue: use system_highpri_wq for highpri workers in rebind_workers()
In reply to Joonsoo KimIn rebind_workers(), we do inserting a work to rebind to cpu for busy workers. Currently, in this case, we use only system_wq. This makes a possible error situation as there is mismatch between cwq->pool and worker->pool.
To prevent this, we should use system_highpri_wq for highpri worker to match theses. This implements it.
kernel/workqueue.c +9 -3
@@ -489,6 +489,11 @@ static int worker_pool_pri(struct worker_pool *pool)return pool - pool->gcwq->pools; } +static struct workqueue_struct *get_pool_system_wq (struct worker_pool *pool) +{ +return worker_pool_pri (pool) ? system_highpri_wq : system_wq; +} +static struct global_cwq *get_gcwq (unsigned int cpu) {if (cpu !=WORK_CPU_UNBOUND )
@@ -1450,9 +1455,10 @@ retry:/* wq doesn't matter, use the default one */ debug_work_activate (rebind_work); -insert_work (get_cwq (gcwq->cpu, system_wq), rebind_work, - worker->scheduled.next, -work_color_to_flags (WORK_NO_COLOR )); +insert_work ( +get_cwq (gcwq->cpu,get_pool_system_wq (worker->pool)), + rebind_work, worker->scheduled.next, +work_color_to_flags (WORK_NO_COLOR )); } }
Signature · 1 line
1.7.9.5
Tejun Heo
maintainerRe: [PATCH 4/5] workqueue: use system_highpri_wq for highpri workers in rebind_workers()
Quoted 26 lines · Joonsoo Kim
In
rebind_workers(), we do inserting a work to rebind to cpu for busy workers. Currently, in this case, we use onlysystem_wq. This makes a possible error situation as there is mismatch betweencwq->poolandworker->pool.To prevent this, we should use
system_highpri_wqfor highpri worker to match theses. This implements it.Signed-off-by Joonsoo Kim <[email protected]>kernel/workqueue.c +5
-0@@ -489,6 +489,11 @@ static int worker_pool_pri(struct worker_pool *pool)return pool - pool->gcwq->pools; } +static struct workqueue_struct *get_pool_system_wq (struct worker_pool *pool) +{ +return worker_pool_pri (pool) ? system_highpri_wq : system_wq; +} +static struct global_cwq *get_gcwq (unsigned int cpu) {if (cpu !=WORK_CPU_UNBOUND )
kernel/workqueue.c retry: · line 1461
Quoted 10 lines
kernel/workqueue.c +4
-3@@ -1450,9 +1455,10 @@ retry:/* wq doesn't matter, use the default one */ debug_work_activate (rebind_work); -insert_work (get_cwq (gcwq->cpu, system_wq), rebind_work, - worker->scheduled.next, -work_color_to_flags (WORK_NO_COLOR )); +insert_work ( +get_cwq (gcwq->cpu,get_pool_system_wq (worker->pool)), + rebind_work, worker->scheduled.next, +work_color_to_flags (WORK_NO_COLOR ));
I think it would be better to just opencode system_wq selection in rebind_workers().
Thanks.
Signature · 1 line
tejun
Re: [PATCH 4/5] workqueue: use system_highpri_wq for highpri workers in rebind_workers()
Quoted 2 lines
I think it would be better to just opencode
system_wqselection inrebind_workers().
Sorry for my poor English skill.
Could you elaborate "opencode system_wq selection" means?
Thanks!
Tejun Heo
maintainerRe: [PATCH 4/5] workqueue: use system_highpri_wq for highpri workers in rebind_workers()
Quoted 5 lines · JoonSoo Kim
Quoted 2 lines
I think it would be better to just opencode
system_wqselection inrebind_workers().Sorry for my poor English skill.
Could you elaborate "opencodesystem_wqselection" means?
Dropping get_pool_system_wq() and putting the logic inside rebind_workers() directly. I tend to dislike short helpers which are used only once. They tend to obfuscate while not really contributing to anything else.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 4/5] workqueue: use system_highpri_wq for highpri workers in rebind_workers()
2012/8/14 Tejun Heo <[email protected]>:
Quoted 11 lines · JoonSoo Kim
Quoted 5 lines
Quoted 2 lines
I think it would be better to just opencode
system_wqselection inrebind_workers().Sorry for my poor English skill.
Could you elaborate "opencodesystem_wqselection" means?Dropping
get_pool_system_wq()and putting the logic insiderebind_workers()directly. I tend to dislike short helpers which are used only once. They tend to obfuscate while not really contributing to anything else.
Okay!
I will send a next version which adopt your recommendation tomorrow.
PATCH 5/5 workqueue: use system_highpri_wq for unbind_work
In reply to Joonsoo KimTo speed cpu down processing up, use system_highpri_wq. As scheduling priority of workers on it is higher than system_wq and it is not contended by other normal works on this cpu, work on it is processed faster than system_wq.
kernel/workqueue.c +1 -1
@@ -3501,7 +3501,7 @@ static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,case CPU_DOWN_PREPARE :/* unbinding should happen on the local CPU */ INIT_WORK_ONSTACK (&unbind_work, gcwq_unbind_fn); -schedule_work_on (cpu, &unbind_work); +queue_work_on (cpu, system_highpri_wq, &unbind_work);flush_work (&unbind_work);break ; }
Signature · 1 line
1.7.9.5
Tejun Heo
maintainerRe: [PATCH 5/5] workqueue: use system_highpri_wq for unbind_work
Quoted 4 lines · Joonsoo Kim
To speed cpu down processing up, use
system_highpri_wq. As scheduling priority of workers on it is higher thansystem_wqand it is not contended by other normal works on this cpu, work on it is processed faster thansystem_wq.
Is this from an actual workload? ie. do you have a test case where this matters?
Thanks.
Signature · 1 line
tejun
Re: [PATCH 5/5] workqueue: use system_highpri_wq for unbind_work
2012/8/14 Tejun Heo <[email protected]>:
Quoted 8 lines · Joonsoo Kim
Quoted 4 lines
To speed cpu down processing up, use
system_highpri_wq. As scheduling priority of workers on it is higher thansystem_wqand it is not contended by other normal works on this cpu, work on it is processed faster thansystem_wq.Is this from an actual workload? ie. do you have a test case where this matters?
No. it is not from an actual workload.
I got this idea suddenly, during implementing others. I will do a test for this tomorrow.
Tejun Heo
maintainerRe: [PATCH 5/5] workqueue: use system_highpri_wq for unbind_work
Hello,
Quoted 8 lines · JoonSoo Kim
2012/8/14 Tejun Heo <[email protected]>:
Quoted 3 lines · Joonsoo Kim
Is this from an actual workload? ie. do you have a test case where this matters?
No. it is not from an actual workload.
I got this idea suddenly, during implementing others. I will do a test for this tomorrow.
I don't think it's a bad idea and guess this wouldn't make much difference under most workload. Was mostly wondering whether this came from some crazy phone powersave optimization.
Thanks.
Signature · 1 line
tejun
Re: [PATCH 5/5] workqueue: use system_highpri_wq for unbind_work
2012/8/14 Tejun Heo <[email protected]>:
Quoted 8 lines
Hello,
Quoted 5 lines · JoonSoo Kim
2012/8/14 Tejun Heo <[email protected]>:
Quoted 3 lines · Joonsoo Kim
Is this from an actual workload? ie. do you have a test case where this matters?
Quoted 7 lines
Quoted 3 lines
No. it is not from an actual workload.
I got this idea suddenly, during implementing others. I will do a test for this tomorrow.I don't think it's a bad idea and guess this wouldn't make much difference under most workload. Was mostly wondering whether this came from some crazy phone powersave optimization.
Okay.
It is not from an actual workload,
but I intent to optimize some crazy phone powersave :)
Thanks for comments.