| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/14 03:28 | flow | patch-triage |
5mArgs:null Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"F2FS_FS",
"F2FS_FS_COMPRESSION",
"ZRAM",
"ZSWAP",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE",
"COMPACTION"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_end",
"erofs_onlinefolio_init",
"f2fs_write_compressed_pages",
"f2fs_compress_write_end_io",
"get_first_zpdesc",
"create_page_chain",
"migrate_folio_unmap",
"split_huge_pages_pid",
"add_hugetlb_folio"
],
"KMSANReasoning": "The patch series removes the PG_private page flag across the kernel in favor of checking folio-\u003eprivate directly (or using helper functions like folio_has_attached_private).\n\nEvaluating the changes under KMSAN vs KASAN criteria:\n1. No uninitialized memory usage or branching on uninitialized variables is introduced: All struct page and struct folio instances are kernel-managed structures zero-initialized at boot or page allocation.\n2. In readahead_control, the new '_forward' field is zero-initialized by DEFINE_READAHEAD (via standard C designated-initializer zeroing) and properly set before use.\n3. In /proc/kpageflags (fs/proc/page.c), removing the KPF_PRIVATE bit extraction does not expose uninitialized bits since the return mask 'u' is explicitly zero-initialized.\n4. No new user-space copy boundaries, ioctls, or padding-sensitive structures are created or modified.\n\nAny potential bugs resulting from this refactor would involve page lifecycle tracking, folio refcounting discrepancies, or NULL pointer dereferences, which fall under the purview of KASAN and standard kernel debuggers (e.g., CONFIG_DEBUG_VM). Spawning a dedicated KMSAN session is therefore not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch series removes the PG_private page flag entirely across core memory management, the page cache, and filesystems (replacing flag checks with folio-\u003eprivate inspections, folio_has_attached_private(), and custom tracking). It modifies core mm behavior (reclaim, migration, THP splitting, zsmalloc, hugetlb), introduces a new reverse readahead iterator (readahead_folio_last) used by EROFS, restructures f2fs compression folio handling, and adds/modifies runtime assertions (VM_WARN_ON_ONCE_PAGE in zsmalloc, DBG_BUGON in erofs). These extensive refactorings in core mm and reachable filesystem code paths warrant fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/14 03:28 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 5613ada8c0ba47c4af37f50b4afcfb53065f9148\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Sep 14 03:28:04 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..7b706d3e54901 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@@ -533,12 +533,11 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)\n \n static void free_buffers(struct page *page)\n {\n-\tstruct buffer_head *bh;\n+\tstruct buffer_head *bh = (struct buffer_head *)page_private(page);\n \n-\tif (!PagePrivate(page))\n+\tif (!bh)\n \t\treturn;\n \n-\tbh = page_buffers(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/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..1aca181d28c62 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_private(page);\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/compress.c b/fs/f2fs/compress.c\nindex ce88092d9ce26..09d9b8d0fdcce 100644\n--- a/fs/f2fs/compress.c\n+++ b/fs/f2fs/compress.c\n@@ -1064,13 +1064,15 @@ static void cancel_cluster_writeback(struct compress_ctx *cc,\n \n \t/* Cancel writeback and stay locked. */\n \tfor (i = 0; i \u003c cc-\u003ecluster_size; i++) {\n+\t\tstruct folio *folio = page_folio(cc-\u003erpages[i]);\n+\n \t\tif (i \u003c submitted) {\n \t\t\tinode_inc_dirty_pages(cc-\u003einode);\n-\t\t\tlock_page(cc-\u003erpages[i]);\n+\t\t\tfolio_lock(folio);\n \t\t}\n-\t\tclear_page_private_gcing(cc-\u003erpages[i]);\n-\t\tif (folio_test_writeback(page_folio(cc-\u003erpages[i])))\n-\t\t\tend_page_writeback(cc-\u003erpages[i]);\n+\t\tfolio_clear_f2fs_gcing(folio);\n+\t\tif (folio_test_writeback(folio))\n+\t\t\tfolio_end_writeback(folio);\n \t}\n }\n \n@@ -1078,11 +1080,15 @@ static void set_cluster_dirty(struct compress_ctx *cc)\n {\n \tint i;\n \n-\tfor (i = 0; i \u003c cc-\u003ecluster_size; i++)\n-\t\tif (cc-\u003erpages[i]) {\n-\t\t\tset_page_dirty(cc-\u003erpages[i]);\n-\t\t\tset_page_private_gcing(cc-\u003erpages[i]);\n-\t\t}\n+\tfor (i = 0; i \u003c cc-\u003ecluster_size; i++) {\n+\t\tstruct folio *folio;\n+\n+\t\tif (!cc-\u003erpages[i])\n+\t\t\tcontinue;\n+\t\tfolio = page_folio(cc-\u003erpages[i]);\n+\t\tfolio_mark_dirty(folio);\n+\t\tfolio_set_f2fs_gcing(folio);\n+\t}\n }\n \n static int prepare_compress_overwrite(struct compress_ctx *cc,\n@@ -1281,7 +1287,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,\n \t\t.op = REQ_OP_WRITE,\n \t\t.op_flags = wbc_to_write_flags(wbc),\n \t\t.old_blkaddr = NEW_ADDR,\n-\t\t.page = NULL,\n+\t\t.folio = NULL,\n \t\t.encrypted_page = NULL,\n \t\t.compressed_page = NULL,\n \t\t.io_type = io_type,\n@@ -1370,7 +1376,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,\n \t\tblock_t blkaddr;\n \n \t\tblkaddr = f2fs_data_blkaddr(\u0026dn);\n-\t\tfio.page = cc-\u003erpages[i];\n+\t\tfio.folio = page_folio(cc-\u003erpages[i]);\n \t\tfio.old_blkaddr = blkaddr;\n \n \t\t/* cluster header */\n@@ -1476,9 +1482,12 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)\n \t}\n \n \tfor (i = 0; i \u003c cic-\u003enr_rpages; i++) {\n+\t\tstruct folio *rfolio;\n+\n \t\tWARN_ON(!cic-\u003erpages[i]);\n-\t\tclear_page_private_gcing(cic-\u003erpages[i]);\n-\t\tend_page_writeback(cic-\u003erpages[i]);\n+\t\trfolio = page_folio(cic-\u003erpages[i]);\n+\t\tfolio_clear_f2fs_gcing(rfolio);\n+\t\tfolio_end_writeback(rfolio);\n \t}\n \n \tpage_array_free(sbi, cic-\u003erpages, cic-\u003enr_rpages);\ndiff --git a/fs/f2fs/data.c b/fs/f2fs/data.c\nindex 21f396ebe22ca..ca8232a9095f8 100644\n--- a/fs/f2fs/data.c\n+++ b/fs/f2fs/data.c\n@@ -2923,7 +2923,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)\n \t\treturn true;\n \n \tif (fio) {\n-\t\tif (page_private_gcing(fio-\u003epage))\n+\t\tif (folio_test_f2fs_gcing(fio-\u003efolio))\n \t\t\treturn true;\n \t\tif (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) \u0026\u0026\n \t\t\tf2fs_is_checkpointed_data(sbi, fio-\u003eold_blkaddr)))\ndiff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h\nindex 9940a6cecf1a2..85937de3d7016 100644\n--- a/fs/f2fs/f2fs.h\n+++ b/fs/f2fs/f2fs.h\n@@ -1357,10 +1357,7 @@ struct f2fs_io_info {\n \tblk_opf_t op_flags;\t/* req_flag_bits */\n \tblock_t new_blkaddr;\t/* new block address to be written */\n \tblock_t old_blkaddr;\t/* old block address before Cow */\n-\tunion {\n-\t\tstruct page *page;\t/* page to be written */\n-\t\tstruct folio *folio;\n-\t};\n+\tstruct folio *folio;\t/* folio to be written */\n \tstruct page *encrypted_page;\t/* encrypted page */\n \tstruct page *compressed_page;\t/* compressed page */\n \tstruct list_head list;\t\t/* serialize IOs */\n@@ -1613,27 +1610,27 @@ static inline void f2fs_set_bit(unsigned int nr, char *addr);\n static inline void f2fs_clear_bit(unsigned int nr, char *addr);\n \n /*\n- * Layout of f2fs page.private:\n+ * Layout of f2fs folio-\u003eprivate:\n *\n * Layout A: lowest bit should be 1\n * | bit0 = 1 | bit1 | bit2 | ... | bit MAX | private data .... |\n- * bit 0\tPAGE_PRIVATE_NOT_POINTER\n- * bit 1\tPAGE_PRIVATE_ONGOING_MIGRATION\n- * bit 2\tPAGE_PRIVATE_INLINE_INODE\n- * bit 3\tPAGE_PRIVATE_REF_RESOURCE\n- * bit 4\tPAGE_PRIVATE_ATOMIC_WRITE\n+ * bit 0\tF2FS_FOLIO_PRIVATE_NOT_POINTER\n+ * bit 1\tF2FS_FOLIO_PRIVATE_ONGOING_MIGRATION\n+ * bit 2\tF2FS_FOLIO_PRIVATE_INLINE_INODE\n+ * bit 3\tF2FS_FOLIO_PRIVATE_REF_RESOURCE\n+ * bit 4\tF2FS_FOLIO_PRIVATE_ATOMIC_WRITE\n * bit 5-\tf2fs private data\n *\n * Layout B: lowest bit should be 0\n- * page.private is a wrapped pointer.\n+ * folio-\u003eprivate is a wrapped pointer.\n */\n enum {\n-\tPAGE_PRIVATE_NOT_POINTER,\t\t/* private contains non-pointer data */\n-\tPAGE_PRIVATE_ONGOING_MIGRATION,\t\t/* data page which is on-going migrating */\n-\tPAGE_PRIVATE_INLINE_INODE,\t\t/* inode page contains inline data */\n-\tPAGE_PRIVATE_REF_RESOURCE,\t\t/* dirty page has referenced resources */\n-\tPAGE_PRIVATE_ATOMIC_WRITE,\t\t/* data page from atomic write path */\n-\tPAGE_PRIVATE_MAX\n+\tF2FS_FOLIO_PRIVATE_NOT_POINTER,\t\t/* private contains non-pointer data */\n+\tF2FS_FOLIO_PRIVATE_ONGOING_MIGRATION,\t\t/* data page which is on-going migrating */\n+\tF2FS_FOLIO_PRIVATE_INLINE_INODE,\t\t/* inode page contains inline data */\n+\tF2FS_FOLIO_PRIVATE_REF_RESOURCE,\t\t/* dirty page has referenced resources */\n+\tF2FS_FOLIO_PRIVATE_ATOMIC_WRITE,\t\t/* data page from atomic write path */\n+\tF2FS_FOLIO_PRIVATE_MAX\n };\n \n /* For compression */\n@@ -2681,86 +2678,68 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,\n \treturn -ENOSPC;\n }\n \n-#define PAGE_PRIVATE_GET_FUNC(name, flagname) \\\n+#define F2FS_FOLIO_PRIVATE_GET_FUNC(name, flagname) \\\n static inline bool folio_test_f2fs_##name(const struct folio *folio)\t\\\n {\t\t\t\t\t\t\t\t\t\\\n \tunsigned long priv = (unsigned long)folio-\u003eprivate;\t\t\\\n-\tunsigned long v = (1UL \u003c\u003c PAGE_PRIVATE_NOT_POINTER) |\t\t\\\n-\t\t\t (1UL \u003c\u003c PAGE_PRIVATE_##flagname);\t\t\\\n+\tunsigned long v = (1UL \u003c\u003c F2FS_FOLIO_PRIVATE_NOT_POINTER) |\t\\\n+\t\t\t (1UL \u003c\u003c F2FS_FOLIO_PRIVATE_##flagname);\t\\\n \treturn (priv \u0026 v) == v;\t\t\t\t\t\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-\t\ttest_bit(PAGE_PRIVATE_NOT_POINTER, \u0026page_private(page)) \u0026\u0026 \\\n-\t\ttest_bit(PAGE_PRIVATE_##flagname, \u0026page_private(page)); \\\n }\n \n-#define PAGE_PRIVATE_SET_FUNC(name, flagname) \\\n+#define F2FS_FOLIO_PRIVATE_SET_FUNC(name, flagname) \\\n static inline void folio_set_f2fs_##name(struct folio *folio)\t\t\\\n {\t\t\t\t\t\t\t\t\t\\\n-\tunsigned long v = (1UL \u003c\u003c PAGE_PRIVATE_NOT_POINTER) |\t\t\\\n-\t\t\t (1UL \u003c\u003c PAGE_PRIVATE_##flagname);\t\t\\\n+\tunsigned long v = (1UL \u003c\u003c F2FS_FOLIO_PRIVATE_NOT_POINTER) |\t\\\n+\t\t\t (1UL \u003c\u003c F2FS_FOLIO_PRIVATE_##flagname);\t\\\n \tif (!folio-\u003eprivate)\t\t\t\t\t\t\\\n \t\tfolio_attach_private(folio, (void *)v);\t\t\t\\\n \telse {\t\t\t\t\t\t\t\t\\\n \t\tv |= (unsigned long)folio-\u003eprivate;\t\t\t\\\n \t\tfolio-\u003eprivate = (void *)v;\t\t\t\t\\\n \t}\t\t\t\t\t\t\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-\tset_bit(PAGE_PRIVATE_##flagname, \u0026page_private(page)); \\\n }\n \n-#define PAGE_PRIVATE_CLEAR_FUNC(name, flagname) \\\n+#define F2FS_FOLIO_PRIVATE_CLEAR_FUNC(name, flagname) \\\n static inline void folio_clear_f2fs_##name(struct folio *folio)\t\t\\\n {\t\t\t\t\t\t\t\t\t\\\n \tunsigned long v = (unsigned long)folio-\u003eprivate;\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n-\tv \u0026= ~(1UL \u003c\u003c PAGE_PRIVATE_##flagname);\t\t\t\t\\\n-\tif (v == (1UL \u003c\u003c PAGE_PRIVATE_NOT_POINTER))\t\t\t\\\n+\tv \u0026= ~(1UL \u003c\u003c F2FS_FOLIO_PRIVATE_##flagname);\t\t\t\\\n+\tif (v == (1UL \u003c\u003c F2FS_FOLIO_PRIVATE_NOT_POINTER))\t\t\\\n \t\tfolio_detach_private(folio);\t\t\t\t\\\n \telse\t\t\t\t\t\t\t\t\\\n \t\tfolio-\u003eprivate = (void *)v;\t\t\t\t\\\n-}\t\t\t\t\t\t\t\t\t\\\n-static inline void clear_page_private_##name(struct page *page) \\\n-{ \\\n-\tclear_bit(PAGE_PRIVATE_##flagname, \u0026page_private(page)); \\\n-\tif (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) \\\n-\t\tdetach_page_private(page); \\\n }\n \n-PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);\n-PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);\n-PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);\n-PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);\n+F2FS_FOLIO_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);\n+F2FS_FOLIO_PRIVATE_GET_FUNC(inline, INLINE_INODE);\n+F2FS_FOLIO_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);\n+F2FS_FOLIO_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);\n \n-PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);\n-PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);\n-PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);\n-PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);\n+F2FS_FOLIO_PRIVATE_SET_FUNC(reference, REF_RESOURCE);\n+F2FS_FOLIO_PRIVATE_SET_FUNC(inline, INLINE_INODE);\n+F2FS_FOLIO_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);\n+F2FS_FOLIO_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);\n \n-PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);\n-PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);\n-PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);\n-PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);\n+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);\n+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);\n+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);\n+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);\n \n static inline unsigned long folio_get_f2fs_data(struct folio *folio)\n {\n \tunsigned long data = (unsigned long)folio-\u003eprivate;\n \n-\tif (!test_bit(PAGE_PRIVATE_NOT_POINTER, \u0026data))\n+\tif (!test_bit(F2FS_FOLIO_PRIVATE_NOT_POINTER, \u0026data))\n \t\treturn 0;\n-\treturn data \u003e\u003e PAGE_PRIVATE_MAX;\n+\treturn data \u003e\u003e F2FS_FOLIO_PRIVATE_MAX;\n }\n \n static inline void folio_set_f2fs_data(struct folio *folio, unsigned long data)\n {\n-\tdata = (1UL \u003c\u003c PAGE_PRIVATE_NOT_POINTER) | (data \u003c\u003c PAGE_PRIVATE_MAX);\n+\tdata = (1UL \u003c\u003c F2FS_FOLIO_PRIVATE_NOT_POINTER) |\n+\t (data \u003c\u003c F2FS_FOLIO_PRIVATE_MAX);\n \n \tif (!folio_test_private(folio))\n \t\tfolio_attach_private(folio, (void *)data);\ndiff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c\nindex 63b712d3d599e..8c156e1fd37d0 100644\n--- a/fs/f2fs/segment.c\n+++ b/fs/f2fs/segment.c\n@@ -3803,7 +3803,7 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)\n \t\tif (is_inode_flag_set(inode, FI_ALIGNED_WRITE))\n \t\t\treturn CURSEG_COLD_DATA_PINNED;\n \n-\t\tif (page_private_gcing(fio-\u003epage)) {\n+\t\tif (folio_test_f2fs_gcing(fio-\u003efolio)) {\n \t\t\tif (fio-\u003esbi-\u003eam.atgc_enabled \u0026\u0026\n \t\t\t\t(fio-\u003eio_type == FS_DATA_IO) \u0026\u0026\n \t\t\t\t(fio-\u003esbi-\u003egc_mode != GC_URGENT_HIGH) \u0026\u0026\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 969594074fd2d..e4ee59beccbaf 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_has_attached_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 462e89e055485..10f4ad075ea48 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+\tPG_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@@ -575,9 +571,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@@ -1169,7 +1170,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@@ -1193,8 +1194,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_has_attached_private - check if the folio has private data attached\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 attached 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_has_attached_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@@ -1204,7 +1225,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_has_attached_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..bcbb0afe1a681 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,29 @@ void page_cache_async_readahead(struct address_space *mapping,\n \tpage_cache_async_ra(\u0026ractl, folio, req_count);\n }\n \n+/*\n+ * Adjust readahead_control to ensure next folio comes from\n+ * [_index, _index + _nr_pages) afterwards and reset _batch_count.\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 +1526,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 +1567,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..3c153b3ad8450 100644\n--- a/include/trace/events/mmflags.h\n+++ b/include/trace/events/mmflags.h\n@@ -144,7 +144,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+\tDEF_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..304652d6d8f2a 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 attached private locklessly */ \\\n+\t(data_race(folio_has_attached_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 30b7c63b0e359..8f4bcdccd8f35 100644\n--- a/mm/huge_memory.c\n+++ b/mm/huge_memory.c\n@@ -4845,8 +4845,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 attached private locklessly.\n \t\t */\n-\t\tif (!folio_test_private(folio) \u0026\u0026\n+\t\tif (!data_race(folio_has_attached_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 d3a0650ff6905..8d551cdaef9db 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -1446,11 +1446,8 @@ 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-\t/*\n-\t * We have to set hugetlb_vmemmap_optimized again as above\n-\t * folio_change_private(folio, NULL) cleared it.\n-\t */\n+\t/* Clear all folio-\u003eprivate flags except hugetlb_vmemmap_optimized. */\n+\tfolio-\u003eprivate = NULL;\n \tfolio_set_hugetlb_vmemmap_optimized(folio);\n \n \tarch_clear_hugetlb_flags(folio);\ndiff --git a/mm/migrate.c b/mm/migrate.c\nindex a369d0c95c386..7e3a81f069744 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_has_attached_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..0d754a678eac2 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 attached private locklessly */\n+\t__folio_mark_dirty(folio, mapping, !data_race(folio_has_attached_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 aaceed4759eeb..c2eb8fa9d5e50 100644\n--- a/mm/vmscan.c\n+++ b/mm/vmscan.c\n@@ -1029,7 +1029,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_has_attached_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 11be37c431718..7ef80e0da6267 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/14 03:28 | agent | patch-evaluator |
3mModel:core Args: null Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"F2FS_FS",
"F2FS_FS_COMPRESSION",
"ZRAM",
"ZSWAP",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE",
"COMPACTION"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_end",
"erofs_onlinefolio_init",
"f2fs_write_compressed_pages",
"f2fs_compress_write_end_io",
"get_first_zpdesc",
"create_page_chain",
"migrate_folio_unmap",
"split_huge_pages_pid",
"add_hugetlb_folio"
],
"Reasoning": "The patch series removes the PG_private page flag entirely across core memory management, the page cache, and filesystems (replacing flag checks with folio-\u003eprivate inspections, folio_has_attached_private(), and custom tracking). It modifies core mm behavior (reclaim, migration, THP splitting, zsmalloc, hugetlb), introduces a new reverse readahead iterator (readahead_folio_last) used by EROFS, restructures f2fs compression folio handling, and adds/modifies runtime assertions (VM_WARN_ON_ONCE_PAGE in zsmalloc, DBG_BUGON in erofs). These extensive refactorings in core mm and reachable filesystem code paths warrant 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 5613ada8c0ba47c4af37f50b4afcfb53065f9148
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 14 03:28:04 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..7b706d3e54901 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);
@@ -533,12 +533,11 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
static void free_buffers(struct page *page)
{
- struct buffer_head *bh;
+ struct buffer_head *bh = (struct buffer_head *)page_private(page);
- if (!PagePrivate(page))
+ if (!bh)
return;
- bh = page_buffers(page);
while (bh) {
struct buffer_head *next = bh->b_this_page;
free_buffer_head(bh);
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..1aca181d28c62 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(page);
}
/*
@@ -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/compress.c b/fs/f2fs/compress.c
index ce88092d9ce26..09d9b8d0fdcce 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1064,13 +1064,15 @@ static void cancel_cluster_writeback(struct compress_ctx *cc,
/* Cancel writeback and stay locked. */
for (i = 0; i < cc->cluster_size; i++) {
+ struct folio *folio = page_folio(cc->rpages[i]);
+
if (i < submitted) {
inode_inc_dirty_pages(cc->inode);
- lock_page(cc->rpages[i]);
+ folio_lock(folio);
}
- clear_page_private_gcing(cc->rpages[i]);
- if (folio_test_writeback(page_folio(cc->rpages[i])))
- end_page_writeback(cc->rpages[i]);
+ folio_clear_f2fs_gcing(folio);
+ if (folio_test_writeback(folio))
+ folio_end_writeback(folio);
}
}
@@ -1078,11 +1080,15 @@ static void set_cluster_dirty(struct compress_ctx *cc)
{
int i;
- for (i = 0; i < cc->cluster_size; i++)
- if (cc->rpages[i]) {
- set_page_dirty(cc->rpages[i]);
- set_page_private_gcing(cc->rpages[i]);
- }
+ for (i = 0; i < cc->cluster_size; i++) {
+ struct folio *folio;
+
+ if (!cc->rpages[i])
+ continue;
+ folio = page_folio(cc->rpages[i]);
+ folio_mark_dirty(folio);
+ folio_set_f2fs_gcing(folio);
+ }
}
static int prepare_compress_overwrite(struct compress_ctx *cc,
@@ -1281,7 +1287,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
.op = REQ_OP_WRITE,
.op_flags = wbc_to_write_flags(wbc),
.old_blkaddr = NEW_ADDR,
- .page = NULL,
+ .folio = NULL,
.encrypted_page = NULL,
.compressed_page = NULL,
.io_type = io_type,
@@ -1370,7 +1376,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
block_t blkaddr;
blkaddr = f2fs_data_blkaddr(&dn);
- fio.page = cc->rpages[i];
+ fio.folio = page_folio(cc->rpages[i]);
fio.old_blkaddr = blkaddr;
/* cluster header */
@@ -1476,9 +1482,12 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
}
for (i = 0; i < cic->nr_rpages; i++) {
+ struct folio *rfolio;
+
WARN_ON(!cic->rpages[i]);
- clear_page_private_gcing(cic->rpages[i]);
- end_page_writeback(cic->rpages[i]);
+ rfolio = page_folio(cic->rpages[i]);
+ folio_clear_f2fs_gcing(rfolio);
+ folio_end_writeback(rfolio);
}
page_array_free(sbi, cic->rpages, cic->nr_rpages);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 21f396ebe22ca..ca8232a9095f8 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2923,7 +2923,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true;
if (fio) {
- if (page_private_gcing(fio->page))
+ if (folio_test_f2fs_gcing(fio->folio))
return true;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
f2fs_is_checkpointed_data(sbi, fio->old_blkaddr)))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9940a6cecf1a2..85937de3d7016 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1357,10 +1357,7 @@ struct f2fs_io_info {
blk_opf_t op_flags; /* req_flag_bits */
block_t new_blkaddr; /* new block address to be written */
block_t old_blkaddr; /* old block address before Cow */
- union {
- struct page *page; /* page to be written */
- struct folio *folio;
- };
+ struct folio *folio; /* folio to be written */
struct page *encrypted_page; /* encrypted page */
struct page *compressed_page; /* compressed page */
struct list_head list; /* serialize IOs */
@@ -1613,27 +1610,27 @@ static inline void f2fs_set_bit(unsigned int nr, char *addr);
static inline void f2fs_clear_bit(unsigned int nr, char *addr);
/*
- * Layout of f2fs page.private:
+ * Layout of f2fs folio->private:
*
* Layout A: lowest bit should be 1
* | bit0 = 1 | bit1 | bit2 | ... | bit MAX | private data .... |
- * bit 0 PAGE_PRIVATE_NOT_POINTER
- * bit 1 PAGE_PRIVATE_ONGOING_MIGRATION
- * bit 2 PAGE_PRIVATE_INLINE_INODE
- * bit 3 PAGE_PRIVATE_REF_RESOURCE
- * bit 4 PAGE_PRIVATE_ATOMIC_WRITE
+ * bit 0 F2FS_FOLIO_PRIVATE_NOT_POINTER
+ * bit 1 F2FS_FOLIO_PRIVATE_ONGOING_MIGRATION
+ * bit 2 F2FS_FOLIO_PRIVATE_INLINE_INODE
+ * bit 3 F2FS_FOLIO_PRIVATE_REF_RESOURCE
+ * bit 4 F2FS_FOLIO_PRIVATE_ATOMIC_WRITE
* bit 5- f2fs private data
*
* Layout B: lowest bit should be 0
- * page.private is a wrapped pointer.
+ * folio->private is a wrapped pointer.
*/
enum {
- PAGE_PRIVATE_NOT_POINTER, /* private contains non-pointer data */
- PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
- PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
- PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
- PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
- PAGE_PRIVATE_MAX
+ F2FS_FOLIO_PRIVATE_NOT_POINTER, /* private contains non-pointer data */
+ F2FS_FOLIO_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
+ F2FS_FOLIO_PRIVATE_INLINE_INODE, /* inode page contains inline data */
+ F2FS_FOLIO_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
+ F2FS_FOLIO_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
+ F2FS_FOLIO_PRIVATE_MAX
};
/* For compression */
@@ -2681,86 +2678,68 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
return -ENOSPC;
}
-#define PAGE_PRIVATE_GET_FUNC(name, flagname) \
+#define F2FS_FOLIO_PRIVATE_GET_FUNC(name, flagname) \
static inline bool folio_test_f2fs_##name(const struct folio *folio) \
{ \
unsigned long priv = (unsigned long)folio->private; \
- unsigned long v = (1UL << PAGE_PRIVATE_NOT_POINTER) | \
- (1UL << PAGE_PRIVATE_##flagname); \
+ unsigned long v = (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER) | \
+ (1UL << F2FS_FOLIO_PRIVATE_##flagname); \
return (priv & v) == v; \
-} \
-static inline bool page_private_##name(struct page *page) \
-{ \
- return PagePrivate(page) && \
- test_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)) && \
- test_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
-#define PAGE_PRIVATE_SET_FUNC(name, flagname) \
+#define F2FS_FOLIO_PRIVATE_SET_FUNC(name, flagname) \
static inline void folio_set_f2fs_##name(struct folio *folio) \
{ \
- unsigned long v = (1UL << PAGE_PRIVATE_NOT_POINTER) | \
- (1UL << PAGE_PRIVATE_##flagname); \
+ unsigned long v = (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER) | \
+ (1UL << F2FS_FOLIO_PRIVATE_##flagname); \
if (!folio->private) \
folio_attach_private(folio, (void *)v); \
else { \
v |= (unsigned long)folio->private; \
folio->private = (void *)v; \
} \
-} \
-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)); \
- set_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
-#define PAGE_PRIVATE_CLEAR_FUNC(name, flagname) \
+#define F2FS_FOLIO_PRIVATE_CLEAR_FUNC(name, flagname) \
static inline void folio_clear_f2fs_##name(struct folio *folio) \
{ \
unsigned long v = (unsigned long)folio->private; \
\
- v &= ~(1UL << PAGE_PRIVATE_##flagname); \
- if (v == (1UL << PAGE_PRIVATE_NOT_POINTER)) \
+ v &= ~(1UL << F2FS_FOLIO_PRIVATE_##flagname); \
+ if (v == (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER)) \
folio_detach_private(folio); \
else \
folio->private = (void *)v; \
-} \
-static inline void clear_page_private_##name(struct page *page) \
-{ \
- clear_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
- if (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) \
- detach_page_private(page); \
}
-PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
-PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
-PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
-PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
+F2FS_FOLIO_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
+F2FS_FOLIO_PRIVATE_GET_FUNC(inline, INLINE_INODE);
+F2FS_FOLIO_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
+F2FS_FOLIO_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
-PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
-PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
-PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
-PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
+F2FS_FOLIO_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
+F2FS_FOLIO_PRIVATE_SET_FUNC(inline, INLINE_INODE);
+F2FS_FOLIO_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
+F2FS_FOLIO_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
-PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
-PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
-PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
-PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
static inline unsigned long folio_get_f2fs_data(struct folio *folio)
{
unsigned long data = (unsigned long)folio->private;
- if (!test_bit(PAGE_PRIVATE_NOT_POINTER, &data))
+ if (!test_bit(F2FS_FOLIO_PRIVATE_NOT_POINTER, &data))
return 0;
- return data >> PAGE_PRIVATE_MAX;
+ return data >> F2FS_FOLIO_PRIVATE_MAX;
}
static inline void folio_set_f2fs_data(struct folio *folio, unsigned long data)
{
- data = (1UL << PAGE_PRIVATE_NOT_POINTER) | (data << PAGE_PRIVATE_MAX);
+ data = (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER) |
+ (data << F2FS_FOLIO_PRIVATE_MAX);
if (!folio_test_private(folio))
folio_attach_private(folio, (void *)data);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 63b712d3d599e..8c156e1fd37d0 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -3803,7 +3803,7 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
return CURSEG_COLD_DATA_PINNED;
- if (page_private_gcing(fio->page)) {
+ if (folio_test_f2fs_gcing(fio->folio)) {
if (fio->sbi->am.atgc_enabled &&
(fio->io_type == FS_DATA_IO) &&
(fio->sbi->gc_mode != GC_URGENT_HIGH) &&
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 969594074fd2d..e4ee59beccbaf 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_has_attached_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 462e89e055485..10f4ad075ea48 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 */
@@ -575,9 +571,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 */
@@ -1169,7 +1170,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)
@@ -1193,8 +1194,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_has_attached_private - check if the folio has private data attached
+ * @folio: The folio to check.
+ *
+ * Use this in code that may encounter swapcache or hugetlb folios but only
+ * wants to detect attached 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_has_attached_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
@@ -1204,7 +1225,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_has_attached_private(folio) || folio_test_private_2(folio);
}
#undef PF_ANY
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 939f3a5e973f6..bcbb0afe1a681 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,29 @@ void page_cache_async_readahead(struct address_space *mapping,
page_cache_async_ra(&ractl, folio, req_count);
}
+/*
+ * Adjust readahead_control to ensure next folio comes from
+ * [_index, _index + _nr_pages) afterwards and reset _batch_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 +1526,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 +1567,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..3c153b3ad8450 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -144,7 +144,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..304652d6d8f2a 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 attached private locklessly */ \
+ (data_race(folio_has_attached_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 30b7c63b0e359..8f4bcdccd8f35 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4845,8 +4845,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 attached private locklessly.
*/
- if (!folio_test_private(folio) &&
+ if (!data_race(folio_has_attached_private(folio)) &&
folio_expected_ref_count(folio) != folio_ref_count(folio))
goto next;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index d3a0650ff6905..8d551cdaef9db 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1446,11 +1446,8 @@ void add_hugetlb_folio(struct hstate *h, struct folio *folio,
}
__folio_set_hugetlb(folio);
- folio_change_private(folio, NULL);
- /*
- * We have to set hugetlb_vmemmap_optimized again as above
- * folio_change_private(folio, NULL) cleared it.
- */
+ /* Clear all folio->private flags except hugetlb_vmemmap_optimized. */
+ folio->private = NULL;
folio_set_hugetlb_vmemmap_optimized(folio);
arch_clear_hugetlb_flags(folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index a369d0c95c386..7e3a81f069744 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_has_attached_private(src)) {
try_to_free_buffers(src);
goto out;
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index eeab25d6ce364..0d754a678eac2 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 attached private locklessly */
+ __folio_mark_dirty(folio, mapping, !data_race(folio_has_attached_private(folio)));
if (mapping->host) {
/* !PageAnon && !swapper_space */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index aaceed4759eeb..c2eb8fa9d5e50 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1029,7 +1029,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_has_attached_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 11be37c431718..7ef80e0da6267 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/14 03:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20630 output: 20 thoughts: 2074 Thoughts: **Patch Analysis: WorthFuzzing = TRUE!** Okay, let's break this down. First, the instructions are clear: prioritize reachability and don't blindly trust claims of "no functional change" or "refactoring." If reachable, executable code is touched, it *must* be fuzzed, and that applies doubly to assertions. Good, I'm already on board with that approach. Based on the target architecture (amd64) and the files listed, this patch is screaming "core memory management and filesystems." We're talking about modifications to `Documentation/admin-guide/kdump/vmcoreinfo.rst`, `arch/x86/events/intel/bts.c`, `drivers/md/md-bitmap.c`, `fs/ceph/addr.c`, and so on, but most importantly `mm/`, and `fs/` directories. Specifically, it looks like this patch is fundamentally changing how page private data is handled by *removing* the `PG_private` flag entirely and using `folio->private` or `page_private(page)` instead. This touches vital areas like `mm/page-writeback.c`, `mm/vmscan.c`, `mm/huge_memory.c`, `mm/hugetlb.c`, `mm/migrate.c`, `fs/f2fs/`, `fs/erofs/`, and related include files! This is *definitely* reachable executable code within core subsystems, so `WorthFuzzing` is unequivocally `TRUE`. The core subsystems it touches are clear indications for fuzzing. My next step is to drill down into the specifics. I'll need to use tools like `grepper` (which I can use to `git grep` the kernel sources), `read-file`, `codesearch-dir-index`, or `set-results` (if the tool is available). I'll examine the git commit messages and the functions added or modified within this patch by using `git grep` and `read-file` to get a better understanding of what's going on. The goal is to fully understand the impact and identify *specific* functions and codepaths that require fuzzing. From my quick review, here are a few modified functions from the `.c` files that stand out: * `erofs_onlinefolio_init`, `erofs_onlinefolio_split`, `erofs_onlinefolio_end`, `z_erofs_readahead` (filesystem). * `f2fs_compress_write_end_io`, `set_cluster_dirty`, and `prepare_compress_overwrite` (filesystem). * `get_first_zpdesc`, `obj_allocated` (with `VM_WARN_ON_ONCE_PAGE`) and `create_page_chain` in `mm/zsmalloc.c`. * `folio_migrate_flags`, `migrate_folio_unmap` from `mm/migrate.c`. * `readahead_folio_last` which is a *new* function declared within `include/linux/pagemap.h`. I see that `readahead_folio_last` is used in the `z_erofs_readahead` function in `fs/erofs/zdata.c`. I will now use `grepper` to see if `readahead_folio_last` is used anywhere else. This is a critical indicator of where this change has ripple effects. I will see if there are any other direct callers. The bottom line is that this patch demands thorough fuzzing. I need to understand the new logic of removing and replacing `PG_private` and how this impacts all the files that have been touched. |
| 4/2 | 2026/09/14 03:28 | 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=1520=static inline struct folio *readahead_folio(struct readahead_control *ractl)\n--\ninclude/linux/pagemap.h-1529-/**\ninclude/linux/pagemap.h:1530: * readahead_folio_last - Get the next folio to read, from the tail.\ninclude/linux/pagemap.h-1531- * @ractl: The current readahead request.\n--\ninclude/linux/pagemap.h-1539- */\ninclude/linux/pagemap.h:1540:static inline struct folio *readahead_folio_last(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1541-{\n"
} |
| 5/2 | 2026/09/14 03:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23870 output: 36 thoughts: 268 Thoughts: **My Analysis of the EROFS Readahead Folio Optimization** Okay, so `readahead_folio_last` is clearly being called by `z_erofs_readahead`. This change in the readahead folio handling within `fs/erofs/zdata.c` is significant. Before, it was collecting folios using a standard `readahead_folio` and then processing them in reverse order using a linked list. This was likely done to prioritize metadata I/O performance, which makes sense – traversing metadata in the order needed will be faster, right? Now, though, they've simplified things and are directly using `readahead_folio_last`. This suggests a potentially more optimized approach to metadata I/O. Instead of building the list, reversing it and then processing, the new function appears to be traversing directly, ensuring the folios are handled in the most advantageous order. No reversing necessary! Smart. Let's see… I should probably check out `fs/erofs/data.c` as well. The provided context mentions `erofs_onlinefolio_init` and `erofs_onlinefolio_end`, I should inspect it around line 240, to get a better understanding of how these changes interplay with the other core data structures. |
| 6/2 | 2026/09/14 03:28 | tool | read-file |
0mArgs:{
"File": "fs/erofs/data.c",
"FirstLine": 235,
"LineCount": 60
}Results: {
"Contents": " 235:\t\t}\n 236:\t\treturn 0;\n 237:\t}\n 238:\t\n 239:\t/*\n 240:\t * bit 30: I/O error occurred on this folio\n 241:\t * bit 29: CPU has dirty data in D-cache (needs aliasing handling);\n 242:\t * bit 0 - 28: remaining parts to complete this folio, biased by 1 so that\n 243:\t *\t -\u003eprivate stays non-NULL while the folio is attached\n 244:\t */\n 245:\t#define EROFS_ONLINEFOLIO_EIO\t\t30\n 246:\t#define EROFS_ONLINEFOLIO_DIRTY\t\t29\n 247:\t#define EROFS_ONLINEFOLIO_COUNT_MASK\t(BIT(EROFS_ONLINEFOLIO_DIRTY) - 1)\n 248:\t#define EROFS_ONLINEFOLIO_BIAS\t\t1\n 249:\t\n 250:\tvoid erofs_onlinefolio_init(struct folio *folio)\n 251:\t{\n 252:\t\tunion {\n 253:\t\t\tatomic_t o;\n 254:\t\t\tvoid *v;\n 255:\t\t} u = { .o = ATOMIC_INIT(1 + EROFS_ONLINEFOLIO_BIAS) };\n 256:\t\n 257:\t\t/* valid only if file-backed folio is locked */\n 258:\t\tfolio_attach_private(folio, u.v);\n 259:\t}\n 260:\t\n 261:\tvoid erofs_onlinefolio_split(struct folio *folio)\n 262:\t{\n 263:\t\tatomic_inc((atomic_t *)\u0026folio-\u003eprivate);\n 264:\t}\n 265:\t\n 266:\tvoid erofs_onlinefolio_end(struct folio *folio, int err, bool dirty)\n 267:\t{\n 268:\t\tint orig, v;\n 269:\t\n 270:\t\tdo {\n 271:\t\t\torig = atomic_read((atomic_t *)\u0026folio-\u003eprivate);\n 272:\t\t\tDBG_BUGON((orig \u0026 EROFS_ONLINEFOLIO_COUNT_MASK) \u003c= EROFS_ONLINEFOLIO_BIAS);\n 273:\t\t\tv = dirty \u003c\u003c EROFS_ONLINEFOLIO_DIRTY;\n 274:\t\t\tv |= (orig - 1) | (!!err \u003c\u003c EROFS_ONLINEFOLIO_EIO);\n 275:\t\t} while (atomic_cmpxchg((atomic_t *)\u0026folio-\u003eprivate, orig, v) != orig);\n 276:\t\n 277:\t\tif ((v \u0026 EROFS_ONLINEFOLIO_COUNT_MASK) != EROFS_ONLINEFOLIO_BIAS)\n 278:\t\t\treturn;\n 279:\t\tfolio_detach_private(folio);\n 280:\t\tif (v \u0026 BIT(EROFS_ONLINEFOLIO_DIRTY))\n 281:\t\t\tflush_dcache_folio(folio);\n 282:\t\tfolio_end_read(folio, !(v \u0026 BIT(EROFS_ONLINEFOLIO_EIO)));\n 283:\t}\n 284:\t\n 285:\tstruct erofs_iomap_iter_ctx {\n 286:\t\tstruct page *page;\n 287:\t\tvoid *base;\n 288:\t\tstruct inode *realinode;\n 289:\t};\n 290:\t\n 291:\tstatic int erofs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,\n 292:\t\t\tunsigned int flags, struct iomap *iomap, struct iomap *srcmap)\n 293:\t{\n 294:\t\tstruct iomap_iter *iter = container_of(iomap, struct iomap_iter, iomap);\n"
} |
| 7/2 | 2026/09/14 03:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25465 output: 35 thoughts: 15 Thoughts: Here's my summary, as though I'm thinking through this myself, given my expertise: **Diving into `mm/zsmalloc.c`** Okay, so we're talking about `mm/zsmalloc.c`, right? That immediately flags a specific area of interest: memory management, specifically dealing with zero-sized allocations, or, more broadly, the optimization around "small" memory chunks. Given my experience with kernel internals, I'm already anticipating this file will be crucial for understanding how the kernel handles frequently requested small blocks without incurring massive overhead. I expect to see implementations of caching, pooling, and potentially some clever bitwise tricks to make it all lightning fast. Let's see what gems are contained within. I want to understand how it's integrated with other memory subsystems, and if there are any gotchas I should be aware of. |
| 8/2 | 2026/09/14 03:28 | tool | read-file |
0mArgs:{
"File": "mm/zsmalloc.c",
"FirstLine": 480,
"LineCount": 40
}Results: {
"Contents": " 480:\tstatic inline void mod_zspage_inuse(struct zspage *zspage, int val)\n 481:\t{\n 482:\t\tzspage-\u003einuse += val;\n 483:\t}\n 484:\t\n 485:\tstatic struct zpdesc *get_first_zpdesc(struct zspage *zspage)\n 486:\t{\n 487:\t\tstruct zpdesc *first_zpdesc = zspage-\u003efirst_zpdesc;\n 488:\t\n 489:\t\t/* the first zpdesc must point back to this zspage */\n 490:\t\tVM_WARN_ON_ONCE_PAGE(first_zpdesc-\u003ezspage != zspage, zpdesc_page(first_zpdesc));\n 491:\t\treturn first_zpdesc;\n 492:\t}\n 493:\t\n 494:\t#define FIRST_OBJ_PAGE_TYPE_MASK\t0xffffff\n 495:\t\n 496:\tstatic inline unsigned int get_first_obj_offset(struct zpdesc *zpdesc)\n 497:\t{\n 498:\t\tVM_WARN_ON_ONCE(!PageZsmalloc(zpdesc_page(zpdesc)));\n 499:\t\treturn zpdesc-\u003efirst_obj_offset \u0026 FIRST_OBJ_PAGE_TYPE_MASK;\n 500:\t}\n 501:\t\n 502:\tstatic inline void set_first_obj_offset(struct zpdesc *zpdesc, unsigned int offset)\n 503:\t{\n 504:\t\t/* With 24 bits available, we can support offsets into 16 MiB pages. */\n 505:\t\tBUILD_BUG_ON(PAGE_SIZE \u003e SZ_16M);\n 506:\t\tVM_WARN_ON_ONCE(!PageZsmalloc(zpdesc_page(zpdesc)));\n 507:\t\tVM_WARN_ON_ONCE(offset \u0026 ~FIRST_OBJ_PAGE_TYPE_MASK);\n 508:\t\tzpdesc-\u003efirst_obj_offset \u0026= ~FIRST_OBJ_PAGE_TYPE_MASK;\n 509:\t\tzpdesc-\u003efirst_obj_offset |= offset \u0026 FIRST_OBJ_PAGE_TYPE_MASK;\n 510:\t}\n 511:\t\n 512:\tstatic inline unsigned int get_freeobj(struct zspage *zspage)\n 513:\t{\n 514:\t\treturn zspage-\u003efreeobj;\n 515:\t}\n 516:\t\n 517:\tstatic inline void set_freeobj(struct zspage *zspage, unsigned int obj)\n 518:\t{\n 519:\t\tzspage-\u003efreeobj = obj;\n"
} |
| 9/2 | 2026/09/14 03:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26404 output: 35 thoughts: 25 Thoughts: Here's my analysis of the provided code snippet, structured as if I were reviewing it: **Diving into `mm/zsmalloc.c` - Lines 825-860** Okay, so let's dig into that `mm/zsmalloc.c` file, specifically the code block from lines 825 to 860. I need to get a clear picture of what's happening in this section. My initial thought is to quickly identify the function or the process this chunk is a part of. Judging from the surrounding context, I'm assuming this section likely handles some core allocation or deallocation logic within the zsmalloc implementation. Given the file and function name, it's probably related to zero-sized allocations. I should carefully examine any loops, conditional statements and potential memory access patterns. Specifically, I'm going to look for potential issues like race conditions, memory leaks, or inefficient memory handling, common in these types of low-level memory management code. I'm also interested to see how it interacts with the underlying page allocator. Does it seem like this is optimizing for small, frequent allocations? Or does it seem more robust? I want to see if the code carefully handles edge cases like allocation failures, especially regarding the overall allocation or deallocation strategies it uses. Are there any potential performance bottlenecks I should be aware of, like excessive locking or cache thrashing? Finally, I need to look for any hidden complexities that might become a pain to maintain later. |
| 10/2 | 2026/09/14 03:29 | tool | read-file |
0mArgs:{
"File": "mm/zsmalloc.c",
"FirstLine": 825,
"LineCount": 40
}Results: {
"Contents": " 825:\tstatic inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,\n 826:\t\t\t\t\t unsigned long *phandle)\n 827:\t{\n 828:\t\tunsigned long handle;\n 829:\t\tstruct zspage *zspage = get_zspage(zpdesc);\n 830:\t\n 831:\t\tif (unlikely(ZsHugePage(zspage))) {\n 832:\t\t\t/* only first zpdesc holds the handle */\n 833:\t\t\tVM_WARN_ON_ONCE_PAGE(zspage-\u003efirst_zpdesc != zpdesc, zpdesc_page(zpdesc));\n 834:\t\t\thandle = zpdesc-\u003ehandle;\n 835:\t\t} else\n 836:\t\t\thandle = *(unsigned long *)obj;\n 837:\t\n 838:\t\tif (!(handle \u0026 OBJ_ALLOCATED_TAG))\n 839:\t\t\treturn false;\n 840:\t\n 841:\t\t/* Clear all tags before returning the handle */\n 842:\t\t*phandle = handle \u0026 ~OBJ_TAG_MASK;\n 843:\t\treturn true;\n 844:\t}\n 845:\t\n 846:\tstatic void reset_zpdesc(struct zpdesc *zpdesc)\n 847:\t{\n 848:\t\tzpdesc-\u003ezspage = NULL;\n 849:\t\tzpdesc-\u003enext = NULL;\n 850:\t\t/* PageZsmalloc is sticky until the page is freed to the buddy. */\n 851:\t}\n 852:\t\n 853:\tstatic int trylock_zspage(struct zspage *zspage)\n 854:\t{\n 855:\t\tstruct zpdesc *cursor, *fail;\n 856:\t\n 857:\t\tfor (cursor = get_first_zpdesc(zspage); cursor != NULL; cursor =\n 858:\t\t\t\t\t\tget_next_zpdesc(cursor)) {\n 859:\t\t\tif (!zpdesc_trylock(cursor)) {\n 860:\t\t\t\tfail = cursor;\n 861:\t\t\t\tgoto unlock;\n 862:\t\t\t}\n 863:\t\t}\n 864:\t\n"
} |
| 11/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27363 output: 37 thoughts: 16 Thoughts: Here's my take on the thoughts, rendered as though they are my own: **Diving into `mm/page-writeback.c`** Alright, let's turn our attention to `mm/page-writeback.c`. I'm going to dig into this file to understand the intricacies of page writeback within the memory management subsystem. I'm particularly interested in how the kernel handles the dirty page lifecycle, from marking pages as needing to be written to disk, to actually initiating and managing those I/O operations. I'll be looking closely at the core functions responsible for this, the data structures involved (like the `struct address_space` and associated page lists), and the algorithms used to schedule and throttle writeback to maintain system performance. I'll need to remember to focus on the interplay between the various writeback mechanisms, such as those driven by the background writeback daemon versus direct write operations. Ultimately, I'm expecting to gain a deeper, operational understanding of how this critical function keeps the file system in sync with the physical memory. |
| 12/2 | 2026/09/14 03:29 | tool | read-file |
0mArgs:{
"File": "mm/page-writeback.c",
"FirstLine": 2700,
"LineCount": 20
}Results: {
"Contents": "2700:\t * simply hold the folio lock, but e.g. zap_pte_range() calls with the\n2701:\t * folio mapped and the pte lock held, which also locks out truncation.\n2702:\t */\n2703:\tbool filemap_dirty_folio(struct address_space *mapping, struct folio *folio)\n2704:\t{\n2705:\t\tif (folio_test_set_dirty(folio))\n2706:\t\t\treturn false;\n2707:\t\n2708:\t\t/* data_race() is used to read attached private locklessly */\n2709:\t\t__folio_mark_dirty(folio, mapping, !data_race(folio_has_attached_private(folio)));\n2710:\t\n2711:\t\tif (mapping-\u003ehost) {\n2712:\t\t\t/* !PageAnon \u0026\u0026 !swapper_space */\n2713:\t\t\t__mark_inode_dirty(mapping-\u003ehost, I_DIRTY_PAGES);\n2714:\t\t}\n2715:\t\treturn true;\n2716:\t}\n2717:\tEXPORT_SYMBOL(filemap_dirty_folio);\n2718:\t\n2719:\t/**\n"
} |
| 13/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27960 output: 35 thoughts: 12 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `mm/vmscan.c`** Okay, time to dig in. I need to take a look at `mm/vmscan.c`. It's the core of the virtual memory scanning process, so it's the logical place to start when I'm chasing down this memory management issue. I need to understand how pages are being scanned, what criteria are being used for eviction, and where the performance bottlenecks might be. I'm expecting to find a lot of the critical logic related to the page reclaim and swapping mechanisms in here. Let's see what's what. I'm especially interested in how the scan works with NUMA architectures and different memory tiers, and whether any of the recently introduced optimization patches have made it into this version. Time to get my hands dirty. |
| 14/2 | 2026/09/14 03:29 | tool | read-file |
0mArgs:{
"File": "mm/vmscan.c",
"FirstLine": 1025,
"LineCount": 25
}Results: {
"Contents": "1025:\t\t}\n1026:\t\n1027:\t\t/* By default assume that the folio flags are accurate */\n1028:\t\t*dirty = folio_test_dirty(folio);\n1029:\t\t*writeback = folio_test_writeback(folio);\n1030:\t\n1031:\t\t/* Verify dirty/writeback state if the filesystem supports it */\n1032:\t\tif (!folio_has_attached_private(folio))\n1033:\t\t\treturn;\n1034:\t\n1035:\t\tmapping = folio_mapping(folio);\n1036:\t\tif (mapping \u0026\u0026 mapping-\u003ea_ops-\u003eis_dirty_writeback)\n1037:\t\t\tmapping-\u003ea_ops-\u003eis_dirty_writeback(folio, dirty, writeback);\n1038:\t}\n1039:\t\n1040:\tstatic struct folio *alloc_demote_folio(struct folio *src,\n1041:\t\t\tunsigned long private)\n1042:\t{\n1043:\t\tstruct migration_target_control *mtc, target_nid_mtc;\n1044:\t\tstruct folio *dst;\n1045:\t\n1046:\t\tmtc = (struct migration_target_control *)private;\n1047:\t\n1048:\t\t/*\n1049:\t\t * make sure we allocate from the target node first also trying to\n"
} |
| 15/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28561 output: 34 thoughts: 11 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Diving into `mm/migrate.c`** Alright, time to get my hands dirty. Let's start by digging into `mm/migrate.c`. I need to refresh my memory on the migration logic, especially given the context of [*Insert user's field of expertise, e.g., "the recent NUMA optimizations we've been discussing," or "the performance issues reported on system X," or "the new experimental page-fault handling" etc.*]. I'm expecting to see the core functions responsible for moving pages between zones, handling different migration types, and possibly the interaction with the page allocator. I'm particularly interested in how they've handled [ *mention specific areas that the user is interested in, e.g., "the potential for contention on the migration lock," "the handling of huge pages," or "the interaction with the THP coalescing code" etc.*]. I want to understand how the code manages the complexities of atomic operations, potential race conditions, and error handling during the migration process. Let's see if the comments are up to snuff; hopefully they're clear and detail the intricacies of the algorithm, including its rationale. From this file, I'm hoping to figure out: 1. What locking strategies they're employing. 2. How the prefetchers are used, if at all. 3. The impact of the various migration types. 4. How the code works with the user's process and handles signals and faults. I'll also keep an eye out for any potential areas for optimization or any possible sources of bugs. Time to jump in! |
| 16/2 | 2026/09/14 03:29 | tool | read-file |
0mArgs:{
"File": "mm/migrate.c",
"FirstLine": 1320,
"LineCount": 25
}Results: {
"Contents": "1320:\t\t * Calling try_to_unmap() against a src-\u003emapping==NULL page will\n1321:\t\t * trigger a BUG. So handle it here.\n1322:\t\t * 2. An orphaned page (see truncate_cleanup_page) might have\n1323:\t\t * fs-private metadata. The page can be picked up due to memory\n1324:\t\t * offlining. Everywhere else except page reclaim, the page is\n1325:\t\t * invisible to the vm, so the page can not be migrated. So try to\n1326:\t\t * free the metadata, so the page can be freed.\n1327:\t\t */\n1328:\t\tif (!src-\u003emapping) {\n1329:\t\t\tif (folio_has_attached_private(src)) {\n1330:\t\t\t\ttry_to_free_buffers(src);\n1331:\t\t\t\tgoto out;\n1332:\t\t\t}\n1333:\t\t} else if (folio_mapped(src)) {\n1334:\t\t\t/* Establish migration ptes */\n1335:\t\t\tVM_BUG_ON_FOLIO(folio_test_anon(src) \u0026\u0026\n1336:\t\t\t\t !folio_test_ksm(src) \u0026\u0026 !anon_vma, src);\n1337:\t\t\ttry_to_migrate(src, mode == MIGRATE_ASYNC ? TTU_BATCH_FLUSH : 0);\n1338:\t\t\told_folio_state |= FOLIO_WAS_MAPPED;\n1339:\t\t}\n1340:\t\n1341:\t\tif (!folio_mapped(src)) {\n1342:\t\t\t__migrate_folio_record(dst, old_folio_state, anon_vma);\n1343:\t\t\treturn 0;\n1344:\t\t}\n"
} |
| 17/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29430 output: 36 thoughts: 14 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into Huge Memory Management** Alright, time to dig into `mm/huge_memory.c`. I need to refresh my understanding of the current implementation specifics. I want to see how the kernel handles huge pages, specifically how it manages the allocation and deallocation. I'm looking for the core data structures, any recent changes, and any potential bottlenecks in this area. I need to get a firm grasp on the intricacies of this file, the dependencies it has, and how it interacts with the rest of the memory management subsystem. Let's see how much has changed since the last time I really looked at this section. |
| 18/2 | 2026/09/14 03:29 | tool | read-file |
0mArgs:{
"File": "mm/huge_memory.c",
"FirstLine": 4835,
"LineCount": 30
}Results: {
"Contents": "4835:\t\t\t\tmapping = folio-\u003emapping;\n4836:\t\t\t\ttarget_order = max(new_order,\n4837:\t\t\t\t\t\t mapping_min_folio_order(mapping));\n4838:\t\t\t}\n4839:\t\n4840:\t\t\tif (target_order \u003e= folio_order(folio))\n4841:\t\t\t\tgoto next;\n4842:\t\n4843:\t\t\ttotal++;\n4844:\t\t\t/*\n4845:\t\t\t * For folios with private, split_huge_page_to_list_to_order()\n4846:\t\t\t * will try to drop it before split and then check if the folio\n4847:\t\t\t * can be split or not. So skip the check here.\n4848:\t\t\t * data_race() is used to read attached private locklessly.\n4849:\t\t\t */\n4850:\t\t\tif (!data_race(folio_has_attached_private(folio)) \u0026\u0026\n4851:\t\t\t folio_expected_ref_count(folio) != folio_ref_count(folio))\n4852:\t\t\t\tgoto next;\n4853:\t\n4854:\t\t\tif (!folio_trylock(folio))\n4855:\t\t\t\tgoto next;\n4856:\t\t\tfolio_get(folio);\n4857:\t\t\tfolio_walk_end(\u0026fw, vma);\n4858:\t\n4859:\t\t\tif (!folio_test_anon(folio) \u0026\u0026 folio-\u003emapping != mapping)\n4860:\t\t\t\tgoto unlock;\n4861:\t\n4862:\t\t\tif (in_folio_offset \u003c 0 ||\n4863:\t\t\t in_folio_offset \u003e= folio_nr_pages(folio)) {\n4864:\t\t\t\tif (!split_folio_to_order(folio, target_order))\n"
} |
| 19/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30093 output: 27 thoughts: 219 Thoughts: **Analyzing Huge Page Folio Management** Okay, let's dive into this. I'm taking a look at `split_huge_pages_pid` because, well, that's where the action is likely to be. My next stop? `mm/hugetlb.c`. I'm particularly interested in what happens in `add_hugetlb_folio`. Specifically, I see this bit of code: ```c __folio_set_hugetlb(folio); /* Clear all folio->private flags except hugetlb_vmemmap_optimized. */ folio->private = NULL; folio_set_hugetlb_vmemmap_optimized(folio); arch_clear_hugetlb_flags(folio); ``` Hold on... I need to understand this. The comment *clearly* states, "Clear all `folio->private` flags except `hugetlb_vmemmap_optimized`." So they're explicitly setting `folio->private` to `NULL`, and then *only* setting `hugetlb_vmemmap_optimized`. Interesting, very interesting. I think I need to dig into `folio_set_hugetlb_vmemmap_optimized(folio)` to understand *why* this is being done. I'll check the definition, probably still in `mm/hugetlb.c`, but could be somewhere else, of course. Let's see... |
| 20/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "folio_set_hugetlb_vmemmap_optimized"
}Results: {
"Output": "mm/hugetlb.c=1430=void add_hugetlb_folio(struct hstate *h, struct folio *folio,\n--\nmm/hugetlb.c-1450-\tfolio-\u003eprivate = NULL;\nmm/hugetlb.c:1451:\tfolio_set_hugetlb_vmemmap_optimized(folio);\nmm/hugetlb.c-1452-\n--\nmm/hugetlb.c=3313=static void __init gather_bootmem_prealloc_node(unsigned long nid)\n--\nmm/hugetlb.c-3340-\t\tif (vmemmap_optimizable_order(pfn_to_section_compound_order(pfn)))\nmm/hugetlb.c:3341:\t\t\tfolio_set_hugetlb_vmemmap_optimized(folio);\nmm/hugetlb.c-3342-\t\tsection_set_compound_order_range(pfn, folio_nr_pages(folio), 0);\n--\nmm/hugetlb_vmemmap.c=496=static int __hugetlb_vmemmap_optimize_folio(const struct hstate *h,\n--\nmm/hugetlb_vmemmap.c-526-\t */\nmm/hugetlb_vmemmap.c:527:\tfolio_set_hugetlb_vmemmap_optimized(folio);\nmm/hugetlb_vmemmap.c-528-\n"
} |
| 21/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31029 output: 26 thoughts: 643 Thoughts: **My Analysis of Recent Kernel Changes** Okay, let's break this down. First, I noticed a change in `add_hugetlb_folio` regarding how folio private data is handled. Specifically, there's a refinement around clearing the folio's private data, but making sure to keep the `hugetlb_vmemmap_optimized` flag set. This warrants a closer look at the rationale behind this optimization. Next, I'm going to shift my focus to filesystem-related changes. The modifications in `fs/f2fs/compress.c` look interesting. I'll need to understand the impact of the changes to `cancel_cluster_writeback`, `set_cluster_dirty`, `f2fs_write_compressed_pages`, and `f2fs_compress_write_end_io`. Along with that I also need to understand `f2fs_should_update_outplace` in `fs/f2fs/data.c` and `__get_segment_type_6` in `fs/f2fs/segment.c`. Similarly, I should assess the implications of the updates in `fs/erofs/data.c`, particularly the alterations to `erofs_onlinefolio_init`, `erofs_onlinefolio_split`, and `erofs_onlinefolio_end`, as well as `z_erofs_readahead` in `fs/erofs/zdata.c`, especially since it's calling `readahead_folio_last`. I'm expanding my scope to review other core kernel components. I'm noting significant changes to `zsmalloc.c`, including modifications to `get_first_zpdesc`, `obj_allocated`, and `create_page_chain`. Then I see a flurry of changes in other areas of the kernel. `drivers/md/md-bitmap.c` has changes related to `write_file_page` and `free_buffers`. Also I want to investigate the impact on `drivers/xen/grant-table.c`, where I see changes involving `gnttab_pages_set_private` and `gnttab_pages_clear_private`. I will also investigate `kernel/events/ring_buffer.c`, due to changes in `rb_alloc_aux_page` and `rb_free_aux_page`. And then I'll examine the modifications in `arch/x86/events/intel/bts.c` and `pt.c`, focusing on `buf_nr_pages`, `topa_insert_pages`, and `pt_buffer_try_single`. I'm curious about the work in `fs/nfs/write.c`, where I'm seeing updates to `nfs_inode_add_request` and `nfs_inode_remove_request`. Finally, to get a comprehensive understanding, I should check the Kconfig dependencies for these features. I will begin by examining the configuration requirements for `zsmalloc` and verify the `ZSMALLOC` setting. This will help determine the overall build configuration and dependencies. |
| 22/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "config ZSMALLOC",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/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"
} |
| 23/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32560 output: 18 thoughts: 12 Thoughts: Here's my thought process, as if I were analyzing that prompt: **Analyzing ZSMALLOC Activation and Configuration** Okay, so the prompt is asking me to figure out how to determine the conditions that "select" or "enable" ZSMALLOC. This implies there's a mechanism, likely in the kernel, that makes a decision about whether or not to use ZSMALLOC. I need to figure out what factors influence that decision. My initial thought is to consider the various configuration options available. I'll need to think about kernel build flags, boot parameters, and potential runtime sysfs or procfs entries that might control ZSMALLOC's behavior. I'll need to understand if the enablement is static (determined at compile time) or dynamic (influenced by runtime conditions). I'll start by brainstorming potential areas to look, considering things like: * **Kernel configuration (`.config` file):** Are there specific options related to ZSMALLOC, compression algorithms used with it (if applicable), memory management settings, or NUMA configuration that act as prerequisites or directly enable it? I'd need to search for keywords like `ZSMALLOC`, `ZRAM`, `COMPRESSION`, and possibly even `MEMORY_HOTPLUG` or `NUMA` related settings. * **Boot parameters:** Are there kernel command-line arguments that can force or disable ZSMALLOC, or that adjust its behavior at boot? Common options related to memory management or storage might be relevant. * **Runtime sysfs/procfs:** I'll check if there are sysfs or procfs entries that show ZSMALLOC's status, configuration, or statistics. This could reveal runtime parameters or decisions about its use. * **Code analysis:** I'd have to delve into the kernel source code itself, likely in areas related to memory management, the page allocator, or the ZRAM implementation (since ZSMALLOC is often used with ZRAM). Key functions to look for are initializations, decisions on page allocation/freeing, and compression/decompression routines. I should search for conditional statements (e.g., `if (CONFIG_ZSMALLOC)`, `#ifdef CONFIG_ZSMALLOC`, etc.) that control its enablement. * **Hardware and System Requirements:** There might be dependencies on the hardware platform. For instance, some CPU features or specific storage technologies might be required or preferred for ZSMALLOC to be beneficial. Ultimately, I expect to find a combination of configuration options and perhaps runtime checks that determine whether ZSMALLOC is selected and enabled for use. |
| 24/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "select ZSMALLOC"
}Results: {
"Output": "drivers/block/zram/Kconfig=2=config ZRAM\n--\ndrivers/block/zram/Kconfig-4-\tdepends on BLOCK \u0026\u0026 SYSFS \u0026\u0026 MMU\ndrivers/block/zram/Kconfig:5:\tselect ZSMALLOC\ndrivers/block/zram/Kconfig-6-\thelp\n--\nmm/Kconfig=22=config ZSWAP\n--\nmm/Kconfig-25-\tselect CRYPTO\nmm/Kconfig:26:\tselect ZSMALLOC\nmm/Kconfig-27-\thelp\n"
} |
| 25/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33270 output: 31 thoughts: 35 Thoughts: **Considering Storage Optimization Strategies** Okay, so I'm thinking about memory and storage efficiency here. ZSMALLOC, ZRAM, or ZSWAP – those are the usual suspects for a bit of on-the-fly compression and swapping. But I wonder if there's a better approach for the kind of workload I'm considering. Perhaps, I should explore alternative, more static methods. EROFS_FS… that's an interesting thought. I've been meaning to look into that again. Let's see... I'll pull up `fs/erofs/Kconfig`. That's where all the configuration options for that file system are defined. It'll be the best place to get a good overview of its functionality and any limitations. Time to dive in! |
| 26/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "config EROFS_FS",
"PathPrefix": "fs/erofs/"
}Results: {
"Output": "fs/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"
} |
| 27/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34430 output: 29 thoughts: 40 Thoughts: **My EROFS Compilation Observations** Okay, so I'm looking at `fs/erofs/zdata.c`, and I immediately see something interesting: this file is *only* compiled when `EROFS_FS_ZIP` is defined. That's good to know – it keeps the code size down and makes it easier to track dependencies. Now, I need to understand how that dependency is managed. Time to dive into `fs/erofs/Makefile` and see how the build system handles that conditional compilation. Let's see what the makefile logic is doing. |
| 28/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "zdata\\.o",
"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"
} |
| 29/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34739 output: 29 thoughts: 76 Thoughts: Okay, here's my interpretation of these code snippets, written as if they are my thoughts: **Analyzing EROFS and F2FS Compression in the Build Process** Right, let's see, the line `erofs-$(CONFIG_EROFS_FS_ZIP) += decompressor.o zmap.o zdata.o zutil.o` immediately suggests that when the `CONFIG_EROFS_FS_ZIP` configuration option is *enabled*, the EROFS filesystem will include these specific object files: `decompressor.o`, `zmap.o`, `zdata.o`, and `zutil.o`. Given the filenames, this almost certainly means these are related to ZIP-style decompression within the EROFS filesystem. This is standard practice in build systems – based on a pre-defined flag the build environment links appropriate object files. Then we've got `fs/f2fs/compress.c` being compiled only when `F2FS_FS_COMPRESSION` is enabled. That's pretty straightforward. We know F2FS (Flash-Friendly File System) supports compression, so the `compress.c` file must contain the core compression/decompression logic for that file system. The `F2FS_FS_COMPRESSION` config option dictates whether this code is built into the final kernel image or module. Now, let's just make sure that's as I expect it to be. Let me check the `fs/f2fs/Makefile`. |
| 30/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "compress\\.o",
"PathPrefix": "fs/f2fs/"
}Results: {
"Output": "fs/f2fs/Makefile=10=f2fs-$(CONFIG_FS_VERITY) += verity.o\nfs/f2fs/Makefile:11:f2fs-$(CONFIG_F2FS_FS_COMPRESSION) += compress.o\nfs/f2fs/Makefile-12-f2fs-$(CONFIG_F2FS_IOSTAT) += iostat.o\n"
} |
| 31/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35269 output: 33 thoughts: 766 Thoughts: **Focusing on Targeted Functions** Okay, so I'm diving into this patch with the goal of identifying *focused* functions for fuzzing. I need to avoid the ubiquitous ones, the core memory allocators, page fault handlers, and the like. The aim is to target the specific logic that's been added or changed, or the entry points for the feature under review. And I need to be careful about static inline functions in headers – they don't have distinct symbols, so I need to find the non-static callers in the .c files that *exercise* those inline helpers. First, I looked at this initial line `f2fs-$(CONFIG_F2FS_FS_COMPRESSION) += compress.o` which told me to investigate f2fs compression. Then `mm/hugetlb.c` being compiled when `HUGETLB_PAGE` is enabled - so check `mm/huge_memory.c` : `TRANSPARENT_HUGEPAGE` and `mm/migrate.c`: `MIGRATION` - all of these are important, but not necessarily where the most focused attention is needed. Now, let's get into the meat of it. I've identified these non-ubiquitous functions that seem to be added or modified: 1. **erofs:** In `fs/erofs/zdata.c`, `z_erofs_readahead` looks interesting, especially since it calls `readahead_folio_last`, a new function. Then, in `fs/erofs/data.c` there's `erofs_onlinefolio_init`, `erofs_onlinefolio_end`, and `erofs_onlinefolio_split`. These look very targeted to the new EROFS features. 2. **f2fs:** `fs/f2fs/compress.c` has a cluster of functions: `f2fs_write_compressed_pages`, `f2fs_compress_write_end_io`, `cancel_cluster_writeback`, `set_cluster_dirty`, and `prepare_compress_overwrite`. Also I saw `fs/f2fs/data.c`: `f2fs_should_update_outplace` and `fs/f2fs/segment.c`: `__get_segment_type_6`. The f2fs ones look very specific to the compression changes and data handling, so they're solid candidates. 3. **zsmalloc:** In `mm/zsmalloc.c`, I noticed `get_first_zpdesc` (with a `VM_WARN_ON_ONCE_PAGE` addition!), `obj_allocated` (also with a `VM_WARN_ON_ONCE_PAGE` addition), `reset_zpdesc`, and `create_page_chain`. Hang on, now I need to check something... are `get_first_zpdesc` and `obj_allocated` static inline? Yup. Okay, let's see. `get_first_zpdesc` is static *non*-inline! That's great, it's a good target. But `obj_allocated` *is* inline. So, I need to check who calls `get_first_zpdesc`. |
| 32/2 | 2026/09/14 03:29 | tool | grepper |
0mArgs:{
"Expression": "get_first_zpdesc",
"PathPrefix": "mm/zsmalloc.c"
}Results: {
"Output": "mm/zsmalloc.c=480=static inline void mod_zspage_inuse(struct zspage *zspage, int val)\n--\nmm/zsmalloc.c-484-\nmm/zsmalloc.c:485:static struct zpdesc *get_first_zpdesc(struct zspage *zspage)\nmm/zsmalloc.c-486-{\n--\nmm/zsmalloc.c=853=static int trylock_zspage(struct zspage *zspage)\n--\nmm/zsmalloc.c-856-\nmm/zsmalloc.c:857:\tfor (cursor = get_first_zpdesc(zspage); cursor != NULL; cursor =\nmm/zsmalloc.c-858-\t\t\t\t\tget_next_zpdesc(cursor)) {\n--\nmm/zsmalloc.c-866-unlock:\nmm/zsmalloc.c:867:\tfor (cursor = get_first_zpdesc(zspage); cursor != fail; cursor =\nmm/zsmalloc.c-868-\t\t\t\t\tget_next_zpdesc(cursor))\n--\nmm/zsmalloc.c=890=static inline void __free_zspage_lockless(struct zspage *zspage)\n--\nmm/zsmalloc.c-896-\nmm/zsmalloc.c:897:\tnext = zpdesc = get_first_zpdesc(zspage);\nmm/zsmalloc.c-898-\tdo {\n--\nmm/zsmalloc.c=941=static void init_zspage(struct size_class *class, struct zspage *zspage)\n--\nmm/zsmalloc.c-944-\tunsigned long off = 0;\nmm/zsmalloc.c:945:\tstruct zpdesc *zpdesc = get_first_zpdesc(zspage);\nmm/zsmalloc.c-946-\n--\nmm/zsmalloc.c=1255=static unsigned long obj_malloc(struct zs_pool *pool,\n--\nmm/zsmalloc.c-1272-\tm_offset = offset_in_page(offset);\nmm/zsmalloc.c:1273:\tm_zpdesc = get_first_zpdesc(zspage);\nmm/zsmalloc.c-1274-\n--\nmm/zsmalloc.c=1588=static void migrate_zspage(struct zs_pool *pool, struct zspage *src_zspage,\n--\nmm/zsmalloc.c-1593-\tint obj_idx = 0;\nmm/zsmalloc.c:1594:\tstruct zpdesc *s_zpdesc = get_first_zpdesc(src_zspage);\nmm/zsmalloc.c-1595-\tstruct size_class *class = pool-\u003esize_class[src_zspage-\u003eclass];\n--\nmm/zsmalloc.c=1679=static void lock_zspage(struct zspage *zspage)\n--\nmm/zsmalloc.c-1692-\t\tzspage_read_lock(zspage);\nmm/zsmalloc.c:1693:\t\tzpdesc = get_first_zpdesc(zspage);\nmm/zsmalloc.c-1694-\t\tif (zpdesc_trylock(zpdesc))\n--\nmm/zsmalloc.c=1717=static void replace_sub_page(struct size_class *class, struct zspage *zspage,\n--\nmm/zsmalloc.c-1724-\nmm/zsmalloc.c:1725:\tzpdesc = get_first_zpdesc(zspage);\nmm/zsmalloc.c-1726-\tdo {\n--\nmm/zsmalloc.c=1920=static void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage)\nmm/zsmalloc.c-1921-{\nmm/zsmalloc.c:1922:\tstruct zpdesc *zpdesc = get_first_zpdesc(zspage);\nmm/zsmalloc.c-1923-\n"
} |
| 33/2 | 2026/09/14 03:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37835 output: 29 thoughts: 777 Thoughts: **My Analysis of Potential Code Locations for Inspection** Okay, let's see. I need to figure out where this issue might be cropping up. I'm focusing on areas related to memory management, specifically where there might be interactions with private data attached to folios and pages, and potentially some I/O or filesystem considerations. I'm starting with `mm/zsmalloc.c` since I have `get_first_zpdesc` (with its `VM_WARN_ON_ONCE_PAGE` check, interesting...) and `create_page_chain` there. Those seem relevant. Next, I'm thinking about migration, so I'm looking at `mm/migrate.c`. `migrate_folio_unmap` has a check for attached private data and a call to `try_to_free_buffers` if present, which seems like a decent starting point. The `folio_migrate_flags` might also be something to review. Then, huge pages are always interesting. I'll check `mm/huge_memory.c` specifically, the `split_huge_pages_pid` function. It seems to have a check for attached private data and reference counts, which could be part of the problem. Now, `mm/page-writeback.c`... ah, `filemap_dirty_folio`. Wait a minute, isn't that... *ubiquitous*? Every single dirty page write in the page cache uses it! Right, the advice I got was to avoid ubiquitous lifecycle hot-paths like that, as it would dilute the fuzzing effort. Yes, `filemap_dirty_folio` is definitely ubiquitous. I should probably focus on something more specific. But let's look at `folio_check_dirty_writeback` in `mm/vmscan.c`. It's called in page reclaim, which might be a more targeted place to look. Okay, let's move on to the drivers. I'll look at some more specific drivers now to rule them out. I think drivers that touch pages are more likely candidates. Let's see... `drivers/md/md-bitmap.c`. `write_file_page`, `free_buffers` - md-bitmap is specific to RAID/MD, so it's a bit more specialized. Less likely, but worth a quick glance later. Then, `drivers/xen/grant-table.c` - Xen grant table. That seems highly specialized. I'll keep going... Then, `arch/x86/events/intel/bts.c` and `pt.c` - these are Intel BTS and PT PMUs, so also very specialized. I'll skip these for now. Let's check `fs/f2fs/compress.c`. This is file system specific. `f2fs_write_compressed_pages`, `f2fs_compress_write_end_io`, `cancel_cluster_writeback`, `set_cluster_dirty`, `prepare_compress_overwrite` - these are clearly f2fs compression routines, so not ubiquitous. Good. That's a possibility. And then, `fs/erofs/`. `z_erofs_readahead`, `erofs_onlinefolio_init`, `erofs_onlinefolio_end`, `erofs_onlinefolio_split` – these are specific to erofs, not general purpose. This is also likely to be a good place to test. Okay, back to `mm/zsmalloc.c`. I should review those again: `get_first_zpdesc`, `create_page_chain`, `reset_zpdesc`. I think this is a central location. And then, again, `mm/huge_memory.c`. I need to review `split_huge_pages_pid` (sysfs/debugfs interface) more thoroughly. I'll come back to this. |
| 34/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "split_huge_pages_pid",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/huge_memory.c=4761=static inline bool vma_not_suitable_for_thp_split(struct vm_area_struct *vma)\n--\nmm/huge_memory.c-4774-\nmm/huge_memory.c:4775:static int split_huge_pages_pid(int pid, unsigned long vaddr_start,\nmm/huge_memory.c-4776-\t\t\t\tunsigned long vaddr_end, unsigned int new_order,\n--\nmm/huge_memory.c=4963=static ssize_t split_huge_pages_write(struct file *file, const char __user *buf,\n--\nmm/huge_memory.c-5029-\nmm/huge_memory.c:5030:\tret = split_huge_pages_pid(pid, vaddr_start, vaddr_end, new_order,\nmm/huge_memory.c-5031-\t\t\tin_folio_offset);\n"
} |
| 35/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39724 output: 29 thoughts: 64 Thoughts: **My Initial Thoughts on Huge Page Management** Okay, so `split_huge_pages_pid` is definitely called from `split_huge_pages_write` – that's a debugfs entry point, which tells me the developers are leaning into observability in this area. But what's the whole picture here? What about `add_hugetlb_folio` in `mm/hugetlb.c`? That function is crucial for adding folio backing huge pages to the system, so understanding its usage is critical. Time to trace all the call sites for `add_hugetlb_folio` to see how it fits into the broader allocation and management framework. I need to understand its relationship to the split logic. Let's see who's invoking that key function and what other modules are involved. |
| 36/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "add_hugetlb_folio",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/hugetlb.c=1391=void remove_hugetlb_folio(struct hstate *h, struct folio *folio,\n--\nmm/hugetlb.c-1429-\nmm/hugetlb.c:1430:void add_hugetlb_folio(struct hstate *h, struct folio *folio,\nmm/hugetlb.c-1431-\t\t bool adjust_surplus)\n--\nmm/hugetlb.c=1457=static void __update_and_free_hugetlb_folio(struct hstate *h,\n--\nmm/hugetlb.c-1486-\t\t */\nmm/hugetlb.c:1487:\t\tadd_hugetlb_folio(h, folio, true);\nmm/hugetlb.c-1488-\t\tspin_unlock_irq(\u0026hugetlb_lock);\n--\nmm/hugetlb.c=1583=static void bulk_vmemmap_restore_error(struct hstate *h,\n--\nmm/hugetlb.c-1619-\t\t\t\tspin_lock_irq(\u0026hugetlb_lock);\nmm/hugetlb.c:1620:\t\t\t\tadd_hugetlb_folio(h, folio, true);\nmm/hugetlb.c-1621-\t\t\t\tspin_unlock_irq(\u0026hugetlb_lock);\n--\nmm/hugetlb.c=1964=int dissolve_free_hugetlb_folio(struct folio *folio)\n--\nmm/hugetlb.c-1986-\t\t * here would leave it on the free list and, on vmemmap restore\nmm/hugetlb.c:1987:\t\t * failure, the add_hugetlb_folio() rollback corrupts that list.\nmm/hugetlb.c-1988-\t\t */\n--\nmm/hugetlb.c-2036-\t\t\t\tspin_lock_irq(\u0026hugetlb_lock);\nmm/hugetlb.c:2037:\t\t\t\tadd_hugetlb_folio(h, folio, adjust_surplus);\nmm/hugetlb.c-2038-\t\t\t\tif (!adjust_surplus)\n--\nmm/hugetlb.c=4022=long demote_pool_huge_page(struct hstate *src, nodemask_t *nodes_allowed,\n--\nmm/hugetlb.c-4101-\t\t\tlist_del(\u0026folio-\u003elru);\nmm/hugetlb.c:4102:\t\t\tadd_hugetlb_folio(src, folio, false);\nmm/hugetlb.c-4103-\n--\nmm/hugetlb.c-4109-\t\t\tlist_del(\u0026folio-\u003elru);\nmm/hugetlb.c:4110:\t\t\tadd_hugetlb_folio(src, folio, true);\nmm/hugetlb.c-4111-\n--\nmm/hugetlb_internal.h=95=extern void remove_hugetlb_folio(struct hstate *h, struct folio *folio,\nmm/hugetlb_internal.h-96-\t\t\t\t bool adjust_surplus);\nmm/hugetlb_internal.h:97:extern void add_hugetlb_folio(struct hstate *h, struct folio *folio,\nmm/hugetlb_internal.h-98-\t\t\t bool adjust_surplus);\n"
} |
| 37/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 40831 output: 28 thoughts: 63 Thoughts: Okay, let's dive into this. **Examining `mm/migrate.c` for Migration Operations** Alright, I'm specifically interested in the unmapping aspect of folio migration here. Let's start with `mm/migrate.c`. I need to understand the interaction of `migrate_folio_unmap` and `folio_migrate_flags`. I know `migrate_folio_unmap` is a static function, which means it's scoped within this file, and its primary purpose is unmapping pages during the migration process. My next logical step is to identify all the points where `migrate_folio_unmap` is invoked. Knowing the call sites will give me crucial context regarding how and when this unmapping operation happens. With this information I can better understand the overall migration flow. |
| 38/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "migrate_folio_unmap",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/migrate.c=1199=static void migrate_folio_done(struct folio *src,\n--\nmm/migrate.c-1211-/* Obtain the lock on page, remove all ptes. */\nmm/migrate.c:1212:static int migrate_folio_unmap(new_folio_t get_new_folio,\nmm/migrate.c-1213-\t\tfree_folio_t put_new_folio, unsigned long private,\n--\nmm/migrate.c=1362=static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private,\n--\nmm/migrate.c-1460-/*\nmm/migrate.c:1461: * Counterpart of migrate_folio_unmap() and migrate_folio_move() for hugetlb\nmm/migrate.c-1462- * folio migration.\n--\nmm/migrate.c=1810=static int migrate_pages_batch(struct list_head *from,\n--\nmm/migrate.c-1916-\nmm/migrate.c:1917:\t\t\trc = migrate_folio_unmap(get_new_folio, put_new_folio,\nmm/migrate.c-1918-\t\t\t\t\tprivate, folio, \u0026dst, mode, ret_folios);\n"
} |
| 39/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 41420 output: 35 thoughts: 499 Thoughts: **Analyzing the Impact of the Proposed Changes** Okay, let's break this down. First, I need to see exactly how this patch interacts with existing functionality. I'll start by checking `readahead_folio_last`, since I saw it was introduced in `include/linux/pagemap.h` and is called by `z_erofs_readahead` within `fs/erofs/zdata.c`. It's crucial to understand how this readahead change is used by the `erofs` filesystem. Then, I will move on to `f2fs_write_compressed_pages` in `fs/f2fs/compress.c`. I have to trace how this function and related functions like `f2fs_compress_write_end_io`, `cancel_cluster_writeback`, and `set_cluster_dirty` are impacted. This will give me a solid understanding of potential performance implications. Next, I need to identify any other directly impacted or modified functions. A quick scan shows changes in a few areas: `fs/ceph/addr.c` with `ceph_netfs_check_write_begin`, `page_snap_context`, and `ceph_dirty_folio`. Then, `fs/crypto/crypto.c` with `fscrypt_free_bounce_page` and `fscrypt_encrypt_pagecache_blocks`, and `fs/ubifs/file.c` (looks like only comment changes here, but still worth a look). We also have `fs/nfs/write.c` with `nfs_inode_add_request` and `nfs_inode_remove_request`, and finally `drivers/md/md-bitmap.c` touching `write_file_page` and `free_buffers`. This analysis gives me a holistic view of the patch's potential reach. Finally, the configuration requirements. To be certain that everything works as it should, I need to check the Kconfig settings. I'm seeing that `EROFS_FS` and `EROFS_FS_ZIP` are required for `z_erofs_readahead` and `erofs_onlinefolio_*`. `F2FS_FS` and `F2FS_FS_COMPRESSION` are required for `f2fs_write_compressed_pages` and related functionality. Also `ZSMALLOC` (or `ZRAM` / `ZSWAP`) is needed for `mm/zsmalloc.c`, `MIGRATION` for `mm/migrate.c`, `HUGETLB_PAGE` for `mm/hugetlb.c` and `TRANSPARENT_HUGEPAGE` for `mm/huge_memory.c`. I will then go on and look at the Kconfig definitions, specifically `F2FS_FS_COMPRESSION`. This is going to be helpful. This is critical for assessing portability and determining whether all required dependencies are met. |
| 40/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "config F2FS_FS_COMPRESSION",
"PathPrefix": "fs/f2fs/"
}Results: {
"Output": "fs/f2fs/Kconfig=87=config F2FS_FAULT_INJECTION\n--\nfs/f2fs/Kconfig-94-\nfs/f2fs/Kconfig:95:config F2FS_FS_COMPRESSION\nfs/f2fs/Kconfig-96-\tbool \"F2FS compression feature\"\n"
} |
| 41/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42676 output: 35 thoughts: 19 Thoughts: Here's my thought process, as an expert in my field: **Investigating F2FS Compression Dependencies** Okay, so I need to dig into the dependencies of `F2FS_FS_COMPRESSION`. That's the logical starting point. I want to understand what other kernel features, options, or configurations might be tied to this compression feature in the F2FS file system. A dependency analysis will tell me if turning on compression will inadvertently enable or require something else, or if enabling something else will open up the possibilities for compression. This is critical for assessing compatibility, potential conflicts, and the overall impact of enabling this particular functionality within the broader kernel configuration. Time to start checking the `depends on` clauses. I need to see what's connected to this. |
| 42/2 | 2026/09/14 03:30 | tool | read-file |
0mArgs:{
"File": "fs/f2fs/Kconfig",
"FirstLine": 94,
"LineCount": 20
}Results: {
"Contents": " 94:\t\n 95:\tconfig F2FS_FS_COMPRESSION\n 96:\t\tbool \"F2FS compression feature\"\n 97:\t\tdepends on F2FS_FS\n 98:\t\thelp\n 99:\t\t Enable filesystem-level compression on f2fs regular files,\n 100:\t\t multiple back-end compression algorithms are supported.\n 101:\t\n 102:\tconfig F2FS_FS_LZO\n 103:\t\tbool \"LZO compression support\"\n 104:\t\tdepends on F2FS_FS_COMPRESSION\n 105:\t\tdefault y\n 106:\t\thelp\n 107:\t\t Support LZO compress algorithm, if unsure, say Y.\n 108:\t\n 109:\tconfig F2FS_FS_LZORLE\n 110:\t\tbool \"LZO-RLE compression support\"\n 111:\t\tdepends on F2FS_FS_LZO\n 112:\t\tdefault y\n 113:\t\thelp\n"
} |
| 43/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43161 output: 35 thoughts: 51 Thoughts: **Examining Filesystem Dependencies** Okay, so I'm looking at these filesystem configurations and immediately see some key dependencies. It's pretty straightforward, actually. `F2FS_FS_COMPRESSION` relies directly on `F2FS_FS`, which makes perfect sense. Compression features always build upon the core filesystem implementation. Similarly, I see `EROFS_FS_ZIP` depends on `EROFS_FS`. Right. Makes sense. These aren't isolated features, they're extensions of the base filesystem. I'll need to double check the dependencies in the `fs/erofs/Kconfig` file to confirm everything is as expected. Let's see what's in there. |
| 44/2 | 2026/09/14 03:30 | tool | read-file |
0mArgs:{
"File": "fs/erofs/Kconfig",
"FirstLine": 110,
"LineCount": 20
}Results: {
"Contents": " 110:\t\t If you don't want to enable this feature, say N.\n 111:\t\n 112:\tconfig EROFS_FS_ZIP\n 113:\t\tbool \"EROFS Data Compression Support\"\n 114:\t\tdepends on EROFS_FS\n 115:\t\tdefault y\n 116:\t\thelp\n 117:\t\t Enable EROFS compression layouts so that filesystems containing\n 118:\t\t compressed files can be parsed by the kernel.\n 119:\t\n 120:\t\t If you don't want to enable compression feature, say N.\n 121:\t\n 122:\tconfig EROFS_FS_ZIP_LZMA\n 123:\t\tbool \"EROFS LZMA compressed data support\"\n 124:\t\tdepends on EROFS_FS_ZIP\n 125:\t\tdefault y\n 126:\t\thelp\n 127:\t\t Saying Y here includes support for reading EROFS file systems\n 128:\t\t containing LZMA compressed data, specifically called microLZMA. It\n 129:\t\t gives better compression ratios than the default LZ4 format, at the\n"
} |
| 45/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43702 output: 32 thoughts: 61 Thoughts: **Examining `ZSMALLOC` Configuration** Okay, let's dive into the kernel configuration and see what's up with `ZSMALLOC`. First, I'll pull up the `mm/Kconfig` file and search for the `ZSMALLOC` option. Ah, here it is: `config ZSMALLOC tristate`. Right, `tristate` means it can be enabled as a built-in feature, a module, or disabled entirely. But, hang on a second... does it have any dependencies or prompts associated with it, or can it just be set directly? I need to check the surrounding lines in `mm/Kconfig`, around line 125, to see if there are any conditional dependencies or help text to shed more light on its configurability. Time to investigate further and understand the context surrounding this option. |
| 46/2 | 2026/09/14 03:30 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 120,
"LineCount": 20
}Results: {
"Contents": " 120:\t default \"lz4\" if ZSWAP_COMPRESSOR_DEFAULT_LZ4\n 121:\t default \"lz4hc\" if ZSWAP_COMPRESSOR_DEFAULT_LZ4HC\n 122:\t default \"zstd\" if ZSWAP_COMPRESSOR_DEFAULT_ZSTD\n 123:\t default \"\"\n 124:\t\n 125:\tconfig ZSMALLOC\n 126:\t\ttristate\n 127:\t\n 128:\tmenu \"Zsmalloc allocator options\"\n 129:\t\tdepends on ZSMALLOC\n 130:\t\n 131:\tcomment \"Zsmalloc is a common backend allocator for zswap \u0026 zram\"\n 132:\t\n 133:\tconfig ZSMALLOC_STAT\n 134:\t\tbool \"Export zsmalloc statistics\"\n 135:\t\tselect DEBUG_FS\n 136:\t\thelp\n 137:\t\t This option enables code in the zsmalloc to collect various\n 138:\t\t statistics about what's happening in zsmalloc and exports that\n 139:\t\t information to userspace via debugfs.\n"
} |
| 47/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44281 output: 28 thoughts: 60 Thoughts: **My Initial Thoughts on ZRAM, ZSWAP, and ZSMALLOC** Okay, so I'm looking at the core of the memory management within this system. Right off the bat, I see the relationships between `ZRAM`, `ZSWAP`, and `ZSMALLOC` are clearly defined. It's evident that both `ZRAM` and `ZSWAP` are configured to use `ZSMALLOC` as their underlying memory allocator. Therefore, the relationship is now obvious: `ZRAM` `ZSWAP` `ZSMALLOC` Now, to understand the system architecture fully, I should examine how `HUGETLB_PAGE` is configured. Let's delve into `mm/Kconfig` to see how it's defined in the memory management subsystem. |
| 48/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLB_PAGE",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/Kconfig=693=config ARCH_HAS_PMD_SOFTLEAVES\n--\nmm/Kconfig-695-\nmm/Kconfig:696:config HUGETLB_PAGE_SIZE_VARIABLE\nmm/Kconfig-697-\tdef_bool n\n"
} |
| 49/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44621 output: 22 thoughts: 21 Thoughts: Here's my take on those thoughts: **Investigating the `HUGETLB_PAGE` Configuration** Okay, so I need to track down where the `HUGETLB_PAGE` option is defined within the kernel configuration files. My first instinct, and the usual starting point, is to jump into `fs/Kconfig`. That's where a lot of file system related configuration options reside, so it makes sense to start there. However, it's possible it's defined elsewhere, perhaps in a more general section. So, I need to open that up and have a look, or at least remember where these things tend to sit. It's time to dig in and find its location! |
| 50/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "config HUGETLB_PAGE\\b"
}Results: {
"Output": "fs/Kconfig=271=endif # HUGETLBFS\nfs/Kconfig-272-\nfs/Kconfig:273:config HUGETLB_PAGE\nfs/Kconfig-274-\tdef_bool HUGETLBFS\n"
} |
| 51/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44884 output: 32 thoughts: 26 Thoughts: Here's my summary, as I see it: **My Deep Dive into Huge Pages and hugetlbfs** Okay, so `HUGETLBFS` is the key to unlocking the power of `HUGETLB_PAGE` – that's the connection I need to remember. Let's dig into `hugetlbfs` and see exactly how it works. I'm ready to examine its configuration and internal workings so I can understand what it does. |
| 52/2 | 2026/09/14 03:30 | tool | read-file |
0mArgs:{
"File": "fs/Kconfig",
"FirstLine": 250,
"LineCount": 25
}Results: {
"Contents": " 250:\tmenuconfig HUGETLBFS\n 251:\t\tbool \"HugeTLB file system support\"\n 252:\t\tdepends on ARCH_SUPPORTS_HUGETLBFS\n 253:\t\tselect MEMFD_CREATE\n 254:\t\tselect PADATA if SMP\n 255:\t\thelp\n 256:\t\t hugetlbfs is a filesystem backing for HugeTLB pages, based on\n 257:\t\t ramfs. For architectures that support it, say Y here and read\n 258:\t\t \u003cfile:Documentation/admin-guide/mm/hugetlbpage.rst\u003e for details.\n 259:\t\n 260:\t\t If unsure, say N.\n 261:\t\n 262:\tif HUGETLBFS\n 263:\tconfig HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON\n 264:\t\tbool \"HugeTLB Vmemmap Optimization (HVO) defaults to on\"\n 265:\t\tdefault n\n 266:\t\tdepends on HUGETLB_PAGE_OPTIMIZE_VMEMMAP\n 267:\t\thelp\n 268:\t\t The HugeTLB Vmemmap Optimization (HVO) defaults to off. Say Y here to\n 269:\t\t enable HVO by default. It can be disabled via hugetlb_free_vmemmap=off\n 270:\t\t (boot command line) or hugetlb_optimize_vmemmap (sysctl).\n 271:\tendif # HUGETLBFS\n 272:\t\n 273:\tconfig HUGETLB_PAGE\n 274:\t\tdef_bool HUGETLBFS\n"
} |
| 53/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45466 output: 26 thoughts: 35 Thoughts: **My Line of Inquiry: Huge Pages and Migration** Okay, so I'm thinking about `HUGETLBFS` and how it relates to `HUGETLB_PAGE`. That's a pretty straightforward connection. But what about the interaction with `MIGRATION`? That's the real question here. We know about huge pages and how they improve performance by reducing TLB misses. But what happens when we throw migration into the mix? Can we migrate huge pages? Do we need to consider specific configurations? I need to clarify this interaction, so I'm going to quickly check the `config MIGRATION` settings to understand how the system is set up in relation to memory migration. This should give me a solid foundation for assessing the impact on huge pages. |
| 54/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "config MIGRATION",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/Kconfig=672=config NUMA_MIGRATION\n--\nmm/Kconfig-682-\nmm/Kconfig:683:config MIGRATION\nmm/Kconfig-684-\tbool\n"
} |
| 55/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 45754 output: 26 thoughts: 10 Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and delivered in the first person: **Analyzing the Selection Criteria for "MIGRATION"** Alright, let's dive into this. I need to understand *why* "MIGRATION" was selected. My initial thought process is to deconstruct this: what parameters, variables, or conditions would trigger this term? Is it a single, deterministic factor, or a complex interplay of several? I need to immediately begin thinking about the data sources. Are we dealing with time-series data, spatial data, text corpora, or something more specialized? The data type drastically impacts the potential selection criteria. For instance, if this is a time-series context, are we looking for significant shifts in values, cyclical patterns, or perhaps a statistically significant trend? If we're working with a more complex dataset, is it rule-based? Are there specific thresholds, ratios, or dependencies that are being evaluated? I'd immediately want to look at the code or model. What are the key features that would be considered relevant? Is there a feature importance ranking? That is where I would start. If this is text-based, is it a keyword search, a topic modeling output, or sentiment analysis? Again, the specifics are critical. Are there synonyms being considered, or are we dealing with a strict match? The scope of what qualifies as "MIGRATION" drastically changes depending on the domain. For example, is this the migration of physical goods, the migration of data sets, or the movement of people? I have to consider the context. Where is this term used? Is it in a larger framework or application? Understanding the goals of this system is critical. Are we trying to identify a past event, predict a future trend, or simply categorize instances? And, most importantly, what are the potential sources of error? Are we sensitive to noise, missing data, or biased labels? Any potential issues will influence how I interpret the "MIGRATION" designation. I'll need to critically evaluate the assumptions that underlie the selection and identify any edge cases that would influence the selection. I need to fully understand all potential sources of error. So, the key here is to methodically break down the "MIGRATION" selection process. I need to deconstruct the algorithms, examine the data, and understand the application's goals to truly understand why this specific selection was made. |
| 56/2 | 2026/09/14 03:30 | tool | grepper |
0mArgs:{
"Expression": "select MIGRATION",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/Kconfig=581=config MEMORY_HOTREMOVE\nmm/Kconfig-582-\tbool \"Allow for memory hot remove\"\nmm/Kconfig:583:\tselect MIGRATION\nmm/Kconfig-584-\n--\nmm/Kconfig=641=config COMPACTION\n--\nmm/Kconfig-643-\tdefault y\nmm/Kconfig:644:\tselect MIGRATION\nmm/Kconfig-645-\tdepends on MMU\n--\nmm/Kconfig=672=config NUMA_MIGRATION\n--\nmm/Kconfig-675-\tdepends on NUMA \u0026\u0026 MMU\nmm/Kconfig:676:\tselect MIGRATION\nmm/Kconfig-677-\thelp\n--\nmm/Kconfig=764=config MEMORY_FAILURE\n--\nmm/Kconfig-768-\tselect INTERVAL_TREE\nmm/Kconfig:769:\tselect MIGRATION\nmm/Kconfig-770-\thelp\n--\nmm/Kconfig=1055=config CMA\n--\nmm/Kconfig-1057-\tdepends on MMU\nmm/Kconfig:1058:\tselect MIGRATION\nmm/Kconfig-1059-\tselect MEMORY_ISOLATION\n"
} |
| 57/2 | 2026/09/14 03:30 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46627 output: 349 thoughts: 962 Thoughts: **Focusing Fuzzing Targets** Okay, so the decision is to prioritize `MIGRATION` if either `COMPACTION` or `NUMA_MIGRATION` is selected. Now, let's figure out the right *FocusSymbols*. We're aiming for specific, not-everywhere kernel functions. Here's the rundown of candidates I've considered: * `z_erofs_readahead`: This one's interesting because it directly calls `readahead_folio_last()`, and that walking logic has been recently changed. * `erofs_onlinefolio_end`: EROFS folio completion. * `erofs_onlinefolio_init`: Attaches private data to folios. * `f2fs_write_compressed_pages`: Deals with f2fs compression write path updates. * `f2fs_compress_write_end_io`: F2FS compressed write end IO callback. * `cancel_cluster_writeback`: F2FS compression writeback cancellation. * `get_first_zpdesc`: A zsmalloc function that has a new assertion. * `create_page_chain`: zsmalloc's page chaining logic. * `migrate_folio_unmap`: An unmapping migration routine, checks `folio_has_attached_private`. * `split_huge_pages_pid`: Huge page splitting, also checking `folio_has_attached_private`. * `add_hugetlb_folio`: Deals with hugetlb folio addition. Are any of these too common? Let's check: * `z_erofs_readahead`: Only used in EROFS readahead. Good. * `erofs_onlinefolio_end`: Only for EROFS online folios. Good. * `f2fs_write_compressed_pages`: Only for compressed F2FS files. Excellent. * `f2fs_compress_write_end_io`: Only on F2FS compressed write completion. Good. * `get_first_zpdesc`: zsmalloc allocator-specific. Good. * `create_page_chain`: Only called when allocating zspages in zsmalloc. Great. * `split_huge_pages_pid`: Only when splitting huge pages via debugfs. Good. * `add_hugetlb_folio`: For hugetlb management. Good. What about `readahead_folio_last`? It's a static inline, so we can't target it directly, but `z_erofs_readahead` calls it. And `folio_has_attached_private` is static inline as well, but `migrate_folio_unmap`, `split_huge_pages_pid`, and `folio_check_dirty_writeback` call it. The relevant `EnableConfigs` look like this: * `EROFS_FS` * `EROFS_FS_ZIP` * `F2FS_FS` * `F2FS_FS_COMPRESSION` * `ZRAM` or `ZSWAP` (implying `ZSMALLOC`) * `HUGETLBFS` (implying `HUGETLB_PAGE`) * `TRANSPARENT_HUGEPAGE` * `COMPACTION` (implying `MIGRATION`) I've made sure to double-check against the requirements: `WorthFuzzing` is `true`, `EnableConfigs` are correctly formatted, and I've selected non-ubiquitous functions for `FocusSymbols`. All checks out! Time to construct the call to `set-results`. |
| 58/2 | 2026/09/14 03:31 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"F2FS_FS",
"F2FS_FS_COMPRESSION",
"ZRAM",
"ZSWAP",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE",
"COMPACTION"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_end",
"erofs_onlinefolio_init",
"f2fs_write_compressed_pages",
"f2fs_compress_write_end_io",
"get_first_zpdesc",
"create_page_chain",
"migrate_folio_unmap",
"split_huge_pages_pid",
"add_hugetlb_folio"
],
"Reasoning": "The patch series removes the PG_private page flag entirely across core memory management, the page cache, and filesystems (replacing flag checks with folio-\u003eprivate inspections, folio_has_attached_private(), and custom tracking). It modifies core mm behavior (reclaim, migration, THP splitting, zsmalloc, hugetlb), introduces a new reverse readahead iterator (readahead_folio_last) used by EROFS, restructures f2fs compression folio handling, and adds/modifies runtime assertions (VM_WARN_ON_ONCE_PAGE in zsmalloc, DBG_BUGON in erofs). These extensive refactorings in core mm and reachable filesystem code paths warrant fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_ZIP",
"F2FS_FS",
"F2FS_FS_COMPRESSION",
"ZRAM",
"ZSWAP",
"HUGETLBFS",
"TRANSPARENT_HUGEPAGE",
"COMPACTION"
],
"FocusSymbols": [
"z_erofs_readahead",
"erofs_onlinefolio_end",
"erofs_onlinefolio_init",
"f2fs_write_compressed_pages",
"f2fs_compress_write_end_io",
"get_first_zpdesc",
"create_page_chain",
"migrate_folio_unmap",
"split_huge_pages_pid",
"add_hugetlb_folio"
],
"Reasoning": "The patch series removes the PG_private page flag entirely across core memory management, the page cache, and filesystems (replacing flag checks with folio-\u003eprivate inspections, folio_has_attached_private(), and custom tracking). It modifies core mm behavior (reclaim, migration, THP splitting, zsmalloc, hugetlb), introduces a new reverse readahead iterator (readahead_folio_last) used by EROFS, restructures f2fs compression folio handling, and adds/modifies runtime assertions (VM_WARN_ON_ONCE_PAGE in zsmalloc, DBG_BUGON in erofs). These extensive refactorings in core mm and reachable filesystem code paths warrant fuzzing.",
"WorthFuzzing": true
} |
| 59/1 | 2026/09/14 03:31 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 60/2 | 2026/09/14 03:31 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch series removes the PG_private page flag across the kernel in favor of checking folio-\u003eprivate directly (or using helper functions like folio_has_attached_private).\n\nEvaluating the changes under KMSAN vs KASAN criteria:\n1. No uninitialized memory usage or branching on uninitialized variables is introduced: All struct page and struct folio instances are kernel-managed structures zero-initialized at boot or page allocation.\n2. In readahead_control, the new '_forward' field is zero-initialized by DEFINE_READAHEAD (via standard C designated-initializer zeroing) and properly set before use.\n3. In /proc/kpageflags (fs/proc/page.c), removing the KPF_PRIVATE bit extraction does not expose uninitialized bits since the return mask 'u' is explicitly zero-initialized.\n4. No new user-space copy boundaries, ioctls, or padding-sensitive structures are created or modified.\n\nAny potential bugs resulting from this refactor would involve page lifecycle tracking, folio refcounting discrepancies, or NULL pointer dereferences, which fall under the purview of KASAN and standard kernel debuggers (e.g., CONFIG_DEBUG_VM). Spawning a dedicated KMSAN session is therefore 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 5613ada8c0ba47c4af37f50b4afcfb53065f9148
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 14 03:28:04 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..7b706d3e54901 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);
@@ -533,12 +533,11 @@ static void write_file_page(struct bitmap *bitmap, struct page *page, int wait)
static void free_buffers(struct page *page)
{
- struct buffer_head *bh;
+ struct buffer_head *bh = (struct buffer_head *)page_private(page);
- if (!PagePrivate(page))
+ if (!bh)
return;
- bh = page_buffers(page);
while (bh) {
struct buffer_head *next = bh->b_this_page;
free_buffer_head(bh);
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..1aca181d28c62 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(page);
}
/*
@@ -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/compress.c b/fs/f2fs/compress.c
index ce88092d9ce26..09d9b8d0fdcce 100644
--- a/fs/f2fs/compress.c
+++ b/fs/f2fs/compress.c
@@ -1064,13 +1064,15 @@ static void cancel_cluster_writeback(struct compress_ctx *cc,
/* Cancel writeback and stay locked. */
for (i = 0; i < cc->cluster_size; i++) {
+ struct folio *folio = page_folio(cc->rpages[i]);
+
if (i < submitted) {
inode_inc_dirty_pages(cc->inode);
- lock_page(cc->rpages[i]);
+ folio_lock(folio);
}
- clear_page_private_gcing(cc->rpages[i]);
- if (folio_test_writeback(page_folio(cc->rpages[i])))
- end_page_writeback(cc->rpages[i]);
+ folio_clear_f2fs_gcing(folio);
+ if (folio_test_writeback(folio))
+ folio_end_writeback(folio);
}
}
@@ -1078,11 +1080,15 @@ static void set_cluster_dirty(struct compress_ctx *cc)
{
int i;
- for (i = 0; i < cc->cluster_size; i++)
- if (cc->rpages[i]) {
- set_page_dirty(cc->rpages[i]);
- set_page_private_gcing(cc->rpages[i]);
- }
+ for (i = 0; i < cc->cluster_size; i++) {
+ struct folio *folio;
+
+ if (!cc->rpages[i])
+ continue;
+ folio = page_folio(cc->rpages[i]);
+ folio_mark_dirty(folio);
+ folio_set_f2fs_gcing(folio);
+ }
}
static int prepare_compress_overwrite(struct compress_ctx *cc,
@@ -1281,7 +1287,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
.op = REQ_OP_WRITE,
.op_flags = wbc_to_write_flags(wbc),
.old_blkaddr = NEW_ADDR,
- .page = NULL,
+ .folio = NULL,
.encrypted_page = NULL,
.compressed_page = NULL,
.io_type = io_type,
@@ -1370,7 +1376,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
block_t blkaddr;
blkaddr = f2fs_data_blkaddr(&dn);
- fio.page = cc->rpages[i];
+ fio.folio = page_folio(cc->rpages[i]);
fio.old_blkaddr = blkaddr;
/* cluster header */
@@ -1476,9 +1482,12 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
}
for (i = 0; i < cic->nr_rpages; i++) {
+ struct folio *rfolio;
+
WARN_ON(!cic->rpages[i]);
- clear_page_private_gcing(cic->rpages[i]);
- end_page_writeback(cic->rpages[i]);
+ rfolio = page_folio(cic->rpages[i]);
+ folio_clear_f2fs_gcing(rfolio);
+ folio_end_writeback(rfolio);
}
page_array_free(sbi, cic->rpages, cic->nr_rpages);
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 21f396ebe22ca..ca8232a9095f8 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2923,7 +2923,7 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true;
if (fio) {
- if (page_private_gcing(fio->page))
+ if (folio_test_f2fs_gcing(fio->folio))
return true;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
f2fs_is_checkpointed_data(sbi, fio->old_blkaddr)))
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9940a6cecf1a2..85937de3d7016 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1357,10 +1357,7 @@ struct f2fs_io_info {
blk_opf_t op_flags; /* req_flag_bits */
block_t new_blkaddr; /* new block address to be written */
block_t old_blkaddr; /* old block address before Cow */
- union {
- struct page *page; /* page to be written */
- struct folio *folio;
- };
+ struct folio *folio; /* folio to be written */
struct page *encrypted_page; /* encrypted page */
struct page *compressed_page; /* compressed page */
struct list_head list; /* serialize IOs */
@@ -1613,27 +1610,27 @@ static inline void f2fs_set_bit(unsigned int nr, char *addr);
static inline void f2fs_clear_bit(unsigned int nr, char *addr);
/*
- * Layout of f2fs page.private:
+ * Layout of f2fs folio->private:
*
* Layout A: lowest bit should be 1
* | bit0 = 1 | bit1 | bit2 | ... | bit MAX | private data .... |
- * bit 0 PAGE_PRIVATE_NOT_POINTER
- * bit 1 PAGE_PRIVATE_ONGOING_MIGRATION
- * bit 2 PAGE_PRIVATE_INLINE_INODE
- * bit 3 PAGE_PRIVATE_REF_RESOURCE
- * bit 4 PAGE_PRIVATE_ATOMIC_WRITE
+ * bit 0 F2FS_FOLIO_PRIVATE_NOT_POINTER
+ * bit 1 F2FS_FOLIO_PRIVATE_ONGOING_MIGRATION
+ * bit 2 F2FS_FOLIO_PRIVATE_INLINE_INODE
+ * bit 3 F2FS_FOLIO_PRIVATE_REF_RESOURCE
+ * bit 4 F2FS_FOLIO_PRIVATE_ATOMIC_WRITE
* bit 5- f2fs private data
*
* Layout B: lowest bit should be 0
- * page.private is a wrapped pointer.
+ * folio->private is a wrapped pointer.
*/
enum {
- PAGE_PRIVATE_NOT_POINTER, /* private contains non-pointer data */
- PAGE_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
- PAGE_PRIVATE_INLINE_INODE, /* inode page contains inline data */
- PAGE_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
- PAGE_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
- PAGE_PRIVATE_MAX
+ F2FS_FOLIO_PRIVATE_NOT_POINTER, /* private contains non-pointer data */
+ F2FS_FOLIO_PRIVATE_ONGOING_MIGRATION, /* data page which is on-going migrating */
+ F2FS_FOLIO_PRIVATE_INLINE_INODE, /* inode page contains inline data */
+ F2FS_FOLIO_PRIVATE_REF_RESOURCE, /* dirty page has referenced resources */
+ F2FS_FOLIO_PRIVATE_ATOMIC_WRITE, /* data page from atomic write path */
+ F2FS_FOLIO_PRIVATE_MAX
};
/* For compression */
@@ -2681,86 +2678,68 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
return -ENOSPC;
}
-#define PAGE_PRIVATE_GET_FUNC(name, flagname) \
+#define F2FS_FOLIO_PRIVATE_GET_FUNC(name, flagname) \
static inline bool folio_test_f2fs_##name(const struct folio *folio) \
{ \
unsigned long priv = (unsigned long)folio->private; \
- unsigned long v = (1UL << PAGE_PRIVATE_NOT_POINTER) | \
- (1UL << PAGE_PRIVATE_##flagname); \
+ unsigned long v = (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER) | \
+ (1UL << F2FS_FOLIO_PRIVATE_##flagname); \
return (priv & v) == v; \
-} \
-static inline bool page_private_##name(struct page *page) \
-{ \
- return PagePrivate(page) && \
- test_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page)) && \
- test_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
-#define PAGE_PRIVATE_SET_FUNC(name, flagname) \
+#define F2FS_FOLIO_PRIVATE_SET_FUNC(name, flagname) \
static inline void folio_set_f2fs_##name(struct folio *folio) \
{ \
- unsigned long v = (1UL << PAGE_PRIVATE_NOT_POINTER) | \
- (1UL << PAGE_PRIVATE_##flagname); \
+ unsigned long v = (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER) | \
+ (1UL << F2FS_FOLIO_PRIVATE_##flagname); \
if (!folio->private) \
folio_attach_private(folio, (void *)v); \
else { \
v |= (unsigned long)folio->private; \
folio->private = (void *)v; \
} \
-} \
-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)); \
- set_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
}
-#define PAGE_PRIVATE_CLEAR_FUNC(name, flagname) \
+#define F2FS_FOLIO_PRIVATE_CLEAR_FUNC(name, flagname) \
static inline void folio_clear_f2fs_##name(struct folio *folio) \
{ \
unsigned long v = (unsigned long)folio->private; \
\
- v &= ~(1UL << PAGE_PRIVATE_##flagname); \
- if (v == (1UL << PAGE_PRIVATE_NOT_POINTER)) \
+ v &= ~(1UL << F2FS_FOLIO_PRIVATE_##flagname); \
+ if (v == (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER)) \
folio_detach_private(folio); \
else \
folio->private = (void *)v; \
-} \
-static inline void clear_page_private_##name(struct page *page) \
-{ \
- clear_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
- if (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) \
- detach_page_private(page); \
}
-PAGE_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
-PAGE_PRIVATE_GET_FUNC(inline, INLINE_INODE);
-PAGE_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
-PAGE_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
+F2FS_FOLIO_PRIVATE_GET_FUNC(nonpointer, NOT_POINTER);
+F2FS_FOLIO_PRIVATE_GET_FUNC(inline, INLINE_INODE);
+F2FS_FOLIO_PRIVATE_GET_FUNC(gcing, ONGOING_MIGRATION);
+F2FS_FOLIO_PRIVATE_GET_FUNC(atomic, ATOMIC_WRITE);
-PAGE_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
-PAGE_PRIVATE_SET_FUNC(inline, INLINE_INODE);
-PAGE_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
-PAGE_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
+F2FS_FOLIO_PRIVATE_SET_FUNC(reference, REF_RESOURCE);
+F2FS_FOLIO_PRIVATE_SET_FUNC(inline, INLINE_INODE);
+F2FS_FOLIO_PRIVATE_SET_FUNC(gcing, ONGOING_MIGRATION);
+F2FS_FOLIO_PRIVATE_SET_FUNC(atomic, ATOMIC_WRITE);
-PAGE_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
-PAGE_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
-PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
-PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(reference, REF_RESOURCE);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(inline, INLINE_INODE);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
+F2FS_FOLIO_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
static inline unsigned long folio_get_f2fs_data(struct folio *folio)
{
unsigned long data = (unsigned long)folio->private;
- if (!test_bit(PAGE_PRIVATE_NOT_POINTER, &data))
+ if (!test_bit(F2FS_FOLIO_PRIVATE_NOT_POINTER, &data))
return 0;
- return data >> PAGE_PRIVATE_MAX;
+ return data >> F2FS_FOLIO_PRIVATE_MAX;
}
static inline void folio_set_f2fs_data(struct folio *folio, unsigned long data)
{
- data = (1UL << PAGE_PRIVATE_NOT_POINTER) | (data << PAGE_PRIVATE_MAX);
+ data = (1UL << F2FS_FOLIO_PRIVATE_NOT_POINTER) |
+ (data << F2FS_FOLIO_PRIVATE_MAX);
if (!folio_test_private(folio))
folio_attach_private(folio, (void *)data);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 63b712d3d599e..8c156e1fd37d0 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -3803,7 +3803,7 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
return CURSEG_COLD_DATA_PINNED;
- if (page_private_gcing(fio->page)) {
+ if (folio_test_f2fs_gcing(fio->folio)) {
if (fio->sbi->am.atgc_enabled &&
(fio->io_type == FS_DATA_IO) &&
(fio->sbi->gc_mode != GC_URGENT_HIGH) &&
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 969594074fd2d..e4ee59beccbaf 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_has_attached_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 462e89e055485..10f4ad075ea48 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 */
@@ -575,9 +571,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 */
@@ -1169,7 +1170,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)
@@ -1193,8 +1194,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_has_attached_private - check if the folio has private data attached
+ * @folio: The folio to check.
+ *
+ * Use this in code that may encounter swapcache or hugetlb folios but only
+ * wants to detect attached 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_has_attached_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
@@ -1204,7 +1225,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_has_attached_private(folio) || folio_test_private_2(folio);
}
#undef PF_ANY
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 939f3a5e973f6..bcbb0afe1a681 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,29 @@ void page_cache_async_readahead(struct address_space *mapping,
page_cache_async_ra(&ractl, folio, req_count);
}
+/*
+ * Adjust readahead_control to ensure next folio comes from
+ * [_index, _index + _nr_pages) afterwards and reset _batch_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 +1526,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 +1567,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..3c153b3ad8450 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -144,7 +144,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..304652d6d8f2a 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 attached private locklessly */ \
+ (data_race(folio_has_attached_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 30b7c63b0e359..8f4bcdccd8f35 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4845,8 +4845,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 attached private locklessly.
*/
- if (!folio_test_private(folio) &&
+ if (!data_race(folio_has_attached_private(folio)) &&
folio_expected_ref_count(folio) != folio_ref_count(folio))
goto next;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index d3a0650ff6905..8d551cdaef9db 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1446,11 +1446,8 @@ void add_hugetlb_folio(struct hstate *h, struct folio *folio,
}
__folio_set_hugetlb(folio);
- folio_change_private(folio, NULL);
- /*
- * We have to set hugetlb_vmemmap_optimized again as above
- * folio_change_private(folio, NULL) cleared it.
- */
+ /* Clear all folio->private flags except hugetlb_vmemmap_optimized. */
+ folio->private = NULL;
folio_set_hugetlb_vmemmap_optimized(folio);
arch_clear_hugetlb_flags(folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index a369d0c95c386..7e3a81f069744 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_has_attached_private(src)) {
try_to_free_buffers(src);
goto out;
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index eeab25d6ce364..0d754a678eac2 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 attached private locklessly */
+ __folio_mark_dirty(folio, mapping, !data_race(folio_has_attached_private(folio)));
if (mapping->host) {
/* !PageAnon && !swapper_space */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index aaceed4759eeb..c2eb8fa9d5e50 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -1029,7 +1029,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_has_attached_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 11be37c431718..7ef80e0da6267 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",
|
| 61/3 | 2026/09/14 03:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19842 output: 35 thoughts: 1977 Thoughts: **KMSAN Fuzzing Session Justification: Analysis of PG_private to PG_folio Conversion** Okay, let's break this down. My primary concern is whether this patch, which removes the `PG_private` flag from page flags and introduces `PG_folio`, warrants a dedicated KMSAN fuzzing session. I need to assess if this change could introduce or expose uninitialized memory risks, which is the core concern for KMSAN. I'm going to follow the established KMSAN criteria. First, I see that the patch's main thrust is the replacement of `PG_private` with `PG_folio` and the use of `folio->private` instead of using page flags. It looks like a refactoring. I need to understand *where* this flag was used and *how* the replacement affects existing code paths. A quick pass through the diff reveals a lot of changes, doc updates and usage of helpers. Let's drill down and focus on the potential for exposing uninitialized data. Specifically, I'm checking each file to see if any kernel data structures are being exposed to user space. * **`arch/x86/events/intel/bts.c` and `arch/x86/events/intel/pt.c`**: These seem straightforward - the logic around `page_private` appears to be preserved. They use `page_private(page)` to determine the order of the page. Even if it is 0, it behaves as before. * **`drivers/md/md-bitmap.c`**: Here, `page_private(page)` is cast to `buffer_head*`. That needs to be checked carefully for uninitialized read/write. I need to check how these `buffer_head` instances are set up and how they interact with userspace. This seems important. * **`drivers/xen/grant-table.c`**: This one is tricky. It uses `set_page_private` and has a special path for 64-bit systems. I need to understand how the `foreign` pointer is managed here, especially with the conditional compilation based on `BITS_PER_LONG`. If `kzalloc` is not called, is the value properly initialized? It seems to be taking special care to handle it, so it is probably fine. * **`fs/ceph/addr.c`**: Using `page_snap_context` to return a pointer. This looks safe as is. * **`fs/crypto/crypto.c`**: Simple flag removal, safe. * **`fs/erofs/data.c` and `fs/erofs/zdata.c`**: Uses `folio` methods and `readahead_folio_last`, so I'm not seeing any immediate red flags. * **`fs/f2fs/*`**: Pure `folio` changes; seems fine. * **`fs/nfs/*`**: Using `folio->private` instead of helpers. Looks safe. * **`fs/proc/page.c`**: Now we're getting somewhere. I saw the removal of `kpf_copy_bit`, but `u` is initialized, so this looks safe, even though the bit is not set. I need to make *sure* that the correct value is always initialized. I need to double-check this specifically, check how `stable_page_flags` is used. * **`fs/ubifs/file.c`**: Doc update, so no code changes. * **`include/linux/*`**: Pure changes in the helpers. * **`kernel/events/ring_buffer.c`**: `set_page_private(page, 0)` is safe. * **`kernel/vmcore_info.c`**: Flag removed. * **`mm/*`**: Uses the helpers. * **`mm/zpdesc.h`, `mm/zsmalloc.c`**: Replacing `is_first_zpdesc` with direct pointer comparisons. Safe. Now, let's revisit the KMSAN criteria. 1. **Kernel structures to userspace:** I need to look closely at `fs/proc/page.c` and confirm that all values going to userspace are initialized. I also need to make sure that the pointer returned in `drivers/md/md-bitmap.c` and the pointer `drivers/xen/grant-table.c` are initialized. 2. **Uninitialized memory usage within the kernel:** I haven't seen any *obvious* new issues, but I need to make *sure* that changing the representation of `private` doesn't lead to issues. 3. **Use of freed memory:** |
| 62/3 | 2026/09/14 03:31 | tool | read-file |
0mArgs:{
"File": "fs/proc/page.c",
"FirstLine": 225,
"LineCount": 35
}Results: {
"Contents": " 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 255:\t\t\tsize_t count, loff_t *ppos)\n 256:\t{\n 257:\t\treturn kpage_read(file, buf, count, ppos, KPAGE_FLAGS);\n 258:\t}\n 259:\t\n"
} |
| 63/3 | 2026/09/14 03:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23525 output: 19 thoughts: 354 Thoughts: **Analyzing the Bitwise Operation and `readahead_folio_last` Function** Okay, so the initial concern was about uninitialized memory potentially being accessed after removing that bitwise OR operation. But after reviewing the code, it seems like the `u` variable is initialized to 0, which means that removing `u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private);` simply sets the `KPF_PRIVATE` bit to zero. There's no uninitialized memory issue there, which is good. That eliminates that as a potential cause of concern. Now, let's move on to the `readahead_folio_last` function. The code suggests a possible issue if `ractl->_forward` isn't initialized. I need to figure out where the `struct readahead_control` is allocated and how its members are initialized. To be absolutely sure, I'll need to trace the definition of `DEFINE_READAHEAD` and wherever instances of `struct readahead_control` are created. That's the key to knowing whether `ractl->_forward` gets a default value, or if it's left to chance. I need to make sure this isn't undefined behavior, which could lead to intermittent issues. This is a critical point; lack of initialization in control structures is a classic bug. |
| 64/3 | 2026/09/14 03:31 | tool | grepper |
0mArgs:{
"Expression": "DEFINE_READAHEAD"
}Results: {
"Output": "fs/f2fs/file.c=4794=static int redirty_blocks(struct inode *inode, pgoff_t page_idx, int len)\nfs/f2fs/file.c-4795-{\nfs/f2fs/file.c:4796:\tDEFINE_READAHEAD(ractl, NULL, NULL, inode-\u003ei_mapping, page_idx);\nfs/f2fs/file.c-4797-\tstruct address_space *mapping = inode-\u003ei_mapping;\n--\nfs/verity/pagecache.c=42=void generic_readahead_merkle_tree(struct inode *inode, pgoff_t index,\n--\nfs/verity/pagecache.c-51-\t (!IS_ERR(folio) \u0026\u0026 !folio_test_uptodate(folio))) {\nfs/verity/pagecache.c:52:\t\tDEFINE_READAHEAD(ractl, NULL, NULL, inode-\u003ei_mapping, index);\nfs/verity/pagecache.c-53-\n--\ninclude/linux/pagemap.h=1405=struct readahead_control {\n--\ninclude/linux/pagemap.h-1418-\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\\\n--\ninclude/linux/pagemap.h=1451=void page_cache_sync_readahead(struct address_space *mapping,\n--\ninclude/linux/pagemap.h-1454-{\ninclude/linux/pagemap.h:1455:\tDEFINE_READAHEAD(ractl, file, ra, mapping, index);\ninclude/linux/pagemap.h-1456-\tpage_cache_sync_ra(\u0026ractl, req_count);\n--\ninclude/linux/pagemap.h=1473=void page_cache_async_readahead(struct address_space *mapping,\n--\ninclude/linux/pagemap.h-1476-{\ninclude/linux/pagemap.h:1477:\tDEFINE_READAHEAD(ractl, file, ra, mapping, folio-\u003eindex);\ninclude/linux/pagemap.h-1478-\tpage_cache_async_ra(\u0026ractl, folio, req_count);\n--\nmm/filemap.c=2672=static int filemap_readahead(struct kiocb *iocb, struct file *file,\n--\nmm/filemap.c-2675-{\nmm/filemap.c:2676:\tDEFINE_READAHEAD(ractl, file, \u0026file-\u003ef_ra, mapping, folio-\u003eindex);\nmm/filemap.c-2677-\n--\nmm/filemap.c=2686=static int filemap_get_pages(struct kiocb *iocb, size_t count,\n--\nmm/filemap.c-2705-\tif (!folio_batch_count(fbatch)) {\nmm/filemap.c:2706:\t\tDEFINE_READAHEAD(ractl, filp, \u0026filp-\u003ef_ra, mapping, index);\nmm/filemap.c-2707-\n--\nmm/filemap.c=3329=static struct file *do_sync_mmap_readahead(struct vm_fault *vmf)\n--\nmm/filemap.c-3333-\tstruct address_space *mapping = file-\u003ef_mapping;\nmm/filemap.c:3334:\tDEFINE_READAHEAD(ractl, file, ra, mapping, vmf-\u003epgoff);\nmm/filemap.c-3335-\tstruct file *fpin = NULL;\n--\nmm/filemap.c=3452=static struct file *do_async_mmap_readahead(struct vm_fault *vmf,\n--\nmm/filemap.c-3456-\tstruct file_ra_state *ra = \u0026file-\u003ef_ra;\nmm/filemap.c:3457:\tDEFINE_READAHEAD(ractl, file, ra, file-\u003ef_mapping, vmf-\u003epgoff);\nmm/filemap.c-3458-\tstruct file *fpin = NULL;\n--\nmm/internal.h=602=static inline void force_page_cache_readahead(struct address_space *mapping,\n--\nmm/internal.h-604-{\nmm/internal.h:605:\tDEFINE_READAHEAD(ractl, file, \u0026file-\u003ef_ra, mapping, index);\nmm/internal.h-606-\tforce_page_cache_ra(\u0026ractl, nr_to_read);\n"
} |
| 65/3 | 2026/09/14 03:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25382 output: 38 thoughts: 61 Thoughts: Here's my take on those thoughts:
**Analyzing `DEFINE_READAHEAD` in the Kernel**
Okay, so I'm looking at `DEFINE_READAHEAD`. I remember this construct, it uses designated initializers, ` { ... }`, in its struct definition, which is great. It's essentially leveraging C's feature to automatically zero-initialize any fields that *aren't* explicitly set within the initializer list. That's a clean way to ensure a consistent, safe starting point for the struct, especially when dealing with potentially uninitialized memory. Let me quickly double-check the implementation details; I'll need to jog my memory on exactly how this is handled, so lines 1410-1430 in `include/linux/pagemap.h` should have what I'm looking for. That should give me the lowdown on the exact structure being defined and how they are leveraging the zero-initialization.
|
| 66/3 | 2026/09/14 03:31 | tool | read-file |
0mArgs:{
"File": "include/linux/pagemap.h",
"FirstLine": 1410,
"LineCount": 25
}Results: {
"Contents": "1410:\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,\n"
} |
| 67/3 | 2026/09/14 03:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26098 output: 20 thoughts: 204 Thoughts: **My Initial Thoughts on the Readahead Control Structure** Okay, so I'm looking at this `ractl` structure and the read-ahead logic, specifically how `_batch_count` and `_forward` are initialized and manipulated. When `DEFINE_READAHEAD` sets things up, `_batch_count` starts at zero, and `_forward` is set to false (0). I immediately see a potential point of confusion with this initial state. Then, I'm examining the `__readahead_advance` function. It looks like it's designed to advance the read-ahead window based on the `_batch_count`. The core of the logic centers around advancing the index if `_forward` is true, decrementing `_nr_pages`, and resetting `_batch_count` to zero. On the *very* first call, `_batch_count` *is* going to be zero regardless of the logic in the if statement. That initial zero value makes me think this might be simpler than it initially appears. Moving on, I see `__readahead_folio` immediately sets `rac->_forward` to true, and then `readahead_folio_last` sets it back to false. This seems like a typical "start read-ahead, end read-ahead" pattern. I'm starting to think I need a more complete picture to grasp the control flow. I'm wondering if there are other ways this `readahead_control` structure gets allocated or initialized, or if `DEFINE_READAHEAD` is the sole entry point. A quick grep for `struct readahead_control` might reveal other uses or initialization points that could illuminate this behavior and give me a full understanding of the readahead control structure. Let's see what that search turns up. |
| 68/3 | 2026/09/14 03:31 | tool | grepper |
0mArgs:{
"Expression": "struct readahead_control"
}Results: {
"Output": "Documentation/filesystems/iomap/operations.rst=207=iomap_read_folio_ctx``:\n--\nDocumentation/filesystems/iomap/operations.rst-213- struct folio *cur_folio;\nDocumentation/filesystems/iomap/operations.rst:214: struct readahead_control *rac;\nDocumentation/filesystems/iomap/operations.rst-215- void *read_ctx;\n--\nDocumentation/filesystems/locking.rst=245=prototypes::\n--\nDocumentation/filesystems/locking.rst-249-\tbool (*dirty_folio)(struct address_space *, struct folio *folio);\nDocumentation/filesystems/locking.rst:250:\tvoid (*readahead)(struct readahead_control *);\nDocumentation/filesystems/locking.rst-251-\tint (*write_begin)(const struct kiocb *, struct address_space *mapping,\n--\nDocumentation/filesystems/netfs_library.rst=460=for it to intervene at various times::\nDocumentation/filesystems/netfs_library.rst-461-\nDocumentation/filesystems/netfs_library.rst:462:\tvoid netfs_readahead(struct readahead_control *);\nDocumentation/filesystems/netfs_library.rst-463-\tint netfs_read_folio(struct file *, struct folio *);\n--\nDocumentation/filesystems/vfs.rst=749=cache in your filesystem. The following members are defined:\n--\nDocumentation/filesystems/vfs.rst-756-\t\tbool (*dirty_folio)(struct address_space *, struct folio *);\nDocumentation/filesystems/vfs.rst:757:\t\tvoid (*readahead)(struct readahead_control *);\nDocumentation/filesystems/vfs.rst-758-\t\tint (*write_begin)(const struct kiocb *, struct address_space *mapping,\n--\nblock/fops.c=493=static int blkdev_read_folio(struct file *file, struct folio *folio)\n--\nblock/fops.c-497-\nblock/fops.c:498:static void blkdev_readahead(struct readahead_control *rac)\nblock/fops.c-499-{\n--\nblock/fops.c=513=static int blkdev_read_folio(struct file *file, struct folio *folio)\n--\nblock/fops.c-518-\nblock/fops.c:519:static void blkdev_readahead(struct readahead_control *rac)\nblock/fops.c-520-{\n--\nfs/btrfs/extent_io.c=99=struct btrfs_bio_ctrl {\n--\nfs/btrfs/extent_io.c-135-\nfs/btrfs/extent_io.c:136:\tstruct readahead_control *ractl;\nfs/btrfs/extent_io.c-137-\n--\nfs/btrfs/extent_io.c=998=static struct extent_map *get_extent_map(struct btrfs_inode *inode,\n--\nfs/btrfs/extent_io.c-1027-\nfs/btrfs/extent_io.c:1028:static void btrfs_readahead_expand(struct readahead_control *ractl,\nfs/btrfs/extent_io.c-1029-\t\t\t\t const struct extent_map *em)\n--\nfs/btrfs/extent_io.c=2961=int btrfs_writepages(struct address_space *mapping, struct writeback_control *wbc)\n--\nfs/btrfs/extent_io.c-2980-\nfs/btrfs/extent_io.c:2981:void btrfs_readahead(struct readahead_control *rac)\nfs/btrfs/extent_io.c-2982-{\n--\nfs/btrfs/extent_io.h=23=struct fiemap_extent_info;\nfs/btrfs/extent_io.h:24:struct readahead_control;\nfs/btrfs/extent_io.h-25-struct address_space;\n--\nfs/btrfs/extent_io.h=287=void btrfs_btree_wait_writeback_range(struct btrfs_fs_info *fs_info, u64 start, u64 end);\nfs/btrfs/extent_io.h:288:void btrfs_readahead(struct readahead_control *rac);\nfs/btrfs/extent_io.h-289-int set_folio_extent_mapped(struct folio *folio);\n--\nfs/erofs/data.c=398=static int erofs_read_folio(struct file *file, struct folio *folio)\n--\nfs/erofs/data.c-415-\nfs/erofs/data.c:416:static void erofs_readahead(struct readahead_control *rac)\nfs/erofs/data.c-417-{\n--\nfs/erofs/fileio.c=157=static int erofs_fileio_read_folio(struct file *file, struct folio *folio)\n--\nfs/erofs/fileio.c-171-\nfs/erofs/fileio.c:172:static void erofs_fileio_readahead(struct readahead_control *rac)\nfs/erofs/fileio.c-173-{\n--\nfs/erofs/zdata.c=1814=static void z_erofs_pcluster_readmore(struct z_erofs_frontend *f,\nfs/erofs/zdata.c:1815:\t\tstruct readahead_control *rac, bool backmost)\nfs/erofs/zdata.c-1816-{\n--\nfs/erofs/zdata.c=1866=static int z_erofs_read_folio(struct file *file, struct folio *folio)\n--\nfs/erofs/zdata.c-1893-\nfs/erofs/zdata.c:1894:static void z_erofs_readahead(struct readahead_control *rac)\nfs/erofs/zdata.c-1895-{\n--\nfs/exfat/inode.c=244=static int exfat_read_folio(struct file *file, struct folio *folio)\n--\nfs/exfat/inode.c-254-\nfs/exfat/inode.c:255:static void exfat_readahead(struct readahead_control *rac)\nfs/exfat/inode.c-256-{\n--\nfs/ext2/inode.c=891=static int ext2_read_folio(struct file *file, struct folio *folio)\n--\nfs/ext2/inode.c-895-\nfs/ext2/inode.c:896:static void ext2_readahead(struct readahead_control *rac)\nfs/ext2/inode.c-897-{\n--\nfs/ext4/ext4.h=3863=int ext4_read_folio(struct file *file, struct folio *folio);\nfs/ext4/ext4.h:3864:void ext4_readahead(struct readahead_control *rac);\nfs/ext4/ext4.h-3865-int __init ext4_init_verity_caches(void);\n--\nfs/ext4/readpage.c=137=static int ext4_mpage_readpages(struct inode *inode, struct fsverity_info *vi,\nfs/ext4/readpage.c:138:\t\tstruct readahead_control *rac, struct folio *folio)\nfs/ext4/readpage.c-139-{\n--\nfs/ext4/readpage.c=321=int ext4_read_folio(struct file *file, struct folio *folio)\n--\nfs/ext4/readpage.c-341-\nfs/ext4/readpage.c:342:void ext4_readahead(struct readahead_control *rac)\nfs/ext4/readpage.c-343-{\n--\nfs/f2fs/data.c=2200=static inline loff_t f2fs_readpage_limit(struct inode *inode)\n--\nfs/f2fs/data.c-2207-\nfs/f2fs/data.c:2208:static inline blk_opf_t f2fs_ra_op_flags(struct readahead_control *rac)\nfs/f2fs/data.c-2209-{\n--\nfs/f2fs/data.c=2213=static int f2fs_read_single_page(struct inode *inode, struct fsverity_info *vi,\n--\nfs/f2fs/data.c-2217-\t\t\t\t sector_t *last_block_in_bio,\nfs/f2fs/data.c:2218:\t\t\t\t struct readahead_control *rac)\nfs/f2fs/data.c-2219-{\n--\nfs/f2fs/data.c=2317=int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,\nfs/f2fs/data.c-2318-\t\t\t\tunsigned nr_pages, sector_t *last_block_in_bio,\nfs/f2fs/data.c:2319:\t\t\t\tstruct readahead_control *rac, bool for_write)\nfs/f2fs/data.c-2320-{\n--\nfs/f2fs/data.c=2516=static int f2fs_read_data_large_folio(struct inode *inode,\nfs/f2fs/data.c-2517-\t\tstruct fsverity_info *vi,\nfs/f2fs/data.c:2518:\t\tstruct readahead_control *rac, struct folio *folio)\nfs/f2fs/data.c-2519-{\n--\nfs/f2fs/data.c=2668=static int f2fs_mpage_readpages(struct inode *inode, struct fsverity_info *vi,\nfs/f2fs/data.c:2669:\t\tstruct readahead_control *rac, struct folio *folio)\nfs/f2fs/data.c-2670-{\n--\nfs/f2fs/data.c=2796=static int f2fs_read_data_folio(struct file *file, struct folio *folio)\n--\nfs/f2fs/data.c-2821-\nfs/f2fs/data.c:2822:static void f2fs_readahead(struct readahead_control *rac)\nfs/f2fs/data.c-2823-{\n--\nfs/f2fs/f2fs.h=4732=int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,\nfs/f2fs/f2fs.h-4733-\t\t\t\tunsigned nr_pages, sector_t *last_block_in_bio,\nfs/f2fs/f2fs.h:4734:\t\t\t\tstruct readahead_control *rac, bool for_write);\nfs/f2fs/f2fs.h-4735-struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc);\n--\nfs/fat/inode.c=203=static int fat_read_folio(struct file *file, struct folio *folio)\n--\nfs/fat/inode.c-207-\nfs/fat/inode.c:208:static void fat_readahead(struct readahead_control *rac)\nfs/fat/inode.c-209-{\n--\nfs/fuse/file.c=916=static int fuse_handle_readahead(struct folio *folio,\nfs/fuse/file.c:917:\t\t\t\t struct readahead_control *rac,\nfs/fuse/file.c-918-\t\t\t\t struct fuse_fill_read_data *data, loff_t pos,\n--\nfs/fuse/file.c=1057=static void fuse_send_readpages(struct fuse_io_args *ia, struct file *file,\n--\nfs/fuse/file.c-1092-\nfs/fuse/file.c:1093:static void fuse_readahead(struct readahead_control *rac)\nfs/fuse/file.c-1094-{\n--\nfs/gfs2/aops.c=450=ssize_t gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos,\n--\nfs/gfs2/aops.c-492-\nfs/gfs2/aops.c:493:static void gfs2_readahead(struct readahead_control *rac)\nfs/gfs2/aops.c-494-{\n--\nfs/hpfs/file.c=165=static int hpfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/hpfs/file.c-169-\nfs/hpfs/file.c:170:static void hpfs_readahead(struct readahead_control *rac)\nfs/hpfs/file.c-171-{\n--\nfs/iomap/buffered-io.c=728=void iomap_readahead(const struct iomap_ops *ops,\n--\nfs/iomap/buffered-io.c-730-{\nfs/iomap/buffered-io.c:731:\tstruct readahead_control *rac = ctx-\u003erac;\nfs/iomap/buffered-io.c-732-\tstruct iomap_iter iter = {\n--\nfs/isofs/inode.c=1148=static int isofs_read_folio(struct file *file, struct folio *folio)\n--\nfs/isofs/inode.c-1152-\nfs/isofs/inode.c:1153:static void isofs_readahead(struct readahead_control *rac)\nfs/isofs/inode.c-1154-{\n--\nfs/jfs/inode.c=279=static int jfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/jfs/inode.c-283-\nfs/jfs/inode.c:284:static void jfs_readahead(struct readahead_control *rac)\nfs/jfs/inode.c-285-{\n--\nfs/mpage.c=150=static void do_mpage_readpage(struct mpage_readpage_args *args)\n--\nfs/mpage.c-357- */\nfs/mpage.c:358:void mpage_readahead(struct readahead_control *rac, get_block_t get_block)\nfs/mpage.c-359-{\n--\nfs/netfs/buffered_read.c=23=static void netfs_rreq_expand(struct netfs_io_request *rreq,\nfs/netfs/buffered_read.c:24:\t\t\t struct readahead_control *ractl)\nfs/netfs/buffered_read.c-25-{\n--\nfs/netfs/buffered_read.c=81=static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq,\nfs/netfs/buffered_read.c:82:\t\t\t\t\t struct readahead_control *ractl)\nfs/netfs/buffered_read.c-83-{\n--\nfs/netfs/buffered_read.c=211=static void netfs_read_to_pagecache(struct netfs_io_request *rreq,\nfs/netfs/buffered_read.c:212:\t\t\t\t struct readahead_control *ractl)\nfs/netfs/buffered_read.c-213-{\n--\nfs/netfs/buffered_read.c-337- */\nfs/netfs/buffered_read.c:338:void netfs_readahead(struct readahead_control *ractl)\nfs/netfs/buffered_read.c-339-{\n--\nfs/netfs/rolling_buffer.c=120=ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,\nfs/netfs/rolling_buffer.c:121:\t\t\t\t struct readahead_control *ractl,\nfs/netfs/rolling_buffer.c-122-\t\t\t\t struct folio_batch *put_batch)\n--\nfs/nfs/fscache.c=244=int nfs_netfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/nfs/fscache.c-251-\nfs/nfs/fscache.c:252:int nfs_netfs_readahead(struct readahead_control *ractl)\nfs/nfs/fscache.c-253-{\n--\nfs/nfs/fscache.h=98=extern void nfs_fscache_release_file(struct inode *, struct file *);\nfs/nfs/fscache.h:99:extern int nfs_netfs_readahead(struct readahead_control *ractl);\nfs/nfs/fscache.h-100-extern int nfs_netfs_read_folio(struct file *file, struct folio *folio);\n--\nfs/nfs/fscache.h=176=static inline void nfs_fscache_release_file(struct inode *inode, struct file *file) {}\nfs/nfs/fscache.h:177:static inline int nfs_netfs_readahead(struct readahead_control *ractl)\nfs/nfs/fscache.h-178-{\n--\nfs/nfs/read.c=390=int nfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/nfs/read.c-428-\nfs/nfs/read.c:429:void nfs_readahead(struct readahead_control *ractl)\nfs/nfs/read.c-430-{\n--\nfs/nilfs2/inode.c=149=static int nilfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/nilfs2/inode.c-153-\nfs/nilfs2/inode.c:154:static void nilfs_readahead(struct readahead_control *rac)\nfs/nilfs2/inode.c-155-{\n--\nfs/ntfs/aops.c=133=static sector_t ntfs_bmap(struct address_space *mapping, sector_t block)\n--\nfs/ntfs/aops.c-231-\nfs/ntfs/aops.c:232:static void ntfs_readahead(struct readahead_control *rac)\nfs/ntfs/aops.c-233-{\n--\nfs/ntfs3/inode.c=826=static int ntfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/ntfs3/inode.c-859-\nfs/ntfs3/inode.c:860:static void ntfs_readahead(struct readahead_control *rac)\nfs/ntfs3/inode.c-861-{\n--\nfs/ocfs2/aops.c=264=static int ocfs2_read_folio(struct file *file, struct folio *folio)\n--\nfs/ocfs2/aops.c-335- */\nfs/ocfs2/aops.c:336:static void ocfs2_readahead(struct readahead_control *rac)\nfs/ocfs2/aops.c-337-{\n--\nfs/omfs/file.c=287=static int omfs_read_folio(struct file *file, struct folio *folio)\n--\nfs/omfs/file.c-291-\nfs/omfs/file.c:292:static void omfs_readahead(struct readahead_control *rac)\nfs/omfs/file.c-293-{\n--\nfs/orangefs/inode.c=215=static int orangefs_launder_folio(struct folio *);\nfs/orangefs/inode.c-216-\nfs/orangefs/inode.c:217:static void orangefs_readahead(struct readahead_control *rac)\nfs/orangefs/inode.c-218-{\n--\nfs/qnx6/inode.c=98=static int qnx6_read_folio(struct file *file, struct folio *folio)\n--\nfs/qnx6/inode.c-102-\nfs/qnx6/inode.c:103:static void qnx6_readahead(struct readahead_control *rac)\nfs/qnx6/inode.c-104-{\n--\nfs/squashfs/file.c=505=static int squashfs_readahead_fragment(struct inode *inode, struct page **page,\n--\nfs/squashfs/file.c-573-\nfs/squashfs/file.c:574:static void squashfs_readahead(struct readahead_control *ractl)\nfs/squashfs/file.c-575-{\n--\nfs/udf/inode.c=222=static int udf_read_folio(struct file *file, struct folio *folio)\n--\nfs/udf/inode.c-233-\nfs/udf/inode.c:234:static void udf_readahead(struct readahead_control *rac)\nfs/udf/inode.c-235-{\n--\nfs/xfs/xfs_aops.c=625=xfs_vm_readahead(\nfs/xfs/xfs_aops.c:626:\tstruct readahead_control\t*rac)\nfs/xfs/xfs_aops.c-627-{\n--\nfs/zonefs/file.c=118=static int zonefs_read_folio(struct file *unused, struct folio *folio)\n--\nfs/zonefs/file.c-123-\nfs/zonefs/file.c:124:static void zonefs_readahead(struct readahead_control *rac)\nfs/zonefs/file.c-125-{\n--\ninclude/linux/fs.h=335=struct writeback_control;\ninclude/linux/fs.h:336:struct readahead_control;\ninclude/linux/fs.h-337-\n--\ninclude/linux/fs.h=401=struct address_space_operations {\n--\ninclude/linux/fs.h-409-\ninclude/linux/fs.h:410:\tvoid (*readahead)(struct readahead_control *);\ninclude/linux/fs.h-411-\n--\ninclude/linux/iomap.h=580=struct iomap_read_folio_ctx {\n--\ninclude/linux/iomap.h-582-\tstruct folio\t\t*cur_folio;\ninclude/linux/iomap.h:583:\tstruct readahead_control *rac;\ninclude/linux/iomap.h-584-\tvoid\t\t\t*read_ctx;\n--\ninclude/linux/iomap.h=771=static inline void iomap_bio_read_folio(struct folio *folio,\n--\ninclude/linux/iomap.h-781-\ninclude/linux/iomap.h:782:static inline void iomap_bio_readahead(struct readahead_control *rac,\ninclude/linux/iomap.h-783-\t\tconst struct iomap_ops *ops)\n--\ninclude/linux/mpage.h=15=struct writeback_control;\ninclude/linux/mpage.h:16:struct readahead_control;\ninclude/linux/mpage.h-17-\ninclude/linux/mpage.h:18:void mpage_readahead(struct readahead_control *, get_block_t get_block);\ninclude/linux/mpage.h-19-int mpage_read_folio(struct folio *folio, get_block_t get_block);\n--\ninclude/linux/netfs.h=403=int netfs_writeback_single(struct address_space *mapping,\n--\ninclude/linux/netfs.h-407-/* Address operations API */\ninclude/linux/netfs.h:408:struct readahead_control;\ninclude/linux/netfs.h:409:void netfs_readahead(struct readahead_control *);\ninclude/linux/netfs.h-410-int netfs_read_folio(struct file *, struct folio *);\n--\ninclude/linux/nfs_fs.h=646=int nfs_read_folio(struct file *, struct folio *);\ninclude/linux/nfs_fs.h:647:void nfs_readahead(struct readahead_control *);\ninclude/linux/nfs_fs.h-648-\n--\ninclude/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-1484- */\ninclude/linux/pagemap.h:1485:static inline void __readahead_advance(struct readahead_control *rac)\ninclude/linux/pagemap.h-1486-{\n--\ninclude/linux/pagemap.h-1493-\ninclude/linux/pagemap.h:1494:static inline struct folio *__readahead_folio(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1495-{\n--\ninclude/linux/pagemap.h-1519- */\ninclude/linux/pagemap.h:1520:static inline struct folio *readahead_folio(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1521-{\n--\ninclude/linux/pagemap.h-1539- */\ninclude/linux/pagemap.h:1540:static inline struct folio *readahead_folio_last(struct readahead_control *ractl)\ninclude/linux/pagemap.h-1541-{\n--\ninclude/linux/pagemap.h-1561-\ninclude/linux/pagemap.h:1562:static inline unsigned int __readahead_batch(struct readahead_control *rac,\ninclude/linux/pagemap.h-1563-\t\tstruct page **array, unsigned int array_sz)\n--\ninclude/linux/pagemap.h-1592- */\ninclude/linux/pagemap.h:1593:static inline loff_t readahead_pos(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1594-{\n--\ninclude/linux/pagemap.h-1601- */\ninclude/linux/pagemap.h:1602:static inline size_t readahead_length(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1603-{\n--\ninclude/linux/pagemap.h-1610- */\ninclude/linux/pagemap.h:1611:static inline pgoff_t readahead_index(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1612-{\n--\ninclude/linux/pagemap.h-1619- */\ninclude/linux/pagemap.h:1620:static inline unsigned int readahead_count(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1621-{\n--\ninclude/linux/pagemap.h-1628- */\ninclude/linux/pagemap.h:1629:static inline size_t readahead_batch_length(const struct readahead_control *rac)\ninclude/linux/pagemap.h-1630-{\n--\ninclude/linux/rolling_buffer.h=48=ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,\ninclude/linux/rolling_buffer.h:49:\t\t\t\t struct readahead_control *ractl,\ninclude/linux/rolling_buffer.h-50-\t\t\t\t struct folio_batch *put_batch);\n--\nmm/internal.h=597=int folio_unmap_invalidate(struct address_space *mapping, struct folio *folio,\n--\nmm/internal.h-599-\nmm/internal.h:600:void page_cache_ra_order(struct readahead_control *, struct file_ra_state *);\nmm/internal.h:601:void force_page_cache_ra(struct readahead_control *, unsigned long nr);\nmm/internal.h-602-static inline void force_page_cache_readahead(struct address_space *mapping,\n--\nmm/readahead.c=147=EXPORT_SYMBOL_GPL(file_ra_state_init);\n--\nmm/readahead.c-159- */\nmm/readahead.c:160:static void read_pages(struct readahead_control *rac)\nmm/readahead.c-161-{\n--\nmm/readahead.c-194-\nmm/readahead.c:195:static struct folio *ractl_alloc_folio(struct readahead_control *ractl,\nmm/readahead.c-196-\t\t\t\t gfp_t gfp_mask, unsigned int order)\n--\nmm/readahead.c-221- */\nmm/readahead.c:222:void page_cache_ra_unbounded(struct readahead_control *ractl,\nmm/readahead.c-223-\t\tunsigned long nr_to_read, unsigned long lookahead_size)\n--\nmm/readahead.c=318=EXPORT_SYMBOL_GPL(page_cache_ra_unbounded);\n--\nmm/readahead.c-325- */\nmm/readahead.c:326:static void do_page_cache_ra(struct readahead_control *ractl,\nmm/readahead.c-327-\t\tunsigned long nr_to_read, unsigned long lookahead_size)\n--\nmm/readahead.c-355- */\nmm/readahead.c:356:void force_page_cache_ra(struct readahead_control *ractl,\nmm/readahead.c-357-\t\tunsigned long nr_to_read)\n--\nmm/readahead.c=408=static unsigned long get_next_ra_size(struct file_ra_state *ra,\n--\nmm/readahead.c-458-\nmm/readahead.c:459:static inline int ra_alloc_folio(struct readahead_control *ractl, pgoff_t index,\nmm/readahead.c-460-\t\tpgoff_t mark, unsigned int order, gfp_t gfp)\n--\nmm/readahead.c-480-\nmm/readahead.c:481:void page_cache_ra_order(struct readahead_control *ractl,\nmm/readahead.c-482-\t\tstruct file_ra_state *ra)\n--\nmm/readahead.c-561-\nmm/readahead.c:562:static unsigned long ractl_max_pages(struct readahead_control *ractl,\nmm/readahead.c-563-\t\tunsigned long req_size)\n--\nmm/readahead.c-576-\nmm/readahead.c:577:void page_cache_sync_ra(struct readahead_control *ractl,\nmm/readahead.c-578-\t\tunsigned long req_count)\n--\nmm/readahead.c=651=EXPORT_SYMBOL_GPL(page_cache_sync_ra);\nmm/readahead.c-652-\nmm/readahead.c:653:void page_cache_async_ra(struct readahead_control *ractl,\nmm/readahead.c-654-\t\tstruct folio *folio, unsigned long req_count)\n--\nmm/readahead.c=762=COMPAT_SYSCALL_DEFINE4(readahead, int, fd, compat_arg_u64_dual(offset), size_t, count)\n--\nmm/readahead.c-785- */\nmm/readahead.c:786:void readahead_expand(struct readahead_control *ractl,\nmm/readahead.c-787-\t\t loff_t new_start, size_t new_len)\n"
} |
| 69/3 | 2026/09/14 03:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35465 output: 22 thoughts: 721 Thoughts: **Analyzing Potential Uninitialized Memory in Kernel Code** Okay, so I'm diving into this code, and my focus is on potential uninitialized memory vulnerabilities. Let's break down these initial thoughts. The `struct readahead_control` initialization is clean. Because of how it's initialized in C, all the members, like `_forward` and `_batch_count`, start as zero. That means the initial flow of `readahead_folio_last()` is safe. `__readahead_advance(rac)` doesn't touch `_index` until it's been properly initialized with data. The logic of how the read-ahead window shrinks from the end based on whether `_forward` is true or false seems solid; there's no way to reach uninitialized data. Now, I've moved to looking at `fs/erofs/data.c`. The `erofs_onlinefolio_init()` function initializes a union `u`. The atomic value initialized within the union is a safe initialization of the struct. We assign `u.v` to the folio's private data through `folio_attach_private()`, which is, again, fine. No uninitialized stuff here. But hold on! When looking at `drivers/xen/grant-table.c`, I noticed something. The function `gnttab_pages_set_private` is a little concerning. Specifically, this loop on 32-bit: `set_page_private(pages[i], (unsigned long)foreign);`. This code looks fine too, but I need to make sure I am reading it correctly. We need to check how `page_private` is used in this file. Let's see how `grant-table.c` uses `page_private` because I can't immediately see any problems, but I need to be 100% sure. |
| 70/3 | 2026/09/14 03:32 | tool | grepper |
0mArgs:{
"Expression": "gnttab_pages_.*private"
}Results: {
"Output": "drivers/xen/grant-table.c=862=EXPORT_SYMBOL_GPL(gnttab_free_auto_xlat_frames);\ndrivers/xen/grant-table.c-863-\ndrivers/xen/grant-table.c:864:int gnttab_pages_set_private(int nr_pages, struct page **pages)\ndrivers/xen/grant-table.c-865-{\n--\ndrivers/xen/grant-table.c-882-}\ndrivers/xen/grant-table.c:883:EXPORT_SYMBOL_GPL(gnttab_pages_set_private);\ndrivers/xen/grant-table.c-884-\n--\ndrivers/xen/grant-table.c=890=int gnttab_alloc_pages(int nr_pages, struct page **pages)\n--\ndrivers/xen/grant-table.c-897-\ndrivers/xen/grant-table.c:898:\tret = gnttab_pages_set_private(nr_pages, pages);\ndrivers/xen/grant-table.c-899-\tif (ret \u003c 0)\n--\ndrivers/xen/grant-table.c=1027=EXPORT_SYMBOL_GPL(gnttab_page_cache_shrink);\ndrivers/xen/grant-table.c-1028-\ndrivers/xen/grant-table.c:1029:void gnttab_pages_clear_private(int nr_pages, struct page **pages)\ndrivers/xen/grant-table.c-1030-{\n--\ndrivers/xen/grant-table.c-1040-}\ndrivers/xen/grant-table.c:1041:EXPORT_SYMBOL_GPL(gnttab_pages_clear_private);\ndrivers/xen/grant-table.c-1042-\n--\ndrivers/xen/grant-table.c=1048=void gnttab_free_pages(int nr_pages, struct page **pages)\ndrivers/xen/grant-table.c-1049-{\ndrivers/xen/grant-table.c:1050:\tgnttab_pages_clear_private(nr_pages, pages);\ndrivers/xen/grant-table.c-1051-\txen_free_unpopulated_pages(nr_pages, pages);\n--\ndrivers/xen/grant-table.c=1060=int gnttab_dma_alloc_pages(struct gnttab_dma_alloc_args *args)\n--\ndrivers/xen/grant-table.c-1101-\ndrivers/xen/grant-table.c:1102:\tret = gnttab_pages_set_private(args-\u003enr_pages, args-\u003epages);\ndrivers/xen/grant-table.c-1103-\tif (ret \u003c 0)\n--\ndrivers/xen/grant-table.c=1118=int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args)\n--\ndrivers/xen/grant-table.c-1122-\ndrivers/xen/grant-table.c:1123:\tgnttab_pages_clear_private(args-\u003enr_pages, args-\u003epages);\ndrivers/xen/grant-table.c-1124-\n--\ninclude/xen/grant_table.h=261=int gnttab_dma_free_pages(struct gnttab_dma_alloc_args *args);\n--\ninclude/xen/grant_table.h-263-\ninclude/xen/grant_table.h:264:int gnttab_pages_set_private(int nr_pages, struct page **pages);\ninclude/xen/grant_table.h:265:void gnttab_pages_clear_private(int nr_pages, struct page **pages);\ninclude/xen/grant_table.h-266-\n"
} |
| 71/3 | 2026/09/14 03:32 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37571 output: 19 thoughts: 28 Thoughts: Here's how I'd approach summarizing those thoughts, focusing on the technical aspect a Xen expert would appreciate: **Examining 64-bit Xen Page Mapping in Foreign Access** Okay, so I'm thinking about the low-level memory management within Xen, specifically how it handles foreign page access on 64-bit systems. My focus right now is on the potential interactions and behaviors I'd expect to see, or be concerned about, within `xen_page_foreign` and the grant table implementation (`grant-table.c`). I need to understand how the memory is mapped from a guest, at a virtual address, to a physical address on another guest. I need to understand what challenges or opportunities the switch to 64-bit introduces in the mapping and unmapping of pages. Are there any assumptions that we made in the 32 bit code, that are no longer valid? For example, is there a risk of address space exhaustion? Or perhaps, an opportunity to improve the efficiency with larger page sizes? Where are the possible bottlenecks? I want to pinpoint the areas where 64-bit architectures might necessitate changes, performance optimizations, or even entirely new approaches in how we manage and secure memory transfers. I need to dig into the finer details of how the grant table entries are managed, updated, and validated in this new environment. I'm essentially tracing through the critical code paths to mentally simulate how they'll work on a 64-bit Xen system. |
| 72/3 | 2026/09/14 03:32 | tool | grepper |
0mArgs:{
"Expression": "xen_page_foreign"
}Results: {
"Output": "drivers/net/xen-netback/rx.c=193=static void xenvif_rx_copy_add(struct xenvif_queue *queue,\n--\ndrivers/net/xen-netback/rx.c-198-\tstruct page *page;\ndrivers/net/xen-netback/rx.c:199:\tstruct xen_page_foreign *foreign;\ndrivers/net/xen-netback/rx.c-200-\n--\ndrivers/net/xen-netback/rx.c-209-\ndrivers/net/xen-netback/rx.c:210:\tforeign = xen_page_foreign(page);\ndrivers/net/xen-netback/rx.c-211-\tif (foreign) {\n--\ndrivers/xen/grant-table.c=864=int gnttab_pages_set_private(int nr_pages, struct page **pages)\n--\ndrivers/xen/grant-table.c-869-\tfor (i = 0; i \u003c nr_pages; i++) {\ndrivers/xen/grant-table.c:870:\t\tstruct xen_page_foreign *foreign;\ndrivers/xen/grant-table.c-871-\n--\ndrivers/xen/grant-table.c=1245=int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,\n--\ndrivers/xen/grant-table.c-1258-\t\t{\ndrivers/xen/grant-table.c:1259:\t\t\tstruct xen_page_foreign *foreign;\ndrivers/xen/grant-table.c-1260-\ndrivers/xen/grant-table.c-1261-\t\t\tSetPageForeign(pages[i]);\ndrivers/xen/grant-table.c:1262:\t\t\tforeign = xen_page_foreign(pages[i]);\ndrivers/xen/grant-table.c-1263-\t\t\tforeign-\u003edomid = map_ops[i].dom;\n--\ninclude/xen/grant_table.h=287=void gnttab_batch_copy(struct gnttab_copy *batch, unsigned count);\n--\ninclude/xen/grant_table.h-289-\ninclude/xen/grant_table.h:290:struct xen_page_foreign {\ninclude/xen/grant_table.h-291-\tdomid_t domid;\n--\ninclude/xen/grant_table.h-294-\ninclude/xen/grant_table.h:295:static inline struct xen_page_foreign *xen_page_foreign(struct page *page)\ninclude/xen/grant_table.h-296-{\n--\ninclude/xen/grant_table.h-299-#if BITS_PER_LONG \u003c 64\ninclude/xen/grant_table.h:300:\treturn (struct xen_page_foreign *)page-\u003eprivate;\ninclude/xen/grant_table.h-301-#else\ninclude/xen/grant_table.h:302:\tBUILD_BUG_ON(sizeof(struct xen_page_foreign) \u003e BITS_PER_LONG);\ninclude/xen/grant_table.h:303:\treturn (struct xen_page_foreign *)\u0026page-\u003eprivate;\ninclude/xen/grant_table.h-304-#endif\n"
} |
| 73/3 | 2026/09/14 03:32 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38747 output: 37 thoughts: 170 Thoughts: **Examining the Xen Grant Table Implementation** Okay, let's dive into this `xen_page_foreign` structure. I'm focusing on the implementation details and how memory is managed here. First, I need to check out the definition in `include/xen/grant_table.h`. Specifically, I'm looking at how `xen_page_foreign` is retrieved from a `page` structure. Ah, here it is: the macro. This is interesting. On 64-bit systems, it looks like `xen_page_foreign` is cleverly stored *directly within* the `page->private` field. That's a memory optimization trick I've seen before; it's a tight fit, especially considering the `BUILD_BUG_ON` check to ensure the structure size doesn't exceed the word size. That makes sense, keeps things lean and mean. Now, let's take a closer look at lines 290-310 (in `include/xen/grant_table.h`), and see what else is happening within that structure. |
| 74/3 | 2026/09/14 03:32 | tool | read-file |
0mArgs:{
"File": "include/xen/grant_table.h",
"FirstLine": 290,
"LineCount": 25
}Results: {
"Contents": " 290:\tstruct xen_page_foreign {\n 291:\t\tdomid_t domid;\n 292:\t\tgrant_ref_t gref;\n 293:\t};\n 294:\t\n 295:\tstatic inline struct xen_page_foreign *xen_page_foreign(struct page *page)\n 296:\t{\n 297:\t\tif (!PageForeign(page))\n 298:\t\t\treturn NULL;\n 299:\t#if BITS_PER_LONG \u003c 64\n 300:\t\treturn (struct xen_page_foreign *)page-\u003eprivate;\n 301:\t#else\n 302:\t\tBUILD_BUG_ON(sizeof(struct xen_page_foreign) \u003e BITS_PER_LONG);\n 303:\t\treturn (struct xen_page_foreign *)\u0026page-\u003eprivate;\n 304:\t#endif\n 305:\t}\n 306:\t\n 307:\t/* Split Linux page in chunk of the size of the grant and call fn\n 308:\t *\n 309:\t * Parameters of fn:\n 310:\t *\tgfn: guest frame number\n 311:\t *\toffset: offset in the grant\n 312:\t *\tlen: length of the data in the grant.\n 313:\t *\tdata: internal information\n 314:\t */\n"
} |
| 75/3 | 2026/09/14 03:32 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39550 output: 27 thoughts: 2191 Thoughts: **Analyzing Potential Uninitialized Data in Kernel Code** Okay, so I'm diving into this code, looking for potential issues where data might be uninitialized. The primary concern seems to be with the use of `page->private` and how it's being handled across different subsystems. First, I examined `xen_page_foreign`. The initial thought was about ensuring the size of `struct xen_page_foreign` doesn't exceed `unsigned long` or `BITS_PER_LONG`. I see that `xen_page_foreign` relies on checking `PageForeign(page)`. I saw that in `gnttab_pages_set_private`, there was a conditional block based on `BITS_PER_LONG`. On 64-bit systems, this entire block was skipped, and `SetPagePrivate` was called. The problem is, if the check using `PageForeign(page)` relied on this bit being set, and it wasn't, we'd have a problem. The key point is that `gnttab_map_refs` explicitly writes to the `domid` and `gref` fields. This suggests the data is being *initialized* within `gnttab_map_refs`. I also noted in `gnttab_pages_clear_private` that `set_page_private(pages[i], 0)` is used to clear the private data. Next, I looked at `drivers/md/md-bitmap.c`. The change here directly replaced `page_buffers(page)` with `(struct buffer_head *)page_private(page)`. `page_buffers(page)` had a check for `PagePrivate(page)`. This change seems straightforward, but I need to make sure `page->private` is correctly initialized. Moving on to `zsmalloc.c`, I noticed some changes related to `zpdesc` and `zspage`. Specifically, the code now includes `VM_WARN_ON_ONCE_PAGE` to check for specific conditions related to `zspage->first_zpdesc` and `zpdesc->handle`, this seems internal data structure management, and is unlikely to cause a problem. There's also some cleanup in `reset_zpdesc` where `ClearPagePrivate(page)` was removed. Then I focused on `fs/f2fs/*`. Here, I found a change in `struct f2fs_io_info`, where a `union` containing `struct page *page` and `struct folio *folio` was replaced with just `struct folio *folio`. Now, I have to make sure that no one is accessing `fio->page`. A quick check reveals that the compiler will prevent such access, as the `page` field no longer exists. Whenever `fio` is initialized the unused fields are set to NULL using designated initializer syntax. Finally, I checked `folio_set_f2fs_##name`. It manipulates `folio->private`, which is a pointer. It sets some flags within this `folio->private` value. The flags are set using bitwise operations with values. The crucial point here is the initialization of the page's private data: I need to determine how `prep_new_page` or `page_init` handles `page->private`. This is the final step: to confirm that newly allocated pages *always* have their private data initialized correctly, either to zero or a known value. This will be my immediate next focus. |
| 76/3 | 2026/09/14 03:32 | tool | grepper |
0mArgs:{
"Expression": "prep_new_page",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/internal.h=845=struct capture_control {\n--\nmm/internal.h-851-\t * pageblock-order compaction; capture still matches at the\nmm/internal.h:852:\t * original allocation order so prep_new_page() is consistent.\nmm/internal.h-853-\t */\n--\nmm/page_alloc.c=1803=inline void post_alloc_hook(struct page *page, unsigned int order,\n--\nmm/page_alloc.c-1857-\nmm/page_alloc.c:1858:static void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,\nmm/page_alloc.c-1859-\t\t\t\t\t\t\tunsigned int alloc_flags)\n--\nmm/page_alloc.c=3782=get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,\n--\nmm/page_alloc.c-3924-\t\tif (page) {\nmm/page_alloc.c:3925:\t\t\tprep_new_page(page, order, gfp_mask, alloc_flags);\nmm/page_alloc.c-3926-\n--\nmm/page_alloc.c=4121=__alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,\n--\nmm/page_alloc.c-4202-\tif (page)\nmm/page_alloc.c:4203:\t\tprep_new_page(page, order, gfp_mask, alloc_flags);\nmm/page_alloc.c-4204-\n--\nmm/page_alloc.c=5154=unsigned long alloc_pages_bulk_noprof(gfp_t gfp, int preferred_nid,\n--\nmm/page_alloc.c-5274-\nmm/page_alloc.c:5275:\t\tprep_new_page(page, 0, gfp, ALLOC_DEFAULT);\nmm/page_alloc.c-5276-\t\ttrace_mm_page_alloc(page, 0, gfp, ac.migratetype);\n--\nmm/page_alloc.c=7265=int alloc_contig_frozen_range_noprof(unsigned long start, unsigned long end,\n--\nmm/page_alloc.c-7392-\t\tcheck_new_pages(head, order);\nmm/page_alloc.c:7393:\t\tprep_new_page(head, order, gfp_mask, ALLOC_DEFAULT);\nmm/page_alloc.c-7394-\t} else {\n"
} |
| 77/3 | 2026/09/14 03:32 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43179 output: 36 thoughts: 29 Thoughts: Here's a summary of my thought process, as requested: **Diving into `mm/page_alloc.c`** Alright, let's get down to brass tacks. I'm focusing in on `mm/page_alloc.c` because, well, that's where the rubber meets the road when it comes to memory management. Specifically, I want to take a look at `prep_new_page` and `post_alloc_hook`. These functions are crucial, aren't they? `prep_new_page`, as I understand it, has to be involved in the initial preparation of a newly allocated page – setting it up, zeroing it (perhaps conditionally), and ensuring it's ready for use. It's essentially the housekeeping before the real work begins. Then, `post_alloc_hook`... that's where things get interesting. This is the callback, isn't it? The point where we can inject custom behavior after a page has been allocated, and perhaps the system needs to perform some specific tasks depending on how the system is set up. This is where the real architecture specific stuff could come in I imagine, like integrating with NUMA, or handling specific hardware idiosyncrasies, or even logging some information about the allocation. I'm keen to understand the relationship between these two, how they are tied together, and what specific scenarios might trigger different paths through those functions. Thinking about where they're called from will be the next step. |
| 78/3 | 2026/09/14 03:32 | tool | read-file |
0mArgs:{
"File": "mm/page_alloc.c",
"FirstLine": 1850,
"LineCount": 40
}Results: {
"Contents": "1850:\t\tif (init)\n1851:\t\t\tclear_highpages_kasan_tagged(page, 1 \u003c\u003c order);\n1852:\t\n1853:\t\tset_page_owner(page, order, gfp_flags);\n1854:\t\tpage_table_check_alloc(page, order);\n1855:\t\tpgalloc_tag_add(page, current, 1 \u003c\u003c order, alloc_flags);\n1856:\t}\n1857:\t\n1858:\tstatic void prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags,\n1859:\t\t\t\t\t\t\t\tunsigned int alloc_flags)\n1860:\t{\n1861:\t\tpost_alloc_hook(page, order, gfp_flags, alloc_flags);\n1862:\t\n1863:\t\tif (order \u0026\u0026 (gfp_flags \u0026 __GFP_COMP))\n1864:\t\t\tprep_compound_page(page, order);\n1865:\t\n1866:\t\t/*\n1867:\t\t * page is set pfmemalloc when ALLOC_NO_WATERMARKS was necessary to\n1868:\t\t * allocate the page. The expectation is that the caller is taking\n1869:\t\t * steps that will free more memory. The caller should avoid the page\n1870:\t\t * being used for !PFMEMALLOC purposes.\n1871:\t\t */\n1872:\t\tif (alloc_flags \u0026 ALLOC_NO_WATERMARKS)\n1873:\t\t\tset_page_pfmemalloc(page);\n1874:\t\telse\n1875:\t\t\tclear_page_pfmemalloc(page);\n1876:\t}\n1877:\t\n1878:\t/*\n1879:\t * Go through the free lists for the given migratetype and remove\n1880:\t * the smallest available page from the freelists\n1881:\t */\n1882:\tstatic __always_inline\n1883:\tstruct page *__rmqueue_smallest(struct zone *zone, unsigned int order,\n1884:\t\t\t\t\t\t\tint migratetype)\n1885:\t{\n1886:\t\tunsigned int current_order;\n1887:\t\tstruct free_area *area;\n1888:\t\tstruct page *page;\n1889:\t\n"
} |
| 79/3 | 2026/09/14 03:32 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44160 output: 31 thoughts: 40 Thoughts: Okay, here's my interpretation of those thoughts, framed as my own mental process, given my expertise: **Buddy Allocator & Page Private Data - A Quick Mental Check** Right, let me quickly run through the buddy allocator's inner workings. I'm thinking about the `page->private` field. That's the key to tracking the buddy order, isn't it? Critical for that efficient allocation and deallocation process we rely on. So, whenever I'm seeing a page being freed – or, even more critically, when it's being allocated – my internal checklist immediately jumps to how this `page->private` field is being set. I need to verify that I'm checking the correct mechanisms. I want to be sure I'm looking at either `set_page_private` or `free_pages_prepare`. Those are the two places where that buddy order data is managed. It's about ensuring consistency and preventing corruption as the pages move through the states. That `private` field is the glue, so the operations around it really matter, as they are essential to maintaining the buddy system's integrity. It's good to make sure that the values make sense. |
| 80/3 | 2026/09/14 03:32 | tool | grepper |
0mArgs:{
"Expression": "set_page_private\\(.*0\\)",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/balloon.c=46=static void balloon_page_finalize(struct page *page)\n--\nmm/balloon.c-49-\tif (IS_ENABLED(CONFIG_BALLOON_MIGRATION))\nmm/balloon.c:50:\t\tset_page_private(page, 0);\nmm/balloon.c-51-\t/* PageOffline is sticky until the page is freed to the buddy. */\n--\nmm/debug_page_alloc.c=47=void __clear_page_guard(struct zone *zone, struct page *page, unsigned int order)\n--\nmm/debug_page_alloc.c-49-\t__ClearPageGuard(page);\nmm/debug_page_alloc.c:50:\tset_page_private(page, 0);\nmm/debug_page_alloc.c-51-}\n--\nmm/memory-failure.c=1326=void ClearPageHWPoisonTakenOff(struct page *page)\n--\nmm/memory-failure.c-1328-\tif (PageHWPoison(page))\nmm/memory-failure.c:1329:\t\tset_page_private(page, 0);\nmm/memory-failure.c-1330-}\n--\nmm/page_alloc.c=826=static inline void __del_page_from_free_list(struct page *page, struct zone *zone,\n--\nmm/page_alloc.c-840-\t__ClearPageBuddy(page);\nmm/page_alloc.c:841:\tset_page_private(page, 0);\nmm/page_alloc.c-842-\tzone-\u003efree_area[order].nr_free--;\n--\nmm/page_alloc.c=1803=inline void post_alloc_hook(struct page *page, unsigned int order,\n--\nmm/page_alloc.c-1810-\nmm/page_alloc.c:1811:\tset_page_private(page, 0);\nmm/page_alloc.c-1812-\n"
} |
| 81/3 | 2026/09/14 03:32 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44995 output: 35 thoughts: 411 Thoughts: **Analyzing Potential Uninitialized Data Exposure** Okay, so the `post_alloc_hook` explicitly zeros the private field of a page using `set_page_private(page, 0)`. That's good. And the memmap is zeroed during boot and hotplug, which covers a lot of the initial state. The `struct page` (and by extension `struct folio`) are core internal kernel structures managed by the memory management system, so we can assume a certain level of initialization discipline there. But let's be thorough. My gut says to check the flags and private fields of the `struct page`. Could *any* of those contain uninitialized data that's being accessed later? Nope, the kernel mm allocates and zeroes page flags and data structures on boot. The big question now is, could any of this data be copied to user space? That's the vector we need to focus on. Let's see... looking at `kpageflags_read`, we're specifically interested in how page flags are exposed. Aha! We have `stable_page_flags()`. `stable_page_flags()` returns a value, `u`, that's copied to user space through `/proc/kpageflags`. The critical part is how `u` is initialized. Right, `u` is *definitely* initialized to zero, like so: `u64 u = 0;`. This means we're safe and starting with a known state. So, the code snippet provided removes the line that sets the KPF_PRIVATE bit. It was: `u |= kpf_copy_bit(k, KPF_PRIVATE, PG_private);`. Removing this just stops setting bit 35 in the returned value. This change doesn't introduce any uninitialized data concerns, because `u` is initialized before any of the bit setting. Let's double-check lines 130-145 of `fs/proc/page.c` just to be absolutely certain we haven't missed anything, but it looks like we're good. This change seems safe. |
| 82/3 | 2026/09/14 03:33 | tool | read-file |
0mArgs:{
"File": "fs/proc/page.c",
"FirstLine": 130,
"LineCount": 25
}Results: {
"Contents": " 130:\t{\n 131:\t\treturn kpage_read(file, buf, count, ppos, KPAGE_COUNT);\n 132:\t}\n 133:\t\n 134:\tstatic const struct proc_ops kpagecount_proc_ops = {\n 135:\t\t.proc_flags\t= PROC_ENTRY_PERMANENT,\n 136:\t\t.proc_lseek\t= mem_lseek,\n 137:\t\t.proc_read\t= kpagecount_read,\n 138:\t};\n 139:\t\n 140:\t\n 141:\tstatic inline u64 kpf_copy_bit(u64 kflags, int ubit, int kbit)\n 142:\t{\n 143:\t\treturn ((kflags \u003e\u003e kbit) \u0026 1) \u003c\u003c ubit;\n 144:\t}\n 145:\t\n 146:\tu64 stable_page_flags(const struct page *page)\n 147:\t{\n 148:\t\tconst struct folio *folio;\n 149:\t\tstruct page_snapshot ps;\n 150:\t\tunsigned long k;\n 151:\t\tu64 u = 0;\n 152:\t\n 153:\t\t/*\n 154:\t\t * pseudo flag: KPF_NOPAGE\n"
} |
| 83/3 | 2026/09/14 03:33 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 46246 output: 34 thoughts: 569 Thoughts: **KMSAN Consideration: A Deep Dive**
Okay, so `u` is initialized to 0 – good, that takes care of any immediate uninitialized memory leak concerns from the outset. I'm going to revisit the KMSAN guidelines, because they're very specific about when this tool should be engaged. It's a resource hog, so I need to make sure I'm not overusing it.
The rule boils down to: **KMSAN is for *unique* uninitialized memory risks**. If KASAN, LOCKDEP, or any other standard bug detector can catch the issue, don't use KMSAN. It should *only* be flagged with `NeedsKMSAN=true` if it's explicitly about uninitialized memory issues that *KMSAN alone* can detect.
The guidelines break down into specific scenarios: kernel data sent to user space (copying to user, netlink, ioctl, etc.), conditional logic relying on uninitialized variables, allocation of complex structures with potentially uninitialized fields, and bounds checks that expose uninitialized memory.
On the flip side, `NeedsKMSAN=false` if the code primarily risks out-of-bounds access, overflows, NULL pointer dereferences, locking issues, or use-after-free – things KASAN et al. are built to handle. Also, if everything is properly zeroed or initialized (`= {0}`, `memset`, `kzalloc`), then it's a non-issue.
Alright, let's analyze the patch for any sneaky uninitialized memory use. The change involves `PG_private` in `enum pageflags` becoming `PG_folio` (or being removed from the flags check). `folio->private` is a pointer in `struct folio`, which is related to `struct page`.
Now the core of the matter: `order = page_private(p);` in `bts.c` and `pt.c`. Previously, there was a check: `if (PagePrivate(p)) order = page_private(p);`.
**Hold on a second!** If `PagePrivate(p)` was *false* before, what was the value of `order` in `topa_insert_pages` and `pt_buffer_try_single`? That's the critical question. I need to dig into `topa_insert_pages` and `pt_buffer_try_single` in `arch/x86/events/intel/pt.c` *immediately*. This could very well be the KMSAN-specific issue I'm looking for. Time to go hunting!
|
| 84/3 | 2026/09/14 03:33 | tool | grepper |
0mArgs:{
"Expression": "order",
"PathPrefix": "arch/x86/events/intel/pt.c"
}Results: {
"Output": "arch/x86/events/intel/pt.c=777=static int topa_insert_pages(struct pt_buffer *buf, int cpu, gfp_t gfp)\n--\narch/x86/events/intel/pt.c-779-\tstruct topa *topa = buf-\u003elast;\narch/x86/events/intel/pt.c:780:\tint order = 0;\narch/x86/events/intel/pt.c-781-\tstruct page *p;\n--\narch/x86/events/intel/pt.c-783-\tp = virt_to_page(buf-\u003edata_pages[buf-\u003enr_pages]);\narch/x86/events/intel/pt.c:784:\torder = page_private(p);\narch/x86/events/intel/pt.c-785-\n--\narch/x86/events/intel/pt.c-794-\tif (topa-\u003ez_count == topa-\u003elast - 1) {\narch/x86/events/intel/pt.c:795:\t\tif (order == TOPA_ENTRY(topa, topa-\u003elast - 1)-\u003esize)\narch/x86/events/intel/pt.c-796-\t\t\ttopa-\u003ez_count++;\n--\narch/x86/events/intel/pt.c-799-\tTOPA_ENTRY(topa, -1)-\u003ebase = page_to_phys(p) \u003e\u003e TOPA_SHIFT;\narch/x86/events/intel/pt.c:800:\tTOPA_ENTRY(topa, -1)-\u003esize = order;\narch/x86/events/intel/pt.c-801-\tif (!buf-\u003esnapshot \u0026\u0026\n--\narch/x86/events/intel/pt.c-807-\ttopa-\u003elast++;\narch/x86/events/intel/pt.c:808:\ttopa-\u003esize += sizes(order);\narch/x86/events/intel/pt.c-809-\narch/x86/events/intel/pt.c:810:\tbuf-\u003enr_pages += 1ul \u003c\u003c order;\narch/x86/events/intel/pt.c-811-\n--\narch/x86/events/intel/pt.c=1010=pt_topa_entry_for_page(struct pt_buffer *buf, unsigned int pg)\n--\narch/x86/events/intel/pt.c-1023-\t * First, find the ToPA table where @pg fits. With high\narch/x86/events/intel/pt.c:1024:\t * order allocations, there shouldn't be many of these.\narch/x86/events/intel/pt.c-1025-\t */\n--\narch/x86/events/intel/pt.c=1281=static int pt_buffer_try_single(struct pt_buffer *buf, int nr_pages)\n--\narch/x86/events/intel/pt.c-1283-\tstruct page *p = virt_to_page(buf-\u003edata_pages[0]);\narch/x86/events/intel/pt.c:1284:\tint ret = -ENOTSUPP, order = 0;\narch/x86/events/intel/pt.c-1285-\n--\narch/x86/events/intel/pt.c-1297-\narch/x86/events/intel/pt.c:1298:\torder = page_private(p);\narch/x86/events/intel/pt.c-1299-\narch/x86/events/intel/pt.c:1300:\tif (1 \u003c\u003c order != nr_pages)\narch/x86/events/intel/pt.c-1301-\t\tgoto out;\n"
} |
| 85/3 | 2026/09/14 03:33 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 48382 output: 289 thoughts: 708 Thoughts: **My Assessment of the `PG_private` Removal Refactoring** Okay, so I've been looking at this patch where they're removing the `PG_private` page flag and replacing it with direct access to `folio->private` or its equivalent. Specifically, I see the change in `topa_insert_pages` and `pt_buffer_try_single`, where `order` is now initialized directly from `page_private(p)`. My initial thought is to make sure this is done correctly and with no adverse effects. First, I need to make sure I understand the scope of the change. A quick check of the touched files gives a pretty good overview: Documentation, `arch/x86`, `drivers`, `fs`, `include`, `kernel`, `mm`, and `tools`. This looks like a fairly comprehensive change that touches many areas of the kernel. This confirms that this is a significant refactoring of an important internal construct. I've reviewed the files and it seems that this is a straightforward refactoring. They are systematically replacing the direct use of the `PG_private` flag with either access to the private field on the `folio` or via the `page_private` helper function. This is great, as this means we should only see logical errors due to memory management bugs, page lifecycle issues, refcounting bugs, and NULL pointer dereferences or use-after-free conditions. No new buffers are being allocated that could have uninitialized memory, there are no struct copies that could reveal uninitialized padding, and there are no alterations to bounds checks that could lead to out-of-bounds access. My concern is whether KMSAN is necessary for this change. Given the type of change and my review, it seems unlikely. I don't see any obvious potential for kernel memory sanitizer (KMSAN) to find a problem that would not be detected by other tools. Since this is a refactoring, there should be no uninitialized reads unless the refactoring is done incorrectly. This does not seem like a likely scenario. Therefore, `NeedsKMSAN = false`. I'll prepare a tool call to set the results and I'll include a detailed explanation contrasting KASAN (Kernel Address Sanitizer) and KMSAN, because those distinctions are key. KASAN focuses on detecting access to invalid memory addresses and use-after-free conditions, while KMSAN focuses on detecting reads of uninitialized memory. This change doesn't appear to introduce any uninitialized memory risks that aren't already covered by KASAN or other debugging mechanisms. |
| 86/3 | 2026/09/14 03:33 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series removes the PG_private page flag across the kernel in favor of checking folio-\u003eprivate directly (or using helper functions like folio_has_attached_private).\n\nEvaluating the changes under KMSAN vs KASAN criteria:\n1. No uninitialized memory usage or branching on uninitialized variables is introduced: All struct page and struct folio instances are kernel-managed structures zero-initialized at boot or page allocation.\n2. In readahead_control, the new '_forward' field is zero-initialized by DEFINE_READAHEAD (via standard C designated-initializer zeroing) and properly set before use.\n3. In /proc/kpageflags (fs/proc/page.c), removing the KPF_PRIVATE bit extraction does not expose uninitialized bits since the return mask 'u' is explicitly zero-initialized.\n4. No new user-space copy boundaries, ioctls, or padding-sensitive structures are created or modified.\n\nAny potential bugs resulting from this refactor would involve page lifecycle tracking, folio refcounting discrepancies, or NULL pointer dereferences, which fall under the purview of KASAN and standard kernel debuggers (e.g., CONFIG_DEBUG_VM). Spawning a dedicated KMSAN session is therefore not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series removes the PG_private page flag across the kernel in favor of checking folio-\u003eprivate directly (or using helper functions like folio_has_attached_private).\n\nEvaluating the changes under KMSAN vs KASAN criteria:\n1. No uninitialized memory usage or branching on uninitialized variables is introduced: All struct page and struct folio instances are kernel-managed structures zero-initialized at boot or page allocation.\n2. In readahead_control, the new '_forward' field is zero-initialized by DEFINE_READAHEAD (via standard C designated-initializer zeroing) and properly set before use.\n3. In /proc/kpageflags (fs/proc/page.c), removing the KPF_PRIVATE bit extraction does not expose uninitialized bits since the return mask 'u' is explicitly zero-initialized.\n4. No new user-space copy boundaries, ioctls, or padding-sensitive structures are created or modified.\n\nAny potential bugs resulting from this refactor would involve page lifecycle tracking, folio refcounting discrepancies, or NULL pointer dereferences, which fall under the purview of KASAN and standard kernel debuggers (e.g., CONFIG_DEBUG_VM). Spawning a dedicated KMSAN session is therefore not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|