When vmemmap or usemap allocation fails, sparse_init_nid() currently
marks the section non-present and continues. Later boot-time code can
still walk PFNs in that section without checking for this partial setup,
which leads to invalid accesses. subsection_map_init() can also touch an
unallocated usemap.

Auditing and fixing all early PFN walkers for this case is not worth the
complexity. These allocation failures are expected to be fatal anyway,
and other memory models already treat them that way.

Make memmap and usemap allocation failures panic immediately instead of
trying to recover and crashing later in less obvious ways. This is also
consistent with how other memory model configurations handle memmap
allocation failures.

Signed-off-by: Muchun Song <[email protected]>
Acked-by: Mike Rapoport (Microsoft) <[email protected]>
---
v1->v2:
- Add Acked-by from Mike Rapoport
- I refrained from adding panic() to memmap_alloc() as it wouldn't simplify
  the code. However, panic() is still required in sparse_init_nid() because
  the architecture-specific vmemmap_populate() bypasses memmap_alloc().
---
 mm/sparse.c | 44 +++++++++-----------------------------------
 1 file changed, 9 insertions(+), 35 deletions(-)

diff --git a/mm/sparse.c b/mm/sparse.c
index 16ac6df3c89f..c92bbc3f3aa3 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -239,15 +239,8 @@ struct page __init *__populate_section_memmap(unsigned 
long pfn,
                struct dev_pagemap *pgmap)
 {
        unsigned long size = section_map_size();
-       struct page *map;
-       phys_addr_t addr = __pa(MAX_DMA_ADDRESS);
 
-       map = memmap_alloc(size, size, addr, nid, false);
-       if (!map)
-               panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d 
from=%pa\n",
-                     __func__, size, PAGE_SIZE, nid, &addr);
-
-       return map;
+       return memmap_alloc(size, size, __pa(MAX_DMA_ADDRESS), nid, false);
 }
 #endif /* !CONFIG_SPARSEMEM_VMEMMAP */
 
@@ -300,17 +293,14 @@ static void __init sparse_init_nid(int nid, unsigned long 
pnum_begin,
                                   unsigned long map_count)
 {
        unsigned long pnum;
-       struct page *map;
-       struct mem_section *ms;
 
-       if (sparse_usage_init(nid, map_count)) {
-               pr_err("%s: node[%d] usemap allocation failed", __func__, nid);
-               goto failed;
-       }
+       if (sparse_usage_init(nid, map_count))
+               panic("Failed to allocate usemap for node %d\n", nid);
 
        sparse_vmemmap_init_nid_early(nid);
 
        for_each_present_section_nr(pnum_begin, pnum) {
+               struct mem_section *ms;
                unsigned long pfn = section_nr_to_pfn(pnum);
 
                if (pnum >= pnum_end)
@@ -318,34 +308,18 @@ static void __init sparse_init_nid(int nid, unsigned long 
pnum_begin,
 
                ms = __nr_to_section(pnum);
                if (!preinited_vmemmap_section(ms)) {
+                       struct page *map;
+
                        map = __populate_section_memmap(pfn, PAGES_PER_SECTION,
-                                       nid, NULL, NULL);
-                       if (!map) {
-                               pr_err("%s: node[%d] memory map backing failed. 
Some memory will not be available.",
-                                      __func__, nid);
-                               pnum_begin = pnum;
-                               sparse_usage_fini();
-                               goto failed;
-                       }
+                                                       nid, NULL, NULL);
+                       if (!map)
+                               panic("Failed to allocate memmap for section 
%lu\n", pnum);
                        memmap_boot_pages_add(DIV_ROUND_UP(PAGES_PER_SECTION * 
sizeof(struct page),
                                                           PAGE_SIZE));
                        sparse_init_early_section(nid, map, pnum, 0);
                }
        }
        sparse_usage_fini();
-       return;
-failed:
-       /*
-        * We failed to allocate, mark all the following pnums as not present,
-        * except the ones already initialized earlier.
-        */
-       for_each_present_section_nr(pnum_begin, pnum) {
-               if (pnum >= pnum_end)
-                       break;
-               ms = __nr_to_section(pnum);
-               if (!preinited_vmemmap_section(ms))
-                       ms->section_mem_map = 0;
-       }
 }
 
 /*
-- 
2.54.0


Reply via email to