summaryrefslogtreecommitdiffstats
path: root/mm/compaction.c
diff options
context:
space:
mode:
authorMel Gorman <mgorman@techsingularity.net>2019-03-05 18:45:11 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2019-03-06 00:07:16 -0500
commitcb2dcaf023c2cf12d45289c82d4030d33f7df73e (patch)
treeccbcbec1d5aa73199e5c09f0eb09d96a7f812fe3 /mm/compaction.c
parent804d3121ba5f03af0ab225e2f688ee3ee669c0d2 (diff)
mm, compaction: finish pageblock scanning on contention
Async migration aborts on spinlock contention but contention can be high when there are multiple compaction attempts and kswapd is active. The consequence is that the migration scanners move forward uselessly while still contending on locks for longer while leaving suitable migration sources behind. This patch will acquire the lock but track when contention occurs. When it does, the current pageblock will finish as compaction may succeed for that block and then abort. This will have a variable impact on latency as in some cases useless scanning is avoided (reduces latency) but a lock will be contended (increase latency) or a single contended pageblock is scanned that would otherwise have been skipped (increase latency). 5.0.0-rc1 5.0.0-rc1 norescan-v3r16 finishcontend-v3r16 Amean fault-both-1 0.00 ( 0.00%) 0.00 * 0.00%* Amean fault-both-3 3002.07 ( 0.00%) 3153.17 ( -5.03%) Amean fault-both-5 4684.47 ( 0.00%) 4280.52 ( 8.62%) Amean fault-both-7 6815.54 ( 0.00%) 5811.50 * 14.73%* Amean fault-both-12 10864.02 ( 0.00%) 9276.85 ( 14.61%) Amean fault-both-18 12247.52 ( 0.00%) 11032.67 ( 9.92%) Amean fault-both-24 15683.99 ( 0.00%) 14285.70 ( 8.92%) Amean fault-both-30 18620.02 ( 0.00%) 16293.76 * 12.49%* Amean fault-both-32 19250.28 ( 0.00%) 16721.02 * 13.14%* 5.0.0-rc1 5.0.0-rc1 norescan-v3r16 finishcontend-v3r16 Percentage huge-1 0.00 ( 0.00%) 0.00 ( 0.00%) Percentage huge-3 95.00 ( 0.00%) 96.82 ( 1.92%) Percentage huge-5 94.22 ( 0.00%) 95.40 ( 1.26%) Percentage huge-7 92.35 ( 0.00%) 95.92 ( 3.86%) Percentage huge-12 91.90 ( 0.00%) 96.73 ( 5.25%) Percentage huge-18 89.58 ( 0.00%) 96.77 ( 8.03%) Percentage huge-24 90.03 ( 0.00%) 96.05 ( 6.69%) Percentage huge-30 89.14 ( 0.00%) 96.81 ( 8.60%) Percentage huge-32 90.58 ( 0.00%) 97.41 ( 7.54%) There is a variable impact that is mostly good on latency while allocation success rates are slightly higher. System CPU usage is reduced by about 10% but scan rate impact is mixed Compaction migrate scanned 27997659.00 20148867 Compaction free scanned 120782791.00 118324914 Migration scan rates are reduced 28% which is expected as a pageblock is used by the async scanner instead of skipped. The impact on the free scanner is known to be variable. Overall the primary justification for this patch is that completing scanning of a pageblock is very important for later patches. [yuehaibing@huawei.com: fix unused variable warning] Link: http://lkml.kernel.org/r/20190118175136.31341-14-mgorman@techsingularity.net Signed-off-by: Mel Gorman <mgorman@techsingularity.net> Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: YueHaibing <yuehaibing@huawei.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Dan Carpenter <dan.carpenter@oracle.com> Cc: David Rientjes <rientjes@google.com> 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.c90
1 files changed, 34 insertions, 56 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index 00a5126b6548..5325211398f8 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -382,24 +382,25 @@ static bool test_and_set_skip(struct compact_control *cc, struct page *page,
382 382
383/* 383/*
384 * Compaction requires the taking of some coarse locks that are potentially 384 * Compaction requires the taking of some coarse locks that are potentially
385 * very heavily contended. For async compaction, back out if the lock cannot 385 * very heavily contended. For async compaction, trylock and record if the
386 * be taken immediately. For sync compaction, spin on the lock if needed. 386 * lock is contended. The lock will still be acquired but compaction will
387 * abort when the current block is finished regardless of success rate.
388 * Sync compaction acquires the lock.
387 * 389 *
388 * Returns true if the lock is held 390 * Always returns true which makes it easier to track lock state in callers.
389 * Returns false if the lock is not held and compaction should abort
390 */ 391 */
391static bool compact_trylock_irqsave(spinlock_t *lock, unsigned long *flags, 392static bool compact_lock_irqsave(spinlock_t *lock, unsigned long *flags,
392 struct compact_control *cc) 393 struct compact_control *cc)
393{ 394{
394 if (cc->mode == MIGRATE_ASYNC) { 395 /* Track if the lock is contended in async mode */
395 if (!spin_trylock_irqsave(lock, *flags)) { 396 if (cc->mode == MIGRATE_ASYNC && !cc->contended) {
396 cc->contended = true; 397 if (spin_trylock_irqsave(lock, *flags))
397 return false; 398 return true;
398 } 399
399 } else { 400 cc->contended = true;
400 spin_lock_irqsave(lock, *flags);
401 } 401 }
402 402
403 spin_lock_irqsave(lock, *flags);
403 return true; 404 return true;
404} 405}
405 406
@@ -432,10 +433,8 @@ static bool compact_unlock_should_abort(spinlock_t *lock,
432 } 433 }
433 434
434 if (need_resched()) { 435 if (need_resched()) {
435 if (cc->mode == MIGRATE_ASYNC) { 436 if (cc->mode == MIGRATE_ASYNC)
436 cc->contended = true; 437 cc->contended = true;
437 return true;
438 }
439 cond_resched(); 438 cond_resched();
440 } 439 }
441 440
@@ -455,10 +454,8 @@ static inline bool compact_should_abort(struct compact_control *cc)
455{ 454{
456 /* async compaction aborts if contended */ 455 /* async compaction aborts if contended */
457 if (need_resched()) { 456 if (need_resched()) {
458 if (cc->mode == MIGRATE_ASYNC) { 457 if (cc->mode == MIGRATE_ASYNC)
459 cc->contended = true; 458 cc->contended = true;
460 return true;
461 }
462 459
463 cond_resched(); 460 cond_resched();
464 } 461 }
@@ -535,18 +532,8 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
535 * recheck as well. 532 * recheck as well.
536 */ 533 */
537 if (!locked) { 534 if (!locked) {
538 /* 535 locked = compact_lock_irqsave(&cc->zone->lock,
539 * The zone lock must be held to isolate freepages.
540 * Unfortunately this is a very coarse lock and can be
541 * heavily contended if there are parallel allocations
542 * or parallel compactions. For async compaction do not
543 * spin on the lock and we acquire the lock as late as
544 * possible.
545 */
546 locked = compact_trylock_irqsave(&cc->zone->lock,
547 &flags, cc); 536 &flags, cc);
548 if (!locked)
549 break;
550 537
551 /* Recheck this is a buddy page under lock */ 538 /* Recheck this is a buddy page under lock */
552 if (!PageBuddy(page)) 539 if (!PageBuddy(page))
@@ -900,15 +887,9 @@ isolate_migratepages_block(struct compact_control *cc, unsigned long low_pfn,
900 887
901 /* If we already hold the lock, we can skip some rechecking */ 888 /* If we already hold the lock, we can skip some rechecking */
902 if (!locked) { 889 if (!locked) {
903 locked = compact_trylock_irqsave(zone_lru_lock(zone), 890 locked = compact_lock_irqsave(zone_lru_lock(zone),
904 &flags, cc); 891 &flags, cc);
905 892
906 /* Allow future scanning if the lock is contended */
907 if (!locked) {
908 clear_pageblock_skip(page);
909 break;
910 }
911
912 /* Try get exclusive access under lock */ 893 /* Try get exclusive access under lock */
913 if (!skip_updated) { 894 if (!skip_updated) {
914 skip_updated = true; 895 skip_updated = true;
@@ -951,9 +932,12 @@ isolate_success:
951 932
952 /* 933 /*
953 * Avoid isolating too much unless this block is being 934 * Avoid isolating too much unless this block is being
954 * rescanned (e.g. dirty/writeback pages, parallel allocation). 935 * rescanned (e.g. dirty/writeback pages, parallel allocation)
936 * or a lock is contended. For contention, isolate quickly to
937 * potentially remove one source of contention.
955 */ 938 */
956 if (cc->nr_migratepages == COMPACT_CLUSTER_MAX && !cc->rescan) { 939 if (cc->nr_migratepages == COMPACT_CLUSTER_MAX &&
940 !cc->rescan && !cc->contended) {
957 ++low_pfn; 941 ++low_pfn;
958 break; 942 break;
959 } 943 }
@@ -1416,12 +1400,8 @@ static void isolate_freepages(struct compact_control *cc)
1416 isolate_freepages_block(cc, &isolate_start_pfn, block_end_pfn, 1400 isolate_freepages_block(cc, &isolate_start_pfn, block_end_pfn,
1417 freelist, false); 1401 freelist, false);
1418 1402
1419 /* 1403 /* Are enough freepages isolated? */
1420 * If we isolated enough freepages, or aborted due to lock 1404 if (cc->nr_freepages >= cc->nr_migratepages) {
1421 * contention, terminate.
1422 */
1423 if ((cc->nr_freepages >= cc->nr_migratepages)
1424 || cc->contended) {
1425 if (isolate_start_pfn >= block_end_pfn) { 1405 if (isolate_start_pfn >= block_end_pfn) {
1426 /* 1406 /*
1427 * Restart at previous pageblock if more 1407 * Restart at previous pageblock if more
@@ -1463,13 +1443,8 @@ static struct page *compaction_alloc(struct page *migratepage,
1463 struct compact_control *cc = (struct compact_control *)data; 1443 struct compact_control *cc = (struct compact_control *)data;
1464 struct page *freepage; 1444 struct page *freepage;
1465 1445
1466 /*
1467 * Isolate free pages if necessary, and if we are not aborting due to
1468 * contention.
1469 */
1470 if (list_empty(&cc->freepages)) { 1446 if (list_empty(&cc->freepages)) {
1471 if (!cc->contended) 1447 isolate_freepages(cc);
1472 isolate_freepages(cc);
1473 1448
1474 if (list_empty(&cc->freepages)) 1449 if (list_empty(&cc->freepages))
1475 return NULL; 1450 return NULL;
@@ -1733,7 +1708,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
1733 low_pfn = isolate_migratepages_block(cc, low_pfn, 1708 low_pfn = isolate_migratepages_block(cc, low_pfn,
1734 block_end_pfn, isolate_mode); 1709 block_end_pfn, isolate_mode);
1735 1710
1736 if (!low_pfn || cc->contended) 1711 if (!low_pfn)
1737 return ISOLATE_ABORT; 1712 return ISOLATE_ABORT;
1738 1713
1739 /* 1714 /*
@@ -1763,9 +1738,7 @@ static enum compact_result __compact_finished(struct compact_control *cc)
1763{ 1738{
1764 unsigned int order; 1739 unsigned int order;
1765 const int migratetype = cc->migratetype; 1740 const int migratetype = cc->migratetype;
1766 1741 int ret;
1767 if (cc->contended || fatal_signal_pending(current))
1768 return COMPACT_CONTENDED;
1769 1742
1770 /* Compaction run completes if the migrate and free scanner meet */ 1743 /* Compaction run completes if the migrate and free scanner meet */
1771 if (compact_scanners_met(cc)) { 1744 if (compact_scanners_met(cc)) {
@@ -1800,6 +1773,7 @@ static enum compact_result __compact_finished(struct compact_control *cc)
1800 return COMPACT_CONTINUE; 1773 return COMPACT_CONTINUE;
1801 1774
1802 /* Direct compactor: Is a suitable page free? */ 1775 /* Direct compactor: Is a suitable page free? */
1776 ret = COMPACT_NO_SUITABLE_PAGE;
1803 for (order = cc->order; order < MAX_ORDER; order++) { 1777 for (order = cc->order; order < MAX_ORDER; order++) {
1804 struct free_area *area = &cc->zone->free_area[order]; 1778 struct free_area *area = &cc->zone->free_area[order];
1805 bool can_steal; 1779 bool can_steal;
@@ -1839,11 +1813,15 @@ static enum compact_result __compact_finished(struct compact_control *cc)
1839 return COMPACT_SUCCESS; 1813 return COMPACT_SUCCESS;
1840 } 1814 }
1841 1815
1842 return COMPACT_CONTINUE; 1816 ret = COMPACT_CONTINUE;
1817 break;
1843 } 1818 }
1844 } 1819 }
1845 1820
1846 return COMPACT_NO_SUITABLE_PAGE; 1821 if (cc->contended || fatal_signal_pending(current))
1822 ret = COMPACT_CONTENDED;
1823
1824 return ret;
1847} 1825}
1848 1826
1849static enum compact_result compact_finished(struct compact_control *cc) 1827static enum compact_result compact_finished(struct compact_control *cc)