Skip to content

Commit 0ccce3b

Browse files
gormanmtorvalds
authored andcommitted
mm, page_alloc: drain per-cpu pages from workqueue context
The per-cpu page allocator can be drained immediately via drain_all_pages() which sends IPIs to every CPU. In the next patch, the per-cpu allocator will only be used for interrupt-safe allocations which prevents draining it from IPI context. This patch uses workqueues to drain the per-cpu lists instead. This is slower but no slowdown during intensive reclaim was measured and the paths that use drain_all_pages() are not that sensitive to performance. This is particularly true as the path would only be triggered when reclaim is failing. It also makes a some sense to avoid storming a machine with IPIs when it's under memory pressure. Arguably, it should be further adjusted so that only one caller at a time is draining pages but it's beyond the scope of the current patch. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Mel Gorman <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: Hillf Danton <[email protected]> Cc: Jesper Dangaard Brouer <[email protected]> Cc: Tejun Heo <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9cd7555 commit 0ccce3b

File tree

1 file changed

+37
-7
lines changed

1 file changed

+37
-7
lines changed

mm/page_alloc.c

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,19 +2339,21 @@ void drain_local_pages(struct zone *zone)
23392339
drain_pages(cpu);
23402340
}
23412341

2342+
static void drain_local_pages_wq(struct work_struct *work)
2343+
{
2344+
drain_local_pages(NULL);
2345+
}
2346+
23422347
/*
23432348
* Spill all the per-cpu pages from all CPUs back into the buddy allocator.
23442349
*
23452350
* When zone parameter is non-NULL, spill just the single zone's pages.
23462351
*
2347-
* Note that this code is protected against sending an IPI to an offline
2348-
* CPU but does not guarantee sending an IPI to newly hotplugged CPUs:
2349-
* on_each_cpu_mask() blocks hotplug and won't talk to offlined CPUs but
2350-
* nothing keeps CPUs from showing up after we populated the cpumask and
2351-
* before the call to on_each_cpu_mask().
2352+
* Note that this can be extremely slow as the draining happens in a workqueue.
23522353
*/
23532354
void drain_all_pages(struct zone *zone)
23542355
{
2356+
struct work_struct __percpu *works;
23552357
int cpu;
23562358

23572359
/*
@@ -2360,6 +2362,17 @@ void drain_all_pages(struct zone *zone)
23602362
*/
23612363
static cpumask_t cpus_with_pcps;
23622364

2365+
/* Workqueues cannot recurse */
2366+
if (current->flags & PF_WQ_WORKER)
2367+
return;
2368+
2369+
/*
2370+
* As this can be called from reclaim context, do not reenter reclaim.
2371+
* An allocation failure can be handled, it's simply slower
2372+
*/
2373+
get_online_cpus();
2374+
works = alloc_percpu_gfp(struct work_struct, GFP_ATOMIC);
2375+
23632376
/*
23642377
* We don't care about racing with CPU hotplug event
23652378
* as offline notification will cause the notified
@@ -2390,8 +2403,25 @@ void drain_all_pages(struct zone *zone)
23902403
else
23912404
cpumask_clear_cpu(cpu, &cpus_with_pcps);
23922405
}
2393-
on_each_cpu_mask(&cpus_with_pcps, (smp_call_func_t) drain_local_pages,
2394-
zone, 1);
2406+
2407+
if (works) {
2408+
for_each_cpu(cpu, &cpus_with_pcps) {
2409+
struct work_struct *work = per_cpu_ptr(works, cpu);
2410+
INIT_WORK(work, drain_local_pages_wq);
2411+
schedule_work_on(cpu, work);
2412+
}
2413+
for_each_cpu(cpu, &cpus_with_pcps)
2414+
flush_work(per_cpu_ptr(works, cpu));
2415+
} else {
2416+
for_each_cpu(cpu, &cpus_with_pcps) {
2417+
struct work_struct work;
2418+
2419+
INIT_WORK(&work, drain_local_pages_wq);
2420+
schedule_work_on(cpu, &work);
2421+
flush_work(&work);
2422+
}
2423+
}
2424+
put_online_cpus();
23952425
}
23962426

23972427
#ifdef CONFIG_HIBERNATION

0 commit comments

Comments
 (0)