[PATCH 02/10] mm: provide free_reserved_pages(), removing x86 variant

From: David Hildenbrand (Arm)

Date: Wed Jul 08 2026 - 10:17:22 EST


Let's extend free_reserved_page() in page_alloc.c to free_reserved_pages(),
dropping the custom x86 variant. The common-code variant will consume an
order, so adjust the x86 callers accordingly.

Make free_reserved_pages() assume that we are freeing ordinary
high-order pages, just with the special "reserved" flavor. The target
use case for now is freeing vmemmap PMD pages.

Set the refcount directly to 0 (instead of 1) and call
__free_frozen_pages(). Add some kerneldoc. Use a single
adjust_managed_page_count() call.

Signed-off-by: David Hildenbrand (Arm) <david@xxxxxxxxxx>
---
arch/x86/mm/init_64.c | 8 +-------
include/linux/mm.h | 8 ++++++--
mm/page_alloc.c | 33 ++++++++++++++++++++++++++-------
3 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 69e36f02a663a..320e742b0bb3f 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1000,12 +1000,6 @@ int arch_add_memory(int nid, u64 start, u64 size,
return add_pages(nid, start_pfn, nr_pages, params);
}

-static void free_reserved_pages(struct page *page, unsigned long nr_pages)
-{
- while (nr_pages--)
- free_reserved_page(page++);
-}
-
static void __meminit free_pagetable(struct page *page)
{
/* bootmem page has reserved flag */
@@ -1038,7 +1032,7 @@ static void __meminit free_vmemmap_pages(struct page *page, unsigned int order,
while (nr_pages--)
put_page_bootmem(page++);
} else {
- free_reserved_pages(page, nr_pages);
+ free_reserved_pages(page, order);
}
} else {
__free_pages(page, order);
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 113e1752548c2..62b261cccf16f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -3993,8 +3993,12 @@ extern unsigned long free_reserved_area(void *start, void *end,

extern void adjust_managed_page_count(struct page *page, long count);

-/* Free the reserved page into the buddy system, so it gets managed. */
-void free_reserved_page(struct page *page);
+void free_reserved_pages(struct page *page, unsigned int order);
+
+static inline void free_reserved_page(struct page *page)
+{
+ free_reserved_pages(page, 0);
+}

static inline void mark_page_reserved(struct page *page)
{
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index a63733dac659e..9311d1f3793ad 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -6381,15 +6381,34 @@ void adjust_managed_page_count(struct page *page, long count)
}
EXPORT_SYMBOL(adjust_managed_page_count);

-void free_reserved_page(struct page *page)
+/**
+ * free_reserved_pages - free reserved pages
+ * @page: First page to free.
+ * @order: The page order to free.
+ *
+ * Free pages allocated through memblock during boot, letting the buddy
+ * manage them from now on.
+ *
+ * @page must be naturally aligned to the order and the order must not
+ * exceed MAX_PAGE_ORDER. All pages must be reserved.
+ */
+void free_reserved_pages(struct page *page, unsigned int order)
{
- clear_page_tag_ref(page);
- ClearPageReserved(page);
- init_page_count(page);
- __free_page(page);
- adjust_managed_page_count(page, 1);
+ const unsigned long nr_pages = 1UL << order;
+ int i;
+
+ VM_WARN_ON_ONCE(!IS_ALIGNED(page_to_pfn(page), nr_pages));
+ VM_WARN_ON_ONCE(order > MAX_PAGE_ORDER);
+
+ for (i = 0; i < nr_pages; i++) {
+ clear_page_tag_ref(page + i);
+ ClearPageReserved(page + i);
+ set_page_count(page + i, 0);
+ }
+ adjust_managed_page_count(page, nr_pages);
+ __free_frozen_pages(page, order, FPI_NONE);
}
-EXPORT_SYMBOL(free_reserved_page);
+EXPORT_SYMBOL(free_reserved_pages);

static int page_alloc_cpu_dead(unsigned int cpu)
{

--
2.43.0