Currently, when using deferred struct page init, we may end up not freeing the pages depending on where they are. Typically this happens when efi_free_boot_services() tries to free UEFI Boot Services pages. We can hit the !early_page_initialised() test in memblock_free_pages() since the deferred initializer hasn't even started yet. As a result we drop the pages on the floor. Now, memblock_free_late() should only ever be called for pages that are reserved, and thus for which the struct page has already been initialized by memmap_init_reserved_pages(). So it should be safe to just free them normally and ignore the deferred initializer, which will skip over them as it skips over anything still in the memblock reserved list. This recovers something like 130MB of RAM on EC2 t3a.nano instances who only have 512MB to begin with (as to why UEFI uses that much, that's a question for another day). Signed-off-by: Benjamin Herrenschmidt --- mm/internal.h | 2 +- mm/memblock.c | 6 ++++-- mm/mm_init.c | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mm/internal.h b/mm/internal.h index 9e0577413087c..fe6da7c30caf0 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -660,7 +660,7 @@ extern int __isolate_free_page(struct page *page, unsigned int order); extern void __putback_isolated_page(struct page *page, unsigned int order, int mt); extern void memblock_free_pages(struct page *page, unsigned long pfn, - unsigned int order); + unsigned int order, bool reserved); extern void __free_pages_core(struct page *page, unsigned int order, enum meminit_context context); diff --git a/mm/memblock.c b/mm/memblock.c index 3d7b0114442c4..b836b638615d0 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -1718,8 +1718,10 @@ void __init memblock_free_late(phys_addr_t base, phys_addr_t size) cursor = PFN_UP(base); end = PFN_DOWN(base + size); + /* Only free pages that were reserved */ + VM_WARN_ON(!memblock_is_region_reserved(base, size)); for (; cursor < end; cursor++) { - memblock_free_pages(pfn_to_page(cursor), cursor, 0); + memblock_free_pages(pfn_to_page(cursor), cursor, 0, true); totalram_pages_inc(); } } @@ -2141,7 +2143,7 @@ static void __init __free_pages_memory(unsigned long start, unsigned long end) while (start + (1UL << order) > end) order--; - memblock_free_pages(pfn_to_page(start), start, order); + memblock_free_pages(pfn_to_page(start), start, order, false); start += (1UL << order); } diff --git a/mm/mm_init.c b/mm/mm_init.c index 624c1f90ce050..34dc39a21b4bb 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -2440,9 +2440,9 @@ void *__init alloc_large_system_hash(const char *tablename, } void __init memblock_free_pages(struct page *page, unsigned long pfn, - unsigned int order) + unsigned int order, bool reserved) { - if (IS_ENABLED(CONFIG_DEFERRED_STRUCT_PAGE_INIT)) { + if (IS_ENABLED(CONFIG_DEFERRED_STRUCT_PAGE_INIT) && !reserved) { int nid = early_pfn_to_nid(pfn); if (!early_page_initialised(pfn, nid)) -- 2.43.0