* [PATCH 0/4] Keep subpage private zero at free and folio split time
@ 2026-06-29 2:56 Zi Yan
2026-06-29 2:56 ` [PATCH 1/4] mm/compaction: stop recording free page order in page->private Zi Yan
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-29 2:56 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel, Zi Yan
Hi all,
This patchset makes sure subpage->private is zero before compound or
high-order pages are returned to the allocator. It also checks subpages
that become new folio heads during large folio split, before their private
fields are used by new folios.
It is based on v7.2-rc1.
Motivation ===
page->private is zeroed at page free time since commit ac1ea219590c0
("mm/page_alloc: clear page->private in free_pages_prepare()"), since we
concluded that it might be too much to ask every page user to free a page
with ->private zeroed. The holder of the last page reference might not know
whether ->private needs to be cleared.
For compound and high-order pages, subpage->private can also leak to later
users if it is left uncleared. The page allocation path does not zero every
subpage->private field, so they can be seen by new users and cause
unexpected issues[1].
Check subpage->private at page free time, and check tail pages that become
new folio heads during large folio split. With those checks in place,
prep_compound_tail() no longer needs to clear subpage->private when
preparing compound page metadata.
Overview ===
1. Patch 1 removes setting page->private in compaction code when a free
page is taken out of the buddy allocator. cc->freepages is indexed by
page order, so storing the free page order in page->private is
redundant.
2. Patch 2 adds back the page->private check for tail pages promoted to new
folio heads in __split_folio_to_order().
3. Patch 3 adds a subpage->private check in the page free path.
4. Patch 4 removes subpage->private zeroing from prep_compound_tail().
Link: https://lore.kernel.org/all/20260206174017.128673-1-mikhail.v.gavrilov@gmail.com/ [1]
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
Zi Yan (4):
mm/compaction: stop recording free page order in page->private
mm/huge_memory: add page->private check back in __split_folio_to_order()
mm/page_alloc: make sure subpage->private is zero at page free time
mm/page_alloc: remove set_page_private() in prep_compound_tail()
mm/compaction.c | 3 ---
mm/huge_memory.c | 10 ++++++++++
mm/internal.h | 1 -
mm/page_alloc.c | 12 +++++++++---
4 files changed, 19 insertions(+), 7 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260603-keep-subpage-private-zero-at-free-a1e1435025dc
Best regards,
--
Yan, Zi
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/4] mm/compaction: stop recording free page order in page->private
2026-06-29 2:56 [PATCH 0/4] Keep subpage private zero at free and folio split time Zi Yan
@ 2026-06-29 2:56 ` Zi Yan
2026-06-29 14:28 ` Vlastimil Babka (SUSE)
2026-06-30 1:32 ` Baolin Wang
2026-06-29 2:56 ` [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order() Zi Yan
` (2 subsequent siblings)
3 siblings, 2 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-29 2:56 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel, Zi Yan
Commit 733aea0b3a7bb ("mm/compaction: add support for >0 order folio
memory compaction.") stores isolated free pages in an array indexed by free
page orders, it is no longer needed to store the order in each page's
->private field. And there is no code using the stored order. Stop doing
that.
It also prepares for an upcoming change that ensures subpage->private is
zero at page free time and the removal of set_page_private(0) from
prep_compound_tail(). In alloc_contig_frozen_range_noprof(),
isolate_freepages_range() is used to grab free pages from buddy allocator
and it leaves the aforementioned page->private set until
either split_free_frozen_pages() or prep_new_page() is called. That
triggers the upcoming subpage->private nonzero check along once
set_page_private(0) is removed from prep_compound_tail(), which is called
via prep_new_page().
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
mm/compaction.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/mm/compaction.c b/mm/compaction.c
index b776f35ad020..349838cc6c19 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -644,7 +644,6 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
isolated = __isolate_free_page(page, order);
if (!isolated)
break;
- set_page_private(page, order);
nr_scanned += isolated - 1;
total_isolated += isolated;
@@ -1617,7 +1616,6 @@ static void fast_isolate_freepages(struct compact_control *cc)
/* Isolate the page if available */
if (page) {
if (__isolate_free_page(page, order)) {
- set_page_private(page, order);
nr_isolated = 1 << order;
nr_scanned += nr_isolated - 1;
total_isolated += nr_isolated;
@@ -1846,7 +1844,6 @@ static struct folio *compaction_alloc_noprof(struct folio *src, unsigned long da
size >>= 1;
list_add(&freepage[size].lru, &cc->freepages[start_order]);
- set_page_private(&freepage[size], start_order);
}
dst = (struct folio *)freepage;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order()
2026-06-29 2:56 [PATCH 0/4] Keep subpage private zero at free and folio split time Zi Yan
2026-06-29 2:56 ` [PATCH 1/4] mm/compaction: stop recording free page order in page->private Zi Yan
@ 2026-06-29 2:56 ` Zi Yan
2026-06-29 14:39 ` Vlastimil Babka (SUSE)
2026-06-29 2:56 ` [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time Zi Yan
2026-06-29 2:56 ` [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail() Zi Yan
3 siblings, 1 reply; 15+ messages in thread
From: Zi Yan @ 2026-06-29 2:56 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel, Zi Yan
page->private should not be set in tail pages. Commit 4265d67e405a
("mm/migrate_device: add THP splitting during migration") removed it
without a proper reason. Add it back.
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
mm/huge_memory.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 2bccb0a53a0a..037d67fbec6e 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -3594,6 +3594,16 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
new_folio->mapping = folio->mapping;
new_folio->index = folio->index + i;
+ /*
+ * page->private should not be set in tail pages. Fix up and warn once
+ * if private is unexpectedly set. Do it before swap.val assignment
+ * since private overlaps with swap.val.
+ */
+ if (unlikely(new_folio->private)) {
+ VM_WARN_ON_ONCE_PAGE(true, new_head);
+ new_folio->private = NULL;
+ }
+
if (folio_test_swapcache(folio))
new_folio->swap.val = folio->swap.val + i;
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time
2026-06-29 2:56 [PATCH 0/4] Keep subpage private zero at free and folio split time Zi Yan
2026-06-29 2:56 ` [PATCH 1/4] mm/compaction: stop recording free page order in page->private Zi Yan
2026-06-29 2:56 ` [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order() Zi Yan
@ 2026-06-29 2:56 ` Zi Yan
2026-06-29 14:53 ` Vlastimil Babka (SUSE)
2026-06-29 2:56 ` [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail() Zi Yan
3 siblings, 1 reply; 15+ messages in thread
From: Zi Yan @ 2026-06-29 2:56 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel, Zi Yan
Any code using subpage->private of a folio, a compound page or a high-order
page is supposed to reset it after use, otherwise ->private data can leak
to new page user and cause unexpected issues. Add a bad_page() check at
page free path for it.
Assisted-by: Codex:gpt-5 # add the missing "return false" after bad_page()
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
mm/page_alloc.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ee902a468c2f..13c2655e24fb 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1374,15 +1374,21 @@ static __always_inline bool __free_pages_prepare(struct page *page,
#endif
}
for (i = 1; i < (1 << order); i++) {
+ struct page *subpage = page + i;
+
if (compound)
- bad += free_tail_page_prepare(page, page + i);
+ bad += free_tail_page_prepare(page, subpage);
if (is_check_pages_enabled()) {
- if (free_page_is_bad(page + i)) {
+ if (free_page_is_bad(subpage)) {
bad++;
continue;
}
}
- (page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
+ subpage->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
+ if (subpage->private) {
+ bad_page(subpage, "nonzero private");
+ return false;
+ }
}
}
if (folio_test_anon(folio)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail()
2026-06-29 2:56 [PATCH 0/4] Keep subpage private zero at free and folio split time Zi Yan
` (2 preceding siblings ...)
2026-06-29 2:56 ` [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time Zi Yan
@ 2026-06-29 2:56 ` Zi Yan
2026-06-29 15:45 ` Vlastimil Babka (SUSE)
3 siblings, 1 reply; 15+ messages in thread
From: Zi Yan @ 2026-06-29 2:56 UTC (permalink / raw)
To: Andrew Morton, Vlastimil Babka, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel, Zi Yan
With the subpage->private == 0 check added in a prior commit, any allocated
compound page should not have nonzero subpage->private. Remove the
unnecessary subpage->private initialization code in compound page
preparation.
Signed-off-by: Zi Yan <ziy@nvidia.com>
---
mm/internal.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/mm/internal.h b/mm/internal.h
index 181e79f1d6a2..c96421ce9350 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -895,7 +895,6 @@ static inline void prep_compound_tail(struct page *tail,
{
tail->mapping = TAIL_MAPPING;
set_compound_head(tail, head, order);
- set_page_private(tail, 0);
}
static inline void init_compound_tail(struct page *tail,
--
2.53.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] mm/compaction: stop recording free page order in page->private
2026-06-29 2:56 ` [PATCH 1/4] mm/compaction: stop recording free page order in page->private Zi Yan
@ 2026-06-29 14:28 ` Vlastimil Babka (SUSE)
2026-06-29 15:03 ` Zi Yan
2026-06-30 1:32 ` Baolin Wang
1 sibling, 1 reply; 15+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-06-29 14:28 UTC (permalink / raw)
To: Zi Yan, Andrew Morton, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel
On 6/29/26 04:56, Zi Yan wrote:
> Commit 733aea0b3a7bb ("mm/compaction: add support for >0 order folio
> memory compaction.") stores isolated free pages in an array indexed by free
> page orders, it is no longer needed to store the order in each page's
> ->private field. And there is no code using the stored order. Stop doing
> that.
Ah, great observation. Cool.
> It also prepares for an upcoming change that ensures subpage->private is
> zero at page free time and the removal of set_page_private(0) from
> prep_compound_tail(). In alloc_contig_frozen_range_noprof(),
> isolate_freepages_range() is used to grab free pages from buddy allocator
> and it leaves the aforementioned page->private set until
> either split_free_frozen_pages() or prep_new_page() is called. That
> triggers the upcoming subpage->private nonzero check along once
> set_page_private(0) is removed from prep_compound_tail(), which is called
> via prep_new_page().
I'm not sure it needs to be said here (or in such detail?)
> Signed-off-by: Zi Yan <ziy@nvidia.com>
Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
> mm/compaction.c | 3 ---
> 1 file changed, 3 deletions(-)
>
> diff --git a/mm/compaction.c b/mm/compaction.c
> index b776f35ad020..349838cc6c19 100644
> --- a/mm/compaction.c
> +++ b/mm/compaction.c
> @@ -644,7 +644,6 @@ static unsigned long isolate_freepages_block(struct compact_control *cc,
> isolated = __isolate_free_page(page, order);
> if (!isolated)
> break;
> - set_page_private(page, order);
>
> nr_scanned += isolated - 1;
> total_isolated += isolated;
> @@ -1617,7 +1616,6 @@ static void fast_isolate_freepages(struct compact_control *cc)
> /* Isolate the page if available */
> if (page) {
> if (__isolate_free_page(page, order)) {
> - set_page_private(page, order);
> nr_isolated = 1 << order;
> nr_scanned += nr_isolated - 1;
> total_isolated += nr_isolated;
> @@ -1846,7 +1844,6 @@ static struct folio *compaction_alloc_noprof(struct folio *src, unsigned long da
> size >>= 1;
>
> list_add(&freepage[size].lru, &cc->freepages[start_order]);
> - set_page_private(&freepage[size], start_order);
> }
> dst = (struct folio *)freepage;
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order()
2026-06-29 2:56 ` [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order() Zi Yan
@ 2026-06-29 14:39 ` Vlastimil Babka (SUSE)
2026-06-29 15:05 ` Zi Yan
0 siblings, 1 reply; 15+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-06-29 14:39 UTC (permalink / raw)
To: Zi Yan, Andrew Morton, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel
On 6/29/26 04:56, Zi Yan wrote:
> page->private should not be set in tail pages. Commit 4265d67e405a
> ("mm/migrate_device: add THP splitting during migration") removed it
> without a proper reason. Add it back.
>
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
> mm/huge_memory.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
> index 2bccb0a53a0a..037d67fbec6e 100644
> --- a/mm/huge_memory.c
> +++ b/mm/huge_memory.c
> @@ -3594,6 +3594,16 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
> new_folio->mapping = folio->mapping;
> new_folio->index = folio->index + i;
>
> + /*
> + * page->private should not be set in tail pages. Fix up and warn once
> + * if private is unexpectedly set. Do it before swap.val assignment
> + * since private overlaps with swap.val.
> + */
> + if (unlikely(new_folio->private)) {
> + VM_WARN_ON_ONCE_PAGE(true, new_head);
> + new_folio->private = NULL;
> + }
The unconditional warning means this is not expected to happen. In that case
it's odd to check and fixup always, but only warn with CONFIG_DEBUG_VM.
If we are reasonably sure the current code is OK, and only want to catch new
mistakes in development, we could just VM_WARN_ON_ONCE_PAGE() without fixup.
If we are paranoid, leave it as it is, but drop the "VM_" ?
> +
> if (folio_test_swapcache(folio))
> new_folio->swap.val = folio->swap.val + i;
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time
2026-06-29 2:56 ` [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time Zi Yan
@ 2026-06-29 14:53 ` Vlastimil Babka (SUSE)
2026-06-29 15:07 ` Zi Yan
0 siblings, 1 reply; 15+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-06-29 14:53 UTC (permalink / raw)
To: Zi Yan, Andrew Morton, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel
On 6/29/26 04:56, Zi Yan wrote:
> Any code using subpage->private of a folio, a compound page or a high-order
> page is supposed to reset it after use, otherwise ->private data can leak
> to new page user and cause unexpected issues. Add a bad_page() check at
> page free path for it.
>
> Assisted-by: Codex:gpt-5 # add the missing "return false" after bad_page()
> Signed-off-by: Zi Yan <ziy@nvidia.com>
I noticed the word 'subpage' is now frowned upon ;)
See https://lore.kernel.org/all/20260623125723.2503832-1-dev.jain@arm.com/
since this is about tail pages, just call them as such?
> ---
> mm/page_alloc.c | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
> index ee902a468c2f..13c2655e24fb 100644
> --- a/mm/page_alloc.c
> +++ b/mm/page_alloc.c
> @@ -1374,15 +1374,21 @@ static __always_inline bool __free_pages_prepare(struct page *page,
> #endif
> }
> for (i = 1; i < (1 << order); i++) {
i starts at 1
> + struct page *subpage = page + i;
so "tail_page" is accurate?
> +
> if (compound)
> - bad += free_tail_page_prepare(page, page + i);
> + bad += free_tail_page_prepare(page, subpage);
> if (is_check_pages_enabled()) {
> - if (free_page_is_bad(page + i)) {
> + if (free_page_is_bad(subpage)) {
> bad++;
> continue;
> }
> }
> - (page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
> + subpage->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
> + if (subpage->private) {
> + bad_page(subpage, "nonzero private");
> + return false;
> + }
Also why not put this check into the is_check_pages_enabled() block and
handle it the same way?
> }
> }
> if (folio_test_anon(folio)) {
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] mm/compaction: stop recording free page order in page->private
2026-06-29 14:28 ` Vlastimil Babka (SUSE)
@ 2026-06-29 15:03 ` Zi Yan
0 siblings, 0 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-29 15:03 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Andrew Morton, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, David Hildenbrand, Lorenzo Stoakes, Baolin Wang,
Liam R. Howlett, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mike Rapoport, linux-mm, linux-kernel
On 29 Jun 2026, at 10:28, Vlastimil Babka (SUSE) wrote:
> On 6/29/26 04:56, Zi Yan wrote:
>> Commit 733aea0b3a7bb ("mm/compaction: add support for >0 order folio
>> memory compaction.") stores isolated free pages in an array indexed by free
>> page orders, it is no longer needed to store the order in each page's
>> ->private field. And there is no code using the stored order. Stop doing
>> that.
>
> Ah, great observation. Cool.
>
>> It also prepares for an upcoming change that ensures subpage->private is
>> zero at page free time and the removal of set_page_private(0) from
>> prep_compound_tail(). In alloc_contig_frozen_range_noprof(),
>> isolate_freepages_range() is used to grab free pages from buddy allocator
>> and it leaves the aforementioned page->private set until
>> either split_free_frozen_pages() or prep_new_page() is called.
>> That
>> triggers the upcoming subpage->private nonzero check along once
>> set_page_private(0) is removed from prep_compound_tail(), which is called
>> via prep_new_page().
>
> I'm not sure it needs to be said here (or in such detail?)
Will move it to the cover letter.
>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>
> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
Thanks.
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order()
2026-06-29 14:39 ` Vlastimil Babka (SUSE)
@ 2026-06-29 15:05 ` Zi Yan
0 siblings, 0 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-29 15:05 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Andrew Morton, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, David Hildenbrand, Lorenzo Stoakes, Baolin Wang,
Liam R. Howlett, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mike Rapoport, linux-mm, linux-kernel
On 29 Jun 2026, at 10:39, Vlastimil Babka (SUSE) wrote:
> On 6/29/26 04:56, Zi Yan wrote:
>> page->private should not be set in tail pages. Commit 4265d67e405a
>> ("mm/migrate_device: add THP splitting during migration") removed it
>> without a proper reason. Add it back.
>>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>> ---
>> mm/huge_memory.c | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/mm/huge_memory.c b/mm/huge_memory.c
>> index 2bccb0a53a0a..037d67fbec6e 100644
>> --- a/mm/huge_memory.c
>> +++ b/mm/huge_memory.c
>> @@ -3594,6 +3594,16 @@ static void __split_folio_to_order(struct folio *folio, int old_order,
>> new_folio->mapping = folio->mapping;
>> new_folio->index = folio->index + i;
>>
>> + /*
>> + * page->private should not be set in tail pages. Fix up and warn once
>> + * if private is unexpectedly set. Do it before swap.val assignment
>> + * since private overlaps with swap.val.
>> + */
>> + if (unlikely(new_folio->private)) {
>> + VM_WARN_ON_ONCE_PAGE(true, new_head);
>> + new_folio->private = NULL;
>> + }
>
> The unconditional warning means this is not expected to happen. In that case
> it's odd to check and fixup always, but only warn with CONFIG_DEBUG_VM.
>
> If we are reasonably sure the current code is OK, and only want to catch new
> mistakes in development, we could just VM_WARN_ON_ONCE_PAGE() without fixup.
>
> If we are paranoid, leave it as it is, but drop the "VM_" ?
OK, let me drop the fixup unless others have a different thought.
>
>> +
>> if (folio_test_swapcache(folio))
>> new_folio->swap.val = folio->swap.val + i;
>>
>>
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time
2026-06-29 14:53 ` Vlastimil Babka (SUSE)
@ 2026-06-29 15:07 ` Zi Yan
0 siblings, 0 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-29 15:07 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Andrew Morton, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, David Hildenbrand, Lorenzo Stoakes, Baolin Wang,
Liam R. Howlett, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mike Rapoport, linux-mm, linux-kernel
On 29 Jun 2026, at 10:53, Vlastimil Babka (SUSE) wrote:
> On 6/29/26 04:56, Zi Yan wrote:
>> Any code using subpage->private of a folio, a compound page or a high-order
>> page is supposed to reset it after use, otherwise ->private data can leak
>> to new page user and cause unexpected issues. Add a bad_page() check at
>> page free path for it.
>>
>> Assisted-by: Codex:gpt-5 # add the missing "return false" after bad_page()
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>
> I noticed the word 'subpage' is now frowned upon ;)
> See https://lore.kernel.org/all/20260623125723.2503832-1-dev.jain@arm.com/
>
> since this is about tail pages, just call them as such?
I will change to tail pages.
>
>> ---
>> mm/page_alloc.c | 12 +++++++++---
>> 1 file changed, 9 insertions(+), 3 deletions(-)
>>
>> diff --git a/mm/page_alloc.c b/mm/page_alloc.c
>> index ee902a468c2f..13c2655e24fb 100644
>> --- a/mm/page_alloc.c
>> +++ b/mm/page_alloc.c
>> @@ -1374,15 +1374,21 @@ static __always_inline bool __free_pages_prepare(struct page *page,
>> #endif
>> }
>> for (i = 1; i < (1 << order); i++) {
>
> i starts at 1
>
>> + struct page *subpage = page + i;
>
> so "tail_page" is accurate?
Right. Will rename it.
>
>> +
>> if (compound)
>> - bad += free_tail_page_prepare(page, page + i);
>> + bad += free_tail_page_prepare(page, subpage);
>> if (is_check_pages_enabled()) {
>> - if (free_page_is_bad(page + i)) {
>> + if (free_page_is_bad(subpage)) {
>> bad++;
>> continue;
>> }
>> }
>> - (page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
>> + subpage->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;
>> + if (subpage->private) {
>> + bad_page(subpage, "nonzero private");
>> + return false;
>> + }
>
> Also why not put this check into the is_check_pages_enabled() block and
> handle it the same way?
>
Will do. And Sashiko also pointed this out.
>> }
>> }
>> if (folio_test_anon(folio)) {
>>
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail()
2026-06-29 2:56 ` [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail() Zi Yan
@ 2026-06-29 15:45 ` Vlastimil Babka (SUSE)
2026-06-29 16:50 ` Zi Yan
0 siblings, 1 reply; 15+ messages in thread
From: Vlastimil Babka (SUSE) @ 2026-06-29 15:45 UTC (permalink / raw)
To: Zi Yan, Andrew Morton, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Baolin Wang, Liam R. Howlett, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel
On 6/29/26 04:56, Zi Yan wrote:
> With the subpage->private == 0 check added in a prior commit, any allocated
> compound page should not have nonzero subpage->private. Remove the
(not "subpage")
I think the sentence would be more precise if it said that we now expect
(and optionally check) tail pages to have zero ->private when they are
freed, so we can rely on that still being true during subsequent reallocation.
(and if the buddy allocator itself sets it to non-zero due to split/merge of
buddy pages, it will reset it to zero as well as appropriate, but maybe that
doesn't need spelling out)
> unnecessary subpage->private initialization code in compound page
> preparation.
>
> Signed-off-by: Zi Yan <ziy@nvidia.com>
Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
> ---
> mm/internal.h | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/mm/internal.h b/mm/internal.h
> index 181e79f1d6a2..c96421ce9350 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -895,7 +895,6 @@ static inline void prep_compound_tail(struct page *tail,
> {
> tail->mapping = TAIL_MAPPING;
> set_compound_head(tail, head, order);
> - set_page_private(tail, 0);
> }
>
> static inline void init_compound_tail(struct page *tail,
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail()
2026-06-29 15:45 ` Vlastimil Babka (SUSE)
@ 2026-06-29 16:50 ` Zi Yan
0 siblings, 0 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-29 16:50 UTC (permalink / raw)
To: Vlastimil Babka (SUSE)
Cc: Andrew Morton, Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, David Hildenbrand, Lorenzo Stoakes, Baolin Wang,
Liam R. Howlett, Nico Pache, Ryan Roberts, Dev Jain, Barry Song,
Lance Yang, Mike Rapoport, linux-mm, linux-kernel
On 29 Jun 2026, at 11:45, Vlastimil Babka (SUSE) wrote:
> On 6/29/26 04:56, Zi Yan wrote:
>> With the subpage->private == 0 check added in a prior commit, any allocated
>> compound page should not have nonzero subpage->private. Remove the
>
> (not "subpage")
Will change it to tail_page.
>
> I think the sentence would be more precise if it said that we now expect
> (and optionally check) tail pages to have zero ->private when they are
> freed, so we can rely on that still being true during subsequent reallocation.
>
> (and if the buddy allocator itself sets it to non-zero due to split/merge of
> buddy pages, it will reset it to zero as well as appropriate, but maybe that
> doesn't need spelling out)
Will fix the commit message like you suggested above.
>
>> unnecessary subpage->private initialization code in compound page
>> preparation.
>>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>
> Acked-by: Vlastimil Babka (SUSE) <vbabka@kernel.org>
Thanks.
>
>> ---
>> mm/internal.h | 1 -
>> 1 file changed, 1 deletion(-)
>>
>> diff --git a/mm/internal.h b/mm/internal.h
>> index 181e79f1d6a2..c96421ce9350 100644
>> --- a/mm/internal.h
>> +++ b/mm/internal.h
>> @@ -895,7 +895,6 @@ static inline void prep_compound_tail(struct page *tail,
>> {
>> tail->mapping = TAIL_MAPPING;
>> set_compound_head(tail, head, order);
>> - set_page_private(tail, 0);
>> }
>>
>> static inline void init_compound_tail(struct page *tail,
>>
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] mm/compaction: stop recording free page order in page->private
2026-06-29 2:56 ` [PATCH 1/4] mm/compaction: stop recording free page order in page->private Zi Yan
2026-06-29 14:28 ` Vlastimil Babka (SUSE)
@ 2026-06-30 1:32 ` Baolin Wang
2026-06-30 1:37 ` Zi Yan
1 sibling, 1 reply; 15+ messages in thread
From: Baolin Wang @ 2026-06-30 1:32 UTC (permalink / raw)
To: Zi Yan, Andrew Morton, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel
On 6/29/26 10:56 AM, Zi Yan wrote:
> Commit 733aea0b3a7bb ("mm/compaction: add support for >0 order folio
> memory compaction.") stores isolated free pages in an array indexed by free
> page orders, it is no longer needed to store the order in each page's
> ->private field. And there is no code using the stored order. Stop doing
> that.
>
> It also prepares for an upcoming change that ensures subpage->private is
I noticed that people tend to avoid using the term "subpage" now, though
I understand what you mean by it here. :)
[1] https://lore.kernel.org/all/20260623125723.2503832-1-dev.jain@arm.com/
> zero at page free time and the removal of set_page_private(0) from
> prep_compound_tail(). In alloc_contig_frozen_range_noprof(),
> isolate_freepages_range() is used to grab free pages from buddy allocator
> and it leaves the aforementioned page->private set until
> either split_free_frozen_pages() or prep_new_page() is called. That
> triggers the upcoming subpage->private nonzero check along once
> set_page_private(0) is removed from prep_compound_tail(), which is called
> via prep_new_page().
>
> Signed-off-by: Zi Yan <ziy@nvidia.com>
> ---
LGTM.
Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] mm/compaction: stop recording free page order in page->private
2026-06-30 1:32 ` Baolin Wang
@ 2026-06-30 1:37 ` Zi Yan
0 siblings, 0 replies; 15+ messages in thread
From: Zi Yan @ 2026-06-30 1:37 UTC (permalink / raw)
To: Baolin Wang, Andrew Morton, Vlastimil Babka, Suren Baghdasaryan,
Michal Hocko, Brendan Jackman, Johannes Weiner, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Mike Rapoport
Cc: linux-mm, linux-kernel
On Mon Jun 29, 2026 at 9:32 PM EDT, Baolin Wang wrote:
>
>
> On 6/29/26 10:56 AM, Zi Yan wrote:
>> Commit 733aea0b3a7bb ("mm/compaction: add support for >0 order folio
>> memory compaction.") stores isolated free pages in an array indexed by free
>> page orders, it is no longer needed to store the order in each page's
>> ->private field. And there is no code using the stored order. Stop doing
>> that.
>>
>> It also prepares for an upcoming change that ensures subpage->private is
>
> I noticed that people tend to avoid using the term "subpage" now, though
> I understand what you mean by it here. :)
>
> [1] https://lore.kernel.org/all/20260623125723.2503832-1-dev.jain@arm.com/
Thanks. I was confused which to use subpage or tail page. Now I know
tail page is the way to go.
>
>> zero at page free time and the removal of set_page_private(0) from
>> prep_compound_tail(). In alloc_contig_frozen_range_noprof(),
>> isolate_freepages_range() is used to grab free pages from buddy allocator
>> and it leaves the aforementioned page->private set until
>> either split_free_frozen_pages() or prep_new_page() is called. That
>> triggers the upcoming subpage->private nonzero check along once
>> set_page_private(0) is removed from prep_compound_tail(), which is called
>> via prep_new_page().
>>
>> Signed-off-by: Zi Yan <ziy@nvidia.com>
>> ---
>
> LGTM.
> Reviewed-by: Baolin Wang <baolin.wang@linux.alibaba.com>
Thanks.
--
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-06-30 1:37 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-29 2:56 [PATCH 0/4] Keep subpage private zero at free and folio split time Zi Yan
2026-06-29 2:56 ` [PATCH 1/4] mm/compaction: stop recording free page order in page->private Zi Yan
2026-06-29 14:28 ` Vlastimil Babka (SUSE)
2026-06-29 15:03 ` Zi Yan
2026-06-30 1:32 ` Baolin Wang
2026-06-30 1:37 ` Zi Yan
2026-06-29 2:56 ` [PATCH 2/4] mm/huge_memory: add page->private check back in __split_folio_to_order() Zi Yan
2026-06-29 14:39 ` Vlastimil Babka (SUSE)
2026-06-29 15:05 ` Zi Yan
2026-06-29 2:56 ` [PATCH 3/4] mm/page_alloc: make sure subpage->private is zero at page free time Zi Yan
2026-06-29 14:53 ` Vlastimil Babka (SUSE)
2026-06-29 15:07 ` Zi Yan
2026-06-29 2:56 ` [PATCH 4/4] mm/page_alloc: remove set_page_private() in prep_compound_tail() Zi Yan
2026-06-29 15:45 ` Vlastimil Babka (SUSE)
2026-06-29 16:50 ` Zi Yan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox