aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorMel Gorman <mgorman@suse.de>2012-08-21 19:16:15 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-08-21 19:45:03 -0400
commitde74f1cc3b1e9730d9b58580cd11361d30cd182d (patch)
tree436c660650712ede2a31f994d154094bbaddce87 /mm
parent9a9a9a7adafe62a34de8b4fb48936c1c5f9bafa5 (diff)
mm: have order > 0 compaction start near a pageblock with free pages
Commit 7db8889ab05b ("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 <mgorman@suse.de> Reviewed-by: Rik van Riel <riel@redhat.com> Reviewed-by: Minchan Kim <minchan@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/compaction.c54
1 files changed, 28 insertions, 26 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index b6984e234fdf..bcce7897e17a 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -384,6 +384,20 @@ static bool suitable_migration_target(struct page *page)
384} 384}
385 385
386/* 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 */
392static 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
400/*
387 * Based on information in the current compact_control, find blocks 401 * Based on information in the current compact_control, find blocks
388 * suitable for isolating free pages from and then isolate them. 402 * suitable for isolating free pages from and then isolate them.
389 */ 403 */
@@ -422,17 +436,6 @@ static void isolate_freepages(struct zone *zone,
422 pfn -= pageblock_nr_pages) { 436 pfn -= pageblock_nr_pages) {
423 unsigned long isolated; 437 unsigned long isolated;
424 438
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
436 if (!pfn_valid(pfn)) 439 if (!pfn_valid(pfn))
437 continue; 440 continue;
438 441
@@ -474,7 +477,15 @@ static void isolate_freepages(struct zone *zone,
474 */ 477 */
475 if (isolated) { 478 if (isolated) {
476 high_pfn = max(high_pfn, pfn); 479 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)
478 zone->compact_cached_free_pfn = high_pfn; 489 zone->compact_cached_free_pfn = high_pfn;
479 } 490 }
480 } 491 }
@@ -484,6 +495,11 @@ static void isolate_freepages(struct zone *zone,
484 495
485 cc->free_pfn = high_pfn; 496 cc->free_pfn = high_pfn;
486 cc->nr_freepages = nr_freepages; 497 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;
487} 503}
488 504
489/* 505/*
@@ -570,20 +586,6 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
570 return ISOLATE_SUCCESS; 586 return ISOLATE_SUCCESS;
571} 587}
572 588
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 */
579static 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
587static int compact_finished(struct zone *zone, 589static int compact_finished(struct zone *zone,
588 struct compact_control *cc) 590 struct compact_control *cc)
589{ 591{