summaryrefslogtreecommitdiffstats
path: root/mm/compaction.c
diff options
context:
space:
mode:
authorDavid Rientjes <rientjes@google.com>2016-06-24 17:50:10 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-06-24 20:23:52 -0400
commita4f04f2c6955aff5e2c08dcb40aca247ff4d7370 (patch)
tree89db07df7889aae04c766389595fabe40880e824 /mm/compaction.c
parent5c335fe020ea287b2b49cc4dfca9f6756b88bb71 (diff)
mm, compaction: abort free scanner if split fails
If the memory compaction free scanner cannot successfully split a free page (only possible due to per-zone low watermark), terminate the free scanner rather than continuing to scan memory needlessly. If the watermark is insufficient for a free page of order <= cc->order, then terminate the scanner since all future splits will also likely fail. This prevents the compaction freeing scanner from scanning all memory on very large zones (very noticeable for zones > 128GB, for instance) when all splits will likely fail while holding zone->lock. compaction_alloc() iterating a 128GB zone has been benchmarked to take over 400ms on some systems whereas any free page isolated and ready to be split ends up failing in split_free_page() because of the low watermark check and thus the iteration continues. The next time compaction occurs, the freeing scanner will likely start at the end of the zone again since no success was made previously and we get the same lengthy iteration until the zone is brought above the low watermark. All thp page faults can take >400ms in such a state without this fix. Link: http://lkml.kernel.org/r/alpine.DEB.2.10.1606211820350.97086@chino.kir.corp.google.com Signed-off-by: David Rientjes <rientjes@google.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: Minchan Kim <minchan@kernel.org> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Mel Gorman <mgorman@techsingularity.net> Cc: Hugh Dickins <hughd@google.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/compaction.c')
-rw-r--r--mm/compaction.c39
1 files changed, 21 insertions, 18 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index 1427366ad673..79bfe0e06907 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -441,25 +441,23 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
441 441
442 /* Found a free page, break it into order-0 pages */ 442 /* Found a free page, break it into order-0 pages */
443 isolated = split_free_page(page); 443 isolated = split_free_page(page);
444 if (!isolated)
445 break;
446
444 total_isolated += isolated; 447 total_isolated += isolated;
448 cc->nr_freepages += isolated;
445 for (i = 0; i < isolated; i++) { 449 for (i = 0; i < isolated; i++) {
446 list_add(&page->lru, freelist); 450 list_add(&page->lru, freelist);
447 page++; 451 page++;
448 } 452 }
449 453 if (!strict && cc->nr_migratepages <= cc->nr_freepages) {
450 /* If a page was split, advance to the end of it */ 454 blockpfn += isolated;
451 if (isolated) { 455 break;
452 cc->nr_freepages += isolated;
453 if (!strict &&
454 cc->nr_migratepages <= cc->nr_freepages) {
455 blockpfn += isolated;
456 break;
457 }
458
459 blockpfn += isolated - 1;
460 cursor += isolated - 1;
461 continue;
462 } 456 }
457 /* Advance to the end of split page */
458 blockpfn += isolated - 1;
459 cursor += isolated - 1;
460 continue;
463 461
464isolate_fail: 462isolate_fail:
465 if (strict) 463 if (strict)
@@ -469,6 +467,9 @@ isolate_fail:
469 467
470 } 468 }
471 469
470 if (locked)
471 spin_unlock_irqrestore(&cc->zone->lock, flags);
472
472 /* 473 /*
473 * There is a tiny chance that we have read bogus compound_order(), 474 * There is a tiny chance that we have read bogus compound_order(),
474 * so be careful to not go outside of the pageblock. 475 * so be careful to not go outside of the pageblock.
@@ -490,9 +491,6 @@ isolate_fail:
490 if (strict && blockpfn < end_pfn) 491 if (strict && blockpfn < end_pfn)
491 total_isolated = 0; 492 total_isolated = 0;
492 493
493 if (locked)
494 spin_unlock_irqrestore(&cc->zone->lock, flags);
495
496 /* Update the pageblock-skip if the whole pageblock was scanned */ 494 /* Update the pageblock-skip if the whole pageblock was scanned */
497 if (blockpfn == end_pfn) 495 if (blockpfn == end_pfn)
498 update_pageblock_skip(cc, valid_page, total_isolated, false); 496 update_pageblock_skip(cc, valid_page, total_isolated, false);
@@ -1011,6 +1009,7 @@ static void isolate_freepages(struct compact_control *cc)
1011 block_end_pfn = block_start_pfn, 1009 block_end_pfn = block_start_pfn,
1012 block_start_pfn -= pageblock_nr_pages, 1010 block_start_pfn -= pageblock_nr_pages,
1013 isolate_start_pfn = block_start_pfn) { 1011 isolate_start_pfn = block_start_pfn) {
1012 unsigned long isolated;
1014 1013
1015 /* 1014 /*
1016 * This can iterate a massively long zone without finding any 1015 * This can iterate a massively long zone without finding any
@@ -1035,8 +1034,12 @@ static void isolate_freepages(struct compact_control *cc)
1035 continue; 1034 continue;
1036 1035
1037 /* Found a block suitable for isolating free pages from. */ 1036 /* Found a block suitable for isolating free pages from. */
1038 isolate_freepages_block(cc, &isolate_start_pfn, 1037 isolated = isolate_freepages_block(cc, &isolate_start_pfn,
1039 block_end_pfn, freelist, false); 1038 block_end_pfn, freelist, false);
1039 /* If isolation failed early, do not continue needlessly */
1040 if (!isolated && isolate_start_pfn < block_end_pfn &&
1041 cc->nr_migratepages > cc->nr_freepages)
1042 break;
1040 1043
1041 /* 1044 /*
1042 * If we isolated enough freepages, or aborted due to async 1045 * If we isolated enough freepages, or aborted due to async