mm, page_alloc: count movable pages when stealing from pageblock
authorVlastimil Babka <vbabka@suse.cz>
Mon, 8 May 2017 22:54:40 +0000 (15:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 May 2017 00:15:10 +0000 (17:15 -0700)
When stealing pages from pageblock of a different migratetype, we count
how many free pages were stolen, and change the pageblock's migratetype
if more than half of the pageblock was free.  This might be too
conservative, as there might be other pages that are not free, but were
allocated with the same migratetype as our allocation requested.

While we cannot determine the migratetype of allocated pages precisely
(at least without the page_owner functionality enabled), we can count
pages that compaction would try to isolate for migration - those are
either on LRU or __PageMovable().  The rest can be assumed to be
MIGRATE_RECLAIMABLE or MIGRATE_UNMOVABLE, which we cannot easily
distinguish.  This counting can be done as part of free page stealing
with little additional overhead.

The page stealing code is changed so that it considers free pages plus
pages of the "good" migratetype for the decision whether to change
pageblock's migratetype.

The result should be more accurate migratetype of pageblocks wrt the
actual pages in the pageblocks, when stealing from semi-occupied
pageblocks.  This should help the efficiency of page grouping by
mobility.

In testing based on 4.9 kernel with stress-highalloc from mmtests
configured for order-4 GFP_KERNEL allocations, this patch has reduced
the number of unmovable allocations falling back to movable pageblocks
by 47%.  The number of movable allocations falling back to other
pageblocks are increased by 55%, but these events don't cause permanent
fragmentation, so the tradeoff should be positive.  Later patches also
offset the movable fallback increase to some extent.

[akpm@linux-foundation.org: merge fix]
Link: http://lkml.kernel.org/r/20170307131545.28577-5-vbabka@suse.cz
Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
Acked-by: Mel Gorman <mgorman@techsingularity.net>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.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>
include/linux/page-isolation.h
mm/page_alloc.c
mm/page_isolation.c

index 047d64706f2a298157cf0d54305aa8e49f6f7837..d4cd2014fa6f5a18f03f1b0babfccb21fa3ac55f 100644 (file)
@@ -33,10 +33,7 @@ bool has_unmovable_pages(struct zone *zone, struct page *page, int count,
                         bool skip_hwpoisoned_pages);
 void set_pageblock_migratetype(struct page *page, int migratetype);
 int move_freepages_block(struct zone *zone, struct page *page,
-                               int migratetype);
-int move_freepages(struct zone *zone,
-                         struct page *start_page, struct page *end_page,
-                         int migratetype);
+                               int migratetype, int *num_movable);
 
 /*
  * Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE.
index 2f1118b4dda4096ce8e27bc91e6577c3c9e3665e..d90792addeb973514d1556d23aab99836afed24d 100644 (file)
@@ -1832,9 +1832,9 @@ static inline struct page *__rmqueue_cma_fallback(struct zone *zone,
  * Note that start_page and end_pages are not aligned on a pageblock
  * boundary. If alignment is required, use move_freepages_block()
  */
-int move_freepages(struct zone *zone,
+static int move_freepages(struct zone *zone,
                          struct page *start_page, struct page *end_page,
-                         int migratetype)
+                         int migratetype, int *num_movable)
 {
        struct page *page;
        unsigned int order;
@@ -1851,6 +1851,9 @@ int move_freepages(struct zone *zone,
        VM_BUG_ON(page_zone(start_page) != page_zone(end_page));
 #endif
 
+       if (num_movable)
+               *num_movable = 0;
+
        for (page = start_page; page <= end_page;) {
                if (!pfn_valid_within(page_to_pfn(page))) {
                        page++;
@@ -1861,6 +1864,15 @@ int move_freepages(struct zone *zone,
                VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
 
                if (!PageBuddy(page)) {
+                       /*
+                        * We assume that pages that could be isolated for
+                        * migration are movable. But we don't actually try
+                        * isolating, as that would be expensive.
+                        */
+                       if (num_movable &&
+                                       (PageLRU(page) || __PageMovable(page)))
+                               (*num_movable)++;
+
                        page++;
                        continue;
                }
@@ -1876,7 +1888,7 @@ int move_freepages(struct zone *zone,
 }
 
 int move_freepages_block(struct zone *zone, struct page *page,
-                               int migratetype)
+                               int migratetype, int *num_movable)
 {
        unsigned long start_pfn, end_pfn;
        struct page *start_page, *end_page;
@@ -1893,7 +1905,8 @@ int move_freepages_block(struct zone *zone, struct page *page,
        if (!zone_spans_pfn(zone, end_pfn))
                return 0;
 
-       return move_freepages(zone, start_page, end_page, migratetype);
+       return move_freepages(zone, start_page, end_page, migratetype,
+                                                               num_movable);
 }
 
 static void change_pageblock_range(struct page *pageblock_page,
@@ -1943,22 +1956,26 @@ static bool can_steal_fallback(unsigned int order, int start_mt)
 /*
  * This function implements actual steal behaviour. If order is large enough,
  * we can steal whole pageblock. If not, we first move freepages in this
- * pageblock and check whether half of pages are moved or not. If half of
- * pages are moved, we can change migratetype of pageblock and permanently
- * use it's pages as requested migratetype in the future.
+ * pageblock to our migratetype and determine how many already-allocated pages
+ * are there in the pageblock with a compatible migratetype. If at least half
+ * of pages are free or compatible, we can change migratetype of the pageblock
+ * itself, so pages freed in the future will be put on the correct free list.
  */
 static void steal_suitable_fallback(struct zone *zone, struct page *page,
                                        int start_type, bool whole_block)
 {
        unsigned int current_order = page_order(page);
        struct free_area *area;
-       int pages;
+       int free_pages, movable_pages, alike_pages;
+       int old_block_type;
+
+       old_block_type = get_pageblock_migratetype(page);
 
        /*
         * This can happen due to races and we want to prevent broken
         * highatomic accounting.
         */
-       if (is_migrate_highatomic_page(page))
+       if (is_migrate_highatomic(old_block_type))
                goto single_page;
 
        /* Take ownership for orders >= pageblock_order */
@@ -1971,13 +1988,39 @@ static void steal_suitable_fallback(struct zone *zone, struct page *page,
        if (!whole_block)
                goto single_page;
 
-       pages = move_freepages_block(zone, page, start_type);
+       free_pages = move_freepages_block(zone, page, start_type,
+                                               &movable_pages);
+       /*
+        * Determine how many pages are compatible with our allocation.
+        * For movable allocation, it's the number of movable pages which
+        * we just obtained. For other types it's a bit more tricky.
+        */
+       if (start_type == MIGRATE_MOVABLE) {
+               alike_pages = movable_pages;
+       } else {
+               /*
+                * If we are falling back a RECLAIMABLE or UNMOVABLE allocation
+                * to MOVABLE pageblock, consider all non-movable pages as
+                * compatible. If it's UNMOVABLE falling back to RECLAIMABLE or
+                * vice versa, be conservative since we can't distinguish the
+                * exact migratetype of non-movable pages.
+                */
+               if (old_block_type == MIGRATE_MOVABLE)
+                       alike_pages = pageblock_nr_pages
+                                               - (free_pages + movable_pages);
+               else
+                       alike_pages = 0;
+       }
+
        /* moving whole block can fail due to zone boundary conditions */
-       if (!pages)
+       if (!free_pages)
                goto single_page;
 
-       /* Claim the whole block if over half of it is free */
-       if (pages >= (1 << (pageblock_order-1)) ||
+       /*
+        * If a sufficient number of pages in the block are either free or of
+        * comparable migratability as our allocation, claim the whole block.
+        */
+       if (free_pages + alike_pages >= (1 << (pageblock_order-1)) ||
                        page_group_by_mobility_disabled)
                set_pageblock_migratetype(page, start_type);
 
@@ -2055,7 +2098,7 @@ static void reserve_highatomic_pageblock(struct page *page, struct zone *zone,
            && !is_migrate_cma(mt)) {
                zone->nr_reserved_highatomic += pageblock_nr_pages;
                set_pageblock_migratetype(page, MIGRATE_HIGHATOMIC);
-               move_freepages_block(zone, page, MIGRATE_HIGHATOMIC);
+               move_freepages_block(zone, page, MIGRATE_HIGHATOMIC, NULL);
        }
 
 out_unlock:
@@ -2132,7 +2175,8 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
                         * may increase.
                         */
                        set_pageblock_migratetype(page, ac->migratetype);
-                       ret = move_freepages_block(zone, page, ac->migratetype);
+                       ret = move_freepages_block(zone, page, ac->migratetype,
+                                                                       NULL);
                        if (ret) {
                                spin_unlock_irqrestore(&zone->lock, flags);
                                return ret;
index 7927bbb54a4e3200e39fc098b787794f21f6c855..5092e4ef00c832fefb624a1d9d8f618402ad4402 100644 (file)
@@ -66,7 +66,8 @@ out:
 
                set_pageblock_migratetype(page, MIGRATE_ISOLATE);
                zone->nr_isolate_pageblock++;
-               nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE);
+               nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE,
+                                                                       NULL);
 
                __mod_zone_freepage_state(zone, -nr_pages, migratetype);
        }
@@ -120,7 +121,7 @@ static void unset_migratetype_isolate(struct page *page, unsigned migratetype)
         * pageblock scanning for freepage moving.
         */
        if (!isolated_page) {
-               nr_pages = move_freepages_block(zone, page, migratetype);
+               nr_pages = move_freepages_block(zone, page, migratetype, NULL);
                __mod_zone_freepage_state(zone, nr_pages, migratetype);
        }
        set_pageblock_migratetype(page, migratetype);