mm, page_alloc: Use static global work_struct for draining per-cpu pages
Subsystems (2)
PATCH 1/1 mm, page_alloc: Use static global work_struct for draining per-cpu pages
As suggested by Vlastimil Babka and Tejun Heo, this patch uses a static work_struct to co-ordinate the draining of per-cpu pages on the workqueue. Only one task can drain at a time but this is better than the previous scheme that allowed multiple tasks to send IPIs at a time.
One consideration is whether parallel requests should synchronise against each other. This patch does not synchronise for a global drain as the common case for such callers is expected to be multiple parallel direct reclaimers competing for pages when the watermark is close to min. Draining the per-cpu list is unlikely to make much progress and serialising the drain is of dubious merit. Drains are synchonrised for callers such as memory hotplug and CMA that care about the drain being complete when the function returns.
--- mm/page_alloc.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-)
mm/page_alloc.c +23 -18
@@ -92,6 +92,10 @@ EXPORT_PER_CPU_SYMBOL(_numa_mem_);int _node_numa_mem_[MAX_NUMNODES ];#endif +/* work_structs for global per-cpu drains */ +DEFINE_MUTEX (pcpu_drain_mutex); +DEFINE_PER_CPU(struct work_struct, pcpu_drain); +#ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY volatile unsigned long latent_entropy __latent_entropy;EXPORT_SYMBOL (latent_entropy);
@@ -2351,7 +2355,6 @@ static void drain_local_pages_wq(struct work_struct *work)*/ void drain_all_pages (struct zone *zone) { -struct work_struct __percpu *works;int cpu;/*
@@ -2365,11 +2368,21 @@ void drain_all_pages(struct zone *zone)return ;/* +* Do not drain if one is already in progress unless it's specific to +* a zone. Such callers are primarily CMA and memory hotplug and need +* the drain to be complete when the call returns. +*/ +if (unlikely (!mutex_trylock (&pcpu_drain_mutex))) { +if (!zone) +return ; +mutex_lock (&pcpu_drain_mutex); + } + +/* * As this can be called from reclaim context, do not reenter reclaim. * An allocation failure can be handled, it's simply slower */ get_online_cpus (); - works = alloc_percpu_gfp(struct work_struct, GFP_ATOMIC);/* * We don't care about racing with CPU hotplug event
@@ -2402,24 +2415,16 @@ void drain_all_pages(struct zone *zone)cpumask_clear_cpu (cpu, &cpus_with_pcps); } -if (works) { -for_each_cpu (cpu, &cpus_with_pcps) { -struct work_struct *work =per_cpu_ptr (works, cpu); -INIT_WORK (work, drain_local_pages_wq); -schedule_work_on (cpu, work); - } -for_each_cpu (cpu, &cpus_with_pcps) -flush_work (per_cpu_ptr (works, cpu)); - }else { -for_each_cpu (cpu, &cpus_with_pcps) { -struct work_struct work; - -INIT_WORK (&work, drain_local_pages_wq); -schedule_work_on (cpu, &work); -flush_work (&work); - } +for_each_cpu (cpu, &cpus_with_pcps) { +struct work_struct *work =per_cpu_ptr (&pcpu_drain, cpu); +INIT_WORK (work, drain_local_pages_wq); +schedule_work_on (cpu, work); } +for_each_cpu (cpu, &cpus_with_pcps) +flush_work (per_cpu_ptr (&pcpu_drain, cpu)); +put_online_cpus (); +mutex_unlock (&pcpu_drain_mutex); }#ifdef CONFIG_HIBERNATION
Signature · 8 lines
Mel Gorman SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 13 lines · Mel Gorman
As suggested by Vlastimil Babka and Tejun Heo, this patch uses a static
work_structto co-ordinate the draining of per-cpu pages on the workqueue. Only one task can drain at a time but this is better than the previous scheme that allowed multiple tasks to send IPIs at a time.One consideration is whether parallel requests should synchronise against each other. This patch does not synchronise for a global drain as the common case for such callers is expected to be multiple parallel direct reclaimers competing for pages when the watermark is close to min. Draining the per-cpu list is unlikely to make much progress and serialising the drain is of dubious merit. Drains are synchonrised for callers such as memory hotplug and CMA that care about the drain being complete when the function returns.
Quoted 1 line
Signed-off-by Mel Gorman <[email protected]>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 14 lines · Mel Gorman
As suggested by Vlastimil Babka and Tejun Heo, this patch uses a static
work_structto co-ordinate the draining of per-cpu pages on the workqueue. Only one task can drain at a time but this is better than the previous scheme that allowed multiple tasks to send IPIs at a time.One consideration is whether parallel requests should synchronise against each other. This patch does not synchronise for a global drain as the common case for such callers is expected to be multiple parallel direct reclaimers competing for pages when the watermark is close to min. Draining the per-cpu list is unlikely to make much progress and serialising the drain is of dubious merit. Drains are synchonrised for callers such as memory hotplug and CMA that care about the drain being complete when the function returns.
Quoted 2 lines
Signed-off-by Mel Gorman <[email protected]>---
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerQuoted 28 lines · On Wed, 25 Jan 2017 08:30:38 +0000 Mel Gorman <[email protected]>…
As suggested by Vlastimil Babka and Tejun Heo, this patch uses a static
work_structto co-ordinate the draining of per-cpu pages on the workqueue. Only one task can drain at a time but this is better than the previous scheme that allowed multiple tasks to send IPIs at a time.One consideration is whether parallel requests should synchronise against each other. This patch does not synchronise for a global drain as the common case for such callers is expected to be multiple parallel direct reclaimers competing for pages when the watermark is close to min. Draining the per-cpu list is unlikely to make much progress and serialising the drain is of dubious merit. Drains are synchonrised for callers such as memory hotplug and CMA that care about the drain being complete when the function returns.
...
mm/page_alloc.c +0
-9@@ -2402,24 +2415,16 @@ void drain_all_pages(struct zone *zone)cpumask_clear_cpu (cpu, &cpus_with_pcps); } -if (works) { -for_each_cpu (cpu, &cpus_with_pcps) { -struct work_struct *work =per_cpu_ptr (works, cpu); -INIT_WORK (work, drain_local_pages_wq); -schedule_work_on (cpu, work); - } -for_each_cpu (cpu, &cpus_with_pcps) -flush_work (per_cpu_ptr (works, cpu)); - }else {
mm/page_alloc.c void drain_all_pages(struct zone *zone) · line 2420
Quoted 10 lines
mm/page_alloc.c +3
-7-for_each_cpu (cpu, &cpus_with_pcps) { -struct work_struct work; - -INIT_WORK (&work, drain_local_pages_wq); -schedule_work_on (cpu, &work); -flush_work (&work); - } +for_each_cpu (cpu, &cpus_with_pcps) { +struct work_struct *work =per_cpu_ptr (&pcpu_drain, cpu); +INIT_WORK (work, drain_local_pages_wq);
It's strange to repeatedly run INIT_WORK() in this fashion. Overwriting an atomic_t which should already be zero, initializing a list_head which should already be in the initialized state...
Can we instead do this a single time in init code?
mm/page_alloc.c void drain_all_pages(struct zone *zone) · line 2428
Quoted 8 lines
mm/page_alloc.c +5
-0+schedule_work_on (cpu, work); } +for_each_cpu (cpu, &cpus_with_pcps) +flush_work (per_cpu_ptr (&pcpu_drain, cpu)); +put_online_cpus (); +mutex_unlock (&pcpu_drain_mutex); }
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 10 lines · Andrew Morton
mm/page_alloc.c void drain_all_pages(struct zone *zone) · line 2420
Quoted 3 lines
mm/page_alloc.c +3
-0+for_each_cpu (cpu, &cpus_with_pcps) { +struct work_struct *work =per_cpu_ptr (&pcpu_drain, cpu); +INIT_WORK (work, drain_local_pages_wq);It's strange to repeatedly run
INIT_WORK()in this fashion. Overwriting anatomic_twhich should already be zero, initializing alist_headwhich should already be in the initialized state...Can we instead do this a single time in init code?
INIT_WORK does different things depending on whether LOCKDEP is enabled or not and also whether object debugging is enabled. I'd worry that it's not functionally equivalent or some future change would break the assumptions about what INIT_WORK does internally. The init cost is there, but it's insignicant in comparison to the whole workqueue operation or the old cost of sending IPIs for that matter.
Signature · 8 lines
Mel Gorman SUSE Labs -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Hello,
Quoted 12 lines · Mel Gorman
Quoted 10 lines · Andrew Morton
mm/page_alloc.c void drain_all_pages(struct zone *zone) · line 2420
Quoted 3 lines
mm/page_alloc.c +3
-0+for_each_cpu (cpu, &cpus_with_pcps) { +struct work_struct *work =per_cpu_ptr (&pcpu_drain, cpu); +INIT_WORK (work, drain_local_pages_wq);It's strange to repeatedly run
INIT_WORK()in this fashion. Overwriting anatomic_twhich should already be zero, initializing alist_headwhich should already be in the initialized state...Can we instead do this a single time in init code?
Quoted 6 lines
INIT_WORKdoes different things depending on whether LOCKDEP is enabled or not and also whether object debugging is enabled. I'd worry that it's not functionally equivalent or some future change would break the assumptions about whatINIT_WORKdoes internally. The init cost is there, but it's insignicant in comparison to the whole workqueue operation or the old cost of sending IPIs for that matter.
Both initing once or per each invocation are perfectly valid and guaranteed to work. idk, I don't have a strong opinion hereag.
Thanks.
Signature · 7 lines
tejun -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>