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(-)
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
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.
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
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.
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.
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
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.
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
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
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.
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 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.
© 2016 - 2026 Red Hat, Inc.