aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorMel Gorman <mgorman@suse.de>2012-10-08 19:32:45 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-09 03:22:50 -0400
commitc89511ab2f8fe2b47585e60da8af7fd213ec877e (patch)
treec6b04cb5335957e8409edc77ca23ef012d9d326d /mm
parentbb13ffeb9f6bfeb301443994dfbf29f91117dfb3 (diff)
mm: compaction: Restart compaction from near where it left off
This is almost entirely based on Rik's previous patches and discussions with him about how this might be implemented. 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. This patch caches where the migration and free scanner should start from on subsequent compaction invocations using the pageblock-skip information. When compaction starts it begins from the cached restart points and will update the cached restart points until a page is isolated or a pageblock is skipped that would have been scanned by synchronous compaction. Signed-off-by: Mel Gorman <mgorman@suse.de> Acked-by: Rik van Riel <riel@redhat.com> Cc: Richard Davies <richard@arachsys.com> Cc: Shaohua Li <shli@kernel.org> Cc: Avi Kivity <avi@redhat.com> Acked-by: Rafael Aquini <aquini@redhat.com> Cc: Fengguang Wu <fengguang.wu@intel.com> 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.c58
-rw-r--r--mm/internal.h4
2 files changed, 52 insertions, 10 deletions
diff --git a/mm/compaction.c b/mm/compaction.c
index d9dbb97e607b..f94cbc0b99a5 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -80,6 +80,9 @@ static void reset_isolation_suitable(struct zone *zone)
80 */ 80 */
81 if (time_before(jiffies, zone->compact_blockskip_expire)) 81 if (time_before(jiffies, zone->compact_blockskip_expire))
82 return; 82 return;
83
84 zone->compact_cached_migrate_pfn = start_pfn;
85 zone->compact_cached_free_pfn = end_pfn;
83 zone->compact_blockskip_expire = jiffies + (HZ * 5); 86 zone->compact_blockskip_expire = jiffies + (HZ * 5);
84 87
85 /* Walk the zone and mark every pageblock as suitable for isolation */ 88 /* Walk the zone and mark every pageblock as suitable for isolation */
@@ -103,13 +106,29 @@ static void reset_isolation_suitable(struct zone *zone)
103 * If no pages were isolated then mark this pageblock to be skipped in the 106 * If no pages were isolated then mark this pageblock to be skipped in the
104 * future. The information is later cleared by reset_isolation_suitable(). 107 * future. The information is later cleared by reset_isolation_suitable().
105 */ 108 */
106static void update_pageblock_skip(struct page *page, unsigned long nr_isolated) 109static void update_pageblock_skip(struct compact_control *cc,
110 struct page *page, unsigned long nr_isolated,
111 bool migrate_scanner)
107{ 112{
113 struct zone *zone = cc->zone;
108 if (!page) 114 if (!page)
109 return; 115 return;
110 116
111 if (!nr_isolated) 117 if (!nr_isolated) {
118 unsigned long pfn = page_to_pfn(page);
112 set_pageblock_skip(page); 119 set_pageblock_skip(page);
120
121 /* Update where compaction should restart */
122 if (migrate_scanner) {
123 if (!cc->finished_update_migrate &&
124 pfn > zone->compact_cached_migrate_pfn)
125 zone->compact_cached_migrate_pfn = pfn;
126 } else {
127 if (!cc->finished_update_free &&
128 pfn < zone->compact_cached_free_pfn)
129 zone->compact_cached_free_pfn = pfn;
130 }
131 }
113} 132}
114#else 133#else
115static inline bool isolation_suitable(struct compact_control *cc, 134static inline bool isolation_suitable(struct compact_control *cc,
@@ -118,7 +137,9 @@ static inline bool isolation_suitable(struct compact_control *cc,
118 return true; 137 return true;
119} 138}
120 139
121static void update_pageblock_skip(struct page *page, unsigned long nr_isolated) 140static void update_pageblock_skip(struct compact_control *cc,
141 struct page *page, unsigned long nr_isolated,
142 bool migrate_scanner)
122{ 143{
123} 144}
124#endif /* CONFIG_COMPACTION */ 145#endif /* CONFIG_COMPACTION */
@@ -327,7 +348,7 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
327 348
328 /* Update the pageblock-skip if the whole pageblock was scanned */ 349 /* Update the pageblock-skip if the whole pageblock was scanned */
329 if (blockpfn == end_pfn) 350 if (blockpfn == end_pfn)
330 update_pageblock_skip(valid_page, total_isolated); 351 update_pageblock_skip(cc, valid_page, total_isolated, false);
331 352
332 return total_isolated; 353 return total_isolated;
333} 354}
@@ -533,6 +554,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
533 */ 554 */
534 if (!cc->sync && last_pageblock_nr != pageblock_nr && 555 if (!cc->sync && last_pageblock_nr != pageblock_nr &&
535 !migrate_async_suitable(get_pageblock_migratetype(page))) { 556 !migrate_async_suitable(get_pageblock_migratetype(page))) {
557 cc->finished_update_migrate = true;
536 goto next_pageblock; 558 goto next_pageblock;
537 } 559 }
538 560
@@ -583,6 +605,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
583 VM_BUG_ON(PageTransCompound(page)); 605 VM_BUG_ON(PageTransCompound(page));
584 606
585 /* Successfully isolated */ 607 /* Successfully isolated */
608 cc->finished_update_migrate = true;
586 del_page_from_lru_list(page, lruvec, page_lru(page)); 609 del_page_from_lru_list(page, lruvec, page_lru(page));
587 list_add(&page->lru, migratelist); 610 list_add(&page->lru, migratelist);
588 cc->nr_migratepages++; 611 cc->nr_migratepages++;
@@ -609,7 +632,7 @@ next_pageblock:
609 632
610 /* Update the pageblock-skip if the whole pageblock was scanned */ 633 /* Update the pageblock-skip if the whole pageblock was scanned */
611 if (low_pfn == end_pfn) 634 if (low_pfn == end_pfn)
612 update_pageblock_skip(valid_page, nr_isolated); 635 update_pageblock_skip(cc, valid_page, nr_isolated, true);
613 636
614 trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated); 637 trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
615 638
@@ -690,8 +713,10 @@ static void isolate_freepages(struct zone *zone,
690 * looking for free pages, the search will restart here as 713 * looking for free pages, the search will restart here as
691 * page migration may have returned some pages to the allocator 714 * page migration may have returned some pages to the allocator
692 */ 715 */
693 if (isolated) 716 if (isolated) {
717 cc->finished_update_free = true;
694 high_pfn = max(high_pfn, pfn); 718 high_pfn = max(high_pfn, pfn);
719 }
695 } 720 }
696 721
697 /* split_free_page does not map the pages */ 722 /* split_free_page does not map the pages */
@@ -888,6 +913,8 @@ unsigned long compaction_suitable(struct zone *zone, int order)
888static int compact_zone(struct zone *zone, struct compact_control *cc) 913static int compact_zone(struct zone *zone, struct compact_control *cc)
889{ 914{
890 int ret; 915 int ret;
916 unsigned long start_pfn = zone->zone_start_pfn;
917 unsigned long end_pfn = zone->zone_start_pfn + zone->spanned_pages;
891 918
892 ret = compaction_suitable(zone, cc->order); 919 ret = compaction_suitable(zone, cc->order);
893 switch (ret) { 920 switch (ret) {
@@ -900,10 +927,21 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
900 ; 927 ;
901 } 928 }
902 929
903 /* Setup to move all movable pages to the end of the zone */ 930 /*
904 cc->migrate_pfn = zone->zone_start_pfn; 931 * Setup to move all movable pages to the end of the zone. Used cached
905 cc->free_pfn = cc->migrate_pfn + zone->spanned_pages; 932 * information on where the scanners should start but check that it
906 cc->free_pfn &= ~(pageblock_nr_pages-1); 933 * is initialised by ensuring the values are within zone boundaries.
934 */
935 cc->migrate_pfn = zone->compact_cached_migrate_pfn;
936 cc->free_pfn = zone->compact_cached_free_pfn;
937 if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
938 cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
939 zone->compact_cached_free_pfn = cc->free_pfn;
940 }
941 if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
942 cc->migrate_pfn = start_pfn;
943 zone->compact_cached_migrate_pfn = cc->migrate_pfn;
944 }
907 945
908 /* Clear pageblock skip if there are numerous alloc failures */ 946 /* Clear pageblock skip if there are numerous alloc failures */
909 if (zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT) 947 if (zone->compact_defer_shift == COMPACT_MAX_DEFER_SHIFT)
diff --git a/mm/internal.h b/mm/internal.h
index 7ba56ac360b2..7f72f249bc29 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -121,6 +121,10 @@ struct compact_control {
121 unsigned long migrate_pfn; /* isolate_migratepages search base */ 121 unsigned long migrate_pfn; /* isolate_migratepages search base */
122 bool sync; /* Synchronous migration */ 122 bool sync; /* Synchronous migration */
123 bool ignore_skip_hint; /* Scan blocks even if marked skip */ 123 bool ignore_skip_hint; /* Scan blocks even if marked skip */
124 bool finished_update_free; /* True when the zone cached pfns are
125 * no longer being updated
126 */
127 bool finished_update_migrate;
124 128
125 int order; /* order a direct compactor needs */ 129 int order; /* order a direct compactor needs */
126 int migratetype; /* MOVABLE, RECLAIMABLE etc */ 130 int migratetype; /* MOVABLE, RECLAIMABLE etc */