Skip to content

Commit 7db8889

Browse files
Rik van Rieltorvalds
authored andcommitted
mm: have order > 0 compaction start off where it left
Order > 0 compaction stops when enough free pages of the correct page order have been coalesced. When doing subsequent higher order allocations, it is possible for compaction to be invoked many times. However, the compaction code always starts out looking for things to compact at the start of the zone, and for free pages to compact things to at the end of the zone. This can cause quadratic behaviour, with isolate_freepages starting at the end of the zone each time, even though previous invocations of the compaction code already filled up all free memory on that end of the zone. This can cause isolate_freepages to take enormous amounts of CPU with certain workloads on larger memory systems. The obvious solution is to have isolate_freepages remember where it left off last time, and continue at that point the next time it gets invoked for an order > 0 compaction. This could cause compaction to fail if cc->free_pfn and cc->migrate_pfn are close together initially, in that case we restart from the end of the zone and try once more. Forced full (order == -1) compactions are left alone. [[email protected]: checkpatch fixes] [[email protected]: s/laste/last/, use 80 cols] Signed-off-by: Rik van Riel <[email protected]> Reported-by: Jim Schutt <[email protected]> Tested-by: Jim Schutt <[email protected]> Cc: Minchan Kim <[email protected]> Reviewed-by: KAMEZAWA Hiroyuki <[email protected]> Acked-by: Mel Gorman <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent ab21588 commit 7db8889

File tree

4 files changed

+73
-5
lines changed

4 files changed

+73
-5
lines changed

include/linux/mmzone.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ struct zone {
368368
*/
369369
spinlock_t lock;
370370
int all_unreclaimable; /* All pages pinned */
371+
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
372+
/* pfn where the last incremental compaction isolated free pages */
373+
unsigned long compact_cached_free_pfn;
374+
#endif
371375
#ifdef CONFIG_MEMORY_HOTPLUG
372376
/* see spanned/present_pages for more description */
373377
seqlock_t span_seqlock;

mm/compaction.c

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,17 @@ static void isolate_freepages(struct zone *zone,
422422
pfn -= pageblock_nr_pages) {
423423
unsigned long isolated;
424424

425+
/*
426+
* Skip ahead if another thread is compacting in the area
427+
* simultaneously. If we wrapped around, we can only skip
428+
* ahead if zone->compact_cached_free_pfn also wrapped to
429+
* above our starting point.
430+
*/
431+
if (cc->order > 0 && (!cc->wrapped ||
432+
zone->compact_cached_free_pfn >
433+
cc->start_free_pfn))
434+
pfn = min(pfn, zone->compact_cached_free_pfn);
435+
425436
if (!pfn_valid(pfn))
426437
continue;
427438

@@ -461,8 +472,11 @@ static void isolate_freepages(struct zone *zone,
461472
* looking for free pages, the search will restart here as
462473
* page migration may have returned some pages to the allocator
463474
*/
464-
if (isolated)
475+
if (isolated) {
465476
high_pfn = max(high_pfn, pfn);
477+
if (cc->order > 0)
478+
zone->compact_cached_free_pfn = high_pfn;
479+
}
466480
}
467481

468482
/* split_free_page does not map the pages */
@@ -556,6 +570,20 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
556570
return ISOLATE_SUCCESS;
557571
}
558572

573+
/*
574+
* Returns the start pfn of the last page block in a zone. This is the starting
575+
* point for full compaction of a zone. Compaction searches for free pages from
576+
* the end of each zone, while isolate_freepages_block scans forward inside each
577+
* page block.
578+
*/
579+
static unsigned long start_free_pfn(struct zone *zone)
580+
{
581+
unsigned long free_pfn;
582+
free_pfn = zone->zone_start_pfn + zone->spanned_pages;
583+
free_pfn &= ~(pageblock_nr_pages-1);
584+
return free_pfn;
585+
}
586+
559587
static int compact_finished(struct zone *zone,
560588
struct compact_control *cc)
561589
{
@@ -565,8 +593,26 @@ static int compact_finished(struct zone *zone,
565593
if (fatal_signal_pending(current))
566594
return COMPACT_PARTIAL;
567595

568-
/* Compaction run completes if the migrate and free scanner meet */
569-
if (cc->free_pfn <= cc->migrate_pfn)
596+
/*
597+
* A full (order == -1) compaction run starts at the beginning and
598+
* end of a zone; it completes when the migrate and free scanner meet.
599+
* A partial (order > 0) compaction can start with the free scanner
600+
* at a random point in the zone, and may have to restart.
601+
*/
602+
if (cc->free_pfn <= cc->migrate_pfn) {
603+
if (cc->order > 0 && !cc->wrapped) {
604+
/* We started partway through; restart at the end. */
605+
unsigned long free_pfn = start_free_pfn(zone);
606+
zone->compact_cached_free_pfn = free_pfn;
607+
cc->free_pfn = free_pfn;
608+
cc->wrapped = 1;
609+
return COMPACT_CONTINUE;
610+
}
611+
return COMPACT_COMPLETE;
612+
}
613+
614+
/* We wrapped around and ended up where we started. */
615+
if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn)
570616
return COMPACT_COMPLETE;
571617

572618
/*
@@ -664,8 +710,15 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
664710

665711
/* Setup to move all movable pages to the end of the zone */
666712
cc->migrate_pfn = zone->zone_start_pfn;
667-
cc->free_pfn = cc->migrate_pfn + zone->spanned_pages;
668-
cc->free_pfn &= ~(pageblock_nr_pages-1);
713+
714+
if (cc->order > 0) {
715+
/* Incremental compaction. Start where the last one stopped. */
716+
cc->free_pfn = zone->compact_cached_free_pfn;
717+
cc->start_free_pfn = cc->free_pfn;
718+
} else {
719+
/* Order == -1 starts at the end of the zone. */
720+
cc->free_pfn = start_free_pfn(zone);
721+
}
669722

670723
migrate_prep_local();
671724

mm/internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,14 @@ struct compact_control {
118118
unsigned long nr_freepages; /* Number of isolated free pages */
119119
unsigned long nr_migratepages; /* Number of pages to migrate */
120120
unsigned long free_pfn; /* isolate_freepages search base */
121+
unsigned long start_free_pfn; /* where we started the search */
121122
unsigned long migrate_pfn; /* isolate_migratepages search base */
122123
bool sync; /* Synchronous migration */
124+
bool wrapped; /* Order > 0 compactions are
125+
incremental, once free_pfn
126+
and migrate_pfn meet, we restart
127+
from the top of the zone;
128+
remember we wrapped around. */
123129

124130
int order; /* order a direct compactor needs */
125131
int migratetype; /* MOVABLE, RECLAIMABLE etc */

mm/page_alloc.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4397,6 +4397,11 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
43974397

43984398
zone->spanned_pages = size;
43994399
zone->present_pages = realsize;
4400+
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
4401+
zone->compact_cached_free_pfn = zone->zone_start_pfn +
4402+
zone->spanned_pages;
4403+
zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1);
4404+
#endif
44004405
#ifdef CONFIG_NUMA
44014406
zone->node = nid;
44024407
zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)

0 commit comments

Comments
 (0)