| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/08 03:52 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"FS_ENCRYPTION",
"ZSMALLOC"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_init",
"erofs_onlinefolio_end",
"fscrypt_encrypt_pagecache_blocks",
"fscrypt_free_bounce_page",
"zs_page_migrate",
"migrate_folio_unmap",
"folio_check_dirty_writeback",
"split_huge_pages_pid"
],
"KMSANReasoning": "The patch series removes the `PG_private` page flag across the kernel memory management subsystem, VFS, filesystems (EROFS, Ceph, NFS, F2FS, UBIFS), and drivers (Xen, perf/intel_pt/bts), replacing `PagePrivate` / `SetPagePrivate` / `ClearPagePrivate` with direct inspections of `page-\u003eprivate` / `folio-\u003eprivate` and helpers like `folio_test_fs_private()` and `readahead_folio_last()`.\n\nKMSAN Applicability Analysis:\n1. No uninitialized memory reads or leaks: The patch does not introduce or expose uninitialized heap, stack, or page memory. Structures like `struct readahead_control` (which gained `_forward`) are initialized with designated initializers (e.g. `DEFINE_READAHEAD`), zeroing all fields.\n2. User-space copies: The only user-visible modification in `stable_page_flags` (`/proc/kpageflags`) safely removes the `PG_private` bit mapping from an already zero-initialized integer (`u`), with no possibility of uninitialized kernel memory leakage.\n3. Nature of potential issues: Any bugs resulting from this refactoring would pertain to page state transitions, refcount mismatches, page-private lifecycle management, or concurrency races, which are detected by standard assertions (VM_BUG_ON, VM_WARN_ON), KASAN, or LOCKDEP rather than uninitialized memory detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch series eliminates the PG_private page flag in favor of direct folio-\u003eprivate checks and the new folio_test_fs_private() helper. It also introduces readahead_folio_last() for backward iteration during readahead in EROFS, updates zsmalloc component page identification, adjusts fscrypt bounce page tagging, and updates dirty/writeback checks in memory migration and vmscan. These changes touch core memory management and filesystem folio logic that is directly reachable and warrants fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/08 03:52 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 0590aa3a35c51ca8c0aad93842ab6f112af2df0c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Sep 8 03:52:47 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/admin-guide/kdump/vmcoreinfo.rst b/Documentation/admin-guide/kdump/vmcoreinfo.rst\nindex 7663c610fe901..5f1df6d080508 100644\n--- a/Documentation/admin-guide/kdump/vmcoreinfo.rst\n+++ b/Documentation/admin-guide/kdump/vmcoreinfo.rst\n@@ -325,7 +325,7 @@ NR_FREE_PAGES\n On linux-2.6.21 or later, the number of free pages is in\n vm_stat[NR_FREE_PAGES]. Used to get the number of free pages.\n \n-PG_lru|PG_private|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask\n+PG_lru|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask\n --------------------------------------------------------------------------\n \n Page attributes. These flags are used to filter various unnecessary for\ndiff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst\nindex d3a93eec3945f..dec7816303c6a 100644\n--- a/Documentation/filesystems/vfs.rst\n+++ b/Documentation/filesystems/vfs.rst\n@@ -649,8 +649,8 @@ Writeback.\n \n The first can be used independently to the others. The VM can try to\n release clean pages in order to reuse them. To do this it can call\n--\u003erelease_folio on clean folios with the private\n-flag set. Clean pages without PagePrivate and with no external references\n+-\u003erelease_folio on clean folios with folio-\u003eprivate set. Clean pages\n+without folio-\u003eprivate set and with no external references\n will be released without notice being given to the address_space.\n \n To achieve this functionality, pages need to be placed on an LRU with\n@@ -674,7 +674,7 @@ filemap_fdatawait_range, to wait for all writeback to complete.\n \n An address_space handler may attach extra information to a page,\n typically using the 'private' field in the 'struct page'. If such\n-information is attached, the PG_Private flag should be set. This will\n+information is attached, non-NULL 'private' field will\n cause various VM routines to make extra calls into the address_space\n handler to deal with that data.\n \ndiff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c\nindex cbac54cb3a9ec..5849392cf26d5 100644\n--- a/arch/x86/events/intel/bts.c\n+++ b/arch/x86/events/intel/bts.c\n@@ -66,9 +66,6 @@ static struct pmu bts_pmu;\n \n static int buf_nr_pages(struct page *page)\n {\n-\tif (!PagePrivate(page))\n-\t\treturn 1;\n-\n \treturn 1 \u003c\u003c page_private(page);\n }\n \ndiff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c\nindex 5754cd4055628..49349afee6119 100644\n--- a/arch/x86/events/intel/pt.c\n+++ b/arch/x86/events/intel/pt.c\n@@ -781,8 +781,7 @@ static int topa_insert_pages(struct pt_buffer *buf, int cpu, gfp_t gfp)\n \tstruct page *p;\n \n \tp = virt_to_page(buf-\u003edata_pages[buf-\u003enr_pages]);\n-\tif (PagePrivate(p))\n-\t\torder = page_private(p);\n+\torder = page_private(p);\n \n \tif (topa_table_full(topa)) {\n \t\ttopa = topa_alloc(cpu, gfp);\n@@ -1296,8 +1295,7 @@ static int pt_buffer_try_single(struct pt_buffer *buf, int nr_pages)\n \tif (!intel_pt_validate_hw_cap(PT_CAP_single_range_output))\n \t\tgoto out;\n \n-\tif (PagePrivate(p))\n-\t\torder = page_private(p);\n+\torder = page_private(p);\n \n \tif (1 \u003c\u003c order != nr_pages)\n \t\tgoto out;\ndiff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c\nindex b8325cb09a371..0cf14bc1cff32 100644\n--- a/drivers/md/md-bitmap.c\n+++ b/drivers/md/md-bitmap.c\n@@ -516,7 +516,7 @@ static void end_bitmap_write(struct bio *bio)\n \n static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)\n {\n-\tstruct buffer_head *bh = page_buffers(page);\n+\tstruct buffer_head *bh = (struct buffer_head *)page_private(page);\n \n \twhile (bh \u0026\u0026 bh-\u003eb_blocknr) {\n \t\tatomic_inc(\u0026bitmap-\u003epending_writes);\n@@ -535,10 +535,10 @@ static void free_buffers(struct page *page)\n {\n \tstruct buffer_head *bh;\n \n-\tif (!PagePrivate(page))\n+\tif (!page_private(page))\n \t\treturn;\n \n-\tbh = page_buffers(page);\n+\tbh = (struct buffer_head *)page_private(page);\n \twhile (bh) {\n \t\tstruct buffer_head *next = bh-\u003eb_this_page;\n \t\tfree_buffer_head(bh);\ndiff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c\nindex e7f74ea7cd5eb..fdb18348cfdfe 100644\n--- a/drivers/xen/balloon.c\n+++ b/drivers/xen/balloon.c\n@@ -182,6 +182,11 @@ static struct page *balloon_retrieve(bool require_lowmem)\n \n \t__ClearPageOffline(page);\n \tdec_node_page_state(page, NR_BALLOON_PAGES);\n+\t/*\n+\t * clear page-\u003eprivate before giving it out, since it might be used to\n+\t * store xen_page_foreign info.\n+\t */\n+\tset_page_private(page, 0);\n \n \treturn page;\n }\ndiff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c\nindex 69922be28b54c..993f89f048e21 100644\n--- a/drivers/xen/grant-table.c\n+++ b/drivers/xen/grant-table.c\n@@ -863,10 +863,10 @@ EXPORT_SYMBOL_GPL(gnttab_free_auto_xlat_frames);\n \n int gnttab_pages_set_private(int nr_pages, struct page **pages)\n {\n+#if BITS_PER_LONG \u003c 64\n \tint i;\n \n \tfor (i = 0; i \u003c nr_pages; i++) {\n-#if BITS_PER_LONG \u003c 64\n \t\tstruct xen_page_foreign *foreign;\n \n \t\tforeign = kzalloc_obj(*foreign);\n@@ -874,9 +874,9 @@ int gnttab_pages_set_private(int nr_pages, struct page **pages)\n \t\t\treturn -ENOMEM;\n \n \t\tset_page_private(pages[i], (unsigned long)foreign);\n-#endif\n-\t\tSetPagePrivate(pages[i]);\n \t}\n+#endif\n+\t/* Data is stored in page-\u003eprivate on 64-bit */\n \n \treturn 0;\n }\n@@ -1031,12 +1031,11 @@ void gnttab_pages_clear_private(int nr_pages, struct page **pages)\n \tint i;\n \n \tfor (i = 0; i \u003c nr_pages; i++) {\n-\t\tif (PagePrivate(pages[i])) {\n #if BITS_PER_LONG \u003c 64\n+\t\tif (page_private(pages[i]))\n \t\t\tkfree((void *)page_private(pages[i]));\n #endif\n-\t\t\tClearPagePrivate(pages[i]);\n-\t\t}\n+\t\tset_page_private(pages[i], 0);\n \t}\n }\n EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);\ndiff --git a/fs/ceph/addr.c b/fs/ceph/addr.c\nindex 657c2cb0f8811..f9b2ec9550481 100644\n--- a/fs/ceph/addr.c\n+++ b/fs/ceph/addr.c\n@@ -70,9 +70,7 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned\n \n static inline struct ceph_snap_context *page_snap_context(struct page *page)\n {\n-\tif (PagePrivate(page))\n-\t\treturn (void *)page-\u003eprivate;\n-\treturn NULL;\n+\treturn (void *)page-\u003eprivate;\n }\n \n /*\n@@ -124,8 +122,8 @@ static bool ceph_dirty_folio(struct address_space *mapping, struct folio *folio)\n \tspin_unlock(\u0026ci-\u003ei_ceph_lock);\n \n \t/*\n-\t * Reference snap context in folio-\u003eprivate. Also set\n-\t * PagePrivate so that we get invalidate_folio callback.\n+\t * Reference snap context in folio-\u003eprivate. Setting folio-\u003eprivate is\n+\t * what gets us the invalidate_folio callback.\n \t */\n \tVM_WARN_ON_FOLIO(folio-\u003eprivate, folio);\n \tfolio_attach_private(folio, snapc);\ndiff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c\nindex 5286a124b0d98..aced5c50a4601 100644\n--- a/fs/crypto/crypto.c\n+++ b/fs/crypto/crypto.c\n@@ -65,7 +65,6 @@ void fscrypt_free_bounce_page(struct page *bounce_page)\n \tif (!bounce_page)\n \t\treturn;\n \tset_page_private(bounce_page, (unsigned long)NULL);\n-\tClearPagePrivate(bounce_page);\n \tmempool_free(bounce_page, fscrypt_bounce_page_pool);\n }\n EXPORT_SYMBOL(fscrypt_free_bounce_page);\n@@ -210,7 +209,6 @@ struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,\n \t\t\treturn ERR_PTR(err);\n \t\t}\n \t}\n-\tSetPagePrivate(ciphertext_page);\n \tset_page_private(ciphertext_page, (unsigned long)folio);\n \treturn ciphertext_page;\n }\ndiff --git a/fs/erofs/data.c b/fs/erofs/data.c\nindex 0885b1f2fc925..f71c7b51bb720 100644\n--- a/fs/erofs/data.c\n+++ b/fs/erofs/data.c\n@@ -239,19 +239,23 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)\n /*\n * bit 30: I/O error occurred on this folio\n * bit 29: CPU has dirty data in D-cache (needs aliasing handling);\n- * bit 0 - 29: remaining parts to complete this folio\n+ * bit 0 - 28: remaining parts to complete this folio, biased by 1 so that\n+ *\t -\u003eprivate stays non-NULL while the folio is attached\n */\n #define EROFS_ONLINEFOLIO_EIO\t\t30\n #define EROFS_ONLINEFOLIO_DIRTY\t\t29\n+#define EROFS_ONLINEFOLIO_COUNT_MASK\t(BIT(EROFS_ONLINEFOLIO_DIRTY) - 1)\n+#define EROFS_ONLINEFOLIO_BIAS\t\t1\n \n void erofs_onlinefolio_init(struct folio *folio)\n {\n \tunion {\n \t\tatomic_t o;\n \t\tvoid *v;\n-\t} u = { .o = ATOMIC_INIT(1) };\n+\t} u = { .o = ATOMIC_INIT(1 + EROFS_ONLINEFOLIO_BIAS) };\n \n-\tfolio-\u003eprivate = u.v;\t/* valid only if file-backed folio is locked */\n+\t/* valid only if file-backed folio is locked */\n+\tfolio_attach_private(folio, u.v);\n }\n \n void erofs_onlinefolio_split(struct folio *folio)\n@@ -265,14 +269,14 @@ void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)\n \n \tdo {\n \t\torig = atomic_read((atomic_t *)\u0026folio-\u003eprivate);\n-\t\tDBG_BUGON(orig \u003c= 0);\n+\t\tDBG_BUGON((orig \u0026 EROFS_ONLINEFOLIO_COUNT_MASK) \u003c= EROFS_ONLINEFOLIO_BIAS);\n \t\tv = dirty \u003c\u003c EROFS_ONLINEFOLIO_DIRTY;\n \t\tv |= (orig - 1) | (!!err \u003c\u003c EROFS_ONLINEFOLIO_EIO);\n \t} while (atomic_cmpxchg((atomic_t *)\u0026folio-\u003eprivate, orig, v) != orig);\n \n-\tif (v \u0026 (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1))\n+\tif ((v \u0026 EROFS_ONLINEFOLIO_COUNT_MASK) != EROFS_ONLINEFOLIO_BIAS)\n \t\treturn;\n-\tfolio-\u003eprivate = 0;\n+\tfolio_detach_private(folio);\n \tif (v \u0026 BIT(EROFS_ONLINEFOLIO_DIRTY))\n \t\tflush_dcache_folio(folio);\n \tfolio_end_read(folio, !(v \u0026 BIT(EROFS_ONLINEFOLIO_EIO)));\ndiff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c\nindex e1e25ca0d1904..78fd7d980e957 100644\n--- a/fs/erofs/zdata.c\n+++ b/fs/erofs/zdata.c\n@@ -1898,21 +1898,14 @@ static void z_erofs_readahead(struct readahead_control *rac)\n \tstruct inode *realinode = erofs_real_inode(sharedinode, \u0026need_iput);\n \tZ_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, readahead_pos(rac));\n \tunsigned int nrpages = readahead_count(rac);\n-\tstruct folio *head = NULL, *folio;\n+\tstruct folio *folio;\n \tint err;\n \n \ttrace_erofs_readahead(realinode, readahead_index(rac), nrpages, false);\n \tz_erofs_pcluster_readmore(\u0026f, rac, true);\n-\twhile ((folio = readahead_folio(rac))) {\n-\t\tfolio-\u003eprivate = head;\n-\t\thead = folio;\n-\t}\n-\n-\t/* traverse in reverse order for best metadata I/O performance */\n-\twhile (head) {\n-\t\tfolio = head;\n-\t\thead = folio_get_private(folio);\n \n+\t/* traverse from last to first for best metadata I/O performance */\n+\twhile ((folio = readahead_folio_last(rac))) {\n \t\terr = z_erofs_scan_folio(\u0026f, folio, true);\n \t\tif (err \u0026\u0026 err != -EINTR)\n \t\t\terofs_err(realinode-\u003ei_sb, \"readahead error at folio %lu @ nid %llu\",\ndiff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h\nindex 9940a6cecf1a2..2f7ab5888b078 100644\n--- a/fs/f2fs/f2fs.h\n+++ b/fs/f2fs/f2fs.h\n@@ -2691,7 +2691,7 @@ static inline bool folio_test_f2fs_##name(const struct folio *folio)\t\\\n }\t\t\t\t\t\t\t\t\t\\\n static inline bool page_private_##name(struct page *page) \\\n { \\\n-\treturn PagePrivate(page) \u0026\u0026 \\\n+\treturn page_private(page) \u0026\u0026 \\\n \t\ttest_bit(PAGE_PRIVATE_NOT_POINTER, \u0026page_private(page)) \u0026\u0026 \\\n \t\ttest_bit(PAGE_PRIVATE_##flagname, \u0026page_private(page)); \\\n }\n@@ -2710,9 +2710,9 @@ static inline void folio_set_f2fs_##name(struct folio *folio)\t\t\\\n }\t\t\t\t\t\t\t\t\t\\\n static inline void set_page_private_##name(struct page *page) \\\n { \\\n-\tif (!PagePrivate(page)) \\\n-\t\tattach_page_private(page, (void *)0); \\\n-\tset_bit(PAGE_PRIVATE_NOT_POINTER, \u0026page_private(page)); \\\n+\tif (!page_private(page)) \\\n+\t\tattach_page_private(page, \\\n+\t\t\t\t(void *)BIT(PAGE_PRIVATE_NOT_POINTER)); \\\n \tset_bit(PAGE_PRIVATE_##flagname, \u0026page_private(page)); \\\n }\n \ndiff --git a/fs/nfs/file.c b/fs/nfs/file.c\nindex e1bdd10b35f10..38f830a6467c9 100644\n--- a/fs/nfs/file.c\n+++ b/fs/nfs/file.c\n@@ -484,7 +484,7 @@ static int nfs_write_end(const struct kiocb *iocb,\n * Partially or wholly invalidate a page\n * - Release the private state associated with a page if undergoing complete\n * page invalidation\n- * - Called if either PG_private or PG_fscache is set on the page\n+ * - Called if either folio-\u003eprivate or PG_fscache is set on the page\n * - Caller holds page lock\n */\n static void nfs_invalidate_folio(struct folio *folio, size_t offset,\n@@ -555,7 +555,7 @@ static void nfs_check_dirty_writeback(struct folio *folio,\n * Attempt to clear the private state associated with a page when an error\n * occurs that requires the cached contents of an inode to be written back or\n * destroyed\n- * - Called if either PG_private or fscache is set on the page\n+ * - Called if either page-\u003eprivate or fscache is set on the page\n * - Caller holds page lock\n * - Return 0 if successful, -error otherwise\n */\ndiff --git a/fs/nfs/write.c b/fs/nfs/write.c\nindex 623e7ef1f73d5..b6967b5286691 100644\n--- a/fs/nfs/write.c\n+++ b/fs/nfs/write.c\n@@ -717,7 +717,6 @@ static void nfs_inode_add_request(struct nfs_page *req)\n \tnfs_lock_request(req);\n \tspin_lock(\u0026mapping-\u003ei_private_lock);\n \tset_bit(PG_MAPPED, \u0026req-\u003ewb_flags);\n-\tfolio_set_private(folio);\n \tfolio-\u003eprivate = req;\n \tspin_unlock(\u0026mapping-\u003ei_private_lock);\n \tatomic_long_inc(\u0026nfsi-\u003enrequests);\n@@ -745,7 +744,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)\n \n \t\t\tspin_lock(\u0026mapping-\u003ei_private_lock);\n \t\t\tfolio-\u003eprivate = NULL;\n-\t\t\tfolio_clear_private(folio);\n \t\t\tclear_bit(PG_MAPPED, \u0026req-\u003ewb_head-\u003ewb_flags);\n \t\t\tspin_unlock(\u0026mapping-\u003ei_private_lock);\n \ndiff --git a/fs/proc/page.c b/fs/proc/page.c\nindex 260772b20bd99..f90e1030825e9 100644\n--- a/fs/proc/page.c\n+++ b/fs/proc/page.c\n@@ -232,7 +232,6 @@ u64 stable_page_flags(const struct page *page)\n \n \tu |= kpf_copy_bit(k, KPF_RESERVED,\tPG_reserved);\n \tu |= kpf_copy_bit(k, KPF_OWNER_2,\tPG_owner_2);\n-\tu |= kpf_copy_bit(k, KPF_PRIVATE,\tPG_private);\n \tu |= kpf_copy_bit(k, KPF_PRIVATE_2,\tPG_private_2);\n \tu |= kpf_copy_bit(k, KPF_OWNER_PRIVATE,\tPG_owner_priv_1);\n \tu |= kpf_copy_bit(k, KPF_ARCH,\t\tPG_arch_1);\ndiff --git a/fs/ubifs/file.c b/fs/ubifs/file.c\nindex e73c28b12f97f..aa0298ce451ef 100644\n--- a/fs/ubifs/file.c\n+++ b/fs/ubifs/file.c\n@@ -12,14 +12,14 @@\n * This file implements VFS file and inode operations for regular files, device\n * nodes and symlinks as well as address space operations.\n *\n- * UBIFS uses 2 page flags: @PG_private and @PG_checked. @PG_private is set if\n+ * UBIFS uses folio-\u003eprivate and page flag @PG_checked. folio-\u003eprivate is set if\n * the page is dirty and is used for optimization purposes - dirty pages are\n- * not budgeted so the flag shows that 'ubifs_write_end()' should not release\n+ * not budgeted so it shows that 'ubifs_write_end()' should not release\n * the budget for this page. The @PG_checked flag is set if full budgeting is\n * required for the page e.g., when it corresponds to a file hole or it is\n * beyond the file size. The budgeting is done in 'ubifs_write_begin()', because\n * it is OK to fail in this function, and the budget is released in\n- * 'ubifs_write_end()'. So the @PG_private and @PG_checked flags carry\n+ * 'ubifs_write_end()'. So the folio-\u003eprivate and the @PG_checked flag carry\n * information about how the page was budgeted, to make it possible to release\n * the budget properly.\n *\n@@ -1509,7 +1509,7 @@ static vm_fault_t ubifs_vm_page_mkwrite(struct vm_fault *vmf)\n \t *\n \t * At the moment we do not know whether the folio is dirty or not, so we\n \t * assume that it is not and budget for a new folio. We could look at\n-\t * the @PG_private flag and figure this out, but we may race with write\n+\t * folio-\u003eprivate and figure this out, but we may race with write\n \t * back and the folio state may change by the time we lock it, so this\n \t * would need additional care. We do not bother with this at the\n \t * moment, although it might be good idea to do. Instead, we allocate\ndiff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h\nindex fd2c7115c0542..e47fbd0387b69 100644\n--- a/include/linux/buffer_head.h\n+++ b/include/linux/buffer_head.h\n@@ -175,12 +175,6 @@ static inline unsigned long bh_offset(const struct buffer_head *bh)\n \treturn (unsigned long)(bh)-\u003eb_data \u0026 (page_size(bh-\u003eb_page) - 1);\n }\n \n-/* If we *know* page-\u003eprivate refers to buffer_heads */\n-#define page_buffers(page)\t\t\t\t\t\\\n-\t({\t\t\t\t\t\t\t\\\n-\t\tBUG_ON(!PagePrivate(page));\t\t\t\\\n-\t\t((struct buffer_head *)page_private(page));\t\\\n-\t})\n #define folio_buffers(folio)\t\tfolio_get_private(folio)\n \n void buffer_check_dirty_writeback(struct folio *folio,\ndiff --git a/include/linux/kernel-page-flags.h b/include/linux/kernel-page-flags.h\nindex 196778a087c4d..fe5ab6e50bd70 100644\n--- a/include/linux/kernel-page-flags.h\n+++ b/include/linux/kernel-page-flags.h\n@@ -11,7 +11,6 @@\n #define KPF_RESERVED\t\t32\n #define KPF_MLOCKED\t\t33\n #define KPF_OWNER_2\t\t34\n-#define KPF_PRIVATE\t\t35\n #define KPF_PRIVATE_2\t\t36\n #define KPF_OWNER_PRIVATE\t37\n #define KPF_ARCH\t\t38\ndiff --git a/include/linux/mm.h b/include/linux/mm.h\nindex c49ef99b4413b..0ceffe4f9ca02 100644\n--- a/include/linux/mm.h\n+++ b/include/linux/mm.h\n@@ -2048,20 +2048,21 @@ vm_fault_t finish_fault(struct vm_fault *vmf);\n *\n * A pagecache page contains an opaque `private' member, which belongs to the\n * page's address_space. Usually, this is the address of a circular list of\n- * the page's disk buffers. PG_private must be set to tell the VM to call\n- * into the filesystem to release these pages.\n+ * the page's disk buffers. It tells the VM to call into the filesystem to\n+ * release these pages.\n *\n * A folio may belong to an inode's memory mapping. In this case,\n * folio-\u003emapping points to the inode, and folio-\u003eindex is the file\n * offset of the folio, in units of PAGE_SIZE.\n *\n- * If pagecache pages are not associated with an inode, they are said to be\n- * anonymous pages. These may become associated with the swapcache, and in that\n- * case PG_swapcache is set, and page-\u003eprivate is an offset into the swapcache.\n+ * If pagecache folios are not associated with an inode, they are said to be\n+ * anonymous folios. These may become associated with the swapcache, and in that\n+ * case PG_swapcache is set, and folio-\u003eprivate is an offset into the swapcache.\n *\n * In either case (swapcache or inode backed), the pagecache itself holds one\n- * reference to the page. Setting PG_private should also increment the\n- * refcount. The each user mapping also has a reference to the page.\n+ * reference to the folio. Attaching filesystem private data via\n+ * folio_attach_private() also increments the refcount. Each user mapping also\n+ * has a reference to the folio.\n *\n * The pagecache pages are stored in a per-mapping radix tree, which is\n * rooted at mapping-\u003ei_pages, and indexed by offset.\n@@ -3004,9 +3005,9 @@ static inline bool folio_maybe_mapped_shared(struct folio *folio)\n * @folio: the folio\n *\n * Calculate the expected folio refcount, taking references from the pagecache,\n- * swapcache, PG_private and page table mappings into account. Useful in\n- * combination with folio_ref_count() to detect unexpected references (e.g.,\n- * GUP or other temporary references).\n+ * swapcache, private data (folio-\u003eprivate != NULL) and page table mappings into\n+ * account. Useful in combination with folio_ref_count() to detect unexpected\n+ * references (e.g., GUP or other temporary references).\n *\n * Does currently not consider references from the LRU cache. If the folio\n * was isolated from the LRU (which is the case during migration or split),\n@@ -3044,10 +3045,16 @@ static inline int folio_expected_ref_count(const struct folio *folio)\n \tref_count += folio_test_swapcache(folio) \u003c\u003c order;\n \n \tif (!folio_test_anon(folio)) {\n-\t\t/* One reference per page from the pagecache. */\n-\t\tref_count += !!folio-\u003emapping \u003c\u003c order;\n-\t\t/* One reference from PG_private. */\n-\t\tref_count += folio_test_private(folio);\n+\t\t/*\n+\t\t * One reference per page from the pagecache.\n+\t\t * Use data_race() since folio might not be locked.\n+\t\t */\n+\t\tref_count += !!data_race(folio-\u003emapping) \u003c\u003c order;\n+\t\t/*\n+\t\t * One reference from filesystem private data.\n+\t\t * Use data_race() since folio might not be locked.\n+\t\t */\n+\t\tref_count += data_race(folio_test_fs_private(folio));\n \t}\n \n \t/* One reference per page table mapping. */\ndiff --git a/include/linux/mm_types.h b/include/linux/mm_types.h\nindex 5413bd10fff2c..9d6c945ba4104 100644\n--- a/include/linux/mm_types.h\n+++ b/include/linux/mm_types.h\n@@ -108,7 +108,7 @@ struct page {\n \t\t\t};\n \t\t\t/**\n \t\t\t * @private: Mapping-private opaque data.\n-\t\t\t * Usually used for buffer_heads if PagePrivate.\n+\t\t\t * Usually used for buffer_heads.\n \t\t\t * Used for swp_entry_t if swapcache flag set.\n \t\t\t * Indicates order in the buddy system if PageBuddy\n \t\t\t * or on pcp_llist.\n@@ -675,7 +675,7 @@ static inline void ptdesc_pmd_pts_init(struct ptdesc *ptdesc)\n #define STRUCT_PAGE_MAX_SHIFT\t(order_base_2(sizeof(struct page)))\n \n /*\n- * page_private can be used on tail pages. However, PagePrivate is only\n+ * page_private can be used on tail pages. However, it is only\n * checked by the VM on the head page. So page_private on the tail pages\n * should be used for data that's ancillary to the head page (eg attaching\n * buffer heads to tail pages after attaching buffer heads to the head page)\ndiff --git a/include/linux/page-flags.h b/include/linux/page-flags.h\nindex 86dd0470da117..7b7783c0a5216 100644\n--- a/include/linux/page-flags.h\n+++ b/include/linux/page-flags.h\n@@ -44,10 +44,6 @@\n * Consequently, PG_reserved for a page mapped into user space can indicate\n * the zero page, the vDSO, MMIO pages or device memory.\n *\n- * The PG_private bitflag is set on pagecache pages if they contain filesystem\n- * specific data (which is normally at page-\u003eprivate). It can be used by\n- * private allocations for its own usage.\n- *\n * During initiation of disk I/O, PG_locked is set. This bit is set before I/O\n * and cleared when writeback _starts_ or when read _completes_. PG_writeback\n * is set before writeback starts and cleared when it finishes.\n@@ -105,7 +101,7 @@ enum pageflags {\n \tPG_owner_2,\t\t/* Owner use. If pagecache, fs may use */\n \tPG_arch_1,\n \tPG_reserved,\n-\tPG_private,\t\t/* If pagecache, has fs-private data */\n+\t__PG_folio,\t\t/* Do not use: reserved for folio identification */\n \tPG_private_2,\t\t/* If pagecache, has fs aux data */\n \tPG_reclaim,\t\t/* To be reclaimed asap */\n \tPG_swapbacked,\t\t/* Page is backed by RAM/swap */\n@@ -576,9 +572,14 @@ FOLIO_FLAG(swapbacked, FOLIO_HEAD_PAGE)\n /*\n * Private page markings that may be used by the filesystem that owns the page\n * for its own purposes.\n- * - PG_private and PG_private_2 cause release_folio() and co to be invoked\n+ * - folio-\u003eprivate and PG_private_2 cause release_folio() and co to be invoked\n */\n-PAGEFLAG(Private, private, PF_ANY)\n+\n+static __always_inline bool folio_test_private(const struct folio *folio)\n+{\n+\treturn folio-\u003eprivate;\n+}\n+\n FOLIO_FLAG(private_2, FOLIO_HEAD_PAGE)\n \n /* owner_2 can be set on tail pages for anon memory */\n@@ -1170,7 +1171,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)\n */\n #define PAGE_FLAGS_CHECK_AT_FREE\t\t\t\t\\\n \t(1UL \u003c\u003c PG_lru\t\t| 1UL \u003c\u003c PG_locked\t|\t\\\n-\t 1UL \u003c\u003c PG_private\t| 1UL \u003c\u003c PG_private_2\t|\t\\\n+\t 1UL \u003c\u003c PG_private_2\t|\t\t\t\t\\\n \t 1UL \u003c\u003c PG_writeback\t| 1UL \u003c\u003c PG_reserved\t|\t\\\n \t 1UL \u003c\u003c PG_active \t|\t\t\t\t\\\n \t 1UL \u003c\u003c PG_unevictable\t| __PG_MLOCKED | LRU_GEN_MASK)\n@@ -1194,8 +1195,28 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)\n \t(0xffUL /* order */\t\t| 1UL \u003c\u003c PG_has_hwpoisoned |\t\\\n \t 1UL \u003c\u003c PG_large_rmappable\t| 1UL \u003c\u003c PG_partially_mapped)\n \n-#define PAGE_FLAGS_PRIVATE\t\t\t\t\\\n-\t(1UL \u003c\u003c PG_private | 1UL \u003c\u003c PG_private_2)\n+/**\n+ * folio_test_fs_private - check if the folio has filesystem private data\n+ * @folio: The folio to check.\n+ *\n+ * Use this in code that may encounter swapcache or hugetlb folios but only\n+ * wants to detect filesystem private data. Swapcache stores swp_entry_t in\n+ * folio-\u003eswap, a union with folio-\u003eprivate, and hugetlb stores its own flags\n+ * in folio-\u003eprivate; both are excluded.\n+ *\n+ * NOTE: For swapcache, folio-\u003eswap.val PG_swapcache are not set as a whole,\n+ * so folio_test_swapcache() is not reliable to exclude swapcache.\n+ * Use folio_test_swapbacked() instead, since it remains set when a folio is\n+ * added to/removed from swapcache.\n+ *\n+ * Return: true if folio-\u003eprivate is set and the folio is neither swapcache\n+ * nor hugetlb.\n+ */\n+static inline bool folio_test_fs_private(const struct folio *folio)\n+{\n+\treturn folio_test_private(folio) \u0026\u0026 !folio_test_swapbacked(folio) \u0026\u0026\n+\t !folio_test_hugetlb(folio);\n+}\n /**\n * folio_has_private - Determine if folio has private stuff\n * @folio: The folio to be checked\n@@ -1205,7 +1226,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)\n */\n static inline int folio_has_private(const struct folio *folio)\n {\n-\treturn !!(folio-\u003eflags.f \u0026 PAGE_FLAGS_PRIVATE);\n+\treturn folio_test_fs_private(folio) || folio_test_private_2(folio);\n }\n \n #undef PF_ANY\ndiff --git a/include/linux/pagemap.h b/include/linux/pagemap.h\nindex 939f3a5e973f6..cc5c2cca814bc 100644\n--- a/include/linux/pagemap.h\n+++ b/include/linux/pagemap.h\n@@ -594,7 +594,6 @@ static inline void folio_attach_private(struct folio *folio, void *data)\n {\n \tfolio_get(folio);\n \tfolio-\u003eprivate = data;\n-\tfolio_set_private(folio);\n }\n \n /**\n@@ -629,9 +628,8 @@ static inline void *folio_detach_private(struct folio *folio)\n {\n \tvoid *data = folio_get_private(folio);\n \n-\tif (!folio_test_private(folio))\n+\tif (!data)\n \t\treturn NULL;\n-\tfolio_clear_private(folio);\n \tfolio-\u003eprivate = NULL;\n \tfolio_put(folio);\n \n@@ -1415,6 +1413,7 @@ struct readahead_control {\n \tbool dropbehind;\n \tbool _workingset;\n \tunsigned long _pflags;\n+\tbool _forward;\n };\n \n #define DEFINE_READAHEAD(ractl, f, r, m, i)\t\t\t\t\\\n@@ -1479,18 +1478,25 @@ void page_cache_async_readahead(struct address_space *mapping,\n \tpage_cache_async_ra(\u0026ractl, folio, req_count);\n }\n \n+static inline void __readahead_advance(struct readahead_control *rac)\n+{\n+\tif (rac-\u003e_forward)\n+\t\trac-\u003e_index += rac-\u003e_batch_count;\n+\n+\trac-\u003e_nr_pages -= rac-\u003e_batch_count;\n+\trac-\u003e_batch_count = 0;\n+}\n+\n static inline struct folio *__readahead_folio(struct readahead_control *ractl)\n {\n \tstruct folio *folio;\n \n \tBUG_ON(ractl-\u003e_batch_count \u003e ractl-\u003e_nr_pages);\n-\tractl-\u003e_nr_pages -= ractl-\u003e_batch_count;\n-\tractl-\u003e_index += ractl-\u003e_batch_count;\n+\t__readahead_advance(ractl);\n+\tractl-\u003e_forward = true;\n \n-\tif (!ractl-\u003e_nr_pages) {\n-\t\tractl-\u003e_batch_count = 0;\n+\tif (!ractl-\u003e_nr_pages)\n \t\treturn NULL;\n-\t}\n \n \tfolio = xa_load(\u0026ractl-\u003emapping-\u003ei_pages, ractl-\u003e_index);\n \tVM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);\n@@ -1516,6 +1522,39 @@ static inline struct folio *readahead_folio(struct readahead_control *ractl)\n \treturn folio;\n }\n \n+/**\n+ * readahead_folio_last - Get the next folio to read, from the tail.\n+ * @ractl: The current readahead request.\n+ *\n+ * Like readahead_folio(), but walks the range back-to-front. The folio is\n+ * returned locked with its refcount dropped; the caller unlocks it once I/O\n+ * completes. Compound folios are returned once, at their head index.\n+ *\n+ * Context: The folio is locked.\n+ * Return: A pointer to the next folio, or %NULL when done.\n+ */\n+static inline struct folio *readahead_folio_last(struct readahead_control *ractl)\n+{\n+\tstruct folio *folio;\n+\n+\t/* Drop the previously returned batch from the remaining range. */\n+\t__readahead_advance(ractl);\n+\tractl-\u003e_forward = false;\n+\n+\tif (!ractl-\u003e_nr_pages)\n+\t\treturn NULL;\n+\n+\t/* xa_load() follows sibling entries, so a tail index returns the head */\n+\tfolio = xa_load(\u0026ractl-\u003emapping-\u003ei_pages,\n+\t\t\tractl-\u003e_index + ractl-\u003e_nr_pages - 1);\n+\tVM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);\n+\n+\tractl-\u003e_batch_count = folio_nr_pages(folio);\n+\n+\tfolio_put(folio);\n+\treturn folio;\n+}\n+\n static inline unsigned int __readahead_batch(struct readahead_control *rac,\n \t\tstruct page **array, unsigned int array_sz)\n {\n@@ -1524,9 +1563,8 @@ static inline unsigned int __readahead_batch(struct readahead_control *rac,\n \tstruct folio *folio;\n \n \tBUG_ON(rac-\u003e_batch_count \u003e rac-\u003e_nr_pages);\n-\trac-\u003e_nr_pages -= rac-\u003e_batch_count;\n-\trac-\u003e_index += rac-\u003e_batch_count;\n-\trac-\u003e_batch_count = 0;\n+\t__readahead_advance(rac);\n+\trac-\u003e_forward = true;\n \n \txas_set(\u0026xas, rac-\u003e_index);\n \trcu_read_lock();\ndiff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h\nindex ef9aa388b84f7..38a2d30230443 100644\n--- a/include/trace/events/mmflags.h\n+++ b/include/trace/events/mmflags.h\n@@ -130,6 +130,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);\n #endif\n \n #define DEF_PAGEFLAG_NAME(_name) { 1UL \u003c\u003c PG_##_name, __stringify(_name) }\n+#define __DEF_PAGEFLAG_NAME(_name) { 1UL \u003c\u003c __PG_##_name, __stringify(_name) }\n \n #define __def_pageflag_names\t\t\t\t\t\t\\\n \tDEF_PAGEFLAG_NAME(locked),\t\t\t\t\t\\\n@@ -144,7 +145,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);\n \tDEF_PAGEFLAG_NAME(owner_2),\t\t\t\t\t\\\n \tDEF_PAGEFLAG_NAME(arch_1),\t\t\t\t\t\\\n \tDEF_PAGEFLAG_NAME(reserved),\t\t\t\t\t\\\n-\tDEF_PAGEFLAG_NAME(private),\t\t\t\t\t\\\n+\t__DEF_PAGEFLAG_NAME(folio),\t\t\t\t\t\\\n \tDEF_PAGEFLAG_NAME(private_2),\t\t\t\t\t\\\n \tDEF_PAGEFLAG_NAME(writeback),\t\t\t\t\t\\\n \tDEF_PAGEFLAG_NAME(head),\t\t\t\t\t\\\ndiff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h\nindex 36c3a90f0acca..547a550b81ee1 100644\n--- a/include/trace/events/pagemap.h\n+++ b/include/trace/events/pagemap.h\n@@ -22,7 +22,8 @@\n \t(folio_test_swapcache(folio)\t? PAGEMAP_SWAPCACHE : 0) | \\\n \t(folio_test_swapbacked(folio)\t? PAGEMAP_SWAPBACKED : 0) | \\\n \t(folio_test_mappedtodisk(folio)\t? PAGEMAP_MAPPEDDISK : 0) | \\\n-\t(folio_test_private(folio)\t? PAGEMAP_BUFFERS : 0) \\\n+\t/* data_race() is used to read folio-\u003eprivate locklessly */ \\\n+\t(data_race(folio_test_fs_private(folio))\t? PAGEMAP_BUFFERS : 0) \\\n \t)\n \n TRACE_EVENT(mm_lru_insertion,\ndiff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c\nindex 9fe92161715e0..061b5cb222266 100644\n--- a/kernel/events/ring_buffer.c\n+++ b/kernel/events/ring_buffer.c\n@@ -630,11 +630,10 @@ static struct page *rb_alloc_aux_page(int node, int order)\n \t\t/*\n \t\t * Communicate the allocation size to the driver:\n \t\t * if we managed to secure a high-order allocation,\n-\t\t * set its first page's private to this order;\n-\t\t * !PagePrivate(page) means it's just a normal page.\n+\t\t * set its first page's private to this order, otherwise page's\n+\t\t * private remains zero.\n \t\t */\n \t\tsplit_page(page, order);\n-\t\tSetPagePrivate(page);\n \t\tset_page_private(page, order);\n \t}\n \n@@ -645,7 +644,7 @@ static void rb_free_aux_page(struct perf_buffer *rb, int idx)\n {\n \tstruct page *page = virt_to_page(rb-\u003eaux_pages[idx]);\n \n-\tClearPagePrivate(page);\n+\tset_page_private(page, 0);\n \t__free_page(page);\n }\n \ndiff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c\nindex 8614430ca212a..5a417f8a922ab 100644\n--- a/kernel/vmcore_info.c\n+++ b/kernel/vmcore_info.c\n@@ -216,7 +216,6 @@ static int __init crash_save_vmcoreinfo_init(void)\n \tVMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);\n \tVMCOREINFO_NUMBER(NR_FREE_PAGES);\n \tVMCOREINFO_NUMBER(PG_lru);\n-\tVMCOREINFO_NUMBER(PG_private);\n \tVMCOREINFO_NUMBER(PG_swapcache);\n \tVMCOREINFO_NUMBER(PG_swapbacked);\n #define PAGE_SLAB_MAPCOUNT_VALUE\t(PGTY_slab \u003c\u003c 24)\ndiff --git a/mm/huge_memory.c b/mm/huge_memory.c\nindex d1ce061601bcd..4cb7d9bc47544 100644\n--- a/mm/huge_memory.c\n+++ b/mm/huge_memory.c\n@@ -4831,8 +4831,9 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start,\n \t\t * For folios with private, split_huge_page_to_list_to_order()\n \t\t * will try to drop it before split and then check if the folio\n \t\t * can be split or not. So skip the check here.\n+\t\t * data_race() is used to read folio-\u003eprivate locklessly.\n \t\t */\n-\t\tif (!folio_test_private(folio) \u0026\u0026\n+\t\tif (!data_race(folio_test_fs_private(folio)) \u0026\u0026\n \t\t folio_expected_ref_count(folio) != folio_ref_count(folio))\n \t\t\tgoto next;\n \ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex a69bd463b1aef..08a538739d785 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -1445,10 +1445,10 @@ void add_hugetlb_folio(struct hstate *h, struct folio *folio,\n \t}\n \n \t__folio_set_hugetlb(folio);\n-\tfolio_change_private(folio, NULL);\n+\tfolio-\u003eprivate = NULL;\n \t/*\n-\t * We have to set hugetlb_vmemmap_optimized again as above\n-\t * folio_change_private(folio, NULL) cleared it.\n+\t * The hugetlb flags live in folio-\u003eprivate and are cleared by the above\n+\t * assignment. Restore the hugetlb_vmemmap_optimized flag.\n \t */\n \tfolio_set_hugetlb_vmemmap_optimized(folio);\n \ndiff --git a/mm/migrate.c b/mm/migrate.c\nindex a369d0c95c386..77b89d0909f1f 100644\n--- a/mm/migrate.c\n+++ b/mm/migrate.c\n@@ -835,7 +835,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\n \t */\n \tif (folio_test_swapcache(folio))\n \t\tfolio_clear_swapcache(folio);\n-\tfolio_clear_private(folio);\n \n \t/* page-\u003eprivate contains hugetlb specific flags */\n \tif (!folio_test_hugetlb(folio))\n@@ -1327,7 +1326,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio,\n \t * free the metadata, so the page can be freed.\n \t */\n \tif (!src-\u003emapping) {\n-\t\tif (folio_test_private(src)) {\n+\t\tif (folio_test_fs_private(src)) {\n \t\t\ttry_to_free_buffers(src);\n \t\t\tgoto out;\n \t\t}\ndiff --git a/mm/page-writeback.c b/mm/page-writeback.c\nindex eeab25d6ce364..e7389020d2a56 100644\n--- a/mm/page-writeback.c\n+++ b/mm/page-writeback.c\n@@ -2705,7 +2705,8 @@ bool filemap_dirty_folio(struct address_space *mapping, struct folio *folio)\n \tif (folio_test_set_dirty(folio))\n \t\treturn false;\n \n-\t__folio_mark_dirty(folio, mapping, !folio_test_private(folio));\n+\t/* data_race() is used to read folio-\u003eprivate locklessly */\n+\t__folio_mark_dirty(folio, mapping, !data_race(folio_test_fs_private(folio)));\n \n \tif (mapping-\u003ehost) {\n \t\t/* !PageAnon \u0026\u0026 !swapper_space */\ndiff --git a/mm/vmscan.c b/mm/vmscan.c\nindex 40d3f1b48a74c..47352bafc5b6f 100644\n--- a/mm/vmscan.c\n+++ b/mm/vmscan.c\n@@ -978,7 +978,7 @@ static void folio_check_dirty_writeback(struct folio *folio,\n \t*writeback = folio_test_writeback(folio);\n \n \t/* Verify dirty/writeback state if the filesystem supports it */\n-\tif (!folio_test_private(folio))\n+\tif (!folio_test_fs_private(folio))\n \t\treturn;\n \n \tmapping = folio_mapping(folio);\ndiff --git a/mm/zpdesc.h b/mm/zpdesc.h\nindex b8258dc78548d..4fd81c2e80769 100644\n--- a/mm/zpdesc.h\n+++ b/mm/zpdesc.h\n@@ -26,8 +26,8 @@\n * with memcg_data.\n *\n * Page flags used:\n- * * PG_private identifies the first component page.\n * * PG_locked is used by page migration code.\n+ * The first component page has zpdesc-\u003ezspage-\u003efirst_zpdesc == zpdesc\n */\n struct zpdesc {\n \tunsigned long flags;\ndiff --git a/mm/zsmalloc.c b/mm/zsmalloc.c\nindex 825022a7a328f..24526185fd31f 100644\n--- a/mm/zsmalloc.c\n+++ b/mm/zsmalloc.c\n@@ -290,11 +290,6 @@ struct zs_pool {\n \tatomic_t compaction_in_progress;\n };\n \n-static inline void zpdesc_set_first(struct zpdesc *zpdesc)\n-{\n-\tSetPagePrivate(zpdesc_page(zpdesc));\n-}\n-\n static inline void zpdesc_inc_zone_page_state(struct zpdesc *zpdesc)\n {\n \tinc_zone_page_state(zpdesc_page(zpdesc), NR_ZSPAGES);\n@@ -476,11 +471,6 @@ static void record_obj(unsigned long handle, unsigned long obj)\n \tWRITE_ONCE(*(unsigned long *)handle, obj);\n }\n \n-static inline bool __maybe_unused is_first_zpdesc(struct zpdesc *zpdesc)\n-{\n-\treturn PagePrivate(zpdesc_page(zpdesc));\n-}\n-\n /* Protected by class-\u003elock */\n static inline int get_zspage_inuse(struct zspage *zspage)\n {\n@@ -496,7 +486,8 @@ static struct zpdesc *get_first_zpdesc(struct zspage *zspage)\n {\n \tstruct zpdesc *first_zpdesc = zspage-\u003efirst_zpdesc;\n \n-\tVM_BUG_ON_PAGE(!is_first_zpdesc(first_zpdesc), zpdesc_page(first_zpdesc));\n+\t/* the first zpdesc must point back to this zspage */\n+\tVM_WARN_ON_ONCE_PAGE(first_zpdesc-\u003ezspage != zspage, zpdesc_page(first_zpdesc));\n \treturn first_zpdesc;\n }\n \n@@ -838,7 +829,8 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,\n \tstruct zspage *zspage = get_zspage(zpdesc);\n \n \tif (unlikely(ZsHugePage(zspage))) {\n-\t\tVM_BUG_ON_PAGE(!is_first_zpdesc(zpdesc), zpdesc_page(zpdesc));\n+\t\t/* only first zpdesc holds the handle */\n+\t\tVM_WARN_ON_ONCE_PAGE(zspage-\u003efirst_zpdesc != zpdesc, zpdesc_page(zpdesc));\n \t\thandle = zpdesc-\u003ehandle;\n \t} else\n \t\thandle = *(unsigned long *)obj;\n@@ -853,9 +845,6 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,\n \n static void reset_zpdesc(struct zpdesc *zpdesc)\n {\n-\tstruct page *page = zpdesc_page(zpdesc);\n-\n-\tClearPagePrivate(page);\n \tzpdesc-\u003ezspage = NULL;\n \tzpdesc-\u003enext = NULL;\n \t/* PageZsmalloc is sticky until the page is freed to the buddy. */\n@@ -1006,8 +995,8 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,\n \t * 1. all pages are linked together using zpdesc-\u003enext\n \t * 2. each sub-page point to zspage using zpdesc-\u003ezspage\n \t *\n-\t * we set PG_private to identify the first zpdesc (i.e. no other zpdesc\n-\t * has this flag set).\n+\t * The first zpdesc has its zspage-\u003efirst_zpdesc set to itself, no\n+\t * other zpdesc has this set.\n \t */\n \tfor (i = 0; i \u003c nr_zpdescs; i++) {\n \t\tzpdesc = zpdescs[i];\n@@ -1015,7 +1004,6 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,\n \t\tzpdesc-\u003enext = NULL;\n \t\tif (i == 0) {\n \t\t\tzspage-\u003efirst_zpdesc = zpdesc;\n-\t\t\tzpdesc_set_first(zpdesc);\n \t\t\tif (unlikely(class-\u003eobjs_per_zspage == 1 \u0026\u0026\n \t\t\t\t\tclass-\u003epages_per_zspage == 1))\n \t\t\t\tSetZsHugePage(zspage);\ndiff --git a/tools/mm/page-types.c b/tools/mm/page-types.c\nindex 7fc5a8be5997f..47e4781c5fc38 100644\n--- a/tools/mm/page-types.c\n+++ b/tools/mm/page-types.c\n@@ -73,7 +73,6 @@\n #define KPF_RESERVED\t\t32\n #define KPF_MLOCKED\t\t33\n #define KPF_OWNER_2\t\t34\n-#define KPF_PRIVATE\t\t35\n #define KPF_PRIVATE_2\t\t36\n #define KPF_OWNER_PRIVATE\t37\n #define KPF_ARCH\t\t38\n@@ -131,7 +130,6 @@ static const char * const page_flag_names[] = {\n \t[KPF_RESERVED]\t\t= \"r:reserved\",\n \t[KPF_MLOCKED]\t\t= \"m:mlocked\",\n \t[KPF_OWNER_2]\t\t= \"d:owner_2\",\n-\t[KPF_PRIVATE]\t\t= \"P:private\",\n \t[KPF_PRIVATE_2]\t\t= \"p:private_2\",\n \t[KPF_OWNER_PRIVATE]\t= \"O:owner_private\",\n \t[KPF_ARCH]\t\t= \"h:arch\",\n"
} |
| 2/1 | 2026/09/08 03:52 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"FS_ENCRYPTION",
"ZSMALLOC"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_init",
"erofs_onlinefolio_end",
"fscrypt_encrypt_pagecache_blocks",
"fscrypt_free_bounce_page",
"zs_page_migrate",
"migrate_folio_unmap",
"folio_check_dirty_writeback",
"split_huge_pages_pid"
],
"Reasoning": "The patch series eliminates the PG_private page flag in favor of direct folio-\u003eprivate checks and the new folio_test_fs_private() helper. It also introduces readahead_folio_last() for backward iteration during readahead in EROFS, updates zsmalloc component page identification, adjusts fscrypt bounce page tagging, and updates dirty/writeback checks in memory migration and vmscan. These changes touch core memory management and filesystem folio logic that is directly reachable and warrants fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 0590aa3a35c51ca8c0aad93842ab6f112af2df0c
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Sep 8 03:52:47 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/admin-guide/kdump/vmcoreinfo.rst b/Documentation/admin-guide/kdump/vmcoreinfo.rst
index 7663c610fe901..5f1df6d080508 100644
--- a/Documentation/admin-guide/kdump/vmcoreinfo.rst
+++ b/Documentation/admin-guide/kdump/vmcoreinfo.rst
@@ -325,7 +325,7 @@ NR_FREE_PAGES
On linux-2.6.21 or later, the number of free pages is in
vm_stat[NR_FREE_PAGES]. Used to get the number of free pages.
-PG_lru|PG_private|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask
+PG_lru|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask
--------------------------------------------------------------------------
Page attributes. These flags are used to filter various unnecessary for
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index d3a93eec3945f..dec7816303c6a 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -649,8 +649,8 @@ Writeback.
The first can be used independently to the others. The VM can try to
release clean pages in order to reuse them. To do this it can call
-->release_folio on clean folios with the private
-flag set. Clean pages without PagePrivate and with no external references
+->release_folio on clean folios with folio->private set. Clean pages
+without folio->private set and with no external references
will be released without notice being given to the address_space.
To achieve this functionality, pages need to be placed on an LRU with
@@ -674,7 +674,7 @@ filemap_fdatawait_range, to wait for all writeback to complete.
An address_space handler may attach extra information to a page,
typically using the 'private' field in the 'struct page'. If such
-information is attached, the PG_Private flag should be set. This will
+information is attached, non-NULL 'private' field will
cause various VM routines to make extra calls into the address_space
handler to deal with that data.
diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c
index cbac54cb3a9ec..5849392cf26d5 100644
--- a/arch/x86/events/intel/bts.c
+++ b/arch/x86/events/intel/bts.c
@@ -66,9 +66,6 @@ static struct pmu bts_pmu;
static int buf_nr_pages(struct page *page)
{
- if (!PagePrivate(page))
- return 1;
-
return 1 << page_private(page);
}
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index 5754cd4055628..49349afee6119 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -781,8 +781,7 @@ static int topa_insert_pages(struct pt_buffer *buf, int cpu, gfp_t gfp)
struct page *p;
p = virt_to_page(buf->data_pages[buf->nr_pages]);
- if (PagePrivate(p))
- order = page_private(p);
+ order = page_private(p);
if (topa_table_full(topa)) {
topa = topa_alloc(cpu, gfp);
@@ -1296,8 +1295,7 @@ static int pt_buffer_try_single(struct pt_buffer *buf, int nr_pages)
if (!intel_pt_validate_hw_cap(PT_CAP_single_range_output))
goto out;
- if (PagePrivate(p))
- order = page_private(p);
+ order = page_private(p);
if (1 << order != nr_pages)
goto out;
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index b8325cb09a371..0cf14bc1cff32 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -516,7 +516,7 @@ static void end_bitmap_write(struct bio *bio)
static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
{
- struct buffer_head *bh = page_buffers(page);
+ struct buffer_head *bh = (struct buffer_head *)page_private(page);
while (bh && bh->b_blocknr) {
atomic_inc(&bitmap->pending_writes);
@@ -535,10 +535,10 @@ static void free_buffers(struct page *page)
{
struct buffer_head *bh;
- if (!PagePrivate(page))
+ if (!page_private(page))
return;
- bh = page_buffers(page);
+ bh = (struct buffer_head *)page_private(page);
while (bh) {
struct buffer_head *next = bh->b_this_page;
free_buffer_head(bh);
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index e7f74ea7cd5eb..fdb18348cfdfe 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -182,6 +182,11 @@ static struct page *balloon_retrieve(bool require_lowmem)
__ClearPageOffline(page);
dec_node_page_state(page, NR_BALLOON_PAGES);
+ /*
+ * clear page->private before giving it out, since it might be used to
+ * store xen_page_foreign info.
+ */
+ set_page_private(page, 0);
return page;
}
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 69922be28b54c..993f89f048e21 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -863,10 +863,10 @@ EXPORT_SYMBOL_GPL(gnttab_free_auto_xlat_frames);
int gnttab_pages_set_private(int nr_pages, struct page **pages)
{
+#if BITS_PER_LONG < 64
int i;
for (i = 0; i < nr_pages; i++) {
-#if BITS_PER_LONG < 64
struct xen_page_foreign *foreign;
foreign = kzalloc_obj(*foreign);
@@ -874,9 +874,9 @@ int gnttab_pages_set_private(int nr_pages, struct page **pages)
return -ENOMEM;
set_page_private(pages[i], (unsigned long)foreign);
-#endif
- SetPagePrivate(pages[i]);
}
+#endif
+ /* Data is stored in page->private on 64-bit */
return 0;
}
@@ -1031,12 +1031,11 @@ void gnttab_pages_clear_private(int nr_pages, struct page **pages)
int i;
for (i = 0; i < nr_pages; i++) {
- if (PagePrivate(pages[i])) {
#if BITS_PER_LONG < 64
+ if (page_private(pages[i]))
kfree((void *)page_private(pages[i]));
#endif
- ClearPagePrivate(pages[i]);
- }
+ set_page_private(pages[i], 0);
}
}
EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 657c2cb0f8811..f9b2ec9550481 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -70,9 +70,7 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned
static inline struct ceph_snap_context *page_snap_context(struct page *page)
{
- if (PagePrivate(page))
- return (void *)page->private;
- return NULL;
+ return (void *)page->private;
}
/*
@@ -124,8 +122,8 @@ static bool ceph_dirty_folio(struct address_space *mapping, struct folio *folio)
spin_unlock(&ci->i_ceph_lock);
/*
- * Reference snap context in folio->private. Also set
- * PagePrivate so that we get invalidate_folio callback.
+ * Reference snap context in folio->private. Setting folio->private is
+ * what gets us the invalidate_folio callback.
*/
VM_WARN_ON_FOLIO(folio->private, folio);
folio_attach_private(folio, snapc);
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 5286a124b0d98..aced5c50a4601 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -65,7 +65,6 @@ void fscrypt_free_bounce_page(struct page *bounce_page)
if (!bounce_page)
return;
set_page_private(bounce_page, (unsigned long)NULL);
- ClearPagePrivate(bounce_page);
mempool_free(bounce_page, fscrypt_bounce_page_pool);
}
EXPORT_SYMBOL(fscrypt_free_bounce_page);
@@ -210,7 +209,6 @@ struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,
return ERR_PTR(err);
}
}
- SetPagePrivate(ciphertext_page);
set_page_private(ciphertext_page, (unsigned long)folio);
return ciphertext_page;
}
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 0885b1f2fc925..f71c7b51bb720 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -239,19 +239,23 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
/*
* bit 30: I/O error occurred on this folio
* bit 29: CPU has dirty data in D-cache (needs aliasing handling);
- * bit 0 - 29: remaining parts to complete this folio
+ * bit 0 - 28: remaining parts to complete this folio, biased by 1 so that
+ * ->private stays non-NULL while the folio is attached
*/
#define EROFS_ONLINEFOLIO_EIO 30
#define EROFS_ONLINEFOLIO_DIRTY 29
+#define EROFS_ONLINEFOLIO_COUNT_MASK (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1)
+#define EROFS_ONLINEFOLIO_BIAS 1
void erofs_onlinefolio_init(struct folio *folio)
{
union {
atomic_t o;
void *v;
- } u = { .o = ATOMIC_INIT(1) };
+ } u = { .o = ATOMIC_INIT(1 + EROFS_ONLINEFOLIO_BIAS) };
- folio->private = u.v; /* valid only if file-backed folio is locked */
+ /* valid only if file-backed folio is locked */
+ folio_attach_private(folio, u.v);
}
void erofs_onlinefolio_split(struct folio *folio)
@@ -265,14 +269,14 @@ void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)
do {
orig = atomic_read((atomic_t *)&folio->private);
- DBG_BUGON(orig <= 0);
+ DBG_BUGON((orig & EROFS_ONLINEFOLIO_COUNT_MASK) <= EROFS_ONLINEFOLIO_BIAS);
v = dirty << EROFS_ONLINEFOLIO_DIRTY;
v |= (orig - 1) | (!!err << EROFS_ONLINEFOLIO_EIO);
} while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
- if (v & (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1))
+ if ((v & EROFS_ONLINEFOLIO_COUNT_MASK) != EROFS_ONLINEFOLIO_BIAS)
return;
- folio->private = 0;
+ folio_detach_private(folio);
if (v & BIT(EROFS_ONLINEFOLIO_DIRTY))
flush_dcache_folio(folio);
folio_end_read(folio, !(v & BIT(EROFS_ONLINEFOLIO_EIO)));
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index e1e25ca0d1904..78fd7d980e957 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1898,21 +1898,14 @@ static void z_erofs_readahead(struct readahead_control *rac)
struct inode *realinode = erofs_real_inode(sharedinode, &need_iput);
Z_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, readahead_pos(rac));
unsigned int nrpages = readahead_count(rac);
- struct folio *head = NULL, *folio;
+ struct folio *folio;
int err;
trace_erofs_readahead(realinode, readahead_index(rac), nrpages, false);
z_erofs_pcluster_readmore(&f, rac, true);
- while ((folio = readahead_folio(rac))) {
- folio->private = head;
- head = folio;
- }
-
- /* traverse in reverse order for best metadata I/O performance */
- while (head) {
- folio = head;
- head = folio_get_private(folio);
+ /* traverse from last to first for best metadata I/O performance */
+ while ((folio = readahead_folio_last(rac))) {
err = z_erofs_scan_folio(&f, folio, true);
if (err && err != -EINTR)
erofs_err(realinode->i_sb, "readahead error at folio %lu @ nid %llu",
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9940a6cecf1a2..2f7ab5888b078 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2691,7 +2691,7 @@ static inline bool folio_test_f2fs_##name(const struct folio *folio) \
} \
static inline bool page_private_##name(struct page *page) \
{ \
- return PagePrivate(page) && \
+ return page_private(page) && \
test_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)) && \
test_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
@@ -2710,9 +2710,9 @@ static inline void folio_set_f2fs_##name(struct folio *folio) \
} \
static inline void set_page_private_##name(struct page *page) \
{ \
- if (!PagePrivate(page)) \
- attach_page_private(page, (void *)0); \
- set_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)); \
+ if (!page_private(page)) \
+ attach_page_private(page, \
+ (void *)BIT(PAGE_PRIVATE_NOT_POINTER)); \
set_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index e1bdd10b35f10..38f830a6467c9 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -484,7 +484,7 @@ static int nfs_write_end(const struct kiocb *iocb,
* Partially or wholly invalidate a page
* - Release the private state associated with a page if undergoing complete
* page invalidation
- * - Called if either PG_private or PG_fscache is set on the page
+ * - Called if either folio->private or PG_fscache is set on the page
* - Caller holds page lock
*/
static void nfs_invalidate_folio(struct folio *folio, size_t offset,
@@ -555,7 +555,7 @@ static void nfs_check_dirty_writeback(struct folio *folio,
* Attempt to clear the private state associated with a page when an error
* occurs that requires the cached contents of an inode to be written back or
* destroyed
- * - Called if either PG_private or fscache is set on the page
+ * - Called if either page->private or fscache is set on the page
* - Caller holds page lock
* - Return 0 if successful, -error otherwise
*/
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 623e7ef1f73d5..b6967b5286691 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -717,7 +717,6 @@ static void nfs_inode_add_request(struct nfs_page *req)
nfs_lock_request(req);
spin_lock(&mapping->i_private_lock);
set_bit(PG_MAPPED, &req->wb_flags);
- folio_set_private(folio);
folio->private = req;
spin_unlock(&mapping->i_private_lock);
atomic_long_inc(&nfsi->nrequests);
@@ -745,7 +744,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
spin_lock(&mapping->i_private_lock);
folio->private = NULL;
- folio_clear_private(folio);
clear_bit(PG_MAPPED, &req->wb_head->wb_flags);
spin_unlock(&mapping->i_private_lock);
diff --git a/fs/proc/page.c b/fs/proc/page.c
index 260772b20bd99..f90e1030825e9 100644
--- a/fs/proc/page.c
+++ b/fs/proc/page.c
@@ -232,7 +232,6 @@ u64 stable_page_flags(const struct page *page)
u |= kpf_copy_bit(k, KPF_RESERVED, PG_reserved);
u |= kpf_copy_bit(k, KPF_OWNER_2, PG_owner_2);
- u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private);
u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2);
u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1);
u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1);
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index e73c28b12f97f..aa0298ce451ef 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -12,14 +12,14 @@
* This file implements VFS file and inode operations for regular files, device
* nodes and symlinks as well as address space operations.
*
- * UBIFS uses 2 page flags: @PG_private and @PG_checked. @PG_private is set if
+ * UBIFS uses folio->private and page flag @PG_checked. folio->private is set if
* the page is dirty and is used for optimization purposes - dirty pages are
- * not budgeted so the flag shows that 'ubifs_write_end()' should not release
+ * not budgeted so it shows that 'ubifs_write_end()' should not release
* the budget for this page. The @PG_checked flag is set if full budgeting is
* required for the page e.g., when it corresponds to a file hole or it is
* beyond the file size. The budgeting is done in 'ubifs_write_begin()', because
* it is OK to fail in this function, and the budget is released in
- * 'ubifs_write_end()'. So the @PG_private and @PG_checked flags carry
+ * 'ubifs_write_end()'. So the folio->private and the @PG_checked flag carry
* information about how the page was budgeted, to make it possible to release
* the budget properly.
*
@@ -1509,7 +1509,7 @@ static vm_fault_t ubifs_vm_page_mkwrite(struct vm_fault *vmf)
*
* At the moment we do not know whether the folio is dirty or not, so we
* assume that it is not and budget for a new folio. We could look at
- * the @PG_private flag and figure this out, but we may race with write
+ * folio->private and figure this out, but we may race with write
* back and the folio state may change by the time we lock it, so this
* would need additional care. We do not bother with this at the
* moment, although it might be good idea to do. Instead, we allocate
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index fd2c7115c0542..e47fbd0387b69 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -175,12 +175,6 @@ static inline unsigned long bh_offset(const struct buffer_head *bh)
return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1);
}
-/* If we *know* page->private refers to buffer_heads */
-#define page_buffers(page) \
- ({ \
- BUG_ON(!PagePrivate(page)); \
- ((struct buffer_head *)page_private(page)); \
- })
#define folio_buffers(folio) folio_get_private(folio)
void buffer_check_dirty_writeback(struct folio *folio,
diff --git a/include/linux/kernel-page-flags.h b/include/linux/kernel-page-flags.h
index 196778a087c4d..fe5ab6e50bd70 100644
--- a/include/linux/kernel-page-flags.h
+++ b/include/linux/kernel-page-flags.h
@@ -11,7 +11,6 @@
#define KPF_RESERVED 32
#define KPF_MLOCKED 33
#define KPF_OWNER_2 34
-#define KPF_PRIVATE 35
#define KPF_PRIVATE_2 36
#define KPF_OWNER_PRIVATE 37
#define KPF_ARCH 38
diff --git a/include/linux/mm.h b/include/linux/mm.h
index c49ef99b4413b..0ceffe4f9ca02 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2048,20 +2048,21 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
*
* A pagecache page contains an opaque `private' member, which belongs to the
* page's address_space. Usually, this is the address of a circular list of
- * the page's disk buffers. PG_private must be set to tell the VM to call
- * into the filesystem to release these pages.
+ * the page's disk buffers. It tells the VM to call into the filesystem to
+ * release these pages.
*
* A folio may belong to an inode's memory mapping. In this case,
* folio->mapping points to the inode, and folio->index is the file
* offset of the folio, in units of PAGE_SIZE.
*
- * If pagecache pages are not associated with an inode, they are said to be
- * anonymous pages. These may become associated with the swapcache, and in that
- * case PG_swapcache is set, and page->private is an offset into the swapcache.
+ * If pagecache folios are not associated with an inode, they are said to be
+ * anonymous folios. These may become associated with the swapcache, and in that
+ * case PG_swapcache is set, and folio->private is an offset into the swapcache.
*
* In either case (swapcache or inode backed), the pagecache itself holds one
- * reference to the page. Setting PG_private should also increment the
- * refcount. The each user mapping also has a reference to the page.
+ * reference to the folio. Attaching filesystem private data via
+ * folio_attach_private() also increments the refcount. Each user mapping also
+ * has a reference to the folio.
*
* The pagecache pages are stored in a per-mapping radix tree, which is
* rooted at mapping->i_pages, and indexed by offset.
@@ -3004,9 +3005,9 @@ static inline bool folio_maybe_mapped_shared(struct folio *folio)
* @folio: the folio
*
* Calculate the expected folio refcount, taking references from the pagecache,
- * swapcache, PG_private and page table mappings into account. Useful in
- * combination with folio_ref_count() to detect unexpected references (e.g.,
- * GUP or other temporary references).
+ * swapcache, private data (folio->private != NULL) and page table mappings into
+ * account. Useful in combination with folio_ref_count() to detect unexpected
+ * references (e.g., GUP or other temporary references).
*
* Does currently not consider references from the LRU cache. If the folio
* was isolated from the LRU (which is the case during migration or split),
@@ -3044,10 +3045,16 @@ static inline int folio_expected_ref_count(const struct folio *folio)
ref_count += folio_test_swapcache(folio) << order;
if (!folio_test_anon(folio)) {
- /* One reference per page from the pagecache. */
- ref_count += !!folio->mapping << order;
- /* One reference from PG_private. */
- ref_count += folio_test_private(folio);
+ /*
+ * One reference per page from the pagecache.
+ * Use data_race() since folio might not be locked.
+ */
+ ref_count += !!data_race(folio->mapping) << order;
+ /*
+ * One reference from filesystem private data.
+ * Use data_race() since folio might not be locked.
+ */
+ ref_count += data_race(folio_test_fs_private(folio));
}
/* One reference per page table mapping. */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 5413bd10fff2c..9d6c945ba4104 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -108,7 +108,7 @@ struct page {
};
/**
* @private: Mapping-private opaque data.
- * Usually used for buffer_heads if PagePrivate.
+ * Usually used for buffer_heads.
* Used for swp_entry_t if swapcache flag set.
* Indicates order in the buddy system if PageBuddy
* or on pcp_llist.
@@ -675,7 +675,7 @@ static inline void ptdesc_pmd_pts_init(struct ptdesc *ptdesc)
#define STRUCT_PAGE_MAX_SHIFT (order_base_2(sizeof(struct page)))
/*
- * page_private can be used on tail pages. However, PagePrivate is only
+ * page_private can be used on tail pages. However, it is only
* checked by the VM on the head page. So page_private on the tail pages
* should be used for data that's ancillary to the head page (eg attaching
* buffer heads to tail pages after attaching buffer heads to the head page)
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 86dd0470da117..7b7783c0a5216 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -44,10 +44,6 @@
* Consequently, PG_reserved for a page mapped into user space can indicate
* the zero page, the vDSO, MMIO pages or device memory.
*
- * The PG_private bitflag is set on pagecache pages if they contain filesystem
- * specific data (which is normally at page->private). It can be used by
- * private allocations for its own usage.
- *
* During initiation of disk I/O, PG_locked is set. This bit is set before I/O
* and cleared when writeback _starts_ or when read _completes_. PG_writeback
* is set before writeback starts and cleared when it finishes.
@@ -105,7 +101,7 @@ enum pageflags {
PG_owner_2, /* Owner use. If pagecache, fs may use */
PG_arch_1,
PG_reserved,
- PG_private, /* If pagecache, has fs-private data */
+ __PG_folio, /* Do not use: reserved for folio identification */
PG_private_2, /* If pagecache, has fs aux data */
PG_reclaim, /* To be reclaimed asap */
PG_swapbacked, /* Page is backed by RAM/swap */
@@ -576,9 +572,14 @@ FOLIO_FLAG(swapbacked, FOLIO_HEAD_PAGE)
/*
* Private page markings that may be used by the filesystem that owns the page
* for its own purposes.
- * - PG_private and PG_private_2 cause release_folio() and co to be invoked
+ * - folio->private and PG_private_2 cause release_folio() and co to be invoked
*/
-PAGEFLAG(Private, private, PF_ANY)
+
+static __always_inline bool folio_test_private(const struct folio *folio)
+{
+ return folio->private;
+}
+
FOLIO_FLAG(private_2, FOLIO_HEAD_PAGE)
/* owner_2 can be set on tail pages for anon memory */
@@ -1170,7 +1171,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
*/
#define PAGE_FLAGS_CHECK_AT_FREE \
(1UL << PG_lru | 1UL << PG_locked | \
- 1UL << PG_private | 1UL << PG_private_2 | \
+ 1UL << PG_private_2 | \
1UL << PG_writeback | 1UL << PG_reserved | \
1UL << PG_active | \
1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
@@ -1194,8 +1195,28 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
(0xffUL /* order */ | 1UL << PG_has_hwpoisoned | \
1UL << PG_large_rmappable | 1UL << PG_partially_mapped)
-#define PAGE_FLAGS_PRIVATE \
- (1UL << PG_private | 1UL << PG_private_2)
+/**
+ * folio_test_fs_private - check if the folio has filesystem private data
+ * @folio: The folio to check.
+ *
+ * Use this in code that may encounter swapcache or hugetlb folios but only
+ * wants to detect filesystem private data. Swapcache stores swp_entry_t in
+ * folio->swap, a union with folio->private, and hugetlb stores its own flags
+ * in folio->private; both are excluded.
+ *
+ * NOTE: For swapcache, folio->swap.val PG_swapcache are not set as a whole,
+ * so folio_test_swapcache() is not reliable to exclude swapcache.
+ * Use folio_test_swapbacked() instead, since it remains set when a folio is
+ * added to/removed from swapcache.
+ *
+ * Return: true if folio->private is set and the folio is neither swapcache
+ * nor hugetlb.
+ */
+static inline bool folio_test_fs_private(const struct folio *folio)
+{
+ return folio_test_private(folio) && !folio_test_swapbacked(folio) &&
+ !folio_test_hugetlb(folio);
+}
/**
* folio_has_private - Determine if folio has private stuff
* @folio: The folio to be checked
@@ -1205,7 +1226,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
*/
static inline int folio_has_private(const struct folio *folio)
{
- return !!(folio->flags.f & PAGE_FLAGS_PRIVATE);
+ return folio_test_fs_private(folio) || folio_test_private_2(folio);
}
#undef PF_ANY
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 939f3a5e973f6..cc5c2cca814bc 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -594,7 +594,6 @@ static inline void folio_attach_private(struct folio *folio, void *data)
{
folio_get(folio);
folio->private = data;
- folio_set_private(folio);
}
/**
@@ -629,9 +628,8 @@ static inline void *folio_detach_private(struct folio *folio)
{
void *data = folio_get_private(folio);
- if (!folio_test_private(folio))
+ if (!data)
return NULL;
- folio_clear_private(folio);
folio->private = NULL;
folio_put(folio);
@@ -1415,6 +1413,7 @@ struct readahead_control {
bool dropbehind;
bool _workingset;
unsigned long _pflags;
+ bool _forward;
};
#define DEFINE_READAHEAD(ractl, f, r, m, i) \
@@ -1479,18 +1478,25 @@ void page_cache_async_readahead(struct address_space *mapping,
page_cache_async_ra(&ractl, folio, req_count);
}
+static inline void __readahead_advance(struct readahead_control *rac)
+{
+ if (rac->_forward)
+ rac->_index += rac->_batch_count;
+
+ rac->_nr_pages -= rac->_batch_count;
+ rac->_batch_count = 0;
+}
+
static inline struct folio *__readahead_folio(struct readahead_control *ractl)
{
struct folio *folio;
BUG_ON(ractl->_batch_count > ractl->_nr_pages);
- ractl->_nr_pages -= ractl->_batch_count;
- ractl->_index += ractl->_batch_count;
+ __readahead_advance(ractl);
+ ractl->_forward = true;
- if (!ractl->_nr_pages) {
- ractl->_batch_count = 0;
+ if (!ractl->_nr_pages)
return NULL;
- }
folio = xa_load(&ractl->mapping->i_pages, ractl->_index);
VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
@@ -1516,6 +1522,39 @@ static inline struct folio *readahead_folio(struct readahead_control *ractl)
return folio;
}
+/**
+ * readahead_folio_last - Get the next folio to read, from the tail.
+ * @ractl: The current readahead request.
+ *
+ * Like readahead_folio(), but walks the range back-to-front. The folio is
+ * returned locked with its refcount dropped; the caller unlocks it once I/O
+ * completes. Compound folios are returned once, at their head index.
+ *
+ * Context: The folio is locked.
+ * Return: A pointer to the next folio, or %NULL when done.
+ */
+static inline struct folio *readahead_folio_last(struct readahead_control *ractl)
+{
+ struct folio *folio;
+
+ /* Drop the previously returned batch from the remaining range. */
+ __readahead_advance(ractl);
+ ractl->_forward = false;
+
+ if (!ractl->_nr_pages)
+ return NULL;
+
+ /* xa_load() follows sibling entries, so a tail index returns the head */
+ folio = xa_load(&ractl->mapping->i_pages,
+ ractl->_index + ractl->_nr_pages - 1);
+ VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+
+ ractl->_batch_count = folio_nr_pages(folio);
+
+ folio_put(folio);
+ return folio;
+}
+
static inline unsigned int __readahead_batch(struct readahead_control *rac,
struct page **array, unsigned int array_sz)
{
@@ -1524,9 +1563,8 @@ static inline unsigned int __readahead_batch(struct readahead_control *rac,
struct folio *folio;
BUG_ON(rac->_batch_count > rac->_nr_pages);
- rac->_nr_pages -= rac->_batch_count;
- rac->_index += rac->_batch_count;
- rac->_batch_count = 0;
+ __readahead_advance(rac);
+ rac->_forward = true;
xas_set(&xas, rac->_index);
rcu_read_lock();
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index ef9aa388b84f7..38a2d30230443 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -130,6 +130,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);
#endif
#define DEF_PAGEFLAG_NAME(_name) { 1UL << PG_##_name, __stringify(_name) }
+#define __DEF_PAGEFLAG_NAME(_name) { 1UL << __PG_##_name, __stringify(_name) }
#define __def_pageflag_names \
DEF_PAGEFLAG_NAME(locked), \
@@ -144,7 +145,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);
DEF_PAGEFLAG_NAME(owner_2), \
DEF_PAGEFLAG_NAME(arch_1), \
DEF_PAGEFLAG_NAME(reserved), \
- DEF_PAGEFLAG_NAME(private), \
+ __DEF_PAGEFLAG_NAME(folio), \
DEF_PAGEFLAG_NAME(private_2), \
DEF_PAGEFLAG_NAME(writeback), \
DEF_PAGEFLAG_NAME(head), \
diff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h
index 36c3a90f0acca..547a550b81ee1 100644
--- a/include/trace/events/pagemap.h
+++ b/include/trace/events/pagemap.h
@@ -22,7 +22,8 @@
(folio_test_swapcache(folio) ? PAGEMAP_SWAPCACHE : 0) | \
(folio_test_swapbacked(folio) ? PAGEMAP_SWAPBACKED : 0) | \
(folio_test_mappedtodisk(folio) ? PAGEMAP_MAPPEDDISK : 0) | \
- (folio_test_private(folio) ? PAGEMAP_BUFFERS : 0) \
+ /* data_race() is used to read folio->private locklessly */ \
+ (data_race(folio_test_fs_private(folio)) ? PAGEMAP_BUFFERS : 0) \
)
TRACE_EVENT(mm_lru_insertion,
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 9fe92161715e0..061b5cb222266 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -630,11 +630,10 @@ static struct page *rb_alloc_aux_page(int node, int order)
/*
* Communicate the allocation size to the driver:
* if we managed to secure a high-order allocation,
- * set its first page's private to this order;
- * !PagePrivate(page) means it's just a normal page.
+ * set its first page's private to this order, otherwise page's
+ * private remains zero.
*/
split_page(page, order);
- SetPagePrivate(page);
set_page_private(page, order);
}
@@ -645,7 +644,7 @@ static void rb_free_aux_page(struct perf_buffer *rb, int idx)
{
struct page *page = virt_to_page(rb->aux_pages[idx]);
- ClearPagePrivate(page);
+ set_page_private(page, 0);
__free_page(page);
}
diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c
index 8614430ca212a..5a417f8a922ab 100644
--- a/kernel/vmcore_info.c
+++ b/kernel/vmcore_info.c
@@ -216,7 +216,6 @@ static int __init crash_save_vmcoreinfo_init(void)
VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);
VMCOREINFO_NUMBER(NR_FREE_PAGES);
VMCOREINFO_NUMBER(PG_lru);
- VMCOREINFO_NUMBER(PG_private);
VMCOREINFO_NUMBER(PG_swapcache);
VMCOREINFO_NUMBER(PG_swapbacked);
#define PAGE_SLAB_MAPCOUNT_VALUE (PGTY_slab << 24)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index d1ce061601bcd..4cb7d9bc47544 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4831,8 +4831,9 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start,
* For folios with private, split_huge_page_to_list_to_order()
* will try to drop it before split and then check if the folio
* can be split or not. So skip the check here.
+ * data_race() is used to read folio->private locklessly.
*/
- if (!folio_test_private(folio) &&
+ if (!data_race(folio_test_fs_private(folio)) &&
folio_expected_ref_count(folio) != folio_ref_count(folio))
goto next;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index a69bd463b1aef..08a538739d785 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1445,10 +1445,10 @@ void add_hugetlb_folio(struct hstate *h, struct folio *folio,
}
__folio_set_hugetlb(folio);
- folio_change_private(folio, NULL);
+ folio->private = NULL;
/*
- * We have to set hugetlb_vmemmap_optimized again as above
- * folio_change_private(folio, NULL) cleared it.
+ * The hugetlb flags live in folio->private and are cleared by the above
+ * assignment. Restore the hugetlb_vmemmap_optimized flag.
*/
folio_set_hugetlb_vmemmap_optimized(folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index a369d0c95c386..77b89d0909f1f 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -835,7 +835,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
*/
if (folio_test_swapcache(folio))
folio_clear_swapcache(folio);
- folio_clear_private(folio);
/* page->private contains hugetlb specific flags */
if (!folio_test_hugetlb(folio))
@@ -1327,7 +1326,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio,
* free the metadata, so the page can be freed.
*/
if (!src->mapping) {
- if (folio_test_private(src)) {
+ if (folio_test_fs_private(src)) {
try_to_free_buffers(src);
goto out;
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index eeab25d6ce364..e7389020d2a56 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2705,7 +2705,8 @@ bool filemap_dirty_folio(struct address_space *mapping, struct folio *folio)
if (folio_test_set_dirty(folio))
return false;
- __folio_mark_dirty(folio, mapping, !folio_test_private(folio));
+ /* data_race() is used to read folio->private locklessly */
+ __folio_mark_dirty(folio, mapping, !data_race(folio_test_fs_private(folio)));
if (mapping->host) {
/* !PageAnon && !swapper_space */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 40d3f1b48a74c..47352bafc5b6f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -978,7 +978,7 @@ static void folio_check_dirty_writeback(struct folio *folio,
*writeback = folio_test_writeback(folio);
/* Verify dirty/writeback state if the filesystem supports it */
- if (!folio_test_private(folio))
+ if (!folio_test_fs_private(folio))
return;
mapping = folio_mapping(folio);
diff --git a/mm/zpdesc.h b/mm/zpdesc.h
index b8258dc78548d..4fd81c2e80769 100644
--- a/mm/zpdesc.h
+++ b/mm/zpdesc.h
@@ -26,8 +26,8 @@
* with memcg_data.
*
* Page flags used:
- * * PG_private identifies the first component page.
* * PG_locked is used by page migration code.
+ * The first component page has zpdesc->zspage->first_zpdesc == zpdesc
*/
struct zpdesc {
unsigned long flags;
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 825022a7a328f..24526185fd31f 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -290,11 +290,6 @@ struct zs_pool {
atomic_t compaction_in_progress;
};
-static inline void zpdesc_set_first(struct zpdesc *zpdesc)
-{
- SetPagePrivate(zpdesc_page(zpdesc));
-}
-
static inline void zpdesc_inc_zone_page_state(struct zpdesc *zpdesc)
{
inc_zone_page_state(zpdesc_page(zpdesc), NR_ZSPAGES);
@@ -476,11 +471,6 @@ static void record_obj(unsigned long handle, unsigned long obj)
WRITE_ONCE(*(unsigned long *)handle, obj);
}
-static inline bool __maybe_unused is_first_zpdesc(struct zpdesc *zpdesc)
-{
- return PagePrivate(zpdesc_page(zpdesc));
-}
-
/* Protected by class->lock */
static inline int get_zspage_inuse(struct zspage *zspage)
{
@@ -496,7 +486,8 @@ static struct zpdesc *get_first_zpdesc(struct zspage *zspage)
{
struct zpdesc *first_zpdesc = zspage->first_zpdesc;
- VM_BUG_ON_PAGE(!is_first_zpdesc(first_zpdesc), zpdesc_page(first_zpdesc));
+ /* the first zpdesc must point back to this zspage */
+ VM_WARN_ON_ONCE_PAGE(first_zpdesc->zspage != zspage, zpdesc_page(first_zpdesc));
return first_zpdesc;
}
@@ -838,7 +829,8 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,
struct zspage *zspage = get_zspage(zpdesc);
if (unlikely(ZsHugePage(zspage))) {
- VM_BUG_ON_PAGE(!is_first_zpdesc(zpdesc), zpdesc_page(zpdesc));
+ /* only first zpdesc holds the handle */
+ VM_WARN_ON_ONCE_PAGE(zspage->first_zpdesc != zpdesc, zpdesc_page(zpdesc));
handle = zpdesc->handle;
} else
handle = *(unsigned long *)obj;
@@ -853,9 +845,6 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,
static void reset_zpdesc(struct zpdesc *zpdesc)
{
- struct page *page = zpdesc_page(zpdesc);
-
- ClearPagePrivate(page);
zpdesc->zspage = NULL;
zpdesc->next = NULL;
/* PageZsmalloc is sticky until the page is freed to the buddy. */
@@ -1006,8 +995,8 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,
* 1. all pages are linked together using zpdesc->next
* 2. each sub-page point to zspage using zpdesc->zspage
*
- * we set PG_private to identify the first zpdesc (i.e. no other zpdesc
- * has this flag set).
+ * The first zpdesc has its zspage->first_zpdesc set to itself, no
+ * other zpdesc has this set.
*/
for (i = 0; i < nr_zpdescs; i++) {
zpdesc = zpdescs[i];
@@ -1015,7 +1004,6 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,
zpdesc->next = NULL;
if (i == 0) {
zspage->first_zpdesc = zpdesc;
- zpdesc_set_first(zpdesc);
if (unlikely(class->objs_per_zspage == 1 &&
class->pages_per_zspage == 1))
SetZsHugePage(zspage);
diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c
index 7fc5a8be5997f..47e4781c5fc38 100644
--- a/tools/mm/page-types.c
+++ b/tools/mm/page-types.c
@@ -73,7 +73,6 @@
#define KPF_RESERVED 32
#define KPF_MLOCKED 33
#define KPF_OWNER_2 34
-#define KPF_PRIVATE 35
#define KPF_PRIVATE_2 36
#define KPF_OWNER_PRIVATE 37
#define KPF_ARCH 38
@@ -131,7 +130,6 @@ static const char * const page_flag_names[] = {
[KPF_RESERVED] = "r:reserved",
[KPF_MLOCKED] = "m:mlocked",
[KPF_OWNER_2] = "d:owner_2",
- [KPF_PRIVATE] = "P:private",
[KPF_PRIVATE_2] = "p:private_2",
[KPF_OWNER_PRIVATE] = "O:owner_private",
[KPF_ARCH] = "h:arch",
|
| 3/2 | 2026/09/08 03:52 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 17156 output: 20 thoughts: 713 Thoughts: **Initial Assessment of Patch Series: A Deep Dive into Memory Management and Filesystem Changes** Okay, let's break this down. My initial assessment screams "significant." This patch series targets amd64, which is expected. The core of the change seems to be the removal or renaming of the `PG_private` page flag, migrating towards a `__PG_folio` (or outright removal) and utilizing `folio->private` or `folio_test_fs_private(folio)` instead. Seems like a move to Folios, from Page. The scope of this refactor is massive. It's touching a huge swathe of the kernel: * **Memory Management Core:** This goes straight into `mm`, `hugetlb`, `zsmalloc`, migrate, page-writeback, vmscan, all core MM subsystems! * **Filesystems Affected:** EROFS, F2FS, Ceph, and others are directly impacted. This includes modifications to crucial readahead logic (specifically erofs readahead and related folio changes). Buffer heads are touched as well. * **Architectural Implications:** Some x86-specific events (BTS, PT) are included. This thing has a large footprint, and it touches enough core subsystems that I'm immediately marking `WorthFuzzing = true`. No question about it. Now, let's consult the triage rules. My gut feeling is corroborated by the rules! This falls squarely within: * "Core Subsystems: Any logic modifications in memory management (mm/)..." - Check! * "Refactorings and Code Cleanups: Any restructuring of reachable data structures, helper abstractions, or algorithm flows." - Double check! I am not blindly trusting any refactoring claims! Given the nature of the changes, modified assertions MUST be tested. My next step is to examine the "FocusSymbols" and "EnableConfigs" requirements carefully. I'll need to scrutinize the modified functions and new helper functions. I'll probably employ some greps or other tools to aid in this. This could be complex. |
| 4/2 | 2026/09/08 03:52 | tool | grepper |
0mArgs:{
"Expression": "readahead_folio_last"
}Results: {
"Output": "fs/erofs/zdata.c=1894=static void z_erofs_readahead(struct readahead_control *rac)\n--\nfs/erofs/zdata.c-1907-\t/* traverse from last to first for best metadata I/O performance */\nfs/erofs/zdata.c:1908:\twhile ((folio = readahead_folio_last(rac))) {\nfs/erofs/zdata.c-1909-\t\terr = z_erofs_scan_folio(\u0026f, folio, true);\n--\ninclude/linux/pagemap.h=1516=static inline struct folio *readahead_folio(struct readahead_control *ractl)\n--\ninclude/linux/pagemap.h-1525-/**\ninclude/linux/pagemap.h:1526: * readahead_folio_last - Get the next folio to read, from the tail.\ninclude/linux/pagemap.h-1527- * @ractl: The current readahead request.\n--\ninclude/linux/pagemap.h-1535- */\ninclude/linux/pagemap.h:1536:static inline struct folio *readahead_folio_last(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1537-{\n"
} |
| 5/2 | 2026/09/08 03:52 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 18675 output: 46 thoughts: 1014 Thoughts: **Analyzing Code Modifications and Identifying Focus Symbols** Okay, let's break this down. My initial observation is that `readahead_folio_last` is an inline function, and its non-static caller is `z_erofs_readahead` within `fs/erofs/zdata.c`. That's a starting point, but I need a broader view of the changes. I need to see the scope of the modifications, so let's check which functions were modified in .c files. I've got a comprehensive list here: `arch/x86/events/intel/bts.c`, `arch/x86/events/intel/pt.c`, `drivers/md/md-bitmap.c`, `drivers/xen/balloon.c`, `drivers/xen/grant-table.c`, `fs/ceph/addr.c`, `fs/crypto/crypto.c`, `fs/erofs/data.c`, `fs/erofs/zdata.c`, `fs/f2fs/f2fs.h`, `fs/nfs/file.c`, `fs/nfs/write.c`, `fs/proc/page.c`, `fs/ubifs/file.c`, `kernel/events/ring_buffer.c`, `kernel/vmcore_info.c`, `mm/huge_memory.c`, `mm/hugetlb.c`, `mm/migrate.c`, `mm/page-writeback.c`, `mm/vmscan.c`, and `mm/zsmalloc.c`. Now, the trick is to identify the real "focus symbols" within this list, keeping in mind the rule to avoid ubiquitous lifecycle hot-paths and target specific feature logic and entrypoints. I need to pinpoint the functions that are either core to the logic being modified or are key entry points for new features. Looking at `fs/erofs`, I see the changes touch `erofs_onlinefolio_init`, `erofs_onlinefolio_end`, and `z_erofs_readahead`. These seem relevant to how erofs handles online folios, which is something to look more closely at. Then, there's `mm/zsmalloc.c`. This is intriguing because I see several functions listed there: `get_first_zpdesc`, `obj_allocated`, `reset_zpdesc`, and `create_page_chain`. All of these are static, so I need to find the specific callers or entry points that use them. It's likely that zsmalloc is modified, and these will be the key functions to understand what changed. I'll need to dig deeper into the code of `zsmalloc.c`. |
| 6/2 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "create_page_chain|zs_malloc|zs_map_object|zs_free",
"PathPrefix": "mm/zsmalloc.c"
}Results: {
"Output": "mm/zsmalloc.c-24- *\nmm/zsmalloc.c:25: * When ZS_OBJ_CLASS_BITS \u003e 0, zs_free() skips pool-\u003elock; it picks\nmm/zsmalloc.c-26- * the size_class from obj's encoded class_idx and serializes against\n--\nmm/zsmalloc.c-105- *\nmm/zsmalloc.c:106: * Encoding class_idx into obj lets zs_free() locate the size_class\nmm/zsmalloc.c-107- * without holding pool-\u003elock; class_idx is invariant across page\n--\nmm/zsmalloc.c=460=static void cache_free_zspage(struct zspage *zspage)\n--\nmm/zsmalloc.c-465-/*\nmm/zsmalloc.c:466: * Pairs with READ_ONCE() in handle_to_obj(): zs_free() may read the\nmm/zsmalloc.c-467- * handle locklessly, so prevent store tearing here.\n--\nmm/zsmalloc.c=853=static int trylock_zspage(struct zspage *zspage)\n--\nmm/zsmalloc.c-878- * to the buddy allocator. Caller owns all zpdesc locks and has\nmm/zsmalloc.c:879: * removed the zspage from its class list. Used by zs_free() outside\nmm/zsmalloc.c-880- * class-\u003elock so the buddy-side work does not stall the class.\n--\nmm/zsmalloc.c=920=static void free_zspage(struct zs_pool *pool, struct size_class *class,\n--\nmm/zsmalloc.c-926-\t/*\nmm/zsmalloc.c:927:\t * Since zs_free couldn't be sleepable, this function cannot call\nmm/zsmalloc.c-928-\t * lock_page. The page locks trylock_zspage got will be released\n--\nmm/zsmalloc.c=941=static void init_zspage(struct size_class *class, struct zspage *zspage)\n--\nmm/zsmalloc.c-984-\nmm/zsmalloc.c:985:static void create_page_chain(struct size_class *class, struct zspage *zspage,\nmm/zsmalloc.c-986-\t\t\t\tstruct zpdesc *zpdescs[])\n--\nmm/zsmalloc.c=1020=static struct zspage *alloc_zspage(struct zs_pool *pool,\n--\nmm/zsmalloc.c-1056-\nmm/zsmalloc.c:1057:\tcreate_page_chain(class, zspage, zpdescs);\nmm/zsmalloc.c-1058-\tinit_zspage(class, zspage);\n--\nmm/zsmalloc.c=1332=static unsigned long obj_malloc(struct zs_pool *pool,\n--\nmm/zsmalloc.c-1374-/**\nmm/zsmalloc.c:1375: * zs_malloc - Allocate block of given size from pool.\nmm/zsmalloc.c-1376- * @pool: pool to allocate from\n--\nmm/zsmalloc.c-1384- */\nmm/zsmalloc.c:1385:unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp,\nmm/zsmalloc.c-1386-\t\t\tconst int nid)\n--\nmm/zsmalloc.c-1439-}\nmm/zsmalloc.c:1440:EXPORT_SYMBOL_GPL(zs_malloc);\nmm/zsmalloc.c-1441-\n--\nmm/zsmalloc.c=1508=static inline void obj_class_get_and_lock(struct zs_pool *pool, unsigned long handle,\n--\nmm/zsmalloc.c-1526-\nmm/zsmalloc.c:1527:void zs_free(struct zs_pool *pool, unsigned long handle)\nmm/zsmalloc.c-1528-{\n--\nmm/zsmalloc.c-1562-}\nmm/zsmalloc.c:1563:EXPORT_SYMBOL_GPL(zs_free);\nmm/zsmalloc.c-1564-\n--\nmm/zsmalloc.c=1794=static void replace_sub_page(struct size_class *class, struct zspage *zspage,\n--\nmm/zsmalloc.c-1810-\nmm/zsmalloc.c:1811:\tcreate_page_chain(class, zspage, zpdescs);\nmm/zsmalloc.c-1812-\tfirst_obj_offset = get_first_obj_offset(oldzpdesc);\n--\nmm/zsmalloc.c=1829=static int zs_page_migrate(struct page *newpage, struct page *page,\n--\nmm/zsmalloc.c-1859-\t * The pool migrate_lock protects against races between zpage migration\nmm/zsmalloc.c:1860:\t * and zs_free(), but only when ZS_OBJ_CLASS_BITS does not apply.\nmm/zsmalloc.c-1861-\t */\n--\nmm/zsmalloc.c=2031=static unsigned long __zs_compact(struct zs_pool *pool,\n--\nmm/zsmalloc.c-2038-\t/*\nmm/zsmalloc.c:2039:\t * Protect against races between zpage migration and zs_free()\nmm/zsmalloc.c-2040-\t * (only when ZS_OBJ_CLASS_BITS does not apply), as well as\n--\nmm/zsmalloc.c=2217=struct zs_pool *zs_create_pool(const char *name)\n--\nmm/zsmalloc.c-2262-\t\t\t * The object uses ZS_HANDLE_SIZE bytes to store the\nmm/zsmalloc.c:2263:\t\t\t * handle. We need to subtract it, because zs_malloc()\nmm/zsmalloc.c-2264-\t\t\t * unconditionally adds handle size before it performs\n"
} |
| 7/2 | 2026/09/08 03:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 21902 output: 37 thoughts: 345 Thoughts: **My Investigation into Memory Management and File System Interactions** Okay, so I'm trying to get a handle on how different parts of the kernel manage memory, particularly in relation to various file systems and features. My initial focus is on memory operations related to common aspects of write paths. I'll need to follow how writes are initiated, handled and then how memory gets freed or reallocated. First, I need to check how zsmalloc specifically handles memory allocation and deallocation within its context – so that means *zs_page_migrate*, *zs_malloc*, and *zs_free* are a priority in *zsmalloc.c*. Then, *fs/crypto/crypto.c* is the next place to investigate for how encryption interacts with page cache operations: *fscrypt_encrypt_pagecache_blocks* and *fscrypt_free_bounce_page* look relevant. I also need to understand how the MD RAID layer interacts with memory. I'll need to analyze *drivers/md/md-bitmap.c* and focus on how *write_file_page* and *free_buffers* function within that context. Moving on to more generic memory management, I need to look into memory migration: *mm/migrate.c*. This means understanding what *folio_migrate_flags* and *migrate_folio_unmap* are doing. Next, I'll need to look at page writeback: *mm/page-writeback.c* and what *filemap_dirty_folio* indicates for handling modified pages. I'll also check how these interact with page scanning in *mm/vmscan.c* through *folio_check_dirty_writeback*. Since huge pages are also of interest, I need to look at *mm/huge_memory.c* to see what *split_huge_pages_pid* does, and at *mm/hugetlb.c* to explore *add_hugetlb_folio*. Then I'll move on to some more specific kernel modules: *drivers/xen/grant-table.c* – looking at *gnttab_pages_set_private* and *gnttab_pages_clear_private* to understand how memory is shared across domains. And finally, I need to look at a couple of filesystems: *fs/ceph/addr.c* for *ceph_dirty_folio* and *fs/nfs/write.c* for *nfs_inode_add_request* and *nfs_inode_remove_request*. As a final step, I need to see what kernel configurations are at play here. The *Kconfig* dependencies are critical for understanding which modules are actually enabled. This means checking what configs enable features like EROFS, F2FS, ZSMALLOC, FSCRYPT, MD_BITMAP, etc. This is essential for understanding the configuration space in which the memory management code operates. |
| 8/2 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "config (EROFS_FS|ZSMALLOC|FS_ENCRYPTION|MD_BITMAP_FILE)"
}Results: {
"Output": "drivers/md/Kconfig=66=config MD_AUTODETECT\n--\ndrivers/md/Kconfig-79-\ndrivers/md/Kconfig:80:config MD_BITMAP_FILE\ndrivers/md/Kconfig-81-\tbool \"MD bitmap file support (deprecated)\"\n--\nfs/crypto/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/crypto/Kconfig:2:config FS_ENCRYPTION\nfs/crypto/Kconfig-3-\tbool \"FS Encryption (Per-file encryption)\"\n--\nfs/crypto/Kconfig-30-# strongly recommended to enable optimized implementations too.\nfs/crypto/Kconfig:31:config FS_ENCRYPTION_ALGS\nfs/crypto/Kconfig-32-\ttristate\n--\nfs/crypto/Kconfig-37-\nfs/crypto/Kconfig:38:config FS_ENCRYPTION_INLINE_CRYPT\nfs/crypto/Kconfig-39-\tbool\n--\nfs/erofs/Kconfig-2-\nfs/erofs/Kconfig:3:config EROFS_FS\nfs/erofs/Kconfig-4-\ttristate \"EROFS filesystem support\"\n--\nfs/erofs/Kconfig-49-\nfs/erofs/Kconfig:50:config EROFS_FS_DEBUG\nfs/erofs/Kconfig-51-\tbool \"EROFS debugging feature\"\n--\nfs/erofs/Kconfig-59-\nfs/erofs/Kconfig:60:config EROFS_FS_XATTR\nfs/erofs/Kconfig-61-\tbool \"EROFS extended attributes\"\n--\nfs/erofs/Kconfig-70-\nfs/erofs/Kconfig:71:config EROFS_FS_POSIX_ACL\nfs/erofs/Kconfig-72-\tbool \"EROFS Access Control Lists\"\n--\nfs/erofs/Kconfig-84-\nfs/erofs/Kconfig:85:config EROFS_FS_SECURITY\nfs/erofs/Kconfig-86-\tbool \"EROFS Security Labels\"\n--\nfs/erofs/Kconfig-97-\nfs/erofs/Kconfig:98:config EROFS_FS_BACKED_BY_FILE\nfs/erofs/Kconfig-99-\tbool \"File-backed EROFS filesystem support\"\n--\nfs/erofs/Kconfig-111-\nfs/erofs/Kconfig:112:config EROFS_FS_ZIP\nfs/erofs/Kconfig-113-\tbool \"EROFS Data Compression Support\"\n--\nfs/erofs/Kconfig-121-\nfs/erofs/Kconfig:122:config EROFS_FS_ZIP_LZMA\nfs/erofs/Kconfig-123-\tbool \"EROFS LZMA compressed data support\"\n--\nfs/erofs/Kconfig-133-\nfs/erofs/Kconfig:134:config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS\nfs/erofs/Kconfig-135-\tint \"EROFS LZMA default maximum decompression streams\" if SMP\n--\nfs/erofs/Kconfig-150-\nfs/erofs/Kconfig:151:config EROFS_FS_ZIP_DEFLATE\nfs/erofs/Kconfig-152-\tbool \"EROFS DEFLATE compressed data support\"\n--\nfs/erofs/Kconfig-161-\nfs/erofs/Kconfig:162:config EROFS_FS_ZIP_ZSTD\nfs/erofs/Kconfig-163-\tbool \"EROFS Zstandard compressed data support\"\n--\nfs/erofs/Kconfig-172-\nfs/erofs/Kconfig:173:config EROFS_FS_ZIP_ACCEL\nfs/erofs/Kconfig-174-\tbool \"EROFS hardware decompression support\"\n--\nfs/erofs/Kconfig-186-\nfs/erofs/Kconfig:187:config EROFS_FS_PCPU_KTHREAD\nfs/erofs/Kconfig-188-\tbool \"EROFS per-cpu decompression kthread workers\"\n--\nfs/erofs/Kconfig-195-\nfs/erofs/Kconfig:196:config EROFS_FS_PCPU_KTHREAD_HIPRI\nfs/erofs/Kconfig-197-\tbool \"EROFS high priority per-CPU kthread workers\"\n--\nfs/erofs/Kconfig-205-\nfs/erofs/Kconfig:206:config EROFS_FS_PAGE_CACHE_SHARE\nfs/erofs/Kconfig-207-\tbool \"EROFS page cache share support (experimental)\"\n--\nmm/Kconfig=114=config ZSWAP_COMPRESSOR_DEFAULT\n--\nmm/Kconfig-124-\nmm/Kconfig:125:config ZSMALLOC\nmm/Kconfig-126-\ttristate\n--\nmm/Kconfig=131=comment \"Zsmalloc is a common backend allocator for zswap \u0026 zram\"\nmm/Kconfig-132-\nmm/Kconfig:133:config ZSMALLOC_STAT\nmm/Kconfig-134-\tbool \"Export zsmalloc statistics\"\n--\nmm/Kconfig-141-\nmm/Kconfig:142:config ZSMALLOC_CHAIN_SIZE\nmm/Kconfig-143-\tint \"Maximum number of physical pages per-zspage\"\n"
} |
| 9/2 | 2026/09/08 03:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 24263 output: 26 thoughts: 52 Thoughts: **Checking EROFS ZIP Compilation** Okay, let's dive in. I need to verify how `EROFS_FS_ZIP` is handled during the kernel build process. First, let's confirm the obvious: `fs/erofs/zdata.c` should indeed be compiled when the `CONFIG_EROFS_FS_ZIP` configuration option is enabled. That's the core of the zipped data support, so that's where I'd expect to see the conditional compilation. Just a quick sanity check to start. Now, the definitive test is the Makefile. I'll need to double-check `fs/erofs/Makefile` to see how the build system integrates that file and its conditional compilation of `zdata.c`. This will confirm if the compile is correct. |
| 10/2 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "zdata",
"PathPrefix": "fs/erofs"
}Results: {
"Output": "fs/erofs/Makefile=5=erofs-$(CONFIG_EROFS_FS_XATTR) += xattr.o\nfs/erofs/Makefile:6:erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o\nfs/erofs/Makefile-7-erofs-$(CONFIG_EROFS_FS_ZIP_LZMA) += decompressor_lzma.o\n"
} |
| 11/2 | 2026/09/08 03:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 24626 output: 29 thoughts: 134 Thoughts: **Kernel Configuration Brainstorm** Okay, let's get down to brass tacks on this kernel configuration. First, I need to make sure I'm considering the right file systems, so I'm going to quickly review `CONFIG_EROFS_FS` and `CONFIG_EROFS_FS_ZIP`. They're relevant to the current work. Then, let's consider compression, I'll go over `ZSMALLOC`, `ZRAM`, and `ZSWAP`. Next up, I should consider other file systems that might be relevant, so I'll quickly check `F2FS_FS`. Thinking about security, I'll review `FS_ENCRYPTION`. Finally, for more memory management concerns, let's explore `MIGRATION`, `TRANSPARENT_HUGEPAGE`, and `HUGETLB_PAGE`. A deeper dive is needed, so I should look into how `split_huge_pages_pid` works in `mm/huge_memory.c`. I have to remember to check that specific config option for its dependencies before assuming it's useful. |
| 12/2 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "split_huge_pages_pid",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/huge_memory.c=4747=static inline bool vma_not_suitable_for_thp_split(struct vm_area_struct *vma)\n--\nmm/huge_memory.c-4760-\nmm/huge_memory.c:4761:static int split_huge_pages_pid(int pid, unsigned long vaddr_start,\nmm/huge_memory.c-4762-\t\t\t\tunsigned long vaddr_end, unsigned int new_order,\n--\nmm/huge_memory.c=4949=static ssize_t split_huge_pages_write(struct file *file, const char __user *buf,\n--\nmm/huge_memory.c-5015-\nmm/huge_memory.c:5016:\tret = split_huge_pages_pid(pid, vaddr_start, vaddr_end, new_order,\nmm/huge_memory.c-5017-\t\t\tin_folio_offset);\n"
} |
| 13/2 | 2026/09/08 03:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 25274 output: 116 thoughts: 1134 Thoughts: **Analyzing Recent Changes to Page Private Flags and Folio Handling** Okay, let's break down these changes related to folio private flags and associated functions. My initial thought is to understand the impact on different subsystems and identify any potential gotchas. The `split_huge_pages_write` debugfs entry, under `CONFIG_TRANSPARENT_HUGEPAGE`, is the starting point, and I'll keep that in mind as I look at these revisions. Specifically, I'm noting the refactor of `PG_private` and how it's being replaced with `__PG_folio` and the `folio_test_private` check. My key focus right now is understanding the distinction between filesystem-private data and other types (swapcache, hugetlb), which is clarified by the introduction of `folio_test_fs_private()`. This is good, it allows for more targeted checks. `folio_has_private()` is now leveraging that distinction as well, that's crucial. I'm noting that `readahead_folio_last()` has been introduced, replacing the iteration logic from first to last to last to first. I'll need to see how that's being used. Specifically, the updated `z_erofs_readahead` which now uses this function. The EROFS changes, and the adjustments of the bias count in `erofs_onlinefolio_init` and `erofs_onlinefolio_end` seem relevant. I will need to understand what this bias count is and how it ensures that `->private` remains non-NULL. I see a few other interesting updates - the removal of `PG_private` in `zsmalloc`, and the updated checks for `is_first_zpdesc` now verify if `zspage->first_zpdesc == zpdesc` and `first_zpdesc->zspage != zspage`. The updates in the MD bitmap code, along with Xen balloon and grant table drivers, also require scrutiny. The `fs/crypto/crypto.c` changes are pretty straightforward: they are removing the old macros for managing private flags. The migration and VM scanning updates are important too - replacing the general `folio_test_private` check with `folio_test_fs_private` will make for more clarity. The replace in `page-writeback.c` with a `data_race` is interesting, this is good. I'm checking the specific configurations that affect this area, so I know which features these changes might impact - EROFS, filesystem encryption, ZSMALLOC, THP, migration. Okay, let's now identify the specific symbols which are affected by the changes. The more ubiquitous functions, such as `filemap_dirty_folio`, which is called frequently in page writes, are of less interest, versus more targeted functions. The key symbols I'm watching are: `z_erofs_readahead`, `erofs_onlinefolio_init`, `erofs_onlinefolio_end`, `fscrypt_encrypt_pagecache_blocks`, `fscrypt_free_bounce_page`, `zs_page_migrate`, `zs_malloc`, `zs_free`, `migrate_folio_unmap`, `folio_check_dirty_writeback`, `split_huge_pages_pid`, `gnttab_pages_set_private`, `gnttab_pages_clear_private`. These are the ones where a subtle change to private flag handling could cause the most issues. I'll verify that these symbols exist in the current codebase using grepper, so I understand the exact scope of these changes. |
| 14/2 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "^(void|int|bool|struct [a-zA-Z0-9_]+ \\*|unsigned long)?\\s*(z_erofs_readahead|erofs_onlinefolio_init|erofs_onlinefolio_end|fscrypt_encrypt_pagecache_blocks|fscrypt_free_bounce_page|zs_page_migrate|migrate_folio_unmap|folio_check_dirty_writeback|split_huge_pages_pid)"
}Results: {
"Output": "fs/ceph/addr.c=723=static int write_folio_nounlock(struct folio *folio,\n--\nfs/ceph/addr.c-841-\t\t\t\t\t\t req-\u003er_end_latency);\nfs/ceph/addr.c:842:\tfscrypt_free_bounce_page(bounce_page);\nfs/ceph/addr.c-843-\tceph_osdc_put_request(req);\n--\nfs/ceph/addr.c=892=static void writepages_finish(struct ceph_osd_request *req)\n--\nfs/ceph/addr.c-947-\t\t\t\tpage = fscrypt_pagecache_page(page);\nfs/ceph/addr.c:948:\t\t\t\tfscrypt_free_bounce_page(osd_data-\u003epages[j]);\nfs/ceph/addr.c-949-\t\t\t\tosd_data-\u003epages[j] = page;\n--\nfs/crypto/crypto.c=45=static struct page *fscrypt_alloc_bounce_page(gfp_t gfp_flags)\n--\nfs/crypto/crypto.c-62- */\nfs/crypto/crypto.c:63:void fscrypt_free_bounce_page(struct page *bounce_page)\nfs/crypto/crypto.c-64-{\n--\nfs/crypto/crypto.c=101=static int fscrypt_crypt_data_unit(const struct fscrypt_inode_info *ci,\n--\nfs/crypto/crypto.c-172- */\nfs/crypto/crypto.c:173:struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,\nfs/crypto/crypto.c-174-\t\tsize_t len, size_t offs, gfp_t gfp_flags)\n--\nfs/crypto/crypto.c-207-\t\tif (err) {\nfs/crypto/crypto.c:208:\t\t\tfscrypt_free_bounce_page(ciphertext_page);\nfs/crypto/crypto.c-209-\t\t\treturn ERR_PTR(err);\n--\nfs/erofs/data.c=197=int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)\n--\nfs/erofs/data.c-249-\nfs/erofs/data.c:250:void erofs_onlinefolio_init(struct folio *folio)\nfs/erofs/data.c-251-{\n--\nfs/erofs/data.c=261=void erofs_onlinefolio_split(struct folio *folio)\n--\nfs/erofs/data.c-265-\nfs/erofs/data.c:266:void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)\nfs/erofs/data.c-267-{\n--\nfs/erofs/fileio.c=22=static void erofs_fileio_ki_complete(struct kiocb *iocb, long ret)\n--\nfs/erofs/fileio.c-32-\t\t\tDBG_BUGON(folio_test_uptodate(fi.folio));\nfs/erofs/fileio.c:33:\t\t\terofs_onlinefolio_end(fi.folio, ret \u003c 0, false);\nfs/erofs/fileio.c-34-\t\t}\n--\nfs/erofs/fileio.c=89=static int erofs_fileio_scan_folio(struct erofs_fileio *io,\n--\nfs/erofs/fileio.c-96-\nfs/erofs/fileio.c:97:\terofs_onlinefolio_init(folio);\nfs/erofs/fileio.c-98-\twhile (cur \u003c end) {\n--\nfs/erofs/fileio.c-152-\t}\nfs/erofs/fileio.c:153:\terofs_onlinefolio_end(folio, err, false);\nfs/erofs/fileio.c-154-\treturn err;\n--\nfs/erofs/fileio.c=198=int erofs_read_meta_folio(struct file *file, struct folio *folio)\n--\nfs/erofs/fileio.c-212-\t\t(io.dev.m_dif-\u003efsoff + io.dev.m_pa) \u003e\u003e 9;\nfs/erofs/fileio.c:213:\terofs_onlinefolio_init(folio);\nfs/erofs/fileio.c-214-\tbio_add_folio_nofail(\u0026io.rq-\u003ebio, folio, folio_size(folio), 0);\n--\nfs/erofs/internal.h=419=int erofs_map_blocks(struct inode *inode, struct erofs_map_blocks *map);\nfs/erofs/internal.h:420:void erofs_onlinefolio_init(struct folio *folio);\nfs/erofs/internal.h-421-void erofs_onlinefolio_split(struct folio *folio);\nfs/erofs/internal.h:422:void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty);\nfs/erofs/internal.h-423-struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);\n--\nfs/erofs/zdata.c=1010=static int z_erofs_scan_folio(struct z_erofs_frontend *f,\n--\nfs/erofs/zdata.c-1021-\ttight = (bs == PAGE_SIZE);\nfs/erofs/zdata.c:1022:\terofs_onlinefolio_init(folio);\nfs/erofs/zdata.c-1023-\tdo {\n--\nfs/erofs/zdata.c-1097-\t} while ((end = cur) \u003e 0);\nfs/erofs/zdata.c:1098:\terofs_onlinefolio_end(folio, err, false);\nfs/erofs/zdata.c-1099-\treturn err;\n--\nfs/erofs/zdata.c=1153=static void z_erofs_fill_other_copies(struct z_erofs_backend *be, int err)\n--\nfs/erofs/zdata.c-1187-\t\tkunmap_local(dst);\nfs/erofs/zdata.c:1188:\t\terofs_onlinefolio_end(page_folio(bvi-\u003ebvec.page), err, true);\nfs/erofs/zdata.c-1189-\t\tlist_del(p);\n--\nfs/erofs/zdata.c=1254=static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, bool eio)\n--\nfs/erofs/zdata.c-1359-\t\tif (!z_erofs_is_shortlived_page(page)) {\nfs/erofs/zdata.c:1360:\t\t\terofs_onlinefolio_end(page_folio(page), err, true);\nfs/erofs/zdata.c-1361-\t\t\tcontinue;\n--\ninclude/linux/fscrypt.h=318=static inline void fscrypt_prepare_dentry(struct dentry *dentry,\n--\ninclude/linux/fscrypt.h-348-\ninclude/linux/fscrypt.h:349:struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,\ninclude/linux/fscrypt.h-350-\t\tsize_t len, size_t offs, gfp_t gfp_flags);\n--\ninclude/linux/fscrypt.h=375=struct folio *fscrypt_pagecache_folio(const struct folio *bounce_folio)\n--\ninclude/linux/fscrypt.h-379-\ninclude/linux/fscrypt.h:380:void fscrypt_free_bounce_page(struct page *bounce_page);\ninclude/linux/fscrypt.h-381-\n--\nmm/vmscan.c=1081=static unsigned int shrink_folio_list(struct list_head *folio_list,\n--\nmm/vmscan.c-1147-\t\t */\nmm/vmscan.c:1148:\t\tfolio_check_dirty_writeback(folio, \u0026dirty, \u0026writeback);\nmm/vmscan.c-1149-\t\tif (dirty || writeback)\n"
} |
| 15/2 | 2026/09/08 03:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 29402 output: 27 thoughts: 11 Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person: **Investigating `zs_page_migrate`** Alright, time to dive into `zs_page_migrate`. Let's see what's going on here. I'm going to start by quickly evaluating the overall architecture, looking for any obvious red flags or potential performance bottlenecks. I'm assuming, given the name, this handles some sort of page migration process, likely related to database or data storage changes, but I need to be sure. I'll need to understand the source and destination of the migration. Is it a full table copy, or something more nuanced like a schema evolution or data transformation? What are the key performance metrics I need to keep in mind, and the constraints of the migration process? From there, I'll need to understand how the process is triggered, is this a batch job, or an on-demand process? What triggers it? What dependencies does it have? I'm curious about the error handling and rollback strategy. This is crucial: if something goes wrong during the migration, what safeguards are in place? And what sort of logs and monitoring do they use to track its progress? Does the migration process have appropriate idempotency handling? Also, I need to look into how this interacts with any other related services. Is it loosely coupled, or does it have tight dependencies? Let's check the code and see what we can find. I expect to be able to tell pretty quickly whether this is a well-designed, robust piece of code, or a potential source of headaches down the line. |
| 16/2 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "zs_page_migrate",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/zsmalloc.c=1819=static bool zs_page_isolate(struct page *page, isolate_mode_t mode)\n--\nmm/zsmalloc.c-1828-\nmm/zsmalloc.c:1829:static int zs_page_migrate(struct page *newpage, struct page *page,\nmm/zsmalloc.c-1830-\t\tenum migrate_mode mode)\n--\nmm/zsmalloc.c=1940=const struct movable_operations zsmalloc_mops = {\nmm/zsmalloc.c-1941-\t.isolate_page = zs_page_isolate,\nmm/zsmalloc.c:1942:\t.migrate_page = zs_page_migrate,\nmm/zsmalloc.c-1943-\t.putback_page = zs_page_putback,\n"
} |
| 17/2 | 2026/09/08 03:53 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 30006 output: 255 thoughts: 318 Thoughts: **Final Thoughts on This Patch Series** Okay, these symbols look promising, quite a strong list actually. Before I commit to calling this complete, let's just run through the configurations one more time. I'm seeing: `EROFS_FS`, `EROFS_FS_ZIP`, `FS_ENCRYPTION`, and `ZSMALLOC` all correctly enabled. Good, that's what I expected. Now, a final sanity check of the reasoning, the focus symbols, and the enable configurations. I need to be absolutely certain. The core of this patch series is the shift away from the deprecated `PG_private` flag towards `folio->private` checks and `folio_test_fs_private()`, alongside the introduction of `readahead_folio_last()` for efficient reverse readahead in EROFS. We're also seeing updates for zsmalloc, fscrypt, page migration, vmscan, and THP handling. Essentially, these changes touch fundamental aspects of memory management, filesystem folio private handling, and page flags across a range of critical subsystems. The EnableConfigs are spot on: `EROFS_FS`, `EROFS_FS_ZIP`, `FS_ENCRYPTION`, and `ZSMALLOC`, as expected. And the focus symbols? Right on the money: I've got `z_erofs_readahead`, `erofs_onlinefolio_init`, `erofs_onlinefolio_end`, `fscrypt_encrypt_pagecache_blocks`, `fscrypt_free_bounce_page`, `zs_page_migrate`, `migrate_folio_unmap`, `folio_check_dirty_writeback`, and `split_huge_pages_pid`. These all clearly align with the described changes and affected subsystems. Alright, I'm satisfied. Let's call `set-results`. It's done. |
| 18/2 | 2026/09/08 03:53 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"FS_ENCRYPTION",
"ZSMALLOC"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_init",
"erofs_onlinefolio_end",
"fscrypt_encrypt_pagecache_blocks",
"fscrypt_free_bounce_page",
"zs_page_migrate",
"migrate_folio_unmap",
"folio_check_dirty_writeback",
"split_huge_pages_pid"
],
"Reasoning": "The patch series eliminates the PG_private page flag in favor of direct folio-\u003eprivate checks and the new folio_test_fs_private() helper. It also introduces readahead_folio_last() for backward iteration during readahead in EROFS, updates zsmalloc component page identification, adjusts fscrypt bounce page tagging, and updates dirty/writeback checks in memory migration and vmscan. These changes touch core memory management and filesystem folio logic that is directly reachable and warrants fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"FS_ENCRYPTION",
"ZSMALLOC"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_init",
"erofs_onlinefolio_end",
"fscrypt_encrypt_pagecache_blocks",
"fscrypt_free_bounce_page",
"zs_page_migrate",
"migrate_folio_unmap",
"folio_check_dirty_writeback",
"split_huge_pages_pid"
],
"Reasoning": "The patch series eliminates the PG_private page flag in favor of direct folio-\u003eprivate checks and the new folio_test_fs_private() helper. It also introduces readahead_folio_last() for backward iteration during readahead in EROFS, updates zsmalloc component page identification, adjusts fscrypt bounce page tagging, and updates dirty/writeback checks in memory migration and vmscan. These changes touch core memory management and filesystem folio logic that is directly reachable and warrants fuzzing.",
"WorthFuzzing": true
} |
| 19/1 | 2026/09/08 03:53 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 20/2 | 2026/09/08 03:53 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch series removes the `PG_private` page flag across the kernel memory management subsystem, VFS, filesystems (EROFS, Ceph, NFS, F2FS, UBIFS), and drivers (Xen, perf/intel_pt/bts), replacing `PagePrivate` / `SetPagePrivate` / `ClearPagePrivate` with direct inspections of `page-\u003eprivate` / `folio-\u003eprivate` and helpers like `folio_test_fs_private()` and `readahead_folio_last()`.\n\nKMSAN Applicability Analysis:\n1. No uninitialized memory reads or leaks: The patch does not introduce or expose uninitialized heap, stack, or page memory. Structures like `struct readahead_control` (which gained `_forward`) are initialized with designated initializers (e.g. `DEFINE_READAHEAD`), zeroing all fields.\n2. User-space copies: The only user-visible modification in `stable_page_flags` (`/proc/kpageflags`) safely removes the `PG_private` bit mapping from an already zero-initialized integer (`u`), with no possibility of uninitialized kernel memory leakage.\n3. Nature of potential issues: Any bugs resulting from this refactoring would pertain to page state transitions, refcount mismatches, page-private lifecycle management, or concurrency races, which are detected by standard assertions (VM_BUG_ON, VM_WARN_ON), KASAN, or LOCKDEP rather than uninitialized memory detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 0590aa3a35c51ca8c0aad93842ab6f112af2df0c
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Sep 8 03:52:47 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/admin-guide/kdump/vmcoreinfo.rst b/Documentation/admin-guide/kdump/vmcoreinfo.rst
index 7663c610fe901..5f1df6d080508 100644
--- a/Documentation/admin-guide/kdump/vmcoreinfo.rst
+++ b/Documentation/admin-guide/kdump/vmcoreinfo.rst
@@ -325,7 +325,7 @@ NR_FREE_PAGES
On linux-2.6.21 or later, the number of free pages is in
vm_stat[NR_FREE_PAGES]. Used to get the number of free pages.
-PG_lru|PG_private|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask
+PG_lru|PG_swapcache|PG_swapbacked|PG_hwpoison|PG_head_mask
--------------------------------------------------------------------------
Page attributes. These flags are used to filter various unnecessary for
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index d3a93eec3945f..dec7816303c6a 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -649,8 +649,8 @@ Writeback.
The first can be used independently to the others. The VM can try to
release clean pages in order to reuse them. To do this it can call
-->release_folio on clean folios with the private
-flag set. Clean pages without PagePrivate and with no external references
+->release_folio on clean folios with folio->private set. Clean pages
+without folio->private set and with no external references
will be released without notice being given to the address_space.
To achieve this functionality, pages need to be placed on an LRU with
@@ -674,7 +674,7 @@ filemap_fdatawait_range, to wait for all writeback to complete.
An address_space handler may attach extra information to a page,
typically using the 'private' field in the 'struct page'. If such
-information is attached, the PG_Private flag should be set. This will
+information is attached, non-NULL 'private' field will
cause various VM routines to make extra calls into the address_space
handler to deal with that data.
diff --git a/arch/x86/events/intel/bts.c b/arch/x86/events/intel/bts.c
index cbac54cb3a9ec..5849392cf26d5 100644
--- a/arch/x86/events/intel/bts.c
+++ b/arch/x86/events/intel/bts.c
@@ -66,9 +66,6 @@ static struct pmu bts_pmu;
static int buf_nr_pages(struct page *page)
{
- if (!PagePrivate(page))
- return 1;
-
return 1 << page_private(page);
}
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index 5754cd4055628..49349afee6119 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -781,8 +781,7 @@ static int topa_insert_pages(struct pt_buffer *buf, int cpu, gfp_t gfp)
struct page *p;
p = virt_to_page(buf->data_pages[buf->nr_pages]);
- if (PagePrivate(p))
- order = page_private(p);
+ order = page_private(p);
if (topa_table_full(topa)) {
topa = topa_alloc(cpu, gfp);
@@ -1296,8 +1295,7 @@ static int pt_buffer_try_single(struct pt_buffer *buf, int nr_pages)
if (!intel_pt_validate_hw_cap(PT_CAP_single_range_output))
goto out;
- if (PagePrivate(p))
- order = page_private(p);
+ order = page_private(p);
if (1 << order != nr_pages)
goto out;
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index b8325cb09a371..0cf14bc1cff32 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -516,7 +516,7 @@ static void end_bitmap_write(struct bio *bio)
static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
{
- struct buffer_head *bh = page_buffers(page);
+ struct buffer_head *bh = (struct buffer_head *)page_private(page);
while (bh && bh->b_blocknr) {
atomic_inc(&bitmap->pending_writes);
@@ -535,10 +535,10 @@ static void free_buffers(struct page *page)
{
struct buffer_head *bh;
- if (!PagePrivate(page))
+ if (!page_private(page))
return;
- bh = page_buffers(page);
+ bh = (struct buffer_head *)page_private(page);
while (bh) {
struct buffer_head *next = bh->b_this_page;
free_buffer_head(bh);
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index e7f74ea7cd5eb..fdb18348cfdfe 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -182,6 +182,11 @@ static struct page *balloon_retrieve(bool require_lowmem)
__ClearPageOffline(page);
dec_node_page_state(page, NR_BALLOON_PAGES);
+ /*
+ * clear page->private before giving it out, since it might be used to
+ * store xen_page_foreign info.
+ */
+ set_page_private(page, 0);
return page;
}
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 69922be28b54c..993f89f048e21 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -863,10 +863,10 @@ EXPORT_SYMBOL_GPL(gnttab_free_auto_xlat_frames);
int gnttab_pages_set_private(int nr_pages, struct page **pages)
{
+#if BITS_PER_LONG < 64
int i;
for (i = 0; i < nr_pages; i++) {
-#if BITS_PER_LONG < 64
struct xen_page_foreign *foreign;
foreign = kzalloc_obj(*foreign);
@@ -874,9 +874,9 @@ int gnttab_pages_set_private(int nr_pages, struct page **pages)
return -ENOMEM;
set_page_private(pages[i], (unsigned long)foreign);
-#endif
- SetPagePrivate(pages[i]);
}
+#endif
+ /* Data is stored in page->private on 64-bit */
return 0;
}
@@ -1031,12 +1031,11 @@ void gnttab_pages_clear_private(int nr_pages, struct page **pages)
int i;
for (i = 0; i < nr_pages; i++) {
- if (PagePrivate(pages[i])) {
#if BITS_PER_LONG < 64
+ if (page_private(pages[i]))
kfree((void *)page_private(pages[i]));
#endif
- ClearPagePrivate(pages[i]);
- }
+ set_page_private(pages[i], 0);
}
}
EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 657c2cb0f8811..f9b2ec9550481 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -70,9 +70,7 @@ static int ceph_netfs_check_write_begin(struct file *file, loff_t pos, unsigned
static inline struct ceph_snap_context *page_snap_context(struct page *page)
{
- if (PagePrivate(page))
- return (void *)page->private;
- return NULL;
+ return (void *)page->private;
}
/*
@@ -124,8 +122,8 @@ static bool ceph_dirty_folio(struct address_space *mapping, struct folio *folio)
spin_unlock(&ci->i_ceph_lock);
/*
- * Reference snap context in folio->private. Also set
- * PagePrivate so that we get invalidate_folio callback.
+ * Reference snap context in folio->private. Setting folio->private is
+ * what gets us the invalidate_folio callback.
*/
VM_WARN_ON_FOLIO(folio->private, folio);
folio_attach_private(folio, snapc);
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 5286a124b0d98..aced5c50a4601 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -65,7 +65,6 @@ void fscrypt_free_bounce_page(struct page *bounce_page)
if (!bounce_page)
return;
set_page_private(bounce_page, (unsigned long)NULL);
- ClearPagePrivate(bounce_page);
mempool_free(bounce_page, fscrypt_bounce_page_pool);
}
EXPORT_SYMBOL(fscrypt_free_bounce_page);
@@ -210,7 +209,6 @@ struct page *fscrypt_encrypt_pagecache_blocks(struct folio *folio,
return ERR_PTR(err);
}
}
- SetPagePrivate(ciphertext_page);
set_page_private(ciphertext_page, (unsigned long)folio);
return ciphertext_page;
}
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 0885b1f2fc925..f71c7b51bb720 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -239,19 +239,23 @@ int erofs_map_dev(struct super_block *sb, struct erofs_map_dev *map)
/*
* bit 30: I/O error occurred on this folio
* bit 29: CPU has dirty data in D-cache (needs aliasing handling);
- * bit 0 - 29: remaining parts to complete this folio
+ * bit 0 - 28: remaining parts to complete this folio, biased by 1 so that
+ * ->private stays non-NULL while the folio is attached
*/
#define EROFS_ONLINEFOLIO_EIO 30
#define EROFS_ONLINEFOLIO_DIRTY 29
+#define EROFS_ONLINEFOLIO_COUNT_MASK (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1)
+#define EROFS_ONLINEFOLIO_BIAS 1
void erofs_onlinefolio_init(struct folio *folio)
{
union {
atomic_t o;
void *v;
- } u = { .o = ATOMIC_INIT(1) };
+ } u = { .o = ATOMIC_INIT(1 + EROFS_ONLINEFOLIO_BIAS) };
- folio->private = u.v; /* valid only if file-backed folio is locked */
+ /* valid only if file-backed folio is locked */
+ folio_attach_private(folio, u.v);
}
void erofs_onlinefolio_split(struct folio *folio)
@@ -265,14 +269,14 @@ void erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)
do {
orig = atomic_read((atomic_t *)&folio->private);
- DBG_BUGON(orig <= 0);
+ DBG_BUGON((orig & EROFS_ONLINEFOLIO_COUNT_MASK) <= EROFS_ONLINEFOLIO_BIAS);
v = dirty << EROFS_ONLINEFOLIO_DIRTY;
v |= (orig - 1) | (!!err << EROFS_ONLINEFOLIO_EIO);
} while (atomic_cmpxchg((atomic_t *)&folio->private, orig, v) != orig);
- if (v & (BIT(EROFS_ONLINEFOLIO_DIRTY) - 1))
+ if ((v & EROFS_ONLINEFOLIO_COUNT_MASK) != EROFS_ONLINEFOLIO_BIAS)
return;
- folio->private = 0;
+ folio_detach_private(folio);
if (v & BIT(EROFS_ONLINEFOLIO_DIRTY))
flush_dcache_folio(folio);
folio_end_read(folio, !(v & BIT(EROFS_ONLINEFOLIO_EIO)));
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index e1e25ca0d1904..78fd7d980e957 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1898,21 +1898,14 @@ static void z_erofs_readahead(struct readahead_control *rac)
struct inode *realinode = erofs_real_inode(sharedinode, &need_iput);
Z_EROFS_DEFINE_FRONTEND(f, realinode, sharedinode, readahead_pos(rac));
unsigned int nrpages = readahead_count(rac);
- struct folio *head = NULL, *folio;
+ struct folio *folio;
int err;
trace_erofs_readahead(realinode, readahead_index(rac), nrpages, false);
z_erofs_pcluster_readmore(&f, rac, true);
- while ((folio = readahead_folio(rac))) {
- folio->private = head;
- head = folio;
- }
-
- /* traverse in reverse order for best metadata I/O performance */
- while (head) {
- folio = head;
- head = folio_get_private(folio);
+ /* traverse from last to first for best metadata I/O performance */
+ while ((folio = readahead_folio_last(rac))) {
err = z_erofs_scan_folio(&f, folio, true);
if (err && err != -EINTR)
erofs_err(realinode->i_sb, "readahead error at folio %lu @ nid %llu",
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9940a6cecf1a2..2f7ab5888b078 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -2691,7 +2691,7 @@ static inline bool folio_test_f2fs_##name(const struct folio *folio) \
} \
static inline bool page_private_##name(struct page *page) \
{ \
- return PagePrivate(page) && \
+ return page_private(page) && \
test_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)) && \
test_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
@@ -2710,9 +2710,9 @@ static inline void folio_set_f2fs_##name(struct folio *folio) \
} \
static inline void set_page_private_##name(struct page *page) \
{ \
- if (!PagePrivate(page)) \
- attach_page_private(page, (void *)0); \
- set_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)); \
+ if (!page_private(page)) \
+ attach_page_private(page, \
+ (void *)BIT(PAGE_PRIVATE_NOT_POINTER)); \
set_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index e1bdd10b35f10..38f830a6467c9 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -484,7 +484,7 @@ static int nfs_write_end(const struct kiocb *iocb,
* Partially or wholly invalidate a page
* - Release the private state associated with a page if undergoing complete
* page invalidation
- * - Called if either PG_private or PG_fscache is set on the page
+ * - Called if either folio->private or PG_fscache is set on the page
* - Caller holds page lock
*/
static void nfs_invalidate_folio(struct folio *folio, size_t offset,
@@ -555,7 +555,7 @@ static void nfs_check_dirty_writeback(struct folio *folio,
* Attempt to clear the private state associated with a page when an error
* occurs that requires the cached contents of an inode to be written back or
* destroyed
- * - Called if either PG_private or fscache is set on the page
+ * - Called if either page->private or fscache is set on the page
* - Caller holds page lock
* - Return 0 if successful, -error otherwise
*/
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 623e7ef1f73d5..b6967b5286691 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -717,7 +717,6 @@ static void nfs_inode_add_request(struct nfs_page *req)
nfs_lock_request(req);
spin_lock(&mapping->i_private_lock);
set_bit(PG_MAPPED, &req->wb_flags);
- folio_set_private(folio);
folio->private = req;
spin_unlock(&mapping->i_private_lock);
atomic_long_inc(&nfsi->nrequests);
@@ -745,7 +744,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
spin_lock(&mapping->i_private_lock);
folio->private = NULL;
- folio_clear_private(folio);
clear_bit(PG_MAPPED, &req->wb_head->wb_flags);
spin_unlock(&mapping->i_private_lock);
diff --git a/fs/proc/page.c b/fs/proc/page.c
index 260772b20bd99..f90e1030825e9 100644
--- a/fs/proc/page.c
+++ b/fs/proc/page.c
@@ -232,7 +232,6 @@ u64 stable_page_flags(const struct page *page)
u |= kpf_copy_bit(k, KPF_RESERVED, PG_reserved);
u |= kpf_copy_bit(k, KPF_OWNER_2, PG_owner_2);
- u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private);
u |= kpf_copy_bit(k, KPF_PRIVATE_2, PG_private_2);
u |= kpf_copy_bit(k, KPF_OWNER_PRIVATE, PG_owner_priv_1);
u |= kpf_copy_bit(k, KPF_ARCH, PG_arch_1);
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index e73c28b12f97f..aa0298ce451ef 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -12,14 +12,14 @@
* This file implements VFS file and inode operations for regular files, device
* nodes and symlinks as well as address space operations.
*
- * UBIFS uses 2 page flags: @PG_private and @PG_checked. @PG_private is set if
+ * UBIFS uses folio->private and page flag @PG_checked. folio->private is set if
* the page is dirty and is used for optimization purposes - dirty pages are
- * not budgeted so the flag shows that 'ubifs_write_end()' should not release
+ * not budgeted so it shows that 'ubifs_write_end()' should not release
* the budget for this page. The @PG_checked flag is set if full budgeting is
* required for the page e.g., when it corresponds to a file hole or it is
* beyond the file size. The budgeting is done in 'ubifs_write_begin()', because
* it is OK to fail in this function, and the budget is released in
- * 'ubifs_write_end()'. So the @PG_private and @PG_checked flags carry
+ * 'ubifs_write_end()'. So the folio->private and the @PG_checked flag carry
* information about how the page was budgeted, to make it possible to release
* the budget properly.
*
@@ -1509,7 +1509,7 @@ static vm_fault_t ubifs_vm_page_mkwrite(struct vm_fault *vmf)
*
* At the moment we do not know whether the folio is dirty or not, so we
* assume that it is not and budget for a new folio. We could look at
- * the @PG_private flag and figure this out, but we may race with write
+ * folio->private and figure this out, but we may race with write
* back and the folio state may change by the time we lock it, so this
* would need additional care. We do not bother with this at the
* moment, although it might be good idea to do. Instead, we allocate
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index fd2c7115c0542..e47fbd0387b69 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -175,12 +175,6 @@ static inline unsigned long bh_offset(const struct buffer_head *bh)
return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1);
}
-/* If we *know* page->private refers to buffer_heads */
-#define page_buffers(page) \
- ({ \
- BUG_ON(!PagePrivate(page)); \
- ((struct buffer_head *)page_private(page)); \
- })
#define folio_buffers(folio) folio_get_private(folio)
void buffer_check_dirty_writeback(struct folio *folio,
diff --git a/include/linux/kernel-page-flags.h b/include/linux/kernel-page-flags.h
index 196778a087c4d..fe5ab6e50bd70 100644
--- a/include/linux/kernel-page-flags.h
+++ b/include/linux/kernel-page-flags.h
@@ -11,7 +11,6 @@
#define KPF_RESERVED 32
#define KPF_MLOCKED 33
#define KPF_OWNER_2 34
-#define KPF_PRIVATE 35
#define KPF_PRIVATE_2 36
#define KPF_OWNER_PRIVATE 37
#define KPF_ARCH 38
diff --git a/include/linux/mm.h b/include/linux/mm.h
index c49ef99b4413b..0ceffe4f9ca02 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2048,20 +2048,21 @@ vm_fault_t finish_fault(struct vm_fault *vmf);
*
* A pagecache page contains an opaque `private' member, which belongs to the
* page's address_space. Usually, this is the address of a circular list of
- * the page's disk buffers. PG_private must be set to tell the VM to call
- * into the filesystem to release these pages.
+ * the page's disk buffers. It tells the VM to call into the filesystem to
+ * release these pages.
*
* A folio may belong to an inode's memory mapping. In this case,
* folio->mapping points to the inode, and folio->index is the file
* offset of the folio, in units of PAGE_SIZE.
*
- * If pagecache pages are not associated with an inode, they are said to be
- * anonymous pages. These may become associated with the swapcache, and in that
- * case PG_swapcache is set, and page->private is an offset into the swapcache.
+ * If pagecache folios are not associated with an inode, they are said to be
+ * anonymous folios. These may become associated with the swapcache, and in that
+ * case PG_swapcache is set, and folio->private is an offset into the swapcache.
*
* In either case (swapcache or inode backed), the pagecache itself holds one
- * reference to the page. Setting PG_private should also increment the
- * refcount. The each user mapping also has a reference to the page.
+ * reference to the folio. Attaching filesystem private data via
+ * folio_attach_private() also increments the refcount. Each user mapping also
+ * has a reference to the folio.
*
* The pagecache pages are stored in a per-mapping radix tree, which is
* rooted at mapping->i_pages, and indexed by offset.
@@ -3004,9 +3005,9 @@ static inline bool folio_maybe_mapped_shared(struct folio *folio)
* @folio: the folio
*
* Calculate the expected folio refcount, taking references from the pagecache,
- * swapcache, PG_private and page table mappings into account. Useful in
- * combination with folio_ref_count() to detect unexpected references (e.g.,
- * GUP or other temporary references).
+ * swapcache, private data (folio->private != NULL) and page table mappings into
+ * account. Useful in combination with folio_ref_count() to detect unexpected
+ * references (e.g., GUP or other temporary references).
*
* Does currently not consider references from the LRU cache. If the folio
* was isolated from the LRU (which is the case during migration or split),
@@ -3044,10 +3045,16 @@ static inline int folio_expected_ref_count(const struct folio *folio)
ref_count += folio_test_swapcache(folio) << order;
if (!folio_test_anon(folio)) {
- /* One reference per page from the pagecache. */
- ref_count += !!folio->mapping << order;
- /* One reference from PG_private. */
- ref_count += folio_test_private(folio);
+ /*
+ * One reference per page from the pagecache.
+ * Use data_race() since folio might not be locked.
+ */
+ ref_count += !!data_race(folio->mapping) << order;
+ /*
+ * One reference from filesystem private data.
+ * Use data_race() since folio might not be locked.
+ */
+ ref_count += data_race(folio_test_fs_private(folio));
}
/* One reference per page table mapping. */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 5413bd10fff2c..9d6c945ba4104 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -108,7 +108,7 @@ struct page {
};
/**
* @private: Mapping-private opaque data.
- * Usually used for buffer_heads if PagePrivate.
+ * Usually used for buffer_heads.
* Used for swp_entry_t if swapcache flag set.
* Indicates order in the buddy system if PageBuddy
* or on pcp_llist.
@@ -675,7 +675,7 @@ static inline void ptdesc_pmd_pts_init(struct ptdesc *ptdesc)
#define STRUCT_PAGE_MAX_SHIFT (order_base_2(sizeof(struct page)))
/*
- * page_private can be used on tail pages. However, PagePrivate is only
+ * page_private can be used on tail pages. However, it is only
* checked by the VM on the head page. So page_private on the tail pages
* should be used for data that's ancillary to the head page (eg attaching
* buffer heads to tail pages after attaching buffer heads to the head page)
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 86dd0470da117..7b7783c0a5216 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -44,10 +44,6 @@
* Consequently, PG_reserved for a page mapped into user space can indicate
* the zero page, the vDSO, MMIO pages or device memory.
*
- * The PG_private bitflag is set on pagecache pages if they contain filesystem
- * specific data (which is normally at page->private). It can be used by
- * private allocations for its own usage.
- *
* During initiation of disk I/O, PG_locked is set. This bit is set before I/O
* and cleared when writeback _starts_ or when read _completes_. PG_writeback
* is set before writeback starts and cleared when it finishes.
@@ -105,7 +101,7 @@ enum pageflags {
PG_owner_2, /* Owner use. If pagecache, fs may use */
PG_arch_1,
PG_reserved,
- PG_private, /* If pagecache, has fs-private data */
+ __PG_folio, /* Do not use: reserved for folio identification */
PG_private_2, /* If pagecache, has fs aux data */
PG_reclaim, /* To be reclaimed asap */
PG_swapbacked, /* Page is backed by RAM/swap */
@@ -576,9 +572,14 @@ FOLIO_FLAG(swapbacked, FOLIO_HEAD_PAGE)
/*
* Private page markings that may be used by the filesystem that owns the page
* for its own purposes.
- * - PG_private and PG_private_2 cause release_folio() and co to be invoked
+ * - folio->private and PG_private_2 cause release_folio() and co to be invoked
*/
-PAGEFLAG(Private, private, PF_ANY)
+
+static __always_inline bool folio_test_private(const struct folio *folio)
+{
+ return folio->private;
+}
+
FOLIO_FLAG(private_2, FOLIO_HEAD_PAGE)
/* owner_2 can be set on tail pages for anon memory */
@@ -1170,7 +1171,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
*/
#define PAGE_FLAGS_CHECK_AT_FREE \
(1UL << PG_lru | 1UL << PG_locked | \
- 1UL << PG_private | 1UL << PG_private_2 | \
+ 1UL << PG_private_2 | \
1UL << PG_writeback | 1UL << PG_reserved | \
1UL << PG_active | \
1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK)
@@ -1194,8 +1195,28 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
(0xffUL /* order */ | 1UL << PG_has_hwpoisoned | \
1UL << PG_large_rmappable | 1UL << PG_partially_mapped)
-#define PAGE_FLAGS_PRIVATE \
- (1UL << PG_private | 1UL << PG_private_2)
+/**
+ * folio_test_fs_private - check if the folio has filesystem private data
+ * @folio: The folio to check.
+ *
+ * Use this in code that may encounter swapcache or hugetlb folios but only
+ * wants to detect filesystem private data. Swapcache stores swp_entry_t in
+ * folio->swap, a union with folio->private, and hugetlb stores its own flags
+ * in folio->private; both are excluded.
+ *
+ * NOTE: For swapcache, folio->swap.val PG_swapcache are not set as a whole,
+ * so folio_test_swapcache() is not reliable to exclude swapcache.
+ * Use folio_test_swapbacked() instead, since it remains set when a folio is
+ * added to/removed from swapcache.
+ *
+ * Return: true if folio->private is set and the folio is neither swapcache
+ * nor hugetlb.
+ */
+static inline bool folio_test_fs_private(const struct folio *folio)
+{
+ return folio_test_private(folio) && !folio_test_swapbacked(folio) &&
+ !folio_test_hugetlb(folio);
+}
/**
* folio_has_private - Determine if folio has private stuff
* @folio: The folio to be checked
@@ -1205,7 +1226,7 @@ static __always_inline void __ClearPageAnonExclusive(struct page *page)
*/
static inline int folio_has_private(const struct folio *folio)
{
- return !!(folio->flags.f & PAGE_FLAGS_PRIVATE);
+ return folio_test_fs_private(folio) || folio_test_private_2(folio);
}
#undef PF_ANY
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 939f3a5e973f6..cc5c2cca814bc 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -594,7 +594,6 @@ static inline void folio_attach_private(struct folio *folio, void *data)
{
folio_get(folio);
folio->private = data;
- folio_set_private(folio);
}
/**
@@ -629,9 +628,8 @@ static inline void *folio_detach_private(struct folio *folio)
{
void *data = folio_get_private(folio);
- if (!folio_test_private(folio))
+ if (!data)
return NULL;
- folio_clear_private(folio);
folio->private = NULL;
folio_put(folio);
@@ -1415,6 +1413,7 @@ struct readahead_control {
bool dropbehind;
bool _workingset;
unsigned long _pflags;
+ bool _forward;
};
#define DEFINE_READAHEAD(ractl, f, r, m, i) \
@@ -1479,18 +1478,25 @@ void page_cache_async_readahead(struct address_space *mapping,
page_cache_async_ra(&ractl, folio, req_count);
}
+static inline void __readahead_advance(struct readahead_control *rac)
+{
+ if (rac->_forward)
+ rac->_index += rac->_batch_count;
+
+ rac->_nr_pages -= rac->_batch_count;
+ rac->_batch_count = 0;
+}
+
static inline struct folio *__readahead_folio(struct readahead_control *ractl)
{
struct folio *folio;
BUG_ON(ractl->_batch_count > ractl->_nr_pages);
- ractl->_nr_pages -= ractl->_batch_count;
- ractl->_index += ractl->_batch_count;
+ __readahead_advance(ractl);
+ ractl->_forward = true;
- if (!ractl->_nr_pages) {
- ractl->_batch_count = 0;
+ if (!ractl->_nr_pages)
return NULL;
- }
folio = xa_load(&ractl->mapping->i_pages, ractl->_index);
VM_BUG_ON_FOLIO(!folio_test_locked(folio), folio);
@@ -1516,6 +1522,39 @@ static inline struct folio *readahead_folio(struct readahead_control *ractl)
return folio;
}
+/**
+ * readahead_folio_last - Get the next folio to read, from the tail.
+ * @ractl: The current readahead request.
+ *
+ * Like readahead_folio(), but walks the range back-to-front. The folio is
+ * returned locked with its refcount dropped; the caller unlocks it once I/O
+ * completes. Compound folios are returned once, at their head index.
+ *
+ * Context: The folio is locked.
+ * Return: A pointer to the next folio, or %NULL when done.
+ */
+static inline struct folio *readahead_folio_last(struct readahead_control *ractl)
+{
+ struct folio *folio;
+
+ /* Drop the previously returned batch from the remaining range. */
+ __readahead_advance(ractl);
+ ractl->_forward = false;
+
+ if (!ractl->_nr_pages)
+ return NULL;
+
+ /* xa_load() follows sibling entries, so a tail index returns the head */
+ folio = xa_load(&ractl->mapping->i_pages,
+ ractl->_index + ractl->_nr_pages - 1);
+ VM_WARN_ON_ONCE_FOLIO(!folio_test_locked(folio), folio);
+
+ ractl->_batch_count = folio_nr_pages(folio);
+
+ folio_put(folio);
+ return folio;
+}
+
static inline unsigned int __readahead_batch(struct readahead_control *rac,
struct page **array, unsigned int array_sz)
{
@@ -1524,9 +1563,8 @@ static inline unsigned int __readahead_batch(struct readahead_control *rac,
struct folio *folio;
BUG_ON(rac->_batch_count > rac->_nr_pages);
- rac->_nr_pages -= rac->_batch_count;
- rac->_index += rac->_batch_count;
- rac->_batch_count = 0;
+ __readahead_advance(rac);
+ rac->_forward = true;
xas_set(&xas, rac->_index);
rcu_read_lock();
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index ef9aa388b84f7..38a2d30230443 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -130,6 +130,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);
#endif
#define DEF_PAGEFLAG_NAME(_name) { 1UL << PG_##_name, __stringify(_name) }
+#define __DEF_PAGEFLAG_NAME(_name) { 1UL << __PG_##_name, __stringify(_name) }
#define __def_pageflag_names \
DEF_PAGEFLAG_NAME(locked), \
@@ -144,7 +145,7 @@ TRACE_DEFINE_ENUM(___GFP_LAST_BIT);
DEF_PAGEFLAG_NAME(owner_2), \
DEF_PAGEFLAG_NAME(arch_1), \
DEF_PAGEFLAG_NAME(reserved), \
- DEF_PAGEFLAG_NAME(private), \
+ __DEF_PAGEFLAG_NAME(folio), \
DEF_PAGEFLAG_NAME(private_2), \
DEF_PAGEFLAG_NAME(writeback), \
DEF_PAGEFLAG_NAME(head), \
diff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h
index 36c3a90f0acca..547a550b81ee1 100644
--- a/include/trace/events/pagemap.h
+++ b/include/trace/events/pagemap.h
@@ -22,7 +22,8 @@
(folio_test_swapcache(folio) ? PAGEMAP_SWAPCACHE : 0) | \
(folio_test_swapbacked(folio) ? PAGEMAP_SWAPBACKED : 0) | \
(folio_test_mappedtodisk(folio) ? PAGEMAP_MAPPEDDISK : 0) | \
- (folio_test_private(folio) ? PAGEMAP_BUFFERS : 0) \
+ /* data_race() is used to read folio->private locklessly */ \
+ (data_race(folio_test_fs_private(folio)) ? PAGEMAP_BUFFERS : 0) \
)
TRACE_EVENT(mm_lru_insertion,
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 9fe92161715e0..061b5cb222266 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -630,11 +630,10 @@ static struct page *rb_alloc_aux_page(int node, int order)
/*
* Communicate the allocation size to the driver:
* if we managed to secure a high-order allocation,
- * set its first page's private to this order;
- * !PagePrivate(page) means it's just a normal page.
+ * set its first page's private to this order, otherwise page's
+ * private remains zero.
*/
split_page(page, order);
- SetPagePrivate(page);
set_page_private(page, order);
}
@@ -645,7 +644,7 @@ static void rb_free_aux_page(struct perf_buffer *rb, int idx)
{
struct page *page = virt_to_page(rb->aux_pages[idx]);
- ClearPagePrivate(page);
+ set_page_private(page, 0);
__free_page(page);
}
diff --git a/kernel/vmcore_info.c b/kernel/vmcore_info.c
index 8614430ca212a..5a417f8a922ab 100644
--- a/kernel/vmcore_info.c
+++ b/kernel/vmcore_info.c
@@ -216,7 +216,6 @@ static int __init crash_save_vmcoreinfo_init(void)
VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);
VMCOREINFO_NUMBER(NR_FREE_PAGES);
VMCOREINFO_NUMBER(PG_lru);
- VMCOREINFO_NUMBER(PG_private);
VMCOREINFO_NUMBER(PG_swapcache);
VMCOREINFO_NUMBER(PG_swapbacked);
#define PAGE_SLAB_MAPCOUNT_VALUE (PGTY_slab << 24)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index d1ce061601bcd..4cb7d9bc47544 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4831,8 +4831,9 @@ static int split_huge_pages_pid(int pid, unsigned long vaddr_start,
* For folios with private, split_huge_page_to_list_to_order()
* will try to drop it before split and then check if the folio
* can be split or not. So skip the check here.
+ * data_race() is used to read folio->private locklessly.
*/
- if (!folio_test_private(folio) &&
+ if (!data_race(folio_test_fs_private(folio)) &&
folio_expected_ref_count(folio) != folio_ref_count(folio))
goto next;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index a69bd463b1aef..08a538739d785 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1445,10 +1445,10 @@ void add_hugetlb_folio(struct hstate *h, struct folio *folio,
}
__folio_set_hugetlb(folio);
- folio_change_private(folio, NULL);
+ folio->private = NULL;
/*
- * We have to set hugetlb_vmemmap_optimized again as above
- * folio_change_private(folio, NULL) cleared it.
+ * The hugetlb flags live in folio->private and are cleared by the above
+ * assignment. Restore the hugetlb_vmemmap_optimized flag.
*/
folio_set_hugetlb_vmemmap_optimized(folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index a369d0c95c386..77b89d0909f1f 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -835,7 +835,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
*/
if (folio_test_swapcache(folio))
folio_clear_swapcache(folio);
- folio_clear_private(folio);
/* page->private contains hugetlb specific flags */
if (!folio_test_hugetlb(folio))
@@ -1327,7 +1326,7 @@ static int migrate_folio_unmap(new_folio_t get_new_folio,
* free the metadata, so the page can be freed.
*/
if (!src->mapping) {
- if (folio_test_private(src)) {
+ if (folio_test_fs_private(src)) {
try_to_free_buffers(src);
goto out;
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index eeab25d6ce364..e7389020d2a56 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2705,7 +2705,8 @@ bool filemap_dirty_folio(struct address_space *mapping, struct folio *folio)
if (folio_test_set_dirty(folio))
return false;
- __folio_mark_dirty(folio, mapping, !folio_test_private(folio));
+ /* data_race() is used to read folio->private locklessly */
+ __folio_mark_dirty(folio, mapping, !data_race(folio_test_fs_private(folio)));
if (mapping->host) {
/* !PageAnon && !swapper_space */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 40d3f1b48a74c..47352bafc5b6f 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -978,7 +978,7 @@ static void folio_check_dirty_writeback(struct folio *folio,
*writeback = folio_test_writeback(folio);
/* Verify dirty/writeback state if the filesystem supports it */
- if (!folio_test_private(folio))
+ if (!folio_test_fs_private(folio))
return;
mapping = folio_mapping(folio);
diff --git a/mm/zpdesc.h b/mm/zpdesc.h
index b8258dc78548d..4fd81c2e80769 100644
--- a/mm/zpdesc.h
+++ b/mm/zpdesc.h
@@ -26,8 +26,8 @@
* with memcg_data.
*
* Page flags used:
- * * PG_private identifies the first component page.
* * PG_locked is used by page migration code.
+ * The first component page has zpdesc->zspage->first_zpdesc == zpdesc
*/
struct zpdesc {
unsigned long flags;
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 825022a7a328f..24526185fd31f 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -290,11 +290,6 @@ struct zs_pool {
atomic_t compaction_in_progress;
};
-static inline void zpdesc_set_first(struct zpdesc *zpdesc)
-{
- SetPagePrivate(zpdesc_page(zpdesc));
-}
-
static inline void zpdesc_inc_zone_page_state(struct zpdesc *zpdesc)
{
inc_zone_page_state(zpdesc_page(zpdesc), NR_ZSPAGES);
@@ -476,11 +471,6 @@ static void record_obj(unsigned long handle, unsigned long obj)
WRITE_ONCE(*(unsigned long *)handle, obj);
}
-static inline bool __maybe_unused is_first_zpdesc(struct zpdesc *zpdesc)
-{
- return PagePrivate(zpdesc_page(zpdesc));
-}
-
/* Protected by class->lock */
static inline int get_zspage_inuse(struct zspage *zspage)
{
@@ -496,7 +486,8 @@ static struct zpdesc *get_first_zpdesc(struct zspage *zspage)
{
struct zpdesc *first_zpdesc = zspage->first_zpdesc;
- VM_BUG_ON_PAGE(!is_first_zpdesc(first_zpdesc), zpdesc_page(first_zpdesc));
+ /* the first zpdesc must point back to this zspage */
+ VM_WARN_ON_ONCE_PAGE(first_zpdesc->zspage != zspage, zpdesc_page(first_zpdesc));
return first_zpdesc;
}
@@ -838,7 +829,8 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,
struct zspage *zspage = get_zspage(zpdesc);
if (unlikely(ZsHugePage(zspage))) {
- VM_BUG_ON_PAGE(!is_first_zpdesc(zpdesc), zpdesc_page(zpdesc));
+ /* only first zpdesc holds the handle */
+ VM_WARN_ON_ONCE_PAGE(zspage->first_zpdesc != zpdesc, zpdesc_page(zpdesc));
handle = zpdesc->handle;
} else
handle = *(unsigned long *)obj;
@@ -853,9 +845,6 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,
static void reset_zpdesc(struct zpdesc *zpdesc)
{
- struct page *page = zpdesc_page(zpdesc);
-
- ClearPagePrivate(page);
zpdesc->zspage = NULL;
zpdesc->next = NULL;
/* PageZsmalloc is sticky until the page is freed to the buddy. */
@@ -1006,8 +995,8 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,
* 1. all pages are linked together using zpdesc->next
* 2. each sub-page point to zspage using zpdesc->zspage
*
- * we set PG_private to identify the first zpdesc (i.e. no other zpdesc
- * has this flag set).
+ * The first zpdesc has its zspage->first_zpdesc set to itself, no
+ * other zpdesc has this set.
*/
for (i = 0; i < nr_zpdescs; i++) {
zpdesc = zpdescs[i];
@@ -1015,7 +1004,6 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,
zpdesc->next = NULL;
if (i == 0) {
zspage->first_zpdesc = zpdesc;
- zpdesc_set_first(zpdesc);
if (unlikely(class->objs_per_zspage == 1 &&
class->pages_per_zspage == 1))
SetZsHugePage(zspage);
diff --git a/tools/mm/page-types.c b/tools/mm/page-types.c
index 7fc5a8be5997f..47e4781c5fc38 100644
--- a/tools/mm/page-types.c
+++ b/tools/mm/page-types.c
@@ -73,7 +73,6 @@
#define KPF_RESERVED 32
#define KPF_MLOCKED 33
#define KPF_OWNER_2 34
-#define KPF_PRIVATE 35
#define KPF_PRIVATE_2 36
#define KPF_OWNER_PRIVATE 37
#define KPF_ARCH 38
@@ -131,7 +130,6 @@ static const char * const page_flag_names[] = {
[KPF_RESERVED] = "r:reserved",
[KPF_MLOCKED] = "m:mlocked",
[KPF_OWNER_2] = "d:owner_2",
- [KPF_PRIVATE] = "P:private",
[KPF_PRIVATE_2] = "p:private_2",
[KPF_OWNER_PRIVATE] = "O:owner_private",
[KPF_ARCH] = "h:arch",
|
| 21/3 | 2026/09/08 03:53 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16368 output: 35 thoughts: 575 Thoughts: **Analyzing the `PG_private` Removal Patch** Okay, let's dive into this patch series. My initial thought is to meticulously dissect it, paying close attention to the implications, especially in the context of KASAN and KMSAN. The goal here is to grasp the ramifications of removing `PG_private` and replacing its usage throughout the kernel. First, I see the core change is the removal of the `PG_private` page flag. Instead of relying on `PagePrivate`, `SetPagePrivate`, and `ClearPagePrivate`, the code now directly checks whether the `page->private` or `folio->private` pointer is non-NULL or non-zero. Or it uses helpers like `folio_test_fs_private()` and `folio_test_private()`. This is a pretty significant shift in how we manage private data associated with pages. Now, let's go through the specific code changes. I'm noting the following: `erofs/zdata.c` is simplifying its readahead loop, replacing the old method with `readahead_folio_last(rac)`. In `fs/pagemap.h`, `readahead_control` gets a new member, `bool _forward;`, and a couple of helper functions. The `drivers/xen` directory is also touched with the removal of settings or cleaning `page->private`, as well as `fs/crypto/crypto.c`. I see adjustments in `fs/erofs/data.c` to bias `folio->private` so it stays non-NULL, and a specific use of `(void *)BIT(PAGE_PRIVATE_NOT_POINTER)` in `fs/f2fs/f2fs.h`. `fs/nfs/write.c` is dropping `folio_set_private`/`folio_clear_private` and finally, `mm/zsmalloc.c` is also getting rid of `PG_private` to identify the first zpdesc, now checking `zspage->first_zpdesc == zpdesc`. My next critical concern is uninitialized memory. With this change, I *need* to be sure that we aren't introducing any vulnerabilities. Specifically, I'm thinking about: 1. **Kernel structures exposed to userspace:** Any instance of `copy_to_user`, `put_user`, netlink communication, ioctls, or any other means of sending kernel data to userspace needs careful scrutiny. Uninitialized memory here is a security risk. 2. **`readahead_control` structure:** How and where is this structure initialized? I need to track down the `DEFINE_READAHEAD` macro and how `readahead_control` is allocated and initialized. I will be sure to check `include/linux/pagemap.h` around line 1415 and its context. I need to confirm, with extreme certainty, that we haven't created any situations where we're passing uninitialized data to userspace. |
| 22/3 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "struct readahead_control",
"PathPrefix": "include/linux/pagemap.h"
}Results: {
"Output": "include/linux/pagemap.h=1377=static inline bool filemap_range_needs_writeback(struct address_space *mapping,\n--\ninclude/linux/pagemap.h-1389-/**\ninclude/linux/pagemap.h:1390: * struct readahead_control - Describes a readahead request.\ninclude/linux/pagemap.h-1391- *\n--\ninclude/linux/pagemap.h-1404- */\ninclude/linux/pagemap.h:1405:struct readahead_control {\ninclude/linux/pagemap.h-1406-\tstruct file *file;\n--\ninclude/linux/pagemap.h-1419-#define DEFINE_READAHEAD(ractl, f, r, m, i)\t\t\t\t\\\ninclude/linux/pagemap.h:1420:\tstruct readahead_control ractl = {\t\t\t\t\\\ninclude/linux/pagemap.h-1421-\t\t.file = f,\t\t\t\t\t\t\\\n--\ninclude/linux/pagemap.h-1428-\ninclude/linux/pagemap.h:1429:void page_cache_ra_unbounded(struct readahead_control *,\ninclude/linux/pagemap.h-1430-\t\tunsigned long nr_to_read, unsigned long lookahead_count);\ninclude/linux/pagemap.h:1431:void page_cache_sync_ra(struct readahead_control *, unsigned long req_count);\ninclude/linux/pagemap.h:1432:void page_cache_async_ra(struct readahead_control *, struct folio *,\ninclude/linux/pagemap.h-1433-\t\tunsigned long req_count);\ninclude/linux/pagemap.h:1434:void readahead_expand(struct readahead_control *ractl,\ninclude/linux/pagemap.h-1435-\t\t loff_t new_start, size_t new_len);\n--\ninclude/linux/pagemap.h=1473=void page_cache_async_readahead(struct address_space *mapping,\n--\ninclude/linux/pagemap.h-1480-\ninclude/linux/pagemap.h:1481:static inline void __readahead_advance(struct readahead_control *rac)\ninclude/linux/pagemap.h-1482-{\n--\ninclude/linux/pagemap.h-1489-\ninclude/linux/pagemap.h:1490:static inline struct folio *__readahead_folio(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1491-{\n--\ninclude/linux/pagemap.h-1515- */\ninclude/linux/pagemap.h:1516:static inline struct folio *readahead_folio(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1517-{\n--\ninclude/linux/pagemap.h-1535- */\ninclude/linux/pagemap.h:1536:static inline struct folio *readahead_folio_last(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1537-{\n--\ninclude/linux/pagemap.h-1557-\ninclude/linux/pagemap.h:1558:static inline unsigned int __readahead_batch(struct readahead_control *rac,\ninclude/linux/pagemap.h-1559-\t\tstruct page **array, unsigned int array_sz)\n--\ninclude/linux/pagemap.h-1588- */\ninclude/linux/pagemap.h:1589:static inline loff_t readahead_pos(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1590-{\n--\ninclude/linux/pagemap.h-1597- */\ninclude/linux/pagemap.h:1598:static inline size_t readahead_length(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1599-{\n--\ninclude/linux/pagemap.h-1606- */\ninclude/linux/pagemap.h:1607:static inline pgoff_t readahead_index(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1608-{\n--\ninclude/linux/pagemap.h-1615- */\ninclude/linux/pagemap.h:1616:static inline unsigned int readahead_count(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1617-{\n--\ninclude/linux/pagemap.h-1624- */\ninclude/linux/pagemap.h:1625:static inline size_t readahead_batch_length(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1626-{\n"
} |
| 23/3 | 2026/09/08 03:53 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 18896 output: 38 thoughts: 37 Thoughts: Okay, let's dive into this. Here's how I'd approach this task. **Examining readahead_control Structure and Initialization** Alright, my focus is now on the `struct readahead_control`. Time to refresh my memory on where this critical piece lives. I need to pinpoint the definition of `readahead_control` within `include/linux/pagemap.h` and then track down its initializations. I know this structure is fundamental to the kernel's prefetching mechanisms, so I need a clear understanding of its components and how they're set up. My plan is to navigate to `mm/readahead.c` to see how those initializations actually happen, and how those values get set and used in the broader readahead process. I'll be looking for where the fields within `struct readahead_control` are populated. This is key to understanding how the kernel makes decisions about *what* to prefetch, *when* to prefetch, and *how much* to prefetch. I need to understand the variables and structures this is linked to. |
| 24/3 | 2026/09/08 03:53 | tool | read-file |
0mArgs:{
"File": "include/linux/pagemap.h",
"FirstLine": 1400,
"LineCount": 40
}Results: {
"Contents": "1400:\t * @file: The file, used primarily by network filesystems for authentication.\n1401:\t *\t May be NULL if invoked internally by the filesystem.\n1402:\t * @mapping: Readahead this filesystem object.\n1403:\t * @ra: File readahead state. May be NULL.\n1404:\t */\n1405:\tstruct readahead_control {\n1406:\t\tstruct file *file;\n1407:\t\tstruct address_space *mapping;\n1408:\t\tstruct file_ra_state *ra;\n1409:\t/* private: use the readahead_* accessors instead */\n1410:\t\tpgoff_t _index;\n1411:\t\tunsigned int _nr_pages;\n1412:\t\tunsigned int _batch_count;\n1413:\t\tbool dropbehind;\n1414:\t\tbool _workingset;\n1415:\t\tunsigned long _pflags;\n1416:\t\tbool _forward;\n1417:\t};\n1418:\t\n1419:\t#define DEFINE_READAHEAD(ractl, f, r, m, i)\t\t\t\t\\\n1420:\t\tstruct readahead_control ractl = {\t\t\t\t\\\n1421:\t\t\t.file = f,\t\t\t\t\t\t\\\n1422:\t\t\t.mapping = m,\t\t\t\t\t\t\\\n1423:\t\t\t.ra = r,\t\t\t\t\t\t\\\n1424:\t\t\t._index = i,\t\t\t\t\t\t\\\n1425:\t\t}\n1426:\t\n1427:\t#define VM_READAHEAD_PAGES\t(SZ_128K / PAGE_SIZE)\n1428:\t\n1429:\tvoid page_cache_ra_unbounded(struct readahead_control *,\n1430:\t\t\tunsigned long nr_to_read, unsigned long lookahead_count);\n1431:\tvoid page_cache_sync_ra(struct readahead_control *, unsigned long req_count);\n1432:\tvoid page_cache_async_ra(struct readahead_control *, struct folio *,\n1433:\t\t\tunsigned long req_count);\n1434:\tvoid readahead_expand(struct readahead_control *ractl,\n1435:\t\t\t loff_t new_start, size_t new_len);\n1436:\t\n1437:\t/**\n1438:\t * page_cache_sync_readahead - generic file readahead\n1439:\t * @mapping: address_space which holds the pagecache and I/O vectors\n"
} |
| 25/3 | 2026/09/08 03:53 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 19870 output: 31 thoughts: 20 Thoughts: Okay, let's dive into this `struct readahead_control` analysis. **Initial Exploration of `struct readahead_control`** Alright, my immediate goal is to get a comprehensive view of `struct readahead_control`. Where is this struct even *used*? That means I need to pinpoint every single instance where it's initialized, declared, or even just mentioned in the code. I'm thinking I'll need a way to track down all the declarations and initializations. I can't really do anything without knowing all of the places where it comes into play. I'll need to know which functions use the struct directly. This is my starting point for understanding how this control structure functions. This could be a lengthy process depending on the code base. I'll need to figure out what tools I need to use to make the process more efficient. |
| 26/3 | 2026/09/08 03:53 | tool | grepper |
0mArgs:{
"Expression": "struct readahead_control [a-zA-Z0-9_]+"
}Results: {
"Output": "include/linux/pagemap.h=1405=struct readahead_control {\n--\ninclude/linux/pagemap.h-1419-#define DEFINE_READAHEAD(ractl, f, r, m, i)\t\t\t\t\\\ninclude/linux/pagemap.h:1420:\tstruct readahead_control ractl = {\t\t\t\t\\\ninclude/linux/pagemap.h-1421-\t\t.file = f,\t\t\t\t\t\t\\\n"
} |
| 27/3 | 2026/09/08 03:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 20234 output: 35 thoughts: 546 Thoughts: **My Analysis of Readahead Control Structure and Page Flags** Okay, so I'm looking closely at how this `DEFINE_READAHEAD` macro works. It seems like the core of the `struct readahead_control ractl` is initialized using a designated initializer. This is good; it explicitly zeros all the fields that aren't specifically set in the initialization. Fields like `_batch_count`, `dropbehind`, `_workingset`, `_pflags`, and `_forward` are all getting a clean start. That's a solid foundation. Now, I'm digging into `__readahead_advance`. It appears this function is the central point for managing the readahead progression. The key here is the conditional within `__readahead_advance`. It checks `rac->_forward`. Initially, `_batch_count` will be zero. If `_forward` is false, the indexing operation is skipped. The important point I'm seeing is that `__readahead_advance` decrements `_nr_pages` by `_batch_count` and resets `_batch_count` to zero, which makes sense. `readahead_folio` sets `_forward` to true, while `readahead_folio_last` sets it to false. So, in the first call to `readahead_advance`, nothing is done because the batch count is zero and the forward flag is zero. Afterwards the forward flag is set, which is the intention. So far, so good. Everything seems to be initialized and managed consistently. Now, I need to zoom out a bit. I'm going to look at other parts of the diff that might interact with these changes, especially anything related to user-facing interfaces like `/proc/kpageflags` or similar. I'm focusing on those user-space interactions now. I'm glancing through `fs/proc/page.c` and specifically focusing on `stable_page_flags`. The important part here is the initialization of the `u` variable to 0. It is a good practice to ensure flags are clear initially. Let's see how `stable_page_flags` is being used elsewhere. |
| 28/3 | 2026/09/08 03:54 | tool | read-file |
0mArgs:{
"File": "fs/proc/page.c",
"FirstLine": 215,
"LineCount": 40
}Results: {
"Contents": " 215:\t\tu |= kpf_copy_bit(k, KPF_REFERENCED,\tPG_referenced);\n 216:\t\tu |= kpf_copy_bit(k, KPF_ACTIVE,\tPG_active);\n 217:\t\tu |= kpf_copy_bit(k, KPF_RECLAIM,\tPG_reclaim);\n 218:\t\n 219:\t\tif (folio_test_swapcache(folio))\n 220:\t\t\tu |= BIT_ULL(KPF_SWAPCACHE);\n 221:\t\n 222:\t\tu |= kpf_copy_bit(k, KPF_SWAPBACKED,\tPG_swapbacked);\n 223:\t\tu |= kpf_copy_bit(k, KPF_UNEVICTABLE,\tPG_unevictable);\n 224:\t\tu |= kpf_copy_bit(k, KPF_MLOCKED,\tPG_mlocked);\n 225:\t\n 226:\t#ifdef CONFIG_MEMORY_FAILURE\n 227:\t\tif (u \u0026 BIT_ULL(KPF_HUGE))\n 228:\t\t\tu |= kpf_copy_bit(k, KPF_HWPOISON,\tPG_hwpoison);\n 229:\t\telse\n 230:\t\t\tu |= kpf_copy_bit(ps.page_snapshot.flags.f, KPF_HWPOISON, PG_hwpoison);\n 231:\t#endif\n 232:\t\n 233:\t\tu |= kpf_copy_bit(k, KPF_RESERVED,\tPG_reserved);\n 234:\t\tu |= kpf_copy_bit(k, KPF_OWNER_2,\tPG_owner_2);\n 235:\t\tu |= kpf_copy_bit(k, KPF_PRIVATE_2,\tPG_private_2);\n 236:\t\tu |= kpf_copy_bit(k, KPF_OWNER_PRIVATE,\tPG_owner_priv_1);\n 237:\t\tu |= kpf_copy_bit(k, KPF_ARCH,\t\tPG_arch_1);\n 238:\t#ifdef CONFIG_ARCH_USES_PG_ARCH_2\n 239:\t\tu |= kpf_copy_bit(k, KPF_ARCH_2,\tPG_arch_2);\n 240:\t#endif\n 241:\t#ifdef CONFIG_ARCH_USES_PG_ARCH_3\n 242:\t\tu |= kpf_copy_bit(k, KPF_ARCH_3,\tPG_arch_3);\n 243:\t#endif\n 244:\t\n 245:\t\treturn u;\n 246:\t}\n 247:\tEXPORT_SYMBOL_GPL(stable_page_flags);\n 248:\t\n 249:\t/* /proc/kpageflags - an array exposing page flags\n 250:\t *\n 251:\t * Each entry is a u64 representing the corresponding\n 252:\t * physical page flags.\n 253:\t */\n 254:\tstatic ssize_t kpageflags_read(struct file *file, char __user *buf,\n"
} |
| 29/3 | 2026/09/08 03:54 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 22090 output: 354 thoughts: 1525 Thoughts: **KMSAN Assessment: PG_private Removal and page->private Refactoring** Alright, let's break this down. My understanding is that this patch is doing some internal refactoring of how we manage page flags, specifically removing the `PG_private` flag and moving towards direct manipulation and checking of `page->private` or `folio->private` fields, and providing a helper to manage readahead. The goal seems to be a cleaner and more direct approach. I've seen this kind of thing before; it can simplify memory management logic, but we need to make sure we don't introduce any new risks. My primary concern, given KMSAN, is whether this change introduces or exposes uninitialized memory read issues or leads to the leakage of kernel data to user space. It is key to evaluate this because KMSAN is designed to catch these problems. So I've gone through a number of examples across different subsystems. I started by looking at the specific function (`stable_page_flags()`) where the `PG_private` bit is being removed. The intent seems to be to set the relevant bit to zero, there are no reads. Okay, that's clean. Then, I went through some example uses of the current `page->private` field to see how they're being managed. I've looked at `fs/erofs/data.c` for initialization, and it looks like it's initialized. I also looked at `fs/erofs/zdata.c`, `fs/f2fs/f2fs.h`, `kernel/events/ring_buffer.c`, `arch/x86/events/intel/bts.c`, `arch/x86/events/intel/pt.c`, `drivers/xen/balloon.c`, and `drivers/xen/grant-table.c`. I focused on the following: * **Initialization:** Are these variables set to zero? * **Reads:** Are the `page->private` fields only being read after they are set? * **Zeroing:** Are the `page->private` fields cleared when a page is released? In all the cases I inspected, I see that these fields are either initialized or explicitly cleared. I see that there is no memory being leaked in this process. No uninitialized memory is being read, leaked or passed to userspace. Specifically: * In the EROFS examples, `u` is initialized before being attached to `folio->private`. * The readahead code avoids chaining folios through `folio->private`. * The F2FS example uses `set_page_private` which looks like it's correctly initializing and setting the bit. * In the ring buffer allocation, the `order` is used to communicate the allocation size, and if `order` is 0, it means we did not get a high order page. * In Xen, the `page->private` is explicitly cleared before returning the page. So, the code seems well-behaved in terms of initialization, use, and clearing of these fields. This patch *does not* appear to introduce any uninitialized memory risks or data leaks to user space. It also doesn't seem to be changing control flow based on uninitialized data, or doing complex allocations and leaving uninitialized data around. That leaves the question: Do we need a KMSAN session? No. KMSAN is for catching reads of uninitialized memory. This is not the type of bug this patch is likely to introduce. Any bugs this *could* introduce are memory management issues (refcount bugs, use-after-free, double free, wrong page ordering, lifetime bugs), which are the domain of KASAN, assertions, VM_BUG_ONs and standard kernel testing and debugging. Therefore, `NeedsKMSAN = false`. |
| 30/3 | 2026/09/08 03:54 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series removes the `PG_private` page flag across the kernel memory management subsystem, VFS, filesystems (EROFS, Ceph, NFS, F2FS, UBIFS), and drivers (Xen, perf/intel_pt/bts), replacing `PagePrivate` / `SetPagePrivate` / `ClearPagePrivate` with direct inspections of `page-\u003eprivate` / `folio-\u003eprivate` and helpers like `folio_test_fs_private()` and `readahead_folio_last()`.\n\nKMSAN Applicability Analysis:\n1. No uninitialized memory reads or leaks: The patch does not introduce or expose uninitialized heap, stack, or page memory. Structures like `struct readahead_control` (which gained `_forward`) are initialized with designated initializers (e.g. `DEFINE_READAHEAD`), zeroing all fields.\n2. User-space copies: The only user-visible modification in `stable_page_flags` (`/proc/kpageflags`) safely removes the `PG_private` bit mapping from an already zero-initialized integer (`u`), with no possibility of uninitialized kernel memory leakage.\n3. Nature of potential issues: Any bugs resulting from this refactoring would pertain to page state transitions, refcount mismatches, page-private lifecycle management, or concurrency races, which are detected by standard assertions (VM_BUG_ON, VM_WARN_ON), KASAN, or LOCKDEP rather than uninitialized memory detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series removes the `PG_private` page flag across the kernel memory management subsystem, VFS, filesystems (EROFS, Ceph, NFS, F2FS, UBIFS), and drivers (Xen, perf/intel_pt/bts), replacing `PagePrivate` / `SetPagePrivate` / `ClearPagePrivate` with direct inspections of `page-\u003eprivate` / `folio-\u003eprivate` and helpers like `folio_test_fs_private()` and `readahead_folio_last()`.\n\nKMSAN Applicability Analysis:\n1. No uninitialized memory reads or leaks: The patch does not introduce or expose uninitialized heap, stack, or page memory. Structures like `struct readahead_control` (which gained `_forward`) are initialized with designated initializers (e.g. `DEFINE_READAHEAD`), zeroing all fields.\n2. User-space copies: The only user-visible modification in `stable_page_flags` (`/proc/kpageflags`) safely removes the `PG_private` bit mapping from an already zero-initialized integer (`u`), with no possibility of uninitialized kernel memory leakage.\n3. Nature of potential issues: Any bugs resulting from this refactoring would pertain to page state transitions, refcount mismatches, page-private lifecycle management, or concurrency races, which are detected by standard assertions (VM_BUG_ON, VM_WARN_ON), KASAN, or LOCKDEP rather than uninitialized memory detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|