[PATCH v1 00/11] Zero page->private when freeing pages

Zi Yan posted 11 patches 1 month, 3 weeks ago
block/blk-mq-tag.c             |  6 +++++-
drivers/android/binder_alloc.c |  1 +
drivers/block/null_blk/main.c  |  1 +
drivers/gpu/drm/ttm/ttm_pool.c |  1 +
fs/erofs/zutil.c               |  1 +
kernel/relay.c                 |  8 ++++++--
kernel/watch_queue.c           |  8 ++++++--
mm/huge_memory.c               | 10 ++++++++++
mm/page_alloc.c                |  3 ++-
mm/percpu-vm.c                 |  4 +++-
mm/slub.c                      |  1 +
11 files changed, 37 insertions(+), 7 deletions(-)
[PATCH v1 00/11] Zero page->private when freeing pages
Posted by Zi Yan 1 month, 3 weeks ago
Hi all,

Based on a recent discussion with David Hildenbrand on page->private
is not zero when a page is freed[1], this patchset is trying to fix all
users do not zero ->private when freeing a page and add checks to make
sure all freed pages have ->private set to zero. For compound pages,
both head page and tail pages need to have ->private set to zero.

People are only cc'd on the cover letter and related patches.
Mailing lists get all patches.

Patch 1 to 9: fix all users do not zero ->private upon freeing pages.
I only tested part of them, so would like to get reviews to make sure I
did the right thing.

Patch 10    : restores page->private check in folio split code, since
the code was removed in a prior commit without a proper reason.

Patch 11    : adds checks in page freeing path, __free_pages_prepare(),
to make sure freed pages have zeroed ->prviate.

Any comment or suggestion is welcome.

Thanks.


Link: https://lore.kernel.org/all/91F2E741-5473-4D34-ADA1-C9E6EDCBF5E0@nvidia.com/ [1]


# MM - THP
Cc: David Hildenbrand <david@kernel.org>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Baolin Wang <baolin.wang@linux.alibaba.com>
Cc: "Liam R. Howlett" <Liam.Howlett@oracle.com>
Cc: Nico Pache <npache@redhat.com>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: Dev Jain <dev.jain@arm.com>
Cc: Barry Song <baohua@kernel.org>
Cc: Lance Yang <lance.yang@linux.dev>

# MM - page allocator
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Brendan Jackman <jackmanb@google.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>

# relay
Cc: Jason Xing <kernelxing@tencent.com>
Cc: Yushan Zhou <katrinzhou@tencent.com>
Cc: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>

# MM - slub
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Christoph Lameter <cl@gentwo.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Harry Yoo <harry.yoo@oracle.com>

# DRM TTM
Cc: Christian Koenig <christian.koenig@amd.com>
Cc: Huang Rui <ray.huang@amd.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
Cc: Simona Vetter <simona@ffwll.ch>

# Block
Cc: Jens Axboe <axboe@kernel.dk>

# watch queue
Cc: Christian Brauner <brauner@kernel.org>
Cc: K Prateek Nayak <kprateek.nayak@amd.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Eric Sandeen <sandeen@redhat.com>

# binder
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: "Arve Hjønnevåg" <arve@android.com>
Cc: Todd Kjos <tkjos@android.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Carlos Llamas <cmllamas@google.com>
Cc: Alice Ryhl <aliceryhl@google.com>

# null_blk
Cc: Damien Le Moal <dlemoal@kernel.org>
Cc: Johannes Thumshirn <johannes.thumshirn@wdc.com>

# percpu
Cc: Dennis Zhou <dennis@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Christoph Lameter <cl@gentwo.org>

# erofs
Cc: Gao Xiang <xiang@kernel.org>
Cc: Yue Hu <zbestahu@gmail.com>
Cc: Jeffle Xu <jefflexu@linux.alibaba.com>
Cc: Sandeep Dhavale <dhavale@google.com>
Cc: Hongbo Li <lihongbo22@huawei.com>
Cc: Chunhai Guo <guochunhai@vivo.com>

# lists
Cc: linux-erofs@lists.ozlabs.org
Cc: linux-block@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org

Zi Yan (11):
  relay: zero page->private when freeing pages
  mm/slub: zero page->private when freeing pages
  drm/ttm: zero page->private when freeing pages
  blk-mq: zero page->private when freeing pages
  watch_queue: zero page->private when freeing pages
  binder: zero page->private when freeing pages
  null_blk: zero page->private when freeing pages
  percpu: zero page->private when freeing pages
  erofs: zero page->private when freeing pages
  mm/huge_memory: add page->private check back in
    __split_folio_to_order()
  mm/page_alloc: check page->private upon page free

 block/blk-mq-tag.c             |  6 +++++-
 drivers/android/binder_alloc.c |  1 +
 drivers/block/null_blk/main.c  |  1 +
 drivers/gpu/drm/ttm/ttm_pool.c |  1 +
 fs/erofs/zutil.c               |  1 +
 kernel/relay.c                 |  8 ++++++--
 kernel/watch_queue.c           |  8 ++++++--
 mm/huge_memory.c               | 10 ++++++++++
 mm/page_alloc.c                |  3 ++-
 mm/percpu-vm.c                 |  4 +++-
 mm/slub.c                      |  1 +
 11 files changed, 37 insertions(+), 7 deletions(-)

-- 
2.51.0

Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Matthew Wilcox 1 month, 3 weeks ago
On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
> Based on a recent discussion with David Hildenbrand on page->private
> is not zero when a page is freed[1], this patchset is trying to fix all
> users do not zero ->private when freeing a page and add checks to make
> sure all freed pages have ->private set to zero. For compound pages,
> both head page and tail pages need to have ->private set to zero.

Sorry, I didn't notice this conversation.  It seems entirely unnecessary
to me to zero out page->private before freeing.

I'm a bit confused that ac1ea219590c was needed too; I thought we
cleared page->private in the allocation path, and I don't see why
it needs to be cleared in the freeing path.  split_page() should be
clearing page->private.

Can we discuss this at the THP Cabal meeting on Wednesday?  I don't
think that introducing this new rule that page->private must be cleared
by the users is a good idea.
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by David Hildenbrand (Arm) 1 month, 3 weeks ago
On 2/23/26 05:28, Matthew Wilcox wrote:
> On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
>> Based on a recent discussion with David Hildenbrand on page->private
>> is not zero when a page is freed[1], this patchset is trying to fix all
>> users do not zero ->private when freeing a page and add checks to make
>> sure all freed pages have ->private set to zero. For compound pages,
>> both head page and tail pages need to have ->private set to zero.
> 
> Sorry, I didn't notice this conversation.  It seems entirely unnecessary
> to me to zero out page->private before freeing.
> 
> I'm a bit confused that ac1ea219590c was needed too; I thought we
> cleared page->private in the allocation path, and I don't see why
> it needs to be cleared in the freeing path.  split_page() should be
> clearing page->private.

See discussion at 
https://lore.kernel.org/linux-mm/cbc3b5b3-09b5-4e3c-99f0-a1f67582afff@kernel.org/

> 
> Can we discuss this at the THP Cabal meeting on Wednesday?  I don't
> think that introducing this new rule that page->private must be cleared
> by the users is a good idea.

The benefit is that only the users that really use page->private will 
also zero it out.

This implies that all pages that enter+leave the page allocator will 
have page->private initialized and we can also likely do:

diff --git a/mm/internal.h b/mm/internal.h
index 9e0577413087..4ecdae78e0d2 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -724,7 +724,6 @@ static inline void prep_compound_tail(struct page 
*head, int tail_idx)

         p->mapping = TAIL_MAPPING;
         set_compound_head(p, head);
-       set_page_private(p, 0);
  }

  extern void prep_compound_page(struct page *page, unsigned int order);


Which is rather nice.

-- 
Cheers,

David
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Christoph Hellwig 1 month, 3 weeks ago
On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
> Hi all,
> 
> Based on a recent discussion with David Hildenbrand on page->private
> is not zero when a page is freed[1], this patchset is trying to fix all
> users do not zero ->private when freeing a page and add checks to make
> sure all freed pages have ->private set to zero. For compound pages,
> both head page and tail pages need to have ->private set to zero.

Requiring the user to clear a field before freeing is just a way to
awkward interface.  Don't do that.
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Christian König 1 month, 3 weeks ago
On 2/23/26 14:46, Christoph Hellwig wrote:
> On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
>> Hi all,
>>
>> Based on a recent discussion with David Hildenbrand on page->private
>> is not zero when a page is freed[1], this patchset is trying to fix all
>> users do not zero ->private when freeing a page and add checks to make
>> sure all freed pages have ->private set to zero. For compound pages,
>> both head page and tail pages need to have ->private set to zero.
> 
> Requiring the user to clear a field before freeing is just a way to
> awkward interface.  Don't do that.

Completely agree. This is just asking for trouble.

The cache line(s) backing this struct page are most likely accessed anyway on free/alloc. So I don't see much extra overhead.

Regards,
Christian.
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by David Hildenbrand (Arm) 1 month, 3 weeks ago
On 2/23/26 15:06, Christian König wrote:
> On 2/23/26 14:46, Christoph Hellwig wrote:
>> On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
>>> Hi all,
>>>
>>> Based on a recent discussion with David Hildenbrand on page->private
>>> is not zero when a page is freed[1], this patchset is trying to fix all
>>> users do not zero ->private when freeing a page and add checks to make
>>> sure all freed pages have ->private set to zero. For compound pages,
>>> both head page and tail pages need to have ->private set to zero.
>>
>> Requiring the user to clear a field before freeing is just a way to
>> awkward interface.  Don't do that.
> 
> Completely agree. This is just asking for trouble.
> 
> The cache line(s) backing this struct page are most likely accessed anyway on free/alloc. So I don't see much extra overhead.

I think the question is more around handling non-head pages when freeing 
larger orders. But maybe the overhead of zeroing page->private it there 
as well in __free_pages_prepare() is tolerable.

I'll note, though, that we already require page->mapping and 
page->memcg_data of pages to be zeroed by the caller, so it's not 
completely crazy. (see page_expected_state)

-- 
Cheers,

David
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Christian König 1 month, 3 weeks ago
On 2/23/26 15:14, David Hildenbrand (Arm) wrote:
> On 2/23/26 15:06, Christian König wrote:
>> On 2/23/26 14:46, Christoph Hellwig wrote:
>>> On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
>>>> Hi all,
>>>>
>>>> Based on a recent discussion with David Hildenbrand on page->private
>>>> is not zero when a page is freed[1], this patchset is trying to fix all
>>>> users do not zero ->private when freeing a page and add checks to make
>>>> sure all freed pages have ->private set to zero. For compound pages,
>>>> both head page and tail pages need to have ->private set to zero.
>>>
>>> Requiring the user to clear a field before freeing is just a way to
>>> awkward interface.  Don't do that.
>>
>> Completely agree. This is just asking for trouble.
>>
>> The cache line(s) backing this struct page are most likely accessed anyway on free/alloc. So I don't see much extra overhead.
> 
> I think the question is more around handling non-head pages when freeing larger orders. But maybe the overhead of zeroing page->private it there as well in __free_pages_prepare() is tolerable.

Good point, sounds like that is a bit more than I thought it would be.

> I'll note, though, that we already require page->mapping and page->memcg_data of pages to be zeroed by the caller, so it's not completely crazy. (see page_expected_state)

Well that's not defensive at all, basically everybody which forgets to do that can cause hard to debug trouble. Maybe that practice should be reconsidered.

Regards,
Christian.
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by David Hildenbrand (Arm) 1 month, 3 weeks ago
On 2/23/26 16:22, Christian König wrote:
> On 2/23/26 15:14, David Hildenbrand (Arm) wrote:
>> On 2/23/26 15:06, Christian König wrote:
>>>
>>> Completely agree. This is just asking for trouble.
>>>
>>> The cache line(s) backing this struct page are most likely accessed anyway on free/alloc. So I don't see much extra overhead.
>>
>> I think the question is more around handling non-head pages when freeing larger orders. But maybe the overhead of zeroing page->private it there as well in __free_pages_prepare() is tolerable.
> 
> Good point, sounds like that is a bit more than I thought it would be.

Right. We already iterate over all "tail" pages in 
__free_pages_prepare() to clear page flags

	(page + i)->flags.f &= ~PAGE_FLAGS_CHECK_AT_PREP;

Maybe zeroing page->private there is not too bad.

> 
>> I'll note, though, that we already require page->mapping and page->memcg_data of pages to be zeroed by the caller, so it's not completely crazy. (see page_expected_state)
> 
> Well that's not defensive at all, basically everybody which forgets to do that can cause hard to debug trouble. Maybe that practice should be reconsidered.

Right, we do have check_pages= to activate the page_expected_state() 
checks, where we will detect such problems.

(including the mapcount being -1 etc.)

-- 
Cheers,

David
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Zi Yan 1 month, 3 weeks ago
On 23 Feb 2026, at 8:46, Christoph Hellwig wrote:

> On Sun, Feb 22, 2026 at 10:26:30PM -0500, Zi Yan wrote:
>> Hi all,
>>
>> Based on a recent discussion with David Hildenbrand on page->private
>> is not zero when a page is freed[1], this patchset is trying to fix all
>> users do not zero ->private when freeing a page and add checks to make
>> sure all freed pages have ->private set to zero. For compound pages,
>> both head page and tail pages need to have ->private set to zero.
>
> Requiring the user to clear a field before freeing is just a way to
> awkward interface.  Don't do that.

Page allocator hands the user a zero ->private. I think it is reasonable
to require the user to return a zero ->private. Restoring things back
to their original states is a common standard, isn't it? And most of
users do that.

Do you have any example that shows doing such a thing is difficult,
if not impossible?


--
Best Regards,
Yan, Zi
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Christoph Hellwig 1 month, 3 weeks ago
On Mon, Feb 23, 2026 at 09:00:57AM -0500, Zi Yan wrote:
> > awkward interface.  Don't do that.
> 
> Page allocator hands the user a zero ->private. I think it is reasonable
> to require the user to return a zero ->private. Restoring things back
> to their original states is a common standard, isn't it? And most of
> users do that.

No other relevant interface requires private data pointers, as that would
be silly and cause tons of bugs.
Re: [PATCH v1 00/11] Zero page->private when freeing pages
Posted by Zi Yan 1 month, 3 weeks ago
On 23 Feb 2026, at 9:03, Christoph Hellwig wrote:

> On Mon, Feb 23, 2026 at 09:00:57AM -0500, Zi Yan wrote:
>>> awkward interface.  Don't do that.
>>
>> Page allocator hands the user a zero ->private. I think it is reasonable
>> to require the user to return a zero ->private. Restoring things back
>> to their original states is a common standard, isn't it? And most of
>> users do that.
>
> No other relevant interface requires private data pointers, as that would
> be silly and cause tons of bugs.

filesystems use ->private and zero it before freeing a page. Can you provide a concrete
example showing that requirement will cause bugs or make other functions hard to
implement? With good examples, we can document them and make sure MM handle them
properly (i.e., zeroing ->private for them).

--
Best Regards,
Yan, Zi
[syzbot ci] Re: Zero page->private when freeing pages
Posted by syzbot ci 1 month, 3 weeks ago
syzbot ci has tested the following series

[v1] Zero page->private when freeing pages
https://lore.kernel.org/all/20260223032641.1859381-1-ziy@nvidia.com
* [PATCH v1 01/11] relay: zero page->private when freeing pages
* [PATCH v1 02/11] mm/slub: zero page->private when freeing pages
* [PATCH v1 03/11] drm/ttm: zero page->private when freeing pages
* [PATCH v1 04/11] blk-mq: zero page->private when freeing pages
* [PATCH v1 05/11] watch_queue: zero page->private when freeing pages
* [PATCH v1 06/11] binder: zero page->private when freeing pages
* [PATCH v1 07/11] null_blk: zero page->private when freeing pages
* [PATCH v1 08/11] percpu: zero page->private when freeing pages
* [PATCH v1 09/11] erofs: zero page->private when freeing pages
* [PATCH v1 10/11] mm/huge_memory: add page->private check back in __split_folio_to_order()
* [PATCH v1 11/11] mm/page_alloc: check page->private upon page free

and found the following issue:
WARNING in __free_frozen_pages

Full report is available here:
https://ci.syzbot.org/series/10f470ac-46ac-4e38-902d-dc86ae743494

***

WARNING in __free_frozen_pages

tree:      mm-new
URL:       https://kernel.googlesource.com/pub/scm/linux/kernel/git/akpm/mm.git
base:      a6fdc327de4678e54b5122441c970371014117b0
arch:      amd64
compiler:  Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
config:    https://ci.syzbot.org/builds/0586347c-8ef1-427f-8a9c-7f6c08b616a9/config

Bluetooth: RFCOMM ver 1.11
Bluetooth: BNEP (Ethernet Emulation) ver 1.3
Bluetooth: BNEP filters: protocol multicast
Bluetooth: BNEP socket layer initialized
Bluetooth: HIDP (Human Interface Emulation) ver 1.2
Bluetooth: HIDP socket layer initialized
NET: Registered PF_RXRPC protocol family
Key type rxrpc registered
Key type rxrpc_s registered
NET: Registered PF_KCM protocol family
lec:lane_module_init: lec.c: initialized
mpoa:atm_mpoa_init: mpc.c: initialized
l2tp_core: L2TP core driver, V2.0
l2tp_ppp: PPPoL2TP kernel driver, V2.0
l2tp_ip: L2TP IP encapsulation support (L2TPv3)
l2tp_netlink: L2TP netlink interface
l2tp_eth: L2TP ethernet pseudowire support (L2TPv3)
l2tp_ip6: L2TP IP encapsulation support for IPv6 (L2TPv3)
NET: Registered PF_PHONET protocol family
8021q: 802.1Q VLAN Support v1.8
sctp: Hash tables configured (bind 32/56)
NET: Registered PF_RDS protocol family
Registered RDS/infiniband transport
Registered RDS/tcp transport
tipc: Activated (version 2.0.0)
NET: Registered PF_TIPC protocol family
tipc: Started in single node mode
smc: adding smcd device lo without pnetid
NET: Registered PF_SMC protocol family
9pnet: Installing 9P2000 support
NET: Registered PF_CAIF protocol family
NET: Registered PF_IEEE802154 protocol family
Key type dns_resolver registered
Key type ceph registered
libceph: loaded (mon/osd proto 15/24)
batman_adv: B.A.T.M.A.N. advanced 2025.5 (compatibility version 15) loaded
openvswitch: Open vSwitch switching datapath
NET: Registered PF_VSOCK protocol family
mpls_gso: MPLS GSO support
IPI shorthand broadcast: enabled
sched_clock: Marking stable (19970046340, 93374727)->(20073238384, -9817317)
registered taskstats version 1
Loading compiled-in X.509 certificates
Loaded X.509 cert 'Build time autogenerated kernel key: 98092a222e11368da223b039d625e21c3e2e069c'
zswap: loaded using pool 842
Demotion targets for Node 0: null
Demotion targets for Node 1: null
debug_vm_pgtable: [debug_vm_pgtable         ]: Validating architecture page table helpers
------------[ cut here ]------------
page->private
WARNING: mm/page_alloc.c:1433 at __free_frozen_pages+0x78e/0xe10, CPU#0: swapper/0/1
Modules linked in:
CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:__free_frozen_pages+0x78e/0xe10
Code: 5f 5d e9 05 1e 00 00 48 b8 00 00 00 00 00 fc ff df 0f b6 44 05 00 84 c0 0f 85 d7 02 00 00 c7 03 ff ff ff ff e9 22 fc ff ff 90 <0f> 0b 90 e9 8d fc ff ff bd 01 00 00 00 83 f8 05 0f 85 bb fe ff ff
RSP: 0000:ffffc900000676e0 EFLAGS: 00010282
RAX: 1ffffd4000bad935 RBX: ffffea0005d6c9a8 RCX: dffffc0000000000
RDX: 0000000000000000 RSI: 0000000000000004 RDI: ffffea0005d6c9b4
RBP: ffffea0005d6c9b8 R08: ffffea0005d6c9b7 R09: 1ffffd4000bad936
R10: dffffc0000000000 R11: fffff94000bad937 R12: ffffea0005d6c980
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffff88818de64000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: ffff88823ffff000 CR3: 000000000e54c000 CR4: 00000000000006f0
Call Trace:
 <TASK>
 destroy_args+0x15d/0x570
 debug_vm_pgtable+0x3f8/0x410
 do_one_initcall+0x250/0x8d0
 do_initcall_level+0x104/0x190
 do_initcalls+0x59/0xa0
 kernel_init_freeable+0x2a6/0x3e0
 kernel_init+0x1d/0x1d0
 ret_from_fork+0x51e/0xb90
 ret_from_fork_asm+0x1a/0x30
 </TASK>


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.