Skip to content

Commit de74f1c

Browse files
Mel Gormantorvalds
authored andcommitted
mm: have order > 0 compaction start near a pageblock with free pages
Commit 7db8889 ("mm: have order > 0 compaction start off where it left") introduced a caching mechanism to reduce the amount work the free page scanner does in compaction. However, it has a problem. Consider two process simultaneously scanning free pages C Process A M S F |---------------------------------------| Process B M FS C is zone->compact_cached_free_pfn S is cc->start_pfree_pfn M is cc->migrate_pfn F is cc->free_pfn In this diagram, Process A has just reached its migrate scanner, wrapped around and updated compact_cached_free_pfn accordingly. Simultaneously, Process B finishes isolating in a block and updates compact_cached_free_pfn again to the location of its free scanner. Process A moves to "end_of_zone - one_pageblock" and runs this check if (cc->order > 0 && (!cc->wrapped || zone->compact_cached_free_pfn > cc->start_free_pfn)) pfn = min(pfn, zone->compact_cached_free_pfn); compact_cached_free_pfn is above where it started so the free scanner skips almost the entire space it should have scanned. When there are multiple processes compacting it can end in a situation where the entire zone is not being scanned at all. Further, it is possible for two processes to ping-pong update to compact_cached_free_pfn which is just random. Overall, the end result wrecks allocation success rates. There is not an obvious way around this problem without introducing new locking and state so this patch takes a different approach. First, it gets rid of the skip logic because it's not clear that it matters if two free scanners happen to be in the same block but with racing updates it's too easy for it to skip over blocks it should not. Second, it updates compact_cached_free_pfn in a more limited set of circumstances. If a scanner has wrapped, it updates compact_cached_free_pfn to the end of the zone. When a wrapped scanner isolates a page, it updates compact_cached_free_pfn to point to the highest pageblock it can isolate pages from. If a scanner has not wrapped when it has finished isolated pages it checks if compact_cached_free_pfn is pointing to the end of the zone. If so, the value is updated to point to the highest pageblock that pages were isolated from. This value will not be updated again until a free page scanner wraps and resets compact_cached_free_pfn. This is not optimal and it can still race but the compact_cached_free_pfn will be pointing to or very near a pageblock with free pages. Signed-off-by: Mel Gorman <[email protected]> Reviewed-by: Rik van Riel <[email protected]> Reviewed-by: Minchan Kim <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9a9a9a7 commit de74f1c

File tree

1 file changed

+28
-26
lines changed

1 file changed

+28
-26
lines changed

mm/compaction.c

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,20 @@ static bool suitable_migration_target(struct page *page)
383383
return false;
384384
}
385385

386+
/*
387+
* Returns the start pfn of the last page block in a zone. This is the starting
388+
* point for full compaction of a zone. Compaction searches for free pages from
389+
* the end of each zone, while isolate_freepages_block scans forward inside each
390+
* page block.
391+
*/
392+
static unsigned long start_free_pfn(struct zone *zone)
393+
{
394+
unsigned long free_pfn;
395+
free_pfn = zone->zone_start_pfn + zone->spanned_pages;
396+
free_pfn &= ~(pageblock_nr_pages-1);
397+
return free_pfn;
398+
}
399+
386400
/*
387401
* Based on information in the current compact_control, find blocks
388402
* suitable for isolating free pages from and then isolate them.
@@ -422,17 +436,6 @@ static void isolate_freepages(struct zone *zone,
422436
pfn -= pageblock_nr_pages) {
423437
unsigned long isolated;
424438

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-
436439
if (!pfn_valid(pfn))
437440
continue;
438441

@@ -474,7 +477,15 @@ static void isolate_freepages(struct zone *zone,
474477
*/
475478
if (isolated) {
476479
high_pfn = max(high_pfn, pfn);
477-
if (cc->order > 0)
480+
481+
/*
482+
* If the free scanner has wrapped, update
483+
* compact_cached_free_pfn to point to the highest
484+
* pageblock with free pages. This reduces excessive
485+
* scanning of full pageblocks near the end of the
486+
* zone
487+
*/
488+
if (cc->order > 0 && cc->wrapped)
478489
zone->compact_cached_free_pfn = high_pfn;
479490
}
480491
}
@@ -484,6 +495,11 @@ static void isolate_freepages(struct zone *zone,
484495

485496
cc->free_pfn = high_pfn;
486497
cc->nr_freepages = nr_freepages;
498+
499+
/* If compact_cached_free_pfn is reset then set it now */
500+
if (cc->order > 0 && !cc->wrapped &&
501+
zone->compact_cached_free_pfn == start_free_pfn(zone))
502+
zone->compact_cached_free_pfn = high_pfn;
487503
}
488504

489505
/*
@@ -570,20 +586,6 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
570586
return ISOLATE_SUCCESS;
571587
}
572588

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-
587589
static int compact_finished(struct zone *zone,
588590
struct compact_control *cc)
589591
{

0 commit comments

Comments
 (0)