Add free_frozen_pages_hint(page, order, hints) to free a page while marking it as pre-zeroed when PGHINT_ZEROED is set. The PG_zeroed flag is set after __free_pages_prepare so it survives on the free list.
Add __folio_put_hint(), folio_put_hint(), and put_page_hint() wrappers for the put_page path. These APIs are intended for balloon drivers during deflation when the host has zeroed the pages. Signed-off-by: Michael S. Tsirkin <[email protected]> Assisted-by: Claude:claude-opus-4-6 Assisted-by: cursor-agent:GPT-5.4-xhigh --- include/linux/gfp.h | 2 ++ include/linux/mm.h | 12 ++++++++++++ mm/page_alloc.c | 21 +++++++++++++++------ mm/swap.c | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 14433a20e60c..b226d5e1930e 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -404,6 +404,8 @@ __meminit void *alloc_pages_exact_nid_noprof(int nid, size_t size, gfp_t gfp_mas extern void __free_pages(struct page *page, unsigned int order); extern void free_pages_nolock(struct page *page, unsigned int order); extern void free_pages(unsigned long addr, unsigned int order); +void free_frozen_pages_hint(struct page *page, unsigned int order, + pghint_t hints); #define __free_page(page) __free_pages((page), 0) #define free_page(addr) free_pages((addr), 0) diff --git a/include/linux/mm.h b/include/linux/mm.h index abb4963c1f06..f4e28c55e2c9 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1640,6 +1640,7 @@ static inline struct folio *virt_to_folio(const void *x) } void __folio_put(struct folio *folio); +void __folio_put_hint(struct folio *folio, pghint_t hints); void split_page(struct page *page, unsigned int order); void folio_copy(struct folio *dst, struct folio *src); @@ -1817,6 +1818,17 @@ static inline void folio_put(struct folio *folio) __folio_put(folio); } +static inline void folio_put_hint(struct folio *folio, pghint_t hints) +{ + if (folio_put_testzero(folio)) + __folio_put_hint(folio, hints); +} + +static inline void put_page_hint(struct page *page, pghint_t hints) +{ + folio_put_hint(page_folio(page), hints); +} + /** * folio_put_refs - Reduce the reference count on a folio. * @folio: The folio. diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a4cfd645599a..f04813db3015 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3000,7 +3000,7 @@ static bool free_frozen_page_commit(struct zone *zone, * Free a pcp page */ static void __free_frozen_pages(struct page *page, unsigned int order, - fpi_t fpi_flags) + fpi_t fpi_flags, pghint_t hints) { unsigned long UP_flags; struct per_cpu_pages *pcp; @@ -3016,6 +3016,9 @@ static void __free_frozen_pages(struct page *page, unsigned int order, if (!__free_pages_prepare(page, order, fpi_flags)) return; + if (hints & PGHINT_ZEROED) + __SetPageZeroed(page); + /* * We only track unmovable, reclaimable and movable on pcp lists. * Place ISOLATE pages on the isolated list because they are being @@ -3051,12 +3054,18 @@ static void __free_frozen_pages(struct page *page, unsigned int order, void free_frozen_pages(struct page *page, unsigned int order) { - __free_frozen_pages(page, order, FPI_NONE); + __free_frozen_pages(page, order, FPI_NONE, 0); +} + +void free_frozen_pages_hint(struct page *page, unsigned int order, + pghint_t hints) +{ + __free_frozen_pages(page, order, FPI_NONE, hints); } void free_frozen_pages_nolock(struct page *page, unsigned int order) { - __free_frozen_pages(page, order, FPI_TRYLOCK); + __free_frozen_pages(page, order, FPI_TRYLOCK, 0); } /* @@ -5385,7 +5394,7 @@ static void ___free_pages(struct page *page, unsigned int order, struct alloc_tag *tag = pgalloc_tag_get(page); if (put_page_testzero(page)) - __free_frozen_pages(page, order, fpi_flags); + __free_frozen_pages(page, order, fpi_flags, 0); else if (!head) { pgalloc_tag_sub_pages(tag, (1 << order) - 1); while (order-- > 0) { @@ -5396,7 +5405,7 @@ static void ___free_pages(struct page *page, unsigned int order, */ clear_page_tag_ref(page + (1 << order)); __free_frozen_pages(page + (1 << order), order, - fpi_flags); + fpi_flags, 0); } } } @@ -7879,7 +7888,7 @@ struct page *alloc_frozen_pages_nolock_noprof(gfp_t gfp_flags, int nid, unsigned if (memcg_kmem_online() && page && (gfp_flags & __GFP_ACCOUNT) && unlikely(__memcg_kmem_charge_page(page, alloc_gfp, order) != 0)) { - __free_frozen_pages(page, order, FPI_TRYLOCK); + __free_frozen_pages(page, order, FPI_TRYLOCK, 0); page = NULL; } trace_mm_page_alloc(page, order, alloc_gfp, ac.migratetype); diff --git a/mm/swap.c b/mm/swap.c index bb19ccbece46..1dfd232d3944 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -113,6 +113,25 @@ void __folio_put(struct folio *folio) } EXPORT_SYMBOL(__folio_put); +void __folio_put_hint(struct folio *folio, pghint_t hints) +{ + if (unlikely(folio_is_zone_device(folio))) { + free_zone_device_folio(folio); + return; + } + + if (folio_test_hugetlb(folio)) { + free_huge_folio(folio); + return; + } + + page_cache_release(folio); + folio_unqueue_deferred_split(folio); + mem_cgroup_uncharge(folio); + free_frozen_pages_hint(&folio->page, folio_order(folio), hints); +} +EXPORT_SYMBOL(__folio_put_hint); + typedef void (*move_fn_t)(struct lruvec *lruvec, struct folio *folio); static void lru_add(struct lruvec *lruvec, struct folio *folio) -- MST

