]> git.hungrycats.org Git - linux/commitdiff
mm/page_alloc.c: broken deferred calculation
authorPavel Tatashin <pasha.tatashin@oracle.com>
Thu, 16 Nov 2017 01:38:41 +0000 (17:38 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 24 Nov 2017 07:32:25 +0000 (08:32 +0100)
commit d135e5750205a21a212a19dbb05aeb339e2cbea7 upstream.

In reset_deferred_meminit() we determine number of pages that must not
be deferred.  We initialize pages for at least 2G of memory, but also
pages for reserved memory in this node.

The reserved memory is determined in this function:
memblock_reserved_memory_within(), which operates over physical
addresses, and returns size in bytes.  However, reset_deferred_meminit()
assumes that that this function operates with pfns, and returns page
count.

The result is that in the best case machine boots slower than expected
due to initializing more pages than needed in single thread, and in the
worst case panics because fewer than needed pages are initialized early.

Link: http://lkml.kernel.org/r/20171021011707.15191-1-pasha.tatashin@oracle.com
Fixes: 864b9a393dcb ("mm: consider memblock reservations for deferred memory initialization sizing")
Signed-off-by: Pavel Tatashin <pasha.tatashin@oracle.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/mmzone.h
mm/page_alloc.c

index 5b609a3ce3d75be4665be5ad8dbe3f71a0823529..ff88d61894117f4cb4b19de4ffa91a41be4f7e22 100644 (file)
@@ -688,7 +688,8 @@ typedef struct pglist_data {
         * is the first PFN that needs to be initialised.
         */
        unsigned long first_deferred_pfn;
-       unsigned long static_init_size;
+       /* Number of non-deferred pages */
+       unsigned long static_init_pgcnt;
 #endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
 } pg_data_t;
 
index 6b5421ae86c69a7b60b3ebd9b327e52f6b61fdc3..35ce37b187f9c689af8f1871e64850f7abe5a1bf 100644 (file)
@@ -267,28 +267,37 @@ EXPORT_SYMBOL(nr_online_nodes);
 int page_group_by_mobility_disabled __read_mostly;
 
 #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
+
+/*
+ * Determine how many pages need to be initialized durig early boot
+ * (non-deferred initialization).
+ * The value of first_deferred_pfn will be set later, once non-deferred pages
+ * are initialized, but for now set it ULONG_MAX.
+ */
 static inline void reset_deferred_meminit(pg_data_t *pgdat)
 {
-       unsigned long max_initialise;
-       unsigned long reserved_lowmem;
+       phys_addr_t start_addr, end_addr;
+       unsigned long max_pgcnt;
+       unsigned long reserved;
 
        /*
         * Initialise at least 2G of a node but also take into account that
         * two large system hashes that can take up 1GB for 0.25TB/node.
         */
-       max_initialise = max(2UL << (30 - PAGE_SHIFT),
-               (pgdat->node_spanned_pages >> 8));
+       max_pgcnt = max(2UL << (30 - PAGE_SHIFT),
+                       (pgdat->node_spanned_pages >> 8));
 
        /*
         * Compensate the all the memblock reservations (e.g. crash kernel)
         * from the initial estimation to make sure we will initialize enough
         * memory to boot.
         */
-       reserved_lowmem = memblock_reserved_memory_within(pgdat->node_start_pfn,
-                       pgdat->node_start_pfn + max_initialise);
-       max_initialise += reserved_lowmem;
+       start_addr = PFN_PHYS(pgdat->node_start_pfn);
+       end_addr = PFN_PHYS(pgdat->node_start_pfn + max_pgcnt);
+       reserved = memblock_reserved_memory_within(start_addr, end_addr);
+       max_pgcnt += PHYS_PFN(reserved);
 
-       pgdat->static_init_size = min(max_initialise, pgdat->node_spanned_pages);
+       pgdat->static_init_pgcnt = min(max_pgcnt, pgdat->node_spanned_pages);
        pgdat->first_deferred_pfn = ULONG_MAX;
 }
 
@@ -324,7 +333,7 @@ static inline bool update_defer_init(pg_data_t *pgdat,
                return true;
        /* Initialise at least 2G of the highest zone */
        (*nr_initialised)++;
-       if ((*nr_initialised > pgdat->static_init_size) &&
+       if ((*nr_initialised > pgdat->static_init_pgcnt) &&
            (pfn & (PAGES_PER_SECTION - 1)) == 0) {
                pgdat->first_deferred_pfn = pfn;
                return false;