[RFC PATCH 1/8] mm: page_alloc: add reserved THP pageblock type
From: Qi Zheng
Date: Sat Jun 27 2026 - 03:25:17 EST
From: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>
Introduce a new migratetype MIGRATE_RESERVED_THP in preparation for the
upcoming Reserved THP feature.
Add specific handling in the page allocation and freeing paths (e.g.,
free_unref_folios() and __free_frozen_pages()) to prevent these reserved
pageblocks from being freed to the per-CPU (PCP) lists or being used by
other allocation contexts. Also, add statistical counters in the zone
structure and expose them in /proc/zoneinfo.
Signed-off-by: Qi Zheng <zhengqi.arch@xxxxxxxxxxxxx>
---
include/linux/mmzone.h | 11 ++++++++---
mm/page_alloc.c | 16 +++++++++++++++-
mm/show_mem.c | 5 +++++
3 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ca27121871475..4418d0c9accdc 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -133,10 +133,9 @@ enum migratetype {
* __free_pageblock_cma() function.
*/
MIGRATE_CMA,
- __MIGRATE_TYPE_END = MIGRATE_CMA,
-#else
- __MIGRATE_TYPE_END = MIGRATE_HIGHATOMIC,
#endif
+ MIGRATE_RESERVED_THP,
+ __MIGRATE_TYPE_END = MIGRATE_RESERVED_THP,
#ifdef CONFIG_MEMORY_ISOLATION
MIGRATE_ISOLATE, /* can't allocate from here */
#endif
@@ -161,6 +160,9 @@ extern const char * const migratetype_names[MIGRATE_TYPES];
# define is_migrate_cma_folio(folio, pfn) false
#endif
+#define is_migrate_reserved_thp(migratetype) \
+ unlikely((migratetype) == MIGRATE_RESERVED_THP)
+
static inline bool is_migrate_movable(int mt)
{
return is_migrate_cma(mt) || mt == MIGRATE_MOVABLE;
@@ -975,6 +977,9 @@ struct zone {
unsigned long nr_reserved_highatomic;
unsigned long nr_free_highatomic;
+ unsigned long nr_reserved_thp;
+ unsigned long nr_free_reserved_thp;
+
/*
* We don't know if the memory that we're going to allocate will be
* freeable or/and it will be released eventually, so to avoid totally
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ee902a468c2f5..613a711305072 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -263,6 +263,7 @@ const char * const migratetype_names[MIGRATE_TYPES] = {
#ifdef CONFIG_CMA
"CMA",
#endif
+ "ReserveTHP",
#ifdef CONFIG_MEMORY_ISOLATION
"Isolate",
#endif
@@ -784,6 +785,9 @@ static inline void account_freepages(struct zone *zone, int nr_pages,
else if (migratetype == MIGRATE_HIGHATOMIC)
WRITE_ONCE(zone->nr_free_highatomic,
zone->nr_free_highatomic + nr_pages);
+ else if (migratetype == MIGRATE_RESERVED_THP)
+ WRITE_ONCE(zone->nr_free_reserved_thp,
+ zone->nr_free_reserved_thp + nr_pages);
}
/* Used for pages not on another list */
@@ -2960,7 +2964,8 @@ static void __free_frozen_pages(struct page *page, unsigned int order,
zone = page_zone(page);
migratetype = get_pfnblock_migratetype(page, pfn);
if (unlikely(migratetype >= MIGRATE_PCPTYPES)) {
- if (unlikely(is_migrate_isolate(migratetype))) {
+ if (unlikely(is_migrate_reserved_thp(migratetype) ||
+ is_migrate_isolate(migratetype))) {
free_one_page(zone, page, pfn, order, fpi_flags);
return;
}
@@ -3038,6 +3043,7 @@ void free_unref_folios(struct folio_batch *folios)
/* Different zone requires a different pcp lock */
if (zone != locked_zone ||
+ is_migrate_reserved_thp(migratetype) ||
is_migrate_isolate(migratetype)) {
if (pcp) {
pcp_spin_unlock(pcp);
@@ -3045,6 +3051,12 @@ void free_unref_folios(struct folio_batch *folios)
pcp = NULL;
}
+ if (is_migrate_reserved_thp(migratetype)) {
+ free_one_page(zone, &folio->page, pfn,
+ order, FPI_NONE);
+ continue;
+ }
+
/*
* Free isolated pages directly to the
* allocator, see comment in free_frozen_pages.
@@ -3568,6 +3580,8 @@ static inline long __zone_watermark_unusable_free(struct zone *z,
if (likely(!(alloc_flags & ALLOC_RESERVES)))
unusable_free += READ_ONCE(z->nr_free_highatomic);
+ unusable_free += READ_ONCE(z->nr_free_reserved_thp);
+
#ifdef CONFIG_CMA
/* If allocation can't use CMA areas don't use free CMA pages */
if (!(alloc_flags & ALLOC_CMA))
diff --git a/mm/show_mem.c b/mm/show_mem.c
index 43aca5a2ac990..e9381afca4aca 100644
--- a/mm/show_mem.c
+++ b/mm/show_mem.c
@@ -142,6 +142,7 @@ static void show_migration_types(unsigned char type)
#ifdef CONFIG_CMA
[MIGRATE_CMA] = 'C',
#endif
+ [MIGRATE_RESERVED_THP] = 'T',
#ifdef CONFIG_MEMORY_ISOLATION
[MIGRATE_ISOLATE] = 'I',
#endif
@@ -308,6 +309,8 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z
" high:%lukB"
" reserved_highatomic:%luKB"
" free_highatomic:%luKB"
+ " reserved_thp:%luKB"
+ " free_reserved_thp:%luKB"
" active_anon:%lukB"
" inactive_anon:%lukB"
" active_file:%lukB"
@@ -331,6 +334,8 @@ static void show_free_areas(unsigned int filter, nodemask_t *nodemask, int max_z
K(high_wmark_pages(zone)),
K(zone->nr_reserved_highatomic),
K(zone->nr_free_highatomic),
+ K(zone->nr_reserved_thp),
+ K(zone->nr_free_reserved_thp),
K(zone_page_state(zone, NR_ZONE_ACTIVE_ANON)),
K(zone_page_state(zone, NR_ZONE_INACTIVE_ANON)),
K(zone_page_state(zone, NR_ZONE_ACTIVE_FILE)),
--
2.54.0