]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - mm/compaction.c
mm, compaction: add per-zone migration pfn cache for async compaction
[mirror_ubuntu-bionic-kernel.git] / mm / compaction.c
index 627dc2e4320fdf976895e9b96c2c54d91b2b2760..70c0f8cda33f5345c1363a9cfd22560c59049df8 100644 (file)
@@ -89,7 +89,8 @@ static void __reset_isolation_suitable(struct zone *zone)
        unsigned long end_pfn = zone_end_pfn(zone);
        unsigned long pfn;
 
-       zone->compact_cached_migrate_pfn = start_pfn;
+       zone->compact_cached_migrate_pfn[0] = start_pfn;
+       zone->compact_cached_migrate_pfn[1] = start_pfn;
        zone->compact_cached_free_pfn = end_pfn;
        zone->compact_blockskip_flush = false;
 
@@ -131,9 +132,10 @@ void reset_isolation_suitable(pg_data_t *pgdat)
  */
 static void update_pageblock_skip(struct compact_control *cc,
                        struct page *page, unsigned long nr_isolated,
-                       bool migrate_scanner)
+                       bool set_unsuitable, bool migrate_scanner)
 {
        struct zone *zone = cc->zone;
+       unsigned long pfn;
 
        if (cc->ignore_skip_hint)
                return;
@@ -141,20 +143,31 @@ static void update_pageblock_skip(struct compact_control *cc,
        if (!page)
                return;
 
-       if (!nr_isolated) {
-               unsigned long pfn = page_to_pfn(page);
+       if (nr_isolated)
+               return;
+
+       /*
+        * Only skip pageblocks when all forms of compaction will be known to
+        * fail in the near future.
+        */
+       if (set_unsuitable)
                set_pageblock_skip(page);
 
-               /* Update where compaction should restart */
-               if (migrate_scanner) {
-                       if (!cc->finished_update_migrate &&
-                           pfn > zone->compact_cached_migrate_pfn)
-                               zone->compact_cached_migrate_pfn = pfn;
-               } else {
-                       if (!cc->finished_update_free &&
-                           pfn < zone->compact_cached_free_pfn)
-                               zone->compact_cached_free_pfn = pfn;
-               }
+       pfn = page_to_pfn(page);
+
+       /* Update where async and sync compaction should restart */
+       if (migrate_scanner) {
+               if (cc->finished_update_migrate)
+                       return;
+               if (pfn > zone->compact_cached_migrate_pfn[0])
+                       zone->compact_cached_migrate_pfn[0] = pfn;
+               if (cc->sync && pfn > zone->compact_cached_migrate_pfn[1])
+                       zone->compact_cached_migrate_pfn[1] = pfn;
+       } else {
+               if (cc->finished_update_free)
+                       return;
+               if (pfn < zone->compact_cached_free_pfn)
+                       zone->compact_cached_free_pfn = pfn;
        }
 }
 #else
@@ -166,7 +179,7 @@ static inline bool isolation_suitable(struct compact_control *cc,
 
 static void update_pageblock_skip(struct compact_control *cc,
                        struct page *page, unsigned long nr_isolated,
-                       bool migrate_scanner)
+                       bool set_unsuitable, bool migrate_scanner)
 {
 }
 #endif /* CONFIG_COMPACTION */
@@ -208,12 +221,6 @@ static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
        return true;
 }
 
-static inline bool compact_trylock_irqsave(spinlock_t *lock,
-                       unsigned long *flags, struct compact_control *cc)
-{
-       return compact_checklock_irqsave(lock, flags, false, cc);
-}
-
 /* Returns true if the page is within a block suitable for migration to */
 static bool suitable_migration_target(struct page *page)
 {
@@ -329,7 +336,8 @@ isolate_fail:
 
        /* Update the pageblock-skip if the whole pageblock was scanned */
        if (blockpfn == end_pfn)
-               update_pageblock_skip(cc, valid_page, total_isolated, false);
+               update_pageblock_skip(cc, valid_page, total_isolated, true,
+                                     false);
 
        count_compact_events(COMPACTFREE_SCANNED, nr_scanned);
        if (total_isolated)
@@ -464,7 +472,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
        unsigned long flags;
        bool locked = false;
        struct page *page = NULL, *valid_page = NULL;
-       bool skipped_async_unsuitable = false;
+       bool set_unsuitable = true;
        const isolate_mode_t mode = (!cc->sync ? ISOLATE_ASYNC_MIGRATE : 0) |
                                    (unevictable ? ISOLATE_UNEVICTABLE : 0);
 
@@ -541,8 +549,7 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
                         */
                        mt = get_pageblock_migratetype(page);
                        if (!cc->sync && !migrate_async_suitable(mt)) {
-                               cc->finished_update_migrate = true;
-                               skipped_async_unsuitable = true;
+                               set_unsuitable = false;
                                goto next_pageblock;
                        }
                }
@@ -646,11 +653,10 @@ next_pageblock:
        /*
         * Update the pageblock-skip information and cached scanner pfn,
         * if the whole pageblock was scanned without isolating any page.
-        * This is not done when pageblock was skipped due to being unsuitable
-        * for async compaction, so that eventual sync compaction can try.
         */
-       if (low_pfn == end_pfn && !skipped_async_unsuitable)
-               update_pageblock_skip(cc, valid_page, nr_isolated, true);
+       if (low_pfn == end_pfn)
+               update_pageblock_skip(cc, valid_page, nr_isolated,
+                                     set_unsuitable, true);
 
        trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
 
@@ -671,7 +677,10 @@ static void isolate_freepages(struct zone *zone,
                                struct compact_control *cc)
 {
        struct page *page;
-       unsigned long high_pfn, low_pfn, pfn, z_end_pfn;
+       unsigned long block_start_pfn;  /* start of current pageblock */
+       unsigned long block_end_pfn;    /* end of current pageblock */
+       unsigned long low_pfn;       /* lowest pfn scanner is able to scan */
+       unsigned long next_free_pfn; /* start pfn for scaning at next round */
        int nr_freepages = cc->nr_freepages;
        struct list_head *freelist = &cc->freepages;
 
@@ -679,32 +688,33 @@ static void isolate_freepages(struct zone *zone,
         * Initialise the free scanner. The starting point is where we last
         * successfully isolated from, zone-cached value, or the end of the
         * zone when isolating for the first time. We need this aligned to
-        * the pageblock boundary, because we do pfn -= pageblock_nr_pages
-        * in the for loop.
+        * the pageblock boundary, because we do
+        * block_start_pfn -= pageblock_nr_pages in the for loop.
+        * For ending point, take care when isolating in last pageblock of a
+        * a zone which ends in the middle of a pageblock.
         * The low boundary is the end of the pageblock the migration scanner
         * is using.
         */
-       pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
+       block_start_pfn = cc->free_pfn & ~(pageblock_nr_pages-1);
+       block_end_pfn = min(block_start_pfn + pageblock_nr_pages,
+                                               zone_end_pfn(zone));
        low_pfn = ALIGN(cc->migrate_pfn + 1, pageblock_nr_pages);
 
        /*
-        * Take care that if the migration scanner is at the end of the zone
-        * that the free scanner does not accidentally move to the next zone
-        * in the next isolation cycle.
+        * If no pages are isolated, the block_start_pfn < low_pfn check
+        * will kick in.
         */
-       high_pfn = min(low_pfn, pfn);
-
-       z_end_pfn = zone_end_pfn(zone);
+       next_free_pfn = 0;
 
        /*
         * Isolate free pages until enough are available to migrate the
         * pages on cc->migratepages. We stop searching if the migrate
         * and free page scanners meet or enough free pages are isolated.
         */
-       for (; pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
-                                       pfn -= pageblock_nr_pages) {
+       for (; block_start_pfn >= low_pfn && cc->nr_migratepages > nr_freepages;
+                               block_end_pfn = block_start_pfn,
+                               block_start_pfn -= pageblock_nr_pages) {
                unsigned long isolated;
-               unsigned long end_pfn;
 
                /*
                 * This can iterate a massively long zone without finding any
@@ -713,7 +723,7 @@ static void isolate_freepages(struct zone *zone,
                 */
                cond_resched();
 
-               if (!pfn_valid(pfn))
+               if (!pfn_valid(block_start_pfn))
                        continue;
 
                /*
@@ -723,7 +733,7 @@ static void isolate_freepages(struct zone *zone,
                 * i.e. it's possible that all pages within a zones range of
                 * pages do not belong to a single zone.
                 */
-               page = pfn_to_page(pfn);
+               page = pfn_to_page(block_start_pfn);
                if (page_zone(page) != zone)
                        continue;
 
@@ -736,15 +746,8 @@ static void isolate_freepages(struct zone *zone,
                        continue;
 
                /* Found a block suitable for isolating free pages from */
-               isolated = 0;
-
-               /*
-                * Take care when isolating in last pageblock of a zone which
-                * ends in the middle of a pageblock.
-                */
-               end_pfn = min(pfn + pageblock_nr_pages, z_end_pfn);
-               isolated = isolate_freepages_block(cc, pfn, end_pfn,
-                                                  freelist, false);
+               isolated = isolate_freepages_block(cc, block_start_pfn,
+                                       block_end_pfn, freelist, false);
                nr_freepages += isolated;
 
                /*
@@ -752,9 +755,9 @@ static void isolate_freepages(struct zone *zone,
                 * looking for free pages, the search will restart here as
                 * page migration may have returned some pages to the allocator
                 */
-               if (isolated) {
+               if (isolated && next_free_pfn == 0) {
                        cc->finished_update_free = true;
-                       high_pfn = max(high_pfn, pfn);
+                       next_free_pfn = block_start_pfn;
                }
        }
 
@@ -765,10 +768,10 @@ static void isolate_freepages(struct zone *zone,
         * If we crossed the migrate scanner, we want to keep it that way
         * so that compact_finished() may detect this
         */
-       if (pfn < low_pfn)
-               cc->free_pfn = max(pfn, zone->zone_start_pfn);
-       else
-               cc->free_pfn = high_pfn;
+       if (block_start_pfn < low_pfn)
+               next_free_pfn = cc->migrate_pfn;
+
+       cc->free_pfn = next_free_pfn;
        cc->nr_freepages = nr_freepages;
 }
 
@@ -799,23 +802,32 @@ static struct page *compaction_alloc(struct page *migratepage,
 }
 
 /*
- * We cannot control nr_migratepages and nr_freepages fully when migration is
- * running as migrate_pages() has no knowledge of compact_control. When
- * migration is complete, we count the number of pages on the lists by hand.
+ * This is a migrate-callback that "frees" freepages back to the isolated
+ * freelist.  All pages on the freelist are from the same zone, so there is no
+ * special handling needed for NUMA.
+ */
+static void compaction_free(struct page *page, unsigned long data)
+{
+       struct compact_control *cc = (struct compact_control *)data;
+
+       list_add(&page->lru, &cc->freepages);
+       cc->nr_freepages++;
+}
+
+/*
+ * We cannot control nr_migratepages fully when migration is running as
+ * migrate_pages() has no knowledge of of compact_control.  When migration is
+ * complete, we count the number of pages on the list by hand.
  */
 static void update_nr_listpages(struct compact_control *cc)
 {
        int nr_migratepages = 0;
-       int nr_freepages = 0;
        struct page *page;
 
        list_for_each_entry(page, &cc->migratepages, lru)
                nr_migratepages++;
-       list_for_each_entry(page, &cc->freepages, lru)
-               nr_freepages++;
 
        cc->nr_migratepages = nr_migratepages;
-       cc->nr_freepages = nr_freepages;
 }
 
 /* possible outcome of isolate_migratepages */
@@ -868,7 +880,8 @@ static int compact_finished(struct zone *zone,
        /* Compaction run completes if the migrate and free scanner meet */
        if (cc->free_pfn <= cc->migrate_pfn) {
                /* Let the next compaction start anew. */
-               zone->compact_cached_migrate_pfn = zone->zone_start_pfn;
+               zone->compact_cached_migrate_pfn[0] = zone->zone_start_pfn;
+               zone->compact_cached_migrate_pfn[1] = zone->zone_start_pfn;
                zone->compact_cached_free_pfn = zone_end_pfn(zone);
 
                /*
@@ -993,7 +1006,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
         * information on where the scanners should start but check that it
         * is initialised by ensuring the values are within zone boundaries.
         */
-       cc->migrate_pfn = zone->compact_cached_migrate_pfn;
+       cc->migrate_pfn = zone->compact_cached_migrate_pfn[cc->sync];
        cc->free_pfn = zone->compact_cached_free_pfn;
        if (cc->free_pfn < start_pfn || cc->free_pfn > end_pfn) {
                cc->free_pfn = end_pfn & ~(pageblock_nr_pages-1);
@@ -1001,7 +1014,8 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
        }
        if (cc->migrate_pfn < start_pfn || cc->migrate_pfn > end_pfn) {
                cc->migrate_pfn = start_pfn;
-               zone->compact_cached_migrate_pfn = cc->migrate_pfn;
+               zone->compact_cached_migrate_pfn[0] = cc->migrate_pfn;
+               zone->compact_cached_migrate_pfn[1] = cc->migrate_pfn;
        }
 
        trace_mm_compaction_begin(start_pfn, cc->migrate_pfn, cc->free_pfn, end_pfn);
@@ -1026,7 +1040,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
 
                nr_migrate = cc->nr_migratepages;
                err = migrate_pages(&cc->migratepages, compaction_alloc,
-                               (unsigned long)cc,
+                               compaction_free, (unsigned long)cc,
                                cc->sync ? MIGRATE_SYNC_LIGHT : MIGRATE_ASYNC,
                                MR_COMPACTION);
                update_nr_listpages(cc);
@@ -1165,9 +1179,6 @@ static void __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
                        if (zone_watermark_ok(zone, cc->order,
                                                low_wmark_pages(zone), 0, 0))
                                compaction_defer_reset(zone, cc->order, false);
-                       /* Currently async compaction is never deferred. */
-                       else if (cc->sync)
-                               defer_compaction(zone, cc->order);
                }
 
                VM_BUG_ON(!list_empty(&cc->freepages));