]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - mm/compaction.c
USB: cdc-acm: downgrade message to debug
[mirror_ubuntu-focal-kernel.git] / mm / compaction.c
index 672d3c78c6abfcfa152b95113701982fac8221e2..d686887856feedd53057ef9c94c9adaae03df0b9 100644 (file)
@@ -1276,7 +1276,7 @@ fast_isolate_freepages(struct compact_control *cc)
 {
        unsigned int limit = min(1U, freelist_scan_limit(cc) >> 1);
        unsigned int nr_scanned = 0;
-       unsigned long low_pfn, min_pfn, high_pfn = 0, highest = 0;
+       unsigned long low_pfn, min_pfn, highest = 0;
        unsigned long nr_isolated = 0;
        unsigned long distance;
        struct page *page = NULL;
@@ -1321,6 +1321,7 @@ fast_isolate_freepages(struct compact_control *cc)
                struct page *freepage;
                unsigned long flags;
                unsigned int order_scanned = 0;
+               unsigned long high_pfn = 0;
 
                if (!area->nr_free)
                        continue;
@@ -1629,6 +1630,7 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
        unsigned long pfn = cc->migrate_pfn;
        unsigned long high_pfn;
        int order;
+       bool found_block = false;
 
        /* Skip hints are relied on to avoid repeats on the fast search */
        if (cc->ignore_skip_hint)
@@ -1671,7 +1673,7 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
        high_pfn = pageblock_start_pfn(cc->migrate_pfn + distance);
 
        for (order = cc->order - 1;
-            order >= PAGE_ALLOC_COSTLY_ORDER && pfn == cc->migrate_pfn && nr_scanned < limit;
+            order >= PAGE_ALLOC_COSTLY_ORDER && !found_block && nr_scanned < limit;
             order--) {
                struct free_area *area = &cc->zone->free_area[order];
                struct list_head *freelist;
@@ -1686,7 +1688,11 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
                list_for_each_entry(freepage, freelist, lru) {
                        unsigned long free_pfn;
 
-                       nr_scanned++;
+                       if (nr_scanned++ >= limit) {
+                               move_freelist_tail(freelist, freepage);
+                               break;
+                       }
+
                        free_pfn = page_to_pfn(freepage);
                        if (free_pfn < high_pfn) {
                                /*
@@ -1695,12 +1701,8 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
                                 * the list assumes an entry is deleted, not
                                 * reordered.
                                 */
-                               if (get_pageblock_skip(freepage)) {
-                                       if (list_is_last(freelist, &freepage->lru))
-                                               break;
-
+                               if (get_pageblock_skip(freepage))
                                        continue;
-                               }
 
                                /* Reorder to so a future search skips recent pages */
                                move_freelist_tail(freelist, freepage);
@@ -1708,15 +1710,10 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
                                update_fast_start_pfn(cc, free_pfn);
                                pfn = pageblock_start_pfn(free_pfn);
                                cc->fast_search_fail = 0;
+                               found_block = true;
                                set_pageblock_skip(freepage);
                                break;
                        }
-
-                       if (nr_scanned >= limit) {
-                               cc->fast_search_fail++;
-                               move_freelist_tail(freelist, freepage);
-                               break;
-                       }
                }
                spin_unlock_irqrestore(&cc->zone->lock, flags);
        }
@@ -1727,9 +1724,10 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
         * If fast scanning failed then use a cached entry for a page block
         * that had free pages as the basis for starting a linear scan.
         */
-       if (pfn == cc->migrate_pfn)
+       if (!found_block) {
+               cc->fast_search_fail++;
                pfn = reinit_migrate_pfn(cc);
-
+       }
        return pfn;
 }
 
@@ -2310,16 +2308,26 @@ static enum compact_result compact_zone_order(struct zone *zone, int order,
                .page = NULL,
        };
 
-       if (capture)
-               current->capture_control = &capc;
+       /*
+        * Make sure the structs are really initialized before we expose the
+        * capture control, in case we are interrupted and the interrupt handler
+        * frees a page.
+        */
+       barrier();
+       WRITE_ONCE(current->capture_control, &capc);
 
        ret = compact_zone(&cc, &capc);
 
        VM_BUG_ON(!list_empty(&cc.freepages));
        VM_BUG_ON(!list_empty(&cc.migratepages));
 
-       *capture = capc.page;
-       current->capture_control = NULL;
+       /*
+        * Make sure we hide capture control first before we read the captured
+        * page pointer, otherwise an interrupt could free and capture a page
+        * and we would leak it.
+        */
+       WRITE_ONCE(current->capture_control, NULL);
+       *capture = READ_ONCE(capc.page);
 
        return ret;
 }
@@ -2333,6 +2341,7 @@ int sysctl_extfrag_threshold = 500;
  * @alloc_flags: The allocation flags of the current allocation
  * @ac: The context of current allocation
  * @prio: Determines how hard direct compaction should try to succeed
+ * @capture: Pointer to free page created by compaction will be stored here
  *
  * This is the main entry point for direct page compaction.
  */