mm: enforce pageblock_order < MAX_ORDER
authorDavid Hildenbrand <david@redhat.com>
Tue, 22 Mar 2022 21:43:20 +0000 (14:43 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 22 Mar 2022 22:57:06 +0000 (15:57 -0700)
Some places in the kernel don't really expect pageblock_order >=
MAX_ORDER, and it looks like this is only possible in corner cases:

1) CONFIG_DEFERRED_STRUCT_PAGE_INIT we'll end up freeing pageblock_order
   pages via __free_pages_core(), which cannot possibly work.

2) find_zone_movable_pfns_for_nodes() will roundup the ZONE_MOVABLE
   start PFN to MAX_ORDER_NR_PAGES. Consequently with a bigger
   pageblock_order, we could have a single pageblock partially managed by
   two zones.

3) compaction code runs into __fragmentation_index() with order
   >= MAX_ORDER, when checking WARN_ON_ONCE(order >= MAX_ORDER). [1]

4) mm/page_reporting.c won't be reporting any pages with default
   page_reporting_order == pageblock_order, as we'll be skipping the
   reporting loop inside page_reporting_process_zone().

5) __rmqueue_fallback() will never be able to steal with
   ALLOC_NOFRAGMENT.

pageblock_order >= MAX_ORDER is weird either way: it's a pure
optimization for making alloc_contig_range(), as used for allcoation of
gigantic pages, a little more reliable to succeed.  However, if there is
demand for somewhat reliable allocation of gigantic pages, affected
setups should be using CMA or boottime allocations instead.

So let's make sure that pageblock_order < MAX_ORDER and simplify.

[1] https://lkml.kernel.org/r/87r189a2ks.fsf@linux.ibm.com

Link: https://lkml.kernel.org/r/20220214174132.219303-3-david@redhat.com
Signed-off-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Cc: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Frank Rowand <frowand.list@gmail.com>
Cc: John Garry via iommu <iommu@lists.linux-foundation.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/virtio/virtio_mem.c
include/linux/cma.h
include/linux/pageblock-flags.h
mm/Kconfig
mm/page_alloc.c

index 38becd8d578c72ef880c6e2aac545fa394d57c32..e7d6b679596d382cc2f4b24aba1c8619f9650c35 100644 (file)
@@ -2476,13 +2476,10 @@ static int virtio_mem_init_hotplug(struct virtio_mem *vm)
                                      VIRTIO_MEM_DEFAULT_OFFLINE_THRESHOLD);
 
        /*
-        * We want subblocks to span at least MAX_ORDER_NR_PAGES and
-        * pageblock_nr_pages pages. This:
-        * - Is required for now for alloc_contig_range() to work reliably -
-        *   it doesn't properly handle smaller granularity on ZONE_NORMAL.
+        * TODO: once alloc_contig_range() works reliably with pageblock
+        * granularity on ZONE_NORMAL, use pageblock_nr_pages instead.
         */
-       sb_size = max_t(uint64_t, MAX_ORDER_NR_PAGES,
-                       pageblock_nr_pages) * PAGE_SIZE;
+       sb_size = PAGE_SIZE * MAX_ORDER_NR_PAGES;
        sb_size = max_t(uint64_t, vm->device_block_size, sb_size);
 
        if (sb_size < memory_block_size_bytes() && !force_bbm) {
index 75fe188ec4a1cd2996fd231e293f18ab75b6670b..b1ba94f1cc9c5065e423ccc566fc408d3d35eb33 100644 (file)
@@ -25,8 +25,7 @@
  * -- can deal with only some pageblocks of a higher-order page being
  *  MIGRATE_CMA, we can use pageblock_nr_pages.
  */
-#define CMA_MIN_ALIGNMENT_PAGES max_t(phys_addr_t, MAX_ORDER_NR_PAGES, \
-                                     pageblock_nr_pages)
+#define CMA_MIN_ALIGNMENT_PAGES MAX_ORDER_NR_PAGES
 #define CMA_MIN_ALIGNMENT_BYTES (PAGE_SIZE * CMA_MIN_ALIGNMENT_PAGES)
 
 struct cma;
index 973fd731a520636837f2e3757784161d7a5ec26a..83c7248053a1eb745e0bff410be66d0f04a5e8bb 100644 (file)
@@ -37,8 +37,11 @@ extern unsigned int pageblock_order;
 
 #else /* CONFIG_HUGETLB_PAGE_SIZE_VARIABLE */
 
-/* Huge pages are a constant size */
-#define pageblock_order                HUGETLB_PAGE_ORDER
+/*
+ * Huge pages are a constant size, but don't exceed the maximum allocation
+ * granularity.
+ */
+#define pageblock_order                min_t(unsigned int, HUGETLB_PAGE_ORDER, MAX_ORDER - 1)
 
 #endif /* CONFIG_HUGETLB_PAGE_SIZE_VARIABLE */
 
index 3326ee3903f33078143607ba0a67d131d567a03f..4c91b92e753771f667a3c11a39e1881b3e16c53a 100644 (file)
@@ -262,6 +262,9 @@ config HUGETLB_PAGE_SIZE_VARIABLE
          HUGETLB_PAGE_ORDER when there are multiple HugeTLB page sizes available
          on a platform.
 
+         Note that the pageblock_order cannot exceed MAX_ORDER - 1 and will be
+         clamped down to MAX_ORDER - 1.
+
 config CONTIG_ALLOC
        def_bool (MEMORY_ISOLATION && COMPACTION) || CMA
 
index 06c683754334a9caba3f0f716b423dfebcfbbcce..a4efbdad2f2b62d8721ac5cdac3d461add83396b 100644 (file)
@@ -1072,14 +1072,12 @@ static inline void __free_one_page(struct page *page,
                int migratetype, fpi_t fpi_flags)
 {
        struct capture_control *capc = task_capc(zone);
+       unsigned int max_order = pageblock_order;
        unsigned long buddy_pfn;
        unsigned long combined_pfn;
-       unsigned int max_order;
        struct page *buddy;
        bool to_tail;
 
-       max_order = min_t(unsigned int, MAX_ORDER - 1, pageblock_order);
-
        VM_BUG_ON(!zone_is_initialized(zone));
        VM_BUG_ON_PAGE(page->flags & PAGE_FLAGS_CHECK_AT_PREP, page);
 
@@ -2259,19 +2257,8 @@ void __init init_cma_reserved_pageblock(struct page *page)
        } while (++p, --i);
 
        set_pageblock_migratetype(page, MIGRATE_CMA);
-
-       if (pageblock_order >= MAX_ORDER) {
-               i = pageblock_nr_pages;
-               p = page;
-               do {
-                       set_page_refcounted(p);
-                       __free_pages(p, MAX_ORDER - 1);
-                       p += MAX_ORDER_NR_PAGES;
-               } while (i -= MAX_ORDER_NR_PAGES);
-       } else {
-               set_page_refcounted(page);
-               __free_pages(page, pageblock_order);
-       }
+       set_page_refcounted(page);
+       __free_pages(page, pageblock_order);
 
        adjust_managed_page_count(page, pageblock_nr_pages);
        page_zone(page)->cma_pages += pageblock_nr_pages;
@@ -7382,16 +7369,15 @@ static inline void setup_usemap(struct zone *zone) {}
 /* Initialise the number of pages represented by NR_PAGEBLOCK_BITS */
 void __init set_pageblock_order(void)
 {
-       unsigned int order;
+       unsigned int order = MAX_ORDER - 1;
 
        /* Check that pageblock_nr_pages has not already been setup */
        if (pageblock_order)
                return;
 
-       if (HPAGE_SHIFT > PAGE_SHIFT)
+       /* Don't let pageblocks exceed the maximum allocation granularity. */
+       if (HPAGE_SHIFT > PAGE_SHIFT && HUGETLB_PAGE_ORDER < order)
                order = HUGETLB_PAGE_ORDER;
-       else
-               order = MAX_ORDER - 1;
 
        /*
         * Assume the largest contiguous order of interest is a huge page.
@@ -8979,14 +8965,12 @@ struct page *has_unmovable_pages(struct zone *zone, struct page *page,
 #ifdef CONFIG_CONTIG_ALLOC
 static unsigned long pfn_max_align_down(unsigned long pfn)
 {
-       return pfn & ~(max_t(unsigned long, MAX_ORDER_NR_PAGES,
-                            pageblock_nr_pages) - 1);
+       return ALIGN_DOWN(pfn, MAX_ORDER_NR_PAGES);
 }
 
 static unsigned long pfn_max_align_up(unsigned long pfn)
 {
-       return ALIGN(pfn, max_t(unsigned long, MAX_ORDER_NR_PAGES,
-                               pageblock_nr_pages));
+       return ALIGN(pfn, MAX_ORDER_NR_PAGES);
 }
 
 #if defined(CONFIG_DYNAMIC_DEBUG) || \