| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/25 19:03 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"folio_migrate_flags",
"lru_gen_look_around"
],
"KMSANReasoning": "The patch refactors the internal memory management code, specifically LRU generation and folio flags manipulation, and changes `lru_zone_size` to use `atomic_long_t`. It does not introduce any new structures that are copied to user space, nor does it allocate complex data structures or modify bounds checks in a way that could expose uninitialized memory. The newly introduced out-parameter `is_file` in `folio_update_gen` is always initialized before returning. Any potential bugs introduced by these changes (such as race conditions, use-after-free, or out-of-bounds accesses) would be effectively caught by KASAN, LOCKDEP, and other standard kernel debugging tools. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false,
"Reasoning": "The patch refactors Multi-Gen LRU (MGLRU) page flags handling and fixes a race condition in `lru_zone_size` updates by using atomic operations. It modifies core memory management logic, specifically page migration (`folio_migrate_flags`) and page reclaim lookaround (`lru_gen_look_around`). These are functional changes to reachable core kernel logic, making them worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/25 19:03 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 259a25223876d0f273b34fe6aef34366b756099e\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Aug 25 19:03:21 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h\nindex 215e2e87f42b2..7b89d0cb5f6c6 100644\n--- a/include/linux/memcontrol.h\n+++ b/include/linux/memcontrol.h\n@@ -113,7 +113,7 @@ struct mem_cgroup_per_node {\n \t/* Fields which get updated often at the end. */\n \tstruct lruvec\t\tlruvec;\n \tCACHELINE_PADDING(_pad2_);\n-\tunsigned long\t\tlru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];\n+\tatomic_long_t\t\tlru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];\n \tstruct mem_cgroup_reclaim_iter\titer;\n \n \t/*\n@@ -902,10 +902,15 @@ static inline\n unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,\n \t\tenum lru_list lru, int zone_idx)\n {\n+\tlong val;\n \tstruct mem_cgroup_per_node *mz;\n \n \tmz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);\n-\treturn READ_ONCE(mz-\u003elru_zone_size[zone_idx][lru]);\n+\tval = atomic_long_read(\u0026mz-\u003elru_zone_size[zone_idx][lru]);\n+\tif (WARN_ON_ONCE(val \u003c 0))\n+\t\treturn 0;\n+\n+\treturn val;\n }\n \n void __mem_cgroup_handle_over_high(gfp_t gfp_mask);\ndiff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h\nindex 621c8653d8f7e..8cf82989ec263 100644\n--- a/include/linux/mm_inline.h\n+++ b/include/linux/mm_inline.h\n@@ -10,6 +10,11 @@\n #include \u003clinux/userfaultfd_k.h\u003e\n #include \u003clinux/leafops.h\u003e\n \n+static inline int folio_flags_is_file_lru(const unsigned long *flags)\n+{\n+\treturn !test_bit(PG_swapbacked, flags);\n+}\n+\n /**\n * folio_is_file_lru - Should the folio be on a file LRU or anon LRU?\n * @folio: The folio to test.\n@@ -27,7 +32,7 @@\n */\n static inline int folio_is_file_lru(const struct folio *folio)\n {\n-\treturn !folio_test_swapbacked(folio);\n+\treturn folio_flags_is_file_lru(const_folio_flags(folio, 0));\n }\n \n static __always_inline void __update_lru_size(struct lruvec *lruvec,\n@@ -142,10 +147,42 @@ static inline int lru_tier_from_refs(int refs, bool workingset)\n \treturn workingset ? MAX_NR_TIERS - 1 : order_base_2(refs);\n }\n \n-static inline int folio_lru_refs(const struct folio *folio)\n+/**\n+ * lru_gen_from_flags - Return the LRU generation number from folio flags.\n+ * @flags: folio flags\n+ *\n+ * Returns: A number between 0 and (MAX_NR_GENS - 1), inclusive. Returns\n+ * -1 if the flags indicate the folio is off the list (e.g., isolated).\n+ */\n+static inline int lru_gen_from_flags(unsigned long flags)\n+{\n+\tint gen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF);\n+\n+\tBUILD_BUG_ON(LRU_GEN_MASK \u0026 LRU_REFS_MASK);\n+\tgen -= 1;\n+\tVM_WARN_ON_ONCE(gen != -1 \u0026\u0026 gen \u003e= MAX_NR_GENS);\n+\treturn gen;\n+}\n+\n+/**\n+ * lru_gen_set_flags - Set the LRU generation number to specified folio flags.\n+ * @flags: pointer to the folio flags\n+ * @gen: generation number, between 0 and (MAX_NR_GENS - 1), inclusive.\n+ */\n+static inline void lru_gen_set_flags(unsigned long *flags, int gen)\n {\n-\tunsigned long flags = READ_ONCE(folio-\u003eflags.f);\n+\tVM_WARN_ON_ONCE(gen \u003e= MAX_NR_GENS || gen \u003c 0);\n+\n+\t*flags \u0026= ~LRU_GEN_MASK;\n+\t*flags |= (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n+}\n \n+/**\n+ * lru_refs_from_flags - Return LRU referenced / access count from folio flags.\n+ * @flags: folio flags\n+ */\n+static inline int lru_refs_from_flags(unsigned long flags)\n+{\n \tif (!(flags \u0026 BIT(PG_referenced)))\n \t\treturn 0;\n \t/*\n@@ -155,11 +192,40 @@ static inline int folio_lru_refs(const struct folio *folio)\n \treturn ((flags \u0026 LRU_REFS_MASK) \u003e\u003e LRU_REFS_PGOFF) + 1;\n }\n \n-static inline int folio_lru_gen(const struct folio *folio)\n+/**\n+ * lru_refs_set_flags - Set the LRU referenced / access count to specified folio flags.\n+ * @flags: pointer to the folio flags\n+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.\n+ */\n+static inline void lru_refs_set_flags(unsigned long *flags, unsigned int refs)\n+{\n+\tVM_WARN_ON_ONCE(refs \u003e LRU_REFS_MAX);\n+\tBUILD_BUG_ON(LRU_REFS_MAX != (LRU_REFS_MASK \u003e\u003e LRU_REFS_PGOFF) + 1);\n+\n+\t*flags \u0026= ~LRU_REFS_FLAGS;\n+\tif (!refs)\n+\t\treturn;\n+\t*flags |= (BIT(PG_referenced) | ((refs - 1UL) \u003c\u003c LRU_REFS_PGOFF));\n+}\n+\n+static inline int folio_lru_refs(const struct folio *folio)\n+{\n+\treturn lru_refs_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));\n+}\n+\n+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)\n {\n-\tunsigned long flags = READ_ONCE(folio-\u003eflags.f);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n \n-\treturn ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\tdo {\n+\t\tnew_flags = old_flags;\n+\t\tlru_refs_set_flags(\u0026new_flags, refs);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n+}\n+\n+static inline int folio_lru_gen(const struct folio *folio)\n+{\n+\treturn lru_gen_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));\n }\n \n static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)\n@@ -270,7 +336,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,\n \tgen = lru_gen_from_seq(seq);\n \tflags = (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n \t/* see the comment on MIN_NR_GENS about PG_active */\n-\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_GEN_MASK | BIT(PG_active), flags);\n+\tset_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);\n \n \tlru_gen_update_size(lruvec, folio, -1, gen);\n \t/* for folio_rotate_reclaimable() */\n@@ -295,7 +361,7 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,\n \n \t/* for folio_migrate_flags() */\n \tflags = !reclaiming \u0026\u0026 lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;\n-\tflags = set_mask_bits(\u0026folio-\u003eflags.f, LRU_GEN_MASK, flags);\n+\tflags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);\n \tgen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n \n \tlru_gen_update_size(lruvec, folio, gen, -1);\n@@ -304,11 +370,19 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,\n \treturn true;\n }\n \n-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)\n+/**\n+ * folio_migrate_lru_refs - copy the reference state to a new folio\n+ * @new: the destination folio\n+ * @old: the source folio\n+ *\n+ * Transfer the reference state to @new during migration: the MGLRU\n+ * refs count, including PG_referenced, or just PG_referenced for the\n+ * active/inactive LRU.\n+ */\n+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n {\n-\tunsigned long refs = READ_ONCE(old-\u003eflags.f) \u0026 LRU_REFS_MASK;\n-\n-\tset_mask_bits(\u0026new-\u003eflags.f, LRU_REFS_MASK, refs);\n+\tBUILD_BUG_ON(LRU_REFS_MASK \u0026 BIT(PG_referenced));\n+\tfolio_set_lru_refs(new, folio_lru_refs(old));\n }\n #else /* !CONFIG_LRU_GEN */\n \n@@ -337,9 +411,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,\n \treturn false;\n }\n \n-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)\n+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n {\n-\n+\tif (folio_test_referenced(old))\n+\t\tfolio_set_referenced(new);\n }\n #endif /* CONFIG_LRU_GEN */\n \ndiff --git a/include/linux/mmzone.h b/include/linux/mmzone.h\nindex 94f9c3ff54160..9b27cf53bdbc7 100644\n--- a/include/linux/mmzone.h\n+++ b/include/linux/mmzone.h\n@@ -492,11 +492,14 @@ enum lruvec_flags {\n * folio-\u003eflags, masked by LRU_REFS_MASK.\n */\n #define MAX_NR_TIERS\t\t4U\n+#define LRU_TIER_MIN\t\t0U\n+#define LRU_TIER_MAX\t\t(MAX_NR_TIERS - 1)\n \n #ifndef __GENERATING_BOUNDS_H\n \n #define LRU_GEN_MASK\t\t((BIT(LRU_GEN_WIDTH) - 1) \u003c\u003c LRU_GEN_PGOFF)\n #define LRU_REFS_MASK\t\t((BIT(LRU_REFS_WIDTH) - 1) \u003c\u003c LRU_REFS_PGOFF)\n+#define LRU_REFS_MAX\t\tBIT(LRU_REFS_WIDTH)\n \n /*\n * For folios accessed multiple times through file descriptors,\ndiff --git a/mm/folio.c b/mm/folio.c\nindex c02dcea9c03c2..a932059057ac1 100644\n--- a/mm/folio.c\n+++ b/mm/folio.c\n@@ -353,26 +353,28 @@ static void __lru_cache_activate_folio(struct folio *folio)\n \n static void lru_gen_inc_refs(struct folio *folio)\n {\n-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n+\tint refs;\n \n \tif (folio_test_unevictable(folio))\n \t\treturn;\n \n \t/* see the comment on LRU_REFS_FLAGS */\n-\tif (!folio_test_referenced(folio)) {\n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_MASK, BIT(PG_referenced));\n+\tif (!folio_lru_refs(folio)) {\n+\t\tfolio_set_lru_refs(folio, 1);\n \t\treturn;\n \t}\n \n \tdo {\n-\t\tif ((old_flags \u0026 LRU_REFS_MASK) == LRU_REFS_MASK) {\n+\t\tnew_flags = old_flags;\n+\t\trefs = lru_refs_from_flags(old_flags);\n+\t\tif (refs == LRU_REFS_MAX) {\n \t\t\tif (!folio_test_workingset(folio))\n \t\t\t\tfolio_set_workingset(folio);\n \t\t\treturn;\n \t\t}\n-\n-\t\tnew_flags = old_flags + BIT(LRU_REFS_PGOFF);\n-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n+\t\tlru_refs_set_flags(\u0026new_flags, refs + 1);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n }\n \n static bool lru_gen_clear_refs(struct folio *folio)\n@@ -384,7 +386,8 @@ static bool lru_gen_clear_refs(struct folio *folio)\n \tif (gen \u003c 0)\n \t\treturn true;\n \n-\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0);\n+\tfolio_set_lru_refs(folio, 0);\n+\tfolio_clear_workingset(folio);\n \n \trcu_read_lock();\n \tseq = READ_ONCE(folio_lruvec(folio)-\u003elrugen.min_seq[type]);\ndiff --git a/mm/memcontrol.c b/mm/memcontrol.c\nindex 11b85f4b6828b..a7572ded56c9d 100644\n--- a/mm/memcontrol.c\n+++ b/mm/memcontrol.c\n@@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\n \t\t\t\tint zid, long nr_pages)\n {\n \tstruct mem_cgroup_per_node *mz;\n-\tunsigned long *lru_size;\n-\tlong size;\n \n \tif (mem_cgroup_disabled())\n \t\treturn;\n \n \tmz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);\n-\tlru_size = \u0026mz-\u003elru_zone_size[zid][lru];\n-\n-\tif (nr_pages \u003c 0)\n-\t\t*lru_size += nr_pages;\n-\n-\tsize = *lru_size;\n-\tif (WARN_ONCE(size \u003c 0,\n-\t\t\"%s(%p, %d, %ld): lru_size %ld\\n\",\n-\t\t__func__, lruvec, lru, nr_pages, size)) {\n-\t\tVM_BUG_ON(1);\n-\t\t*lru_size = 0;\n-\t}\n-\n-\tif (nr_pages \u003e 0)\n-\t\t*lru_size += nr_pages;\n+\tatomic_long_add(nr_pages, \u0026mz-\u003elru_zone_size[zid][lru]);\n }\n \n /**\ndiff --git a/mm/migrate.c b/mm/migrate.c\nindex 15b45832bcfa7..a369d0c95c386 100644\n--- a/mm/migrate.c\n+++ b/mm/migrate.c\n@@ -776,8 +776,9 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\n {\n \tint cpupid;\n \n-\tif (folio_test_referenced(folio))\n-\t\tfolio_set_referenced(newfolio);\n+\t/* Copy the reference state, including PG_referenced */\n+\tfolio_migrate_lru_refs(newfolio, folio);\n+\n \tif (folio_test_uptodate(folio))\n \t\tfolio_mark_uptodate(newfolio);\n \tif (folio_test_clear_active(folio)) {\n@@ -807,7 +808,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\n \tif (folio_test_idle(folio))\n \t\tfolio_set_idle(newfolio);\n \n-\tfolio_migrate_refs(newfolio, folio);\n \t/*\n \t * Copy NUMA information to the new page, to prevent over-eager\n \t * future migrations of this same page.\ndiff --git a/mm/vmscan.c b/mm/vmscan.c\nindex 73a81b4a3e164..6fda948aecf2c 100644\n--- a/mm/vmscan.c\n+++ b/mm/vmscan.c\n@@ -843,19 +843,22 @@ static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\n \tif (!folio_test_referenced(folio) \u0026\u0026 !folio_test_workingset(folio)) {\n \t\t/* Activate file-backed executable folios after first usage. */\n \t\tif (is_exec_file_folio(folio, vma_flags)) {\n-\t\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS, BIT(PG_workingset));\n+\t\t\tfolio_set_workingset(folio);\n+\t\t\tfolio_set_lru_refs(folio, 0);\n \t\t\treturn true;\n \t\t}\n \n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_MASK, BIT(PG_referenced));\n+\t\tfolio_set_lru_refs(folio, 1);\n \t\treturn false;\n \t}\n \n \t/* Promote on second access */\n-\tif (folio_lru_refs(folio) \u003e 1)\n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS, BIT(PG_workingset));\n-\telse\n+\tif (folio_lru_refs(folio) \u003e 1) {\n+\t\tfolio_set_workingset(folio);\n+\t\tfolio_set_lru_refs(folio, 0);\n+\t} else {\n \t\tfolio_mark_accessed(folio);\n+\t}\n \treturn true;\n }\n #else\n@@ -3195,8 +3198,8 @@ struct ctrl_pos {\n \tint gain;\n };\n \n-static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,\n-\t\t\t struct ctrl_pos *pos)\n+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,\n+\t\t\t int tier_max, int gain, struct ctrl_pos *pos)\n {\n \tint i;\n \tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\n@@ -3205,7 +3208,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,\n \tpos-\u003egain = gain;\n \tpos-\u003erefaulted = pos-\u003etotal = 0;\n \n-\tfor (i = tier % MAX_NR_TIERS; i \u003c= min(tier, MAX_NR_TIERS - 1); i++) {\n+\tfor (i = tier_min; i \u003c= tier_max; i++) {\n \t\tpos-\u003erefaulted += lrugen-\u003eavg_refaulted[type][i] +\n \t\t\t\t atomic_long_read(\u0026lrugen-\u003erefaulted[hist][type][i]);\n \t\tpos-\u003etotal += lrugen-\u003eavg_total[type][i] +\n@@ -3266,11 +3269,11 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)\n ******************************************************************************/\n \n /* promote pages accessed through page tables */\n-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)\n+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,\n+\t\t\t const vma_flags_t *vma_flags)\n {\n-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n-\n-\tVM_WARN_ON_ONCE(gen \u003e= MAX_NR_GENS);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n+\tint old_gen;\n \n \t/*\n \t * See the comment on LRU_REFS_FLAGS, and activate file-backed\n@@ -3279,20 +3282,25 @@ static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma\n \t */\n \tif (!folio_test_referenced(folio) \u0026\u0026 !folio_test_workingset(folio) \u0026\u0026\n \t !is_exec_file_folio(folio, vma_flags)) {\n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_MASK, BIT(PG_referenced));\n+\t\tfolio_set_lru_refs(folio, 1);\n \t\treturn -1;\n \t}\n \n \tdo {\n+\t\told_gen = lru_gen_from_flags(old_flags);\n+\t\tnew_flags = old_flags;\n+\n \t\t/* lru_gen_del_folio() has isolated this page? */\n-\t\tif (!(old_flags \u0026 LRU_GEN_MASK))\n-\t\t\treturn -1;\n+\t\tif (old_gen \u003c 0)\n+\t\t\tbreak;\n \n-\t\tnew_flags = old_flags \u0026 ~(LRU_GEN_MASK | LRU_REFS_FLAGS);\n-\t\tnew_flags |= ((gen + 1UL) \u003c\u003c LRU_GEN_PGOFF) | BIT(PG_workingset);\n-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n+\t\tlru_gen_set_flags(\u0026new_flags, new_gen);\n+\t\tlru_refs_set_flags(\u0026new_flags, 0);\n+\t\tnew_flags |= BIT(PG_workingset);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n \n-\treturn ((old_flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\t*is_file = folio_flags_is_file_lru(\u0026old_flags);\n+\treturn old_gen;\n }\n \n /* protect pages accessed multiple times through file descriptors */\n@@ -3301,21 +3309,20 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n \tint type = folio_is_file_lru(folio);\n \tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\n \tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\n-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n-\n-\tVM_WARN_ON_ONCE_FOLIO(!(old_flags \u0026 LRU_GEN_MASK), folio);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n \n \tdo {\n-\t\tnew_gen = ((old_flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\t\tnew_gen = lru_gen_from_flags(old_flags);\n+\n \t\t/* folio_update_gen() has promoted this page? */\n \t\tif (new_gen \u003e= 0 \u0026\u0026 new_gen != old_gen)\n \t\t\treturn new_gen;\n \n+\t\tnew_flags = old_flags;\n \t\tnew_gen = (old_gen + 1) % MAX_NR_GENS;\n-\n-\t\tnew_flags = old_flags \u0026 ~(LRU_GEN_MASK | LRU_REFS_FLAGS);\n-\t\tnew_flags |= (new_gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n+\t\tlru_gen_set_flags(\u0026new_flags, new_gen);\n+\t\tlru_refs_set_flags(\u0026new_flags, 0);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n \n \tlru_gen_update_size(lruvec, folio, old_gen, new_gen);\n \n@@ -3323,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n }\n \n static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,\n-\t\t\t int old_gen, int new_gen)\n+\t\t\t int old_gen, int new_gen, int type)\n {\n-\tint type = folio_is_file_lru(folio);\n \tint zone = folio_zonenum(folio);\n \tint delta = folio_nr_pages(folio);\n \n@@ -3512,22 +3518,24 @@ static bool suitable_to_scan(int total, int young)\n }\n \n static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,\n-\t\tstruct folio *folio, int new_gen, bool dirty)\n+\t\tstruct lruvec *lruvec, struct folio *folio, bool dirty)\n {\n-\tint old_gen;\n+\tint new_gen, old_gen, file;\n \n \tif (!folio)\n \t\treturn;\n \n+\tnew_gen = lru_gen_from_seq(READ_ONCE(lruvec-\u003elrugen.max_seq));\n+\n \tif (dirty \u0026\u0026 !folio_test_dirty(folio) \u0026\u0026\n \t !(folio_test_anon(folio) \u0026\u0026 folio_test_swapbacked(folio) \u0026\u0026\n \t !folio_test_swapcache(folio)))\n \t\tfolio_mark_dirty(folio);\n \n \tif (walk) {\n-\t\told_gen = folio_update_gen(folio, new_gen, \u0026vma-\u003eflags);\n+\t\told_gen = folio_update_gen(folio, new_gen, \u0026file, \u0026vma-\u003eflags);\n \t\tif (old_gen \u003e= 0 \u0026\u0026 old_gen != new_gen)\n-\t\t\tupdate_batch_size(walk, folio, old_gen, new_gen);\n+\t\t\tupdate_batch_size(walk, folio, old_gen, new_gen, file);\n \t} else if (lru_gen_set_refs(folio, \u0026vma-\u003eflags)) {\n \t\told_gen = folio_lru_gen(folio);\n \t\tif (old_gen \u003e= 0 \u0026\u0026 old_gen != new_gen)\n@@ -3549,8 +3557,6 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n \tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\n \tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n \tstruct pglist_data *pgdat = lruvec_pgdat(walk-\u003elruvec);\n-\tDEFINE_MAX_SEQ(walk-\u003elruvec);\n-\tint gen = lru_gen_from_seq(max_seq);\n \tunsigned int nr;\n \tpmd_t pmdval;\n \n@@ -3601,7 +3607,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n \t\t\tcontinue;\n \n \t\tif (last != folio) {\n-\t\t\twalk_update_folio(walk, args-\u003evma, last, gen, dirty);\n+\t\t\twalk_update_folio(walk, args-\u003evma, walk-\u003elruvec, last, dirty);\n \n \t\t\tlast = folio;\n \t\t\tdirty = false;\n@@ -3614,7 +3620,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n \t\twalk-\u003emm_stats[MM_LEAF_YOUNG] += nr;\n \t}\n \n-\twalk_update_folio(walk, args-\u003evma, last, gen, dirty);\n+\twalk_update_folio(walk, args-\u003evma, walk-\u003elruvec, last, dirty);\n \tlast = NULL;\n \n \tif (i \u003c PTRS_PER_PTE \u0026\u0026 get_next_vma(PMD_MASK, PAGE_SIZE, args, \u0026start, \u0026end))\n@@ -3637,8 +3643,6 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area\n \tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\n \tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n \tstruct pglist_data *pgdat = lruvec_pgdat(walk-\u003elruvec);\n-\tDEFINE_MAX_SEQ(walk-\u003elruvec);\n-\tint gen = lru_gen_from_seq(max_seq);\n \n \tVM_WARN_ON_ONCE(pud_leaf(*pud));\n \n@@ -3692,7 +3696,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area\n \t\t\tgoto next;\n \n \t\tif (last != folio) {\n-\t\t\twalk_update_folio(walk, vma, last, gen, dirty);\n+\t\t\twalk_update_folio(walk, vma, walk-\u003elruvec, last, dirty);\n \n \t\t\tlast = folio;\n \t\t\tdirty = false;\n@@ -3706,7 +3710,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area\n \t\ti = i \u003e MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;\n \t} while (i \u003c= MIN_LRU_BATCH);\n \n-\twalk_update_folio(walk, vma, last, gen, dirty);\n+\twalk_update_folio(walk, vma, walk-\u003elruvec, last, dirty);\n \n \tlazy_mmu_mode_disable();\n \tspin_unlock(ptl);\n@@ -4270,8 +4274,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \tstruct pglist_data *pgdat = folio_pgdat(folio);\n \tstruct lruvec *lruvec;\n \tstruct lru_gen_mm_state *mm_state;\n-\tunsigned long max_seq;\n-\tint gen;\n \n \tlockdep_assert_held(pvmw-\u003eptl);\n \tVM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio);\n@@ -4308,8 +4310,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \n \tmemcg = get_mem_cgroup_from_folio(folio);\n \tlruvec = mem_cgroup_lruvec(memcg, pgdat);\n-\tmax_seq = READ_ONCE((lruvec)-\u003elrugen.max_seq);\n-\tgen = lru_gen_from_seq(max_seq);\n \tmm_state = get_mm_state(lruvec);\n \n \tlazy_mmu_mode_enable();\n@@ -4341,7 +4341,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \t\t\tcontinue;\n \n \t\tif (last != folio) {\n-\t\t\twalk_update_folio(walk, vma, last, gen, dirty);\n+\t\t\twalk_update_folio(walk, vma, lruvec, last, dirty);\n \n \t\t\tlast = folio;\n \t\t\tdirty = false;\n@@ -4353,13 +4353,14 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \t\tyoung += nr;\n \t}\n \n-\twalk_update_folio(walk, vma, last, gen, dirty);\n+\twalk_update_folio(walk, vma, lruvec, last, dirty);\n \n \tlazy_mmu_mode_disable();\n \n \t/* feedback from rmap walkers to page table walkers */\n \tif (mm_state \u0026\u0026 suitable_to_scan(i, young))\n-\t\tupdate_bloom_filter(mm_state, max_seq, pvmw-\u003epmd);\n+\t\tupdate_bloom_filter(mm_state, READ_ONCE(lruvec-\u003elrugen.max_seq),\n+\t\t\t\t pvmw-\u003epmd);\n \n \tmem_cgroup_put(memcg);\n \n@@ -4716,7 +4717,7 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca\n \n \t/* see the comment on LRU_REFS_FLAGS */\n \tif (!folio_test_referenced(folio))\n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_MASK, 0);\n+\t\tfolio_set_lru_refs(folio, 0);\n \n \tsuccess = lru_gen_del_folio(lruvec, folio, true);\n \tVM_WARN_ON_ONCE_FOLIO(!success, folio);\n@@ -4809,9 +4810,9 @@ static int get_tier_idx(struct lruvec *lruvec, int type)\n \t * This value is chosen because any other tier would have at least twice\n \t * as many refaults as the first tier.\n \t */\n-\tread_ctrl_pos(lruvec, type, 0, 2, \u0026sp);\n-\tfor (tier = 1; tier \u003c MAX_NR_TIERS; tier++) {\n-\t\tread_ctrl_pos(lruvec, type, tier, 3, \u0026pv);\n+\tread_ctrl_pos(lruvec, type, LRU_TIER_MIN, LRU_TIER_MIN, 2, \u0026sp);\n+\tfor (tier = LRU_TIER_MIN + 1; tier \u003c= LRU_TIER_MAX; tier++) {\n+\t\tread_ctrl_pos(lruvec, type, tier, tier, 3, \u0026pv);\n \t\tif (!positive_ctrl_err(\u0026sp, \u0026pv))\n \t\t\tbreak;\n \t}\n@@ -4832,8 +4833,10 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)\n \t * Compare the sum of all tiers of anon with that of file to determine\n \t * which type to scan.\n \t */\n-\tread_ctrl_pos(lruvec, LRU_GEN_ANON, MAX_NR_TIERS, swappiness, \u0026sp);\n-\tread_ctrl_pos(lruvec, LRU_GEN_FILE, MAX_NR_TIERS, MAX_SWAPPINESS - swappiness, \u0026pv);\n+\tread_ctrl_pos(lruvec, LRU_GEN_ANON, LRU_TIER_MIN, LRU_TIER_MAX,\n+\t\t swappiness, \u0026sp);\n+\tread_ctrl_pos(lruvec, LRU_GEN_FILE, LRU_TIER_MIN, LRU_TIER_MAX,\n+\t\t MAX_SWAPPINESS - swappiness, \u0026pv);\n \n \treturn positive_ctrl_err(\u0026sp, \u0026pv);\n }\n@@ -4932,8 +4935,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \t\t}\n \n \t\t/* don't add rejected folios to the oldest generation */\n-\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])\n-\t\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS, BIT(PG_active));\n+\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {\n+\t\t\tfolio_set_lru_refs(folio, 0);\n+\t\t\tfolio_set_active(folio);\n+\t\t}\n \t}\n \n \tmove_folios_to_lru(\u0026list);\n"
} |
| 2/1 | 2026/08/25 19:03 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"folio_migrate_flags",
"lru_gen_look_around"
],
"Reasoning": "The patch refactors Multi-Gen LRU (MGLRU) page flags handling and fixes a race condition in `lru_zone_size` updates by using atomic operations. It modifies core memory management logic, specifically page migration (`folio_migrate_flags`) and page reclaim lookaround (`lru_gen_look_around`). These are functional changes to reachable core kernel logic, making them worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 259a25223876d0f273b34fe6aef34366b756099e
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 25 19:03:21 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 215e2e87f42b2..7b89d0cb5f6c6 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -113,7 +113,7 @@ struct mem_cgroup_per_node {
/* Fields which get updated often at the end. */
struct lruvec lruvec;
CACHELINE_PADDING(_pad2_);
- unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
+ atomic_long_t lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
struct mem_cgroup_reclaim_iter iter;
/*
@@ -902,10 +902,15 @@ static inline
unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,
enum lru_list lru, int zone_idx)
{
+ long val;
struct mem_cgroup_per_node *mz;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- return READ_ONCE(mz->lru_zone_size[zone_idx][lru]);
+ val = atomic_long_read(&mz->lru_zone_size[zone_idx][lru]);
+ if (WARN_ON_ONCE(val < 0))
+ return 0;
+
+ return val;
}
void __mem_cgroup_handle_over_high(gfp_t gfp_mask);
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index 621c8653d8f7e..8cf82989ec263 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -10,6 +10,11 @@
#include <linux/userfaultfd_k.h>
#include <linux/leafops.h>
+static inline int folio_flags_is_file_lru(const unsigned long *flags)
+{
+ return !test_bit(PG_swapbacked, flags);
+}
+
/**
* folio_is_file_lru - Should the folio be on a file LRU or anon LRU?
* @folio: The folio to test.
@@ -27,7 +32,7 @@
*/
static inline int folio_is_file_lru(const struct folio *folio)
{
- return !folio_test_swapbacked(folio);
+ return folio_flags_is_file_lru(const_folio_flags(folio, 0));
}
static __always_inline void __update_lru_size(struct lruvec *lruvec,
@@ -142,10 +147,42 @@ static inline int lru_tier_from_refs(int refs, bool workingset)
return workingset ? MAX_NR_TIERS - 1 : order_base_2(refs);
}
-static inline int folio_lru_refs(const struct folio *folio)
+/**
+ * lru_gen_from_flags - Return the LRU generation number from folio flags.
+ * @flags: folio flags
+ *
+ * Returns: A number between 0 and (MAX_NR_GENS - 1), inclusive. Returns
+ * -1 if the flags indicate the folio is off the list (e.g., isolated).
+ */
+static inline int lru_gen_from_flags(unsigned long flags)
+{
+ int gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF);
+
+ BUILD_BUG_ON(LRU_GEN_MASK & LRU_REFS_MASK);
+ gen -= 1;
+ VM_WARN_ON_ONCE(gen != -1 && gen >= MAX_NR_GENS);
+ return gen;
+}
+
+/**
+ * lru_gen_set_flags - Set the LRU generation number to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @gen: generation number, between 0 and (MAX_NR_GENS - 1), inclusive.
+ */
+static inline void lru_gen_set_flags(unsigned long *flags, int gen)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS || gen < 0);
+
+ *flags &= ~LRU_GEN_MASK;
+ *flags |= (gen + 1UL) << LRU_GEN_PGOFF;
+}
+/**
+ * lru_refs_from_flags - Return LRU referenced / access count from folio flags.
+ * @flags: folio flags
+ */
+static inline int lru_refs_from_flags(unsigned long flags)
+{
if (!(flags & BIT(PG_referenced)))
return 0;
/*
@@ -155,11 +192,40 @@ static inline int folio_lru_refs(const struct folio *folio)
return ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + 1;
}
-static inline int folio_lru_gen(const struct folio *folio)
+/**
+ * lru_refs_set_flags - Set the LRU referenced / access count to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.
+ */
+static inline void lru_refs_set_flags(unsigned long *flags, unsigned int refs)
+{
+ VM_WARN_ON_ONCE(refs > LRU_REFS_MAX);
+ BUILD_BUG_ON(LRU_REFS_MAX != (LRU_REFS_MASK >> LRU_REFS_PGOFF) + 1);
+
+ *flags &= ~LRU_REFS_FLAGS;
+ if (!refs)
+ return;
+ *flags |= (BIT(PG_referenced) | ((refs - 1UL) << LRU_REFS_PGOFF));
+}
+
+static inline int folio_lru_refs(const struct folio *folio)
+{
+ return lru_refs_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
+}
+
+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
- return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ do {
+ new_flags = old_flags;
+ lru_refs_set_flags(&new_flags, refs);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
+}
+
+static inline int folio_lru_gen(const struct folio *folio)
+{
+ return lru_gen_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
}
static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
@@ -270,7 +336,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,
gen = lru_gen_from_seq(seq);
flags = (gen + 1UL) << LRU_GEN_PGOFF;
/* see the comment on MIN_NR_GENS about PG_active */
- set_mask_bits(&folio->flags.f, LRU_GEN_MASK | BIT(PG_active), flags);
+ set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);
lru_gen_update_size(lruvec, folio, -1, gen);
/* for folio_rotate_reclaimable() */
@@ -295,7 +361,7 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
/* for folio_migrate_flags() */
flags = !reclaiming && lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;
- flags = set_mask_bits(&folio->flags.f, LRU_GEN_MASK, flags);
+ flags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);
gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
lru_gen_update_size(lruvec, folio, gen, -1);
@@ -304,11 +370,19 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return true;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+/**
+ * folio_migrate_lru_refs - copy the reference state to a new folio
+ * @new: the destination folio
+ * @old: the source folio
+ *
+ * Transfer the reference state to @new during migration: the MGLRU
+ * refs count, including PG_referenced, or just PG_referenced for the
+ * active/inactive LRU.
+ */
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
- unsigned long refs = READ_ONCE(old->flags.f) & LRU_REFS_MASK;
-
- set_mask_bits(&new->flags.f, LRU_REFS_MASK, refs);
+ BUILD_BUG_ON(LRU_REFS_MASK & BIT(PG_referenced));
+ folio_set_lru_refs(new, folio_lru_refs(old));
}
#else /* !CONFIG_LRU_GEN */
@@ -337,9 +411,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return false;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
-
+ if (folio_test_referenced(old))
+ folio_set_referenced(new);
}
#endif /* CONFIG_LRU_GEN */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 94f9c3ff54160..9b27cf53bdbc7 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -492,11 +492,14 @@ enum lruvec_flags {
* folio->flags, masked by LRU_REFS_MASK.
*/
#define MAX_NR_TIERS 4U
+#define LRU_TIER_MIN 0U
+#define LRU_TIER_MAX (MAX_NR_TIERS - 1)
#ifndef __GENERATING_BOUNDS_H
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
+#define LRU_REFS_MAX BIT(LRU_REFS_WIDTH)
/*
* For folios accessed multiple times through file descriptors,
diff --git a/mm/folio.c b/mm/folio.c
index c02dcea9c03c2..a932059057ac1 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -353,26 +353,28 @@ static void __lru_cache_activate_folio(struct folio *folio)
static void lru_gen_inc_refs(struct folio *folio)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int refs;
if (folio_test_unevictable(folio))
return;
/* see the comment on LRU_REFS_FLAGS */
- if (!folio_test_referenced(folio)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ if (!folio_lru_refs(folio)) {
+ folio_set_lru_refs(folio, 1);
return;
}
do {
- if ((old_flags & LRU_REFS_MASK) == LRU_REFS_MASK) {
+ new_flags = old_flags;
+ refs = lru_refs_from_flags(old_flags);
+ if (refs == LRU_REFS_MAX) {
if (!folio_test_workingset(folio))
folio_set_workingset(folio);
return;
}
-
- new_flags = old_flags + BIT(LRU_REFS_PGOFF);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_refs_set_flags(&new_flags, refs + 1);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
}
static bool lru_gen_clear_refs(struct folio *folio)
@@ -384,7 +386,8 @@ static bool lru_gen_clear_refs(struct folio *folio)
if (gen < 0)
return true;
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0);
+ folio_set_lru_refs(folio, 0);
+ folio_clear_workingset(folio);
rcu_read_lock();
seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 11b85f4b6828b..a7572ded56c9d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
int zid, long nr_pages)
{
struct mem_cgroup_per_node *mz;
- unsigned long *lru_size;
- long size;
if (mem_cgroup_disabled())
return;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- lru_size = &mz->lru_zone_size[zid][lru];
-
- if (nr_pages < 0)
- *lru_size += nr_pages;
-
- size = *lru_size;
- if (WARN_ONCE(size < 0,
- "%s(%p, %d, %ld): lru_size %ld\n",
- __func__, lruvec, lru, nr_pages, size)) {
- VM_BUG_ON(1);
- *lru_size = 0;
- }
-
- if (nr_pages > 0)
- *lru_size += nr_pages;
+ atomic_long_add(nr_pages, &mz->lru_zone_size[zid][lru]);
}
/**
diff --git a/mm/migrate.c b/mm/migrate.c
index 15b45832bcfa7..a369d0c95c386 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -776,8 +776,9 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
{
int cpupid;
- if (folio_test_referenced(folio))
- folio_set_referenced(newfolio);
+ /* Copy the reference state, including PG_referenced */
+ folio_migrate_lru_refs(newfolio, folio);
+
if (folio_test_uptodate(folio))
folio_mark_uptodate(newfolio);
if (folio_test_clear_active(folio)) {
@@ -807,7 +808,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
if (folio_test_idle(folio))
folio_set_idle(newfolio);
- folio_migrate_refs(newfolio, folio);
/*
* Copy NUMA information to the new page, to prevent over-eager
* future migrations of this same page.
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 73a81b4a3e164..6fda948aecf2c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -843,19 +843,22 @@ static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)
if (!folio_test_referenced(folio) && !folio_test_workingset(folio)) {
/* Activate file-backed executable folios after first usage. */
if (is_exec_file_folio(folio, vma_flags)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_workingset));
+ folio_set_workingset(folio);
+ folio_set_lru_refs(folio, 0);
return true;
}
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ folio_set_lru_refs(folio, 1);
return false;
}
/* Promote on second access */
- if (folio_lru_refs(folio) > 1)
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_workingset));
- else
+ if (folio_lru_refs(folio) > 1) {
+ folio_set_workingset(folio);
+ folio_set_lru_refs(folio, 0);
+ } else {
folio_mark_accessed(folio);
+ }
return true;
}
#else
@@ -3195,8 +3198,8 @@ struct ctrl_pos {
int gain;
};
-static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
- struct ctrl_pos *pos)
+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,
+ int tier_max, int gain, struct ctrl_pos *pos)
{
int i;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
@@ -3205,7 +3208,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
pos->gain = gain;
pos->refaulted = pos->total = 0;
- for (i = tier % MAX_NR_TIERS; i <= min(tier, MAX_NR_TIERS - 1); i++) {
+ for (i = tier_min; i <= tier_max; i++) {
pos->refaulted += lrugen->avg_refaulted[type][i] +
atomic_long_read(&lrugen->refaulted[hist][type][i]);
pos->total += lrugen->avg_total[type][i] +
@@ -3266,11 +3269,11 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
******************************************************************************/
/* promote pages accessed through page tables */
-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)
+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,
+ const vma_flags_t *vma_flags)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int old_gen;
/*
* See the comment on LRU_REFS_FLAGS, and activate file-backed
@@ -3279,20 +3282,25 @@ static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma
*/
if (!folio_test_referenced(folio) && !folio_test_workingset(folio) &&
!is_exec_file_folio(folio, vma_flags)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ folio_set_lru_refs(folio, 1);
return -1;
}
do {
+ old_gen = lru_gen_from_flags(old_flags);
+ new_flags = old_flags;
+
/* lru_gen_del_folio() has isolated this page? */
- if (!(old_flags & LRU_GEN_MASK))
- return -1;
+ if (old_gen < 0)
+ break;
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= ((gen + 1UL) << LRU_GEN_PGOFF) | BIT(PG_workingset);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ new_flags |= BIT(PG_workingset);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
- return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ *is_file = folio_flags_is_file_lru(&old_flags);
+ return old_gen;
}
/* protect pages accessed multiple times through file descriptors */
@@ -3301,21 +3309,20 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
int type = folio_is_file_lru(folio);
struct lru_gen_folio *lrugen = &lruvec->lrugen;
int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE_FOLIO(!(old_flags & LRU_GEN_MASK), folio);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
do {
- new_gen = ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ new_gen = lru_gen_from_flags(old_flags);
+
/* folio_update_gen() has promoted this page? */
if (new_gen >= 0 && new_gen != old_gen)
return new_gen;
+ new_flags = old_flags;
new_gen = (old_gen + 1) % MAX_NR_GENS;
-
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF;
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
lru_gen_update_size(lruvec, folio, old_gen, new_gen);
@@ -3323,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
}
static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
- int old_gen, int new_gen)
+ int old_gen, int new_gen, int type)
{
- int type = folio_is_file_lru(folio);
int zone = folio_zonenum(folio);
int delta = folio_nr_pages(folio);
@@ -3512,22 +3518,24 @@ static bool suitable_to_scan(int total, int young)
}
static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
- struct folio *folio, int new_gen, bool dirty)
+ struct lruvec *lruvec, struct folio *folio, bool dirty)
{
- int old_gen;
+ int new_gen, old_gen, file;
if (!folio)
return;
+ new_gen = lru_gen_from_seq(READ_ONCE(lruvec->lrugen.max_seq));
+
if (dirty && !folio_test_dirty(folio) &&
!(folio_test_anon(folio) && folio_test_swapbacked(folio) &&
!folio_test_swapcache(folio)))
folio_mark_dirty(folio);
if (walk) {
- old_gen = folio_update_gen(folio, new_gen, &vma->flags);
+ old_gen = folio_update_gen(folio, new_gen, &file, &vma->flags);
if (old_gen >= 0 && old_gen != new_gen)
- update_batch_size(walk, folio, old_gen, new_gen);
+ update_batch_size(walk, folio, old_gen, new_gen, file);
} else if (lru_gen_set_refs(folio, &vma->flags)) {
old_gen = folio_lru_gen(folio);
if (old_gen >= 0 && old_gen != new_gen)
@@ -3549,8 +3557,6 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
unsigned int nr;
pmd_t pmdval;
@@ -3601,7 +3607,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
continue;
if (last != folio) {
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3614,7 +3620,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
walk->mm_stats[MM_LEAF_YOUNG] += nr;
}
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = NULL;
if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end))
@@ -3637,8 +3643,6 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
VM_WARN_ON_ONCE(pud_leaf(*pud));
@@ -3692,7 +3696,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
goto next;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3706,7 +3710,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
i = i > MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;
} while (i <= MIN_LRU_BATCH);
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
lazy_mmu_mode_disable();
spin_unlock(ptl);
@@ -4270,8 +4274,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
struct pglist_data *pgdat = folio_pgdat(folio);
struct lruvec *lruvec;
struct lru_gen_mm_state *mm_state;
- unsigned long max_seq;
- int gen;
lockdep_assert_held(pvmw->ptl);
VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio);
@@ -4308,8 +4310,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
memcg = get_mem_cgroup_from_folio(folio);
lruvec = mem_cgroup_lruvec(memcg, pgdat);
- max_seq = READ_ONCE((lruvec)->lrugen.max_seq);
- gen = lru_gen_from_seq(max_seq);
mm_state = get_mm_state(lruvec);
lazy_mmu_mode_enable();
@@ -4341,7 +4341,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
continue;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
last = folio;
dirty = false;
@@ -4353,13 +4353,14 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
young += nr;
}
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
lazy_mmu_mode_disable();
/* feedback from rmap walkers to page table walkers */
if (mm_state && suitable_to_scan(i, young))
- update_bloom_filter(mm_state, max_seq, pvmw->pmd);
+ update_bloom_filter(mm_state, READ_ONCE(lruvec->lrugen.max_seq),
+ pvmw->pmd);
mem_cgroup_put(memcg);
@@ -4716,7 +4717,7 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca
/* see the comment on LRU_REFS_FLAGS */
if (!folio_test_referenced(folio))
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, 0);
+ folio_set_lru_refs(folio, 0);
success = lru_gen_del_folio(lruvec, folio, true);
VM_WARN_ON_ONCE_FOLIO(!success, folio);
@@ -4809,9 +4810,9 @@ static int get_tier_idx(struct lruvec *lruvec, int type)
* This value is chosen because any other tier would have at least twice
* as many refaults as the first tier.
*/
- read_ctrl_pos(lruvec, type, 0, 2, &sp);
- for (tier = 1; tier < MAX_NR_TIERS; tier++) {
- read_ctrl_pos(lruvec, type, tier, 3, &pv);
+ read_ctrl_pos(lruvec, type, LRU_TIER_MIN, LRU_TIER_MIN, 2, &sp);
+ for (tier = LRU_TIER_MIN + 1; tier <= LRU_TIER_MAX; tier++) {
+ read_ctrl_pos(lruvec, type, tier, tier, 3, &pv);
if (!positive_ctrl_err(&sp, &pv))
break;
}
@@ -4832,8 +4833,10 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)
* Compare the sum of all tiers of anon with that of file to determine
* which type to scan.
*/
- read_ctrl_pos(lruvec, LRU_GEN_ANON, MAX_NR_TIERS, swappiness, &sp);
- read_ctrl_pos(lruvec, LRU_GEN_FILE, MAX_NR_TIERS, MAX_SWAPPINESS - swappiness, &pv);
+ read_ctrl_pos(lruvec, LRU_GEN_ANON, LRU_TIER_MIN, LRU_TIER_MAX,
+ swappiness, &sp);
+ read_ctrl_pos(lruvec, LRU_GEN_FILE, LRU_TIER_MIN, LRU_TIER_MAX,
+ MAX_SWAPPINESS - swappiness, &pv);
return positive_ctrl_err(&sp, &pv);
}
@@ -4932,8 +4935,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
}
/* don't add rejected folios to the oldest generation */
- if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_active));
+ if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {
+ folio_set_lru_refs(folio, 0);
+ folio_set_active(folio);
+ }
}
move_folios_to_lru(&list);
|
| 3/2 | 2026/08/25 19:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11408 output: 19 thoughts: 535 Thoughts: **Analyzing the Patch: MGLRU Refactoring** Okay, so I've got this patch in front of me, and my immediate concern is whether it introduces functional changes that warrant fuzzing. Given my expertise, I quickly zero in on the modified files: `include/linux/memcontrol.h`, `include/linux/mm_inline.h`, `include/linux/mmzone.h`, `mm/folio.c`, `mm/memcontrol.c`, `mm/migrate.c`, and `mm/vmscan.c`. The fact that it touches so many core memory management areas immediately raises a flag. It's clearly tied to LRU generation (MGLRU), and memcontrol. I need to dig into the details. Alright, the changes in `include/linux/memcontrol.h` are about atomic operations; `lru_zone_size` is now an `atomic_long_t` and `mem_cgroup_get_zone_lru_size` leverages `atomic_long_read`. Smart move, that's likely fixing a race condition. The changes in `include/linux/mm_inline.h` are inline functions for `folio_flags_is_file_lru`, `lru_gen_from_flags`, `lru_gen_set_flags`, `lru_refs_from_flags`, `lru_refs_set_flags`, `folio_set_lru_refs`, and replacing `folio_migrate_refs` with `folio_migrate_lru_refs`. The other file changes, `mm/folio.c`, `mm/memcontrol.c`, `mm/migrate.c`, and `mm/vmscan.c` all seem to be adjusting to the new inline functions and the atomic operations. `lru_gen_inc_refs` and `lru_gen_clear_refs` are updated, and `mem_cgroup_update_lru_size` now uses `atomic_long_add`. The migration and VM scanning updates are all interconnected with the LRU generation and flags. The `read_ctrl_pos` update with the `tier_min` and `tier_max` parameters looks interesting, too. So, in essence, this is a refactoring effort. It seems to be cleaning up the LRU generation flags handling, improving concurrency through atomic operations on `lru_zone_size`, and potentially fixing that race condition. Wait a minute... MGLRU, that's a big deal. Given the changes to the core MM logic, that is definitely something that needs fuzzing. Now, what's the relevant config option for MGLRU? Ah, it's `CONFIG_LRU_GEN`. Excellent. |
| 4/2 | 2026/08/25 19:03 | tool | grepper |
0mArgs:{
"Expression": "config LRU_GEN"
}Results: {
"Output": "mm/Kconfig=1393=config PTE_MARKER_UFFD_WP\n--\nmm/Kconfig-1403-# multi-gen LRU {\nmm/Kconfig:1404:config LRU_GEN\nmm/Kconfig-1405-\tbool \"Multi-Gen LRU\"\n--\nmm/Kconfig-1412-\nmm/Kconfig:1413:config LRU_GEN_ENABLED\nmm/Kconfig-1414-\tbool \"Enable by default\"\n--\nmm/Kconfig-1418-\nmm/Kconfig:1419:config LRU_GEN_STATS\nmm/Kconfig-1420-\tbool \"Full stats for debugging\"\n--\nmm/Kconfig-1427-\nmm/Kconfig:1428:config LRU_GEN_WALKS_MMU\nmm/Kconfig-1429-\tdef_bool y\n"
} |
| 5/2 | 2026/08/25 19:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12788 output: 21 thoughts: 36 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Enabling LRU Generation: A Quick Check** Right, it seems like we need to get `LRU_GEN` and `LRU_GEN_ENABLED` activated. Sounds promising for some performance improvements, or perhaps new functionality in the memory management realm. Before I dive too deep into the implications, let me quickly verify the configuration options themselves. My first instinct is to check `mm/Kconfig`. That's where I expect to find the definitive documentation and perhaps dependencies for `LRU_GEN`. I'll confirm its existence there, read any associated comments for context, and make sure there are no obvious conflicts before proceeding. Time to see what the kernel config has to say! |
| 6/2 | 2026/08/25 19:03 | tool | grepper |
0mArgs:{
"Expression": "depends on.*LRU_GEN"
}Results: {
"Output": "mm/Kconfig=1413=config LRU_GEN_ENABLED\nmm/Kconfig-1414-\tbool \"Enable by default\"\nmm/Kconfig:1415:\tdepends on LRU_GEN\nmm/Kconfig-1416-\thelp\n--\nmm/Kconfig=1419=config LRU_GEN_STATS\nmm/Kconfig-1420-\tbool \"Full stats for debugging\"\nmm/Kconfig:1421:\tdepends on LRU_GEN\nmm/Kconfig-1422-\thelp\n--\nmm/Kconfig=1428=config LRU_GEN_WALKS_MMU\nmm/Kconfig-1429-\tdef_bool y\nmm/Kconfig:1430:\tdepends on LRU_GEN \u0026\u0026 ARCH_HAS_HW_PTE_YOUNG\nmm/Kconfig-1431-# }\n"
} |
| 7/2 | 2026/08/25 19:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13237 output: 20 thoughts: 13 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing the Dependency Chain for `LRU_GEN`** Okay, time to dig into this. I'm starting by taking a look at `config LRU_GEN` and need to figure out the dependencies. It's crucial to understand what other components or features hinge on this to accurately assess its impact and potential conflicts. My immediate thought is, "What downstream effects will a change here cause?" I need to meticulously map this out. I'll need to identify which other flags, configurations, or even code modules are directly or indirectly linked to `LRU_GEN`. Knowing this will determine the necessary precautions required when tweaking `LRU_GEN`. |
| 8/2 | 2026/08/25 19:03 | tool | grepper |
0mArgs:{
"Expression": "config LRU_GEN$"
}Results: {
"Output": "mm/Kconfig=1393=config PTE_MARKER_UFFD_WP\n--\nmm/Kconfig-1403-# multi-gen LRU {\nmm/Kconfig:1404:config LRU_GEN\nmm/Kconfig-1405-\tbool \"Multi-Gen LRU\"\n"
} |
| 9/2 | 2026/08/25 19:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13513 output: 33 thoughts: 17 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person: **Diving into `mm/Kconfig` Line 1404** Okay, time to dig into the memory management configuration. I'm specifically targeting line 1404 in `mm/Kconfig`. I'm guessing that's where a particular option or group of related options for memory management is defined. Given the context of the `mm/` directory, it's likely dealing with some aspect of allocation, virtual memory, NUMA, or perhaps some specific architecture-related memory handling. I'm expecting to see a `config` definition, potentially nested within a `menu`, `choice`, or a conditional block using `if` or `select`. Hopefully, there are comments that give me a clue about the intent of this configuration option or group. I'm anticipating that I will need to understand the surrounding context to fully grasp it. I’m hoping that I have some prior knowledge about the particular subsystem that's touched at that line of code. Let's see what gems we can find... |
| 10/2 | 2026/08/25 19:03 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 1400,
"LineCount": 20
}Results: {
"Contents": "1400:\t\t purposes. It is required to enable userfaultfd write protection on\n1401:\t\t file-backed memory types like shmem and hugetlbfs.\n1402:\t\n1403:\t# multi-gen LRU {\n1404:\tconfig LRU_GEN\n1405:\t\tbool \"Multi-Gen LRU\"\n1406:\t\tdepends on MMU\n1407:\t\t# make sure folio-\u003eflags has enough spare bits\n1408:\t\tdepends on 64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP\n1409:\t\thelp\n1410:\t\t A high performance LRU implementation to overcommit memory. See\n1411:\t\t Documentation/admin-guide/mm/multigen_lru.rst for details.\n1412:\t\n1413:\tconfig LRU_GEN_ENABLED\n1414:\t\tbool \"Enable by default\"\n1415:\t\tdepends on LRU_GEN\n1416:\t\thelp\n1417:\t\t This option enables the multi-gen LRU by default.\n1418:\t\n1419:\tconfig LRU_GEN_STATS\n"
} |
| 11/2 | 2026/08/25 19:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14112 output: 35 thoughts: 410 Thoughts: **Analyzing the LRU Generation Patch** Okay, so I'm looking at this patch related to LRU generation. The key configurations here are `LRU_GEN` and `LRU_GEN_ENABLED` – that's the starting point. Now, I need to get a handle on what this patch actually *does*. I see it modifies several functions. Let's see... Specifically, the following functions have been modified: `mem_cgroup_update_lru_size`, `folio_migrate_flags`, `lru_gen_look_around`, `evict_folios`, `walk_pmd_range_locked`, `walk_pte_range`, `folio_inc_gen`, `folio_update_gen`, `read_ctrl_pos`, `lru_gen_set_refs`, `lru_gen_clear_refs`, `lru_gen_inc_refs`, and `__lru_cache_activate_folio`. Now, I need to zero in on the important bits. My focus is going to be on the non-static and non-inlined functions, as these are the ones most likely to have wider implications. I can see the location of the functions: `mem_cgroup_update_lru_size` is in `mm/memcontrol.c`. `folio_migrate_flags` is in `mm/migrate.c`. `lru_gen_look_around` is in `mm/vmscan.c`. It's pretty clear that the rest of them are static, which narrows my focus considerably. So, let's take a closer look at `mem_cgroup_update_lru_size`, `folio_migrate_flags`, and `lru_gen_look_around`. These are the ones where the code is actually *exposed* and can be called from other modules, they are worth a closer look. I'll need to dig into the code and understand how the LRU generation logic is being integrated within memory management, migration, and the VM scanning process. |
| 12/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "^(void|bool|int|unsigned long) mem_cgroup_update_lru_size"
}Results: {
"Output": "include/linux/memcontrol.h=891=static inline bool mem_cgroup_online(struct mem_cgroup *memcg)\n--\ninclude/linux/memcontrol.h-897-\ninclude/linux/memcontrol.h:898:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\ninclude/linux/memcontrol.h-899-\t\tint zid, long nr_pages);\n--\nmm/memcontrol.c=1501=struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio,\n--\nmm/memcontrol.c-1527- */\nmm/memcontrol.c:1528:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\nmm/memcontrol.c-1529-\t\t\t\tint zid, long nr_pages)\n"
} |
| 13/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15242 output: 29 thoughts: 18 Thoughts: Here's a summary of my thoughts, tailored for an expert audience like yourself: **Considering the Scope of `mem_cgroup_update_lru_size`** Okay, so I'm processing that `mem_cgroup_update_lru_size` function. The key realization here is that it's *exported* and therefore *global*. This immediately triggers a set of considerations in my mind. Because it's visible outside the immediate module or subsystem, it implies it's designed to be invoked from a wider context, potentially by other kernel modules or even userspace, which has serious ramifications for stability and synchronization. The implications of this are significant. This suggests a careful review of its usage patterns, potential race conditions and locking strategies is warranted, given the potential for unexpected interactions. Any changes to this function have to be considered carefully. The very fact that it's global should mean there's an excellent reason, documented, for why that's the case. I need to dig into the call sites and think about the locking contexts thoroughly. |
| 14/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "^(void|bool|int|unsigned long) folio_migrate_flags"
}Results: {
"Output": "include/linux/migrate.h=69=void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)\ninclude/linux/migrate.h-70-\t\t__releases(ptl);\ninclude/linux/migrate.h:71:void folio_migrate_flags(struct folio *newfolio, struct folio *folio);\ninclude/linux/migrate.h-72-int folio_migrate_mapping(struct address_space *mapping,\n--\nmm/migrate.c=739=int migrate_huge_page_move_mapping(struct address_space *mapping,\n--\nmm/migrate.c-774- */\nmm/migrate.c:775:void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\nmm/migrate.c-776-{\n"
} |
| 15/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15721 output: 32 thoughts: 10 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Considering the Scope of `folio_migrate_flags`** Alright, so `folio_migrate_flags`... that's a global variable, isn't it? Okay, so that means I need to be *very* mindful of its scope and potential impact. It's not localized, so changes to it in one part of the system will ripple throughout. I need to think about how different modules or functions might be reading or writing to it. Any race conditions or unintended side effects are a major concern. Best practice would suggest reducing its scope if possible, refactoring to pass as an argument if it makes sense. I have to make sure every instance of its use is well-documented, so future developers, including myself, don't trip over it. Time to assess all the places this flag is used, figure out its purpose, and ensure it's handled safely and efficiently, or consider potential alternatives to this global dependency. |
| 16/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "^(void|bool|int|unsigned long) lru_gen_look_around"
}Results: {
"Output": "include/linux/mmzone.h=694=void lru_gen_init_lruvec(struct lruvec *lruvec);\ninclude/linux/mmzone.h:695:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr);\ninclude/linux/mmzone.h-696-\n--\nmm/vmscan.c=4213=static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-4259- */\nmm/vmscan.c:4260:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\nmm/vmscan.c-4261-{\n"
} |
| 17/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16193 output: 25 thoughts: 353 Thoughts: **My Analysis of Potential Fuzzing Targets** Okay, so I'm looking at these functions related to LRU and folio management, specifically with an eye towards fuzzing. First, I noticed that `lru_gen_look_around` is global, which immediately makes it a potential target. Then, I quickly scanned through `mm/vmscan.c` and `mm/folio.c` to see what else might be interesting, and it looks like a lot of the supporting functions are `static`, like `evict_folios`, `get_type_to_scan`, `isolate_folio`, and several others in both files. They seem pretty contained. So, the ones that are *not* static, and therefore have broader reach, are `mem_cgroup_update_lru_size`, `folio_migrate_flags`, and `lru_gen_look_around`. These seem to be the prime candidates for closer inspection. Now, let's assess their fuzzing potential. `mem_cgroup_update_lru_size` is a crucial function for memcg LRU size updates, essential for memory management. `folio_migrate_flags` is deeply involved in page migration, which is a known area for complex interactions. Finally, `lru_gen_look_around` is a core function for MGLRU. All three of these have a strong connection to core kernel behavior. Hmm, `mem_cgroup_update_lru_size` feels a little more routine than the other two. It's probably quite hot, as it's a fundamental part of the memcg subsystem, but maybe not as likely to reveal tricky edge cases. It's probably worth investigating where it's called from to understand the context. |
| 18/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_update_lru_size"
}Results: {
"Output": "include/linux/memcontrol.h=891=static inline bool mem_cgroup_online(struct mem_cgroup *memcg)\n--\ninclude/linux/memcontrol.h-897-\ninclude/linux/memcontrol.h:898:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\ninclude/linux/memcontrol.h-899-\t\tint zid, long nr_pages);\n--\ninclude/linux/mm_inline.h=52=static __always_inline void update_lru_size(struct lruvec *lruvec,\n--\ninclude/linux/mm_inline.h-57-#ifdef CONFIG_MEMCG\ninclude/linux/mm_inline.h:58:\tmem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages);\ninclude/linux/mm_inline.h-59-#endif\n--\nmm/folio.c=1123=static void lruvec_reparent_lru(struct lruvec *child_lruvec,\n--\nmm/folio.c-1143-\t\t */\nmm/folio.c:1144:\t\tmem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);\nmm/folio.c:1145:\t\tmem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size);\nmm/folio.c-1146-\t}\n--\nmm/memcontrol.c=1501=struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio,\n--\nmm/memcontrol.c-1518-/**\nmm/memcontrol.c:1519: * mem_cgroup_update_lru_size - account for adding or removing an lru page\nmm/memcontrol.c-1520- * @lruvec: mem_cgroup per zone lru vector\n--\nmm/memcontrol.c-1527- */\nmm/memcontrol.c:1528:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\nmm/memcontrol.c-1529-\t\t\t\tint zid, long nr_pages)\n--\nmm/vmscan.c=1592=unsigned int reclaim_clean_pages_from_list(struct zone *zone,\n--\nmm/vmscan.c-1644- * Update LRU sizes after isolating pages. The LRU size updates must\nmm/vmscan.c:1645: * be complete before mem_cgroup_update_lru_size due to a sanity check.\nmm/vmscan.c-1646- */\n--\nmm/vmscan.c=4621=void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid)\n--\nmm/vmscan.c-4642-\t\t\t/* Move the accounting, do not duplicate it. */\nmm/vmscan.c:4643:\t\t\tmem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);\nmm/vmscan.c:4644:\t\t\tmem_cgroup_update_lru_size(child_lruvec, lru, zid, -(long)size);\nmm/vmscan.c-4645-\t\t}\n"
} |
| 19/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17811 output: 22 thoughts: 94 Thoughts: **Analyzing Hot Path Functionality** Okay, so I'm digging into some core memory management code, and a few functions are immediately standing out. `mem_cgroup_update_lru_size` is triggered from `update_lru_size`, which, because it's `__always_inline`, is clearly designed for speed and used *everywhere*. That makes it a serious hot path I need to understand completely. Also, `folio_migrate_flags` is clearly crucial during page migration, and then there's `lru_gen_look_around`, used during page fault lookaround. Given the context, `lru_gen_look_around` is the one I think I should be investigating next. |
| 20/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_look_around"
}Results: {
"Output": "Documentation/mm/multigen_lru.rst=187=can incur the highest CPU cost in the reclaim path.\nDocumentation/mm/multigen_lru.rst-188-\nDocumentation/mm/multigen_lru.rst:189:``lru_gen_look_around()`` exploits spatial locality to reduce the\nDocumentation/mm/multigen_lru.rst-190-trips into the rmap. It scans the adjacent PTEs of a young PTE and\n--\nDocumentation/mm/multigen_lru.rst=199=in the set.\nDocumentation/mm/multigen_lru.rst-200-\nDocumentation/mm/multigen_lru.rst:201:In the eviction path, specifically, in ``lru_gen_look_around()``, if a\nDocumentation/mm/multigen_lru.rst-202-PMD has a sufficient number of hot pages, its address is placed in the\n--\ninclude/linux/mmzone.h=694=void lru_gen_init_lruvec(struct lruvec *lruvec);\ninclude/linux/mmzone.h:695:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr);\ninclude/linux/mmzone.h-696-\n--\ninclude/linux/mmzone.h=713=static inline void lru_gen_init_lruvec(struct lruvec *lruvec)\n--\ninclude/linux/mmzone.h-716-\ninclude/linux/mmzone.h:717:static inline bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw,\ninclude/linux/mmzone.h-718-\t\tunsigned int nr)\n--\nmm/rmap.c=918=static bool folio_referenced_one(struct folio *folio,\n--\nmm/rmap.c-982-\t\tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching() \u0026\u0026 pvmw.pte) {\nmm/rmap.c:983:\t\t\tif (lru_gen_look_around(\u0026pvmw, nr))\nmm/rmap.c-984-\t\t\t\treferenced++;\n--\nmm/vmscan.c=2791=static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)\n--\nmm/vmscan.c-2820- * walk_pmd_range(); the eviction also report them when walking the rmap\nmm/vmscan.c:2821: * in lru_gen_look_around().\nmm/vmscan.c-2822- *\n--\nmm/vmscan.c=4074=static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long seq,\n--\nmm/vmscan.c-4093-\t * If the hardware doesn't automatically set the accessed bit, fallback\nmm/vmscan.c:4094:\t * to lru_gen_look_around(), which only clears the accessed bit in a\nmm/vmscan.c-4095-\t * handful of PTEs. Spreading the work out over a period of time usually\n--\nmm/vmscan.c=4213=static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-4259- */\nmm/vmscan.c:4260:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\nmm/vmscan.c-4261-{\n"
} |
| 21/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18989 output: 19 thoughts: 50 Thoughts: Here's my summary, as the expert: **Fuzzing Target Identification: A Quick Scan** Alright, let's see. The `lru_gen_look_around` function called from `folio_referenced_one` within `mm/rmap.c` is definitely catching my attention. Given its location and likely function within the memory management subsystem, it seems like a prime target for fuzzing. It's a key point of interaction with the LRU and referenced folio flags, suggesting that we could induce some interesting conditions. Now, the natural step is to take a look at `folio_migrate_flags`, to understand any flags that are relevant in this context and see what kind of inputs we can test. Let's see what that reveals. |
| 22/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "folio_migrate_flags"
}Results: {
"Output": "fs/aio.c=460=static int aio_migrate_folio(struct address_space *mapping, struct folio *dst,\n--\nfs/aio.c-512-\tfolio_copy(dst, src);\nfs/aio.c:513:\tfolio_migrate_flags(dst, src);\nfs/aio.c-514-\tBUG_ON(ctx-\u003ering_folios[idx] != src);\n--\nfs/hugetlbfs/inode.c=1024=static int hugetlbfs_migrate_folio(struct address_space *mapping,\n--\nfs/hugetlbfs/inode.c-1039-\nfs/hugetlbfs/inode.c:1040:\tfolio_migrate_flags(dst, src);\nfs/hugetlbfs/inode.c-1041-\n--\ninclude/linux/migrate.h=69=void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)\ninclude/linux/migrate.h-70-\t\t__releases(ptl);\ninclude/linux/migrate.h:71:void folio_migrate_flags(struct folio *newfolio, struct folio *folio);\ninclude/linux/migrate.h-72-int folio_migrate_mapping(struct address_space *mapping,\n--\ninclude/linux/mm_inline.h=351=static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)\n--\ninclude/linux/mm_inline.h-361-\ninclude/linux/mm_inline.h:362:\t/* for folio_migrate_flags() */\ninclude/linux/mm_inline.h-363-\tflags = !reclaiming \u0026\u0026 lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;\n--\nmm/migrate.c=739=int migrate_huge_page_move_mapping(struct address_space *mapping,\n--\nmm/migrate.c-774- */\nmm/migrate.c:775:void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\nmm/migrate.c-776-{\n--\nmm/migrate.c-863-}\nmm/migrate.c:864:EXPORT_SYMBOL(folio_migrate_flags);\nmm/migrate.c-865-\n--\nmm/migrate.c=870=static int __migrate_folio(struct address_space *mapping, struct folio *dst,\n--\nmm/migrate.c-890-\nmm/migrate.c:891:\tfolio_migrate_flags(dst, src);\nmm/migrate.c-892-\treturn 0;\n--\nmm/migrate_device.c=1109=static void __migrate_device_pages(unsigned long *src_pfns,\n--\nmm/migrate_device.c-1251-\t\t\telse\nmm/migrate_device.c:1252:\t\t\t\tfolio_migrate_flags(newfolio, folio);\nmm/migrate_device.c-1253-\t\t}\n"
} |
| 23/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19991 output: 20 thoughts: 397 Thoughts: **My Analysis of Migration Hotspots** Okay, so I've got a handle on the code changes related to folio migration. `folio_migrate_flags` immediately stands out as a strong candidate, given that it's exported and used directly in the migration process. That's a good starting point. Now, let's dig a bit deeper into potential bottlenecks. `mem_cgroup_update_lru_size` is tempting, but it's part of the `update_lru_size` call chain, and that's called frequently from `lruvec_add_folio`, `lruvec_del_folio`, and the like. It's too hot a path to mess with right now. I need to avoid impacting critical performance. What else? Well, a bunch of static functions were modified. Let's see... `folio_update_gen`, `walk_pmd_range_locked`, `walk_pte_range`, `folio_inc_gen`, and `read_ctrl_pos`. Since they're static, I'm less worried about them being exposed as the primary cause of performance drops. But I need to be sure that I'm not missing anything. Alright, I'll review non-static functions again. `folio_flags_is_file_lru`, `lru_gen_from_flags`, `lru_gen_set_flags`, `lru_refs_from_flags`, `lru_refs_set_flags`, `folio_lru_refs`, `folio_set_lru_refs`, `folio_lru_gen`, and `folio_migrate_lru_refs` are all inline, so they are probably safe. Hold on, the patch touches `mm/folio.c` as well. I need to keep that in mind. `lru_gen_inc_refs` and `lru_gen_clear_refs`, both static. So, taking stock, it looks like `folio_migrate_flags` is the primary exposed point of interest. It's the cleanest symbol to focus on. And I'm now fairly confident in also reviewing `lru_gen_look_around` as well. That looks like a possible hotspot. Finally, I should confirm that `LRU_GEN` is enabled in the default configuration. Just to be sure. I need to consider all the angles to ensure optimal performance. |
| 24/2 | 2026/08/25 19:04 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_LRU_GEN"
}Results: {
"Output": "Documentation/admin-guide/mm/multigen_lru.rst=13=Build the kernel with the following configurations.\nDocumentation/admin-guide/mm/multigen_lru.rst-14-\nDocumentation/admin-guide/mm/multigen_lru.rst:15:* ``CONFIG_LRU_GEN=y``\nDocumentation/admin-guide/mm/multigen_lru.rst:16:* ``CONFIG_LRU_GEN_ENABLED=y``\nDocumentation/admin-guide/mm/multigen_lru.rst-17-\n--\nDocumentation/admin-guide/mm/multigen_lru.rst=28=following components. Its default value depends on\nDocumentation/admin-guide/mm/multigen_lru.rst:29:``CONFIG_LRU_GEN_ENABLED``. All the components should be enabled\nDocumentation/admin-guide/mm/multigen_lru.rst-30-unless some of them have unforeseen side effects. Writing to\n--\nDocumentation/admin-guide/mm/multigen_lru.rst=88=concatenation with delimiters ``,`` and ``;``.\n--\nDocumentation/admin-guide/mm/multigen_lru.rst-90-``/sys/kernel/debug/lru_gen_full`` provides additional stats for\nDocumentation/admin-guide/mm/multigen_lru.rst:91:debugging. ``CONFIG_LRU_GEN_STATS=y`` keeps historical stats from\nDocumentation/admin-guide/mm/multigen_lru.rst-92-evicted generations in this file.\n--\ninclude/linux/memcontrol.h=202=struct mem_cgroup {\n--\ninclude/linux/memcontrol.h-283-\ninclude/linux/memcontrol.h:284:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/memcontrol.h-285-\t/* per-memcg mm_struct list */\n--\ninclude/linux/mm_inline.h=87=static __always_inline enum lru_list folio_lru_list(const struct folio *folio)\n--\ninclude/linux/mm_inline.h-102-\ninclude/linux/mm_inline.h:103:#ifdef CONFIG_LRU_GEN\ninclude/linux/mm_inline.h-104-\ninclude/linux/mm_inline.h=105=static inline bool lru_gen_switching(void)\n--\ninclude/linux/mm_inline.h-110-}\ninclude/linux/mm_inline.h:111:#ifdef CONFIG_LRU_GEN_ENABLED\ninclude/linux/mm_inline.h-112-static inline bool lru_gen_enabled(void)\n--\ninclude/linux/mm_inline.h=382=static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n--\ninclude/linux/mm_inline.h-386-}\ninclude/linux/mm_inline.h:387:#else /* !CONFIG_LRU_GEN */\ninclude/linux/mm_inline.h-388-\n--\ninclude/linux/mm_inline.h=414=static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n--\ninclude/linux/mm_inline.h-418-}\ninclude/linux/mm_inline.h:419:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mm_inline.h-420-\n--\ninclude/linux/mm_types.h=1175=struct mm_struct {\n--\ninclude/linux/mm_types.h-1402-#endif /* CONFIG_KSM */\ninclude/linux/mm_types.h:1403:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/mm_types.h-1404-\t\tstruct {\n--\ninclude/linux/mm_types.h-1417-\t\t} lru_gen;\ninclude/linux/mm_types.h:1418:#endif /* CONFIG_LRU_GEN_WALKS_MMU */\ninclude/linux/mm_types.h-1419-#ifdef CONFIG_MM_ID\n--\ninclude/linux/mm_types.h=1480=static inline cpumask_t *mm_cpumask(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1484-\ninclude/linux/mm_types.h:1485:#ifdef CONFIG_LRU_GEN\ninclude/linux/mm_types.h-1486-\ninclude/linux/mm_types.h=1487=struct lru_gen_mm_list {\n--\ninclude/linux/mm_types.h-1493-\ninclude/linux/mm_types.h:1494:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mm_types.h-1495-\ninclude/linux/mm_types.h:1496:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/mm_types.h-1497-\n--\ninclude/linux/mm_types.h=1511=static inline void lru_gen_use_mm(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1520-\ninclude/linux/mm_types.h:1521:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\ninclude/linux/mm_types.h-1522-\n--\ninclude/linux/mm_types.h=1539=static inline void lru_gen_use_mm(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1542-\ninclude/linux/mm_types.h:1543:#endif /* CONFIG_LRU_GEN_WALKS_MMU */\ninclude/linux/mm_types.h-1544-\n--\ninclude/linux/mmzone.h=528=struct page_vma_mapped_walk;\ninclude/linux/mmzone.h-529-\ninclude/linux/mmzone.h:530:#ifdef CONFIG_LRU_GEN\ninclude/linux/mmzone.h-531-\n--\ninclude/linux/mmzone.h=537=enum {\n--\ninclude/linux/mmzone.h-547-/* whether to keep historical stats from evicted generations */\ninclude/linux/mmzone.h:548:#ifdef CONFIG_LRU_GEN_STATS\ninclude/linux/mmzone.h-549-#define NR_HIST_GENS\t\tMAX_NR_GENS\n--\ninclude/linux/mmzone.h=705=void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid);\ninclude/linux/mmzone.h-706-\ninclude/linux/mmzone.h:707:#else /* !CONFIG_LRU_GEN */\ninclude/linux/mmzone.h-708-\n--\ninclude/linux/mmzone.h=757=void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid)\n--\ninclude/linux/mmzone.h-760-\ninclude/linux/mmzone.h:761:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mmzone.h-762-\n--\ninclude/linux/mmzone.h=769=struct lruvec {\n--\ninclude/linux/mmzone.h-788-\tunsigned long\t\t\tflags;\ninclude/linux/mmzone.h:789:#ifdef CONFIG_LRU_GEN\ninclude/linux/mmzone.h-790-\t/* evictable pages divided into generations */\ninclude/linux/mmzone.h-791-\tstruct lru_gen_folio\t\tlrugen;\ninclude/linux/mmzone.h:792:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/mmzone.h-793-\t/* to concurrently iterate lru_gen_mm_list */\n--\ninclude/linux/mmzone.h-795-#endif\ninclude/linux/mmzone.h:796:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mmzone.h-797-#ifdef CONFIG_MEMCG\n--\ninclude/linux/mmzone.h=1481=typedef struct pglist_data {\n--\ninclude/linux/mmzone.h-1598-\ninclude/linux/mmzone.h:1599:#ifdef CONFIG_LRU_GEN\ninclude/linux/mmzone.h-1600-\t/* kswap mm walk data */\n--\ninclude/linux/sched.h=826=struct task_struct {\n--\ninclude/linux/sched.h-1024-#endif\ninclude/linux/sched.h:1025:#ifdef CONFIG_LRU_GEN\ninclude/linux/sched.h-1026-\t/* whether the LRU algorithm may apply to this access */\n--\ninclude/linux/swap.h=146=struct reclaim_state {\n--\ninclude/linux/swap.h-148-\tunsigned long reclaimed;\ninclude/linux/swap.h:149:#ifdef CONFIG_LRU_GEN\ninclude/linux/swap.h-150-\t/* per-thread mm walk data */\n--\nkernel/bounds.c=17=int main(void)\n--\nkernel/bounds.c-25-\tDEFINE(SPINLOCK_SIZE, sizeof(spinlock_t));\nkernel/bounds.c:26:#ifdef CONFIG_LRU_GEN\nkernel/bounds.c-27-\tDEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1));\n--\nkernel/fork.c=2694=pid_t kernel_clone(struct kernel_clone_args *args)\n--\nkernel/fork.c-2771-\nkernel/fork.c:2772:\tif (IS_ENABLED(CONFIG_LRU_GEN_WALKS_MMU) \u0026\u0026 !(clone_flags \u0026 CLONE_VM)) {\nkernel/fork.c-2773-\t\t/* lock the task to synchronize with memcg migration */\n--\nmm/folio.c=322=static void __lru_cache_activate_folio(struct folio *folio)\n--\nmm/folio.c-351-\nmm/folio.c:352:#ifdef CONFIG_LRU_GEN\nmm/folio.c-353-\n--\nmm/folio.c=380=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-398-\nmm/folio.c:399:#else /* !CONFIG_LRU_GEN */\nmm/folio.c-400-\n--\nmm/folio.c=405=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-409-\nmm/folio.c:410:#endif /* CONFIG_LRU_GEN */\nmm/folio.c-411-\n--\nmm/memcontrol.c=4610=static void mem_cgroup_exit(struct task_struct *task)\n--\nmm/memcontrol.c-4626-\nmm/memcontrol.c:4627:#ifdef CONFIG_LRU_GEN\nmm/memcontrol.c-4628-static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset)\n--\nmm/memcontrol.c=4646=static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset) {}\nmm/memcontrol.c:4647:#endif /* CONFIG_LRU_GEN */\nmm/memcontrol.c-4648-\n--\nmm/memory.c=6725=static inline void mm_account_fault(struct mm_struct *mm, struct pt_regs *regs,\n--\nmm/memory.c-6777-\nmm/memory.c:6778:#ifdef CONFIG_LRU_GEN\nmm/memory.c-6779-static void lru_gen_enter_fault(struct vm_area_struct *vma)\n--\nmm/memory.c=6794=static void lru_gen_exit_fault(void)\n--\nmm/memory.c-6796-}\nmm/memory.c:6797:#endif /* CONFIG_LRU_GEN */\nmm/memory.c-6798-\n--\nmm/vmscan.c=827=enum folio_references {\n--\nmm/vmscan.c-832-\nmm/vmscan.c:833:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-834-/*\n--\nmm/vmscan.c=865=static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\n--\nmm/vmscan.c-868-}\nmm/vmscan.c:869:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-870-\n--\nmm/vmscan.c=2683=static bool can_age_anon_pages(struct lruvec *lruvec,\n--\nmm/vmscan.c-2694-\nmm/vmscan.c:2695:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-2696-\nmm/vmscan.c=2697=DEFINE_STATIC_KEY_FALSE(lru_switch);\nmm/vmscan.c:2698:#ifdef CONFIG_LRU_GEN_ENABLED\nmm/vmscan.c-2699-DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);\n--\nmm/vmscan.c=2883=static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\n--\nmm/vmscan.c-2902-\nmm/vmscan.c:2903:#ifdef CONFIG_LRU_GEN_WALKS_MMU\nmm/vmscan.c-2904-\n--\nmm/vmscan.c=3012=void lru_gen_migrate_mm(struct mm_struct *mm)\n--\nmm/vmscan.c-3040-\nmm/vmscan.c:3041:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\nmm/vmscan.c-3042-\n--\nmm/vmscan.c=5953=late_initcall(init_lru_gen);\nmm/vmscan.c-5954-\nmm/vmscan.c:5955:#else /* !CONFIG_LRU_GEN */\nmm/vmscan.c-5956-\n--\nmm/vmscan.c=5967=static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5971-\nmm/vmscan.c:5972:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-5973-\n--\nmm/workingset.c=213=static void unpack_shadow(void *shadow, int *memcgidp, pg_data_t **pgdat,\n--\nmm/workingset.c-232-\nmm/workingset.c:233:#ifdef CONFIG_LRU_GEN\nmm/workingset.c-234-\n--\nmm/workingset.c=292=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-336-\nmm/workingset.c:337:#else /* !CONFIG_LRU_GEN */\nmm/workingset.c-338-\n--\nmm/workingset.c=350=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-353-\nmm/workingset.c:354:#endif /* CONFIG_LRU_GEN */\nmm/workingset.c-355-\n"
} |
| 25/2 | 2026/08/25 19:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24956 output: 149 thoughts: 629 Thoughts: **My Analysis of the MGLRU Patch** Okay, so I'm looking at this MGLRU patch, and here's the breakdown of my thinking. First, `CONFIG_LRU_GEN` is the key configuration option here, and `CONFIG_LRU_GEN_ENABLED` enables it by default. So, for the fuzzing setup, `LRU_GEN` and `LRU_GEN_ENABLED` definitely need to be included in `EnableConfigs`. That's a no-brainer. Now, the crucial question: is this patch introducing functional changes significant enough to warrant fuzzing? Absolutely. The patch touches the very heart of memory management – the LRU generation logic. The changes to `mem_cgroup_update_lru_size` immediately caught my eye; using atomic operations is a critical fix for a potential race condition. Also, changes to `folio_migrate_flags`, `lru_gen_look_around`, and the updates in `mm/vmscan.c` all scream "functional impact." So, `WorthFuzzing` is definitely true. This leads me to `FocusSymbols`. I’m initially thinking `folio_migrate_flags` and `lru_gen_look_around`. But wait a minute, `folio_migrate_flags` is called from `migrate_pages` and `migrate_device_pages`. Page migration isn't as hot as page faults or reclaim, but it's still fundamental to memory management. As for `lru_gen_look_around`, that one is called within `folio_referenced_one` during rmap walking, which is absolutely part of page reclaim. It's a bit hot, given that it's part of MGLRU. Let's double check. Going through the non-static functions, I see the following inlines: `folio_migrate_lru_refs`, `folio_set_lru_refs`, `lru_refs_set_flags`, `lru_gen_set_flags`, `lru_gen_from_flags`, and `folio_flags_is_file_lru`. Those don't concern me, since they are inline. Thus, the only non-static functions are `mem_cgroup_update_lru_size`, `folio_migrate_flags`, and `lru_gen_look_around`. Right, so after reviewing, `folio_migrate_flags` and `lru_gen_look_around` stay in `FocusSymbols`. However, `mem_cgroup_update_lru_size` is too hot to include. To summarize, this patch refactors MGLRU's page flags and fixes a race. It touches core logic: page migration and reclaim. The changes are functional, and that makes it ripe for fuzzing. Okay, I think I have a solid understanding, and I'm ready to call `set-results`. |
| 26/2 | 2026/08/25 19:05 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"folio_migrate_flags",
"lru_gen_look_around"
],
"Reasoning": "The patch refactors Multi-Gen LRU (MGLRU) page flags handling and fixes a race condition in `lru_zone_size` updates by using atomic operations. It modifies core memory management logic, specifically page migration (`folio_migrate_flags`) and page reclaim lookaround (`lru_gen_look_around`). These are functional changes to reachable core kernel logic, making them worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"folio_migrate_flags",
"lru_gen_look_around"
],
"Reasoning": "The patch refactors Multi-Gen LRU (MGLRU) page flags handling and fixes a race condition in `lru_zone_size` updates by using atomic operations. It modifies core memory management logic, specifically page migration (`folio_migrate_flags`) and page reclaim lookaround (`lru_gen_look_around`). These are functional changes to reachable core kernel logic, making them worth fuzzing.",
"WorthFuzzing": true
} |
| 27/1 | 2026/08/25 19:05 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 28/2 | 2026/08/25 19:05 | agent | kmsan-evaluator |
0mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch refactors the internal memory management code, specifically LRU generation and folio flags manipulation, and changes `lru_zone_size` to use `atomic_long_t`. It does not introduce any new structures that are copied to user space, nor does it allocate complex data structures or modify bounds checks in a way that could expose uninitialized memory. The newly introduced out-parameter `is_file` in `folio_update_gen` is always initialized before returning. Any potential bugs introduced by these changes (such as race conditions, use-after-free, or out-of-bounds accesses) would be effectively caught by KASAN, LOCKDEP, and other standard kernel debugging tools. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"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: For your convenience, here is the diff of the changes:
commit 259a25223876d0f273b34fe6aef34366b756099e
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 25 19:03:21 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 215e2e87f42b2..7b89d0cb5f6c6 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -113,7 +113,7 @@ struct mem_cgroup_per_node {
/* Fields which get updated often at the end. */
struct lruvec lruvec;
CACHELINE_PADDING(_pad2_);
- unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
+ atomic_long_t lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
struct mem_cgroup_reclaim_iter iter;
/*
@@ -902,10 +902,15 @@ static inline
unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,
enum lru_list lru, int zone_idx)
{
+ long val;
struct mem_cgroup_per_node *mz;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- return READ_ONCE(mz->lru_zone_size[zone_idx][lru]);
+ val = atomic_long_read(&mz->lru_zone_size[zone_idx][lru]);
+ if (WARN_ON_ONCE(val < 0))
+ return 0;
+
+ return val;
}
void __mem_cgroup_handle_over_high(gfp_t gfp_mask);
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index 621c8653d8f7e..8cf82989ec263 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -10,6 +10,11 @@
#include <linux/userfaultfd_k.h>
#include <linux/leafops.h>
+static inline int folio_flags_is_file_lru(const unsigned long *flags)
+{
+ return !test_bit(PG_swapbacked, flags);
+}
+
/**
* folio_is_file_lru - Should the folio be on a file LRU or anon LRU?
* @folio: The folio to test.
@@ -27,7 +32,7 @@
*/
static inline int folio_is_file_lru(const struct folio *folio)
{
- return !folio_test_swapbacked(folio);
+ return folio_flags_is_file_lru(const_folio_flags(folio, 0));
}
static __always_inline void __update_lru_size(struct lruvec *lruvec,
@@ -142,10 +147,42 @@ static inline int lru_tier_from_refs(int refs, bool workingset)
return workingset ? MAX_NR_TIERS - 1 : order_base_2(refs);
}
-static inline int folio_lru_refs(const struct folio *folio)
+/**
+ * lru_gen_from_flags - Return the LRU generation number from folio flags.
+ * @flags: folio flags
+ *
+ * Returns: A number between 0 and (MAX_NR_GENS - 1), inclusive. Returns
+ * -1 if the flags indicate the folio is off the list (e.g., isolated).
+ */
+static inline int lru_gen_from_flags(unsigned long flags)
+{
+ int gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF);
+
+ BUILD_BUG_ON(LRU_GEN_MASK & LRU_REFS_MASK);
+ gen -= 1;
+ VM_WARN_ON_ONCE(gen != -1 && gen >= MAX_NR_GENS);
+ return gen;
+}
+
+/**
+ * lru_gen_set_flags - Set the LRU generation number to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @gen: generation number, between 0 and (MAX_NR_GENS - 1), inclusive.
+ */
+static inline void lru_gen_set_flags(unsigned long *flags, int gen)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS || gen < 0);
+
+ *flags &= ~LRU_GEN_MASK;
+ *flags |= (gen + 1UL) << LRU_GEN_PGOFF;
+}
+/**
+ * lru_refs_from_flags - Return LRU referenced / access count from folio flags.
+ * @flags: folio flags
+ */
+static inline int lru_refs_from_flags(unsigned long flags)
+{
if (!(flags & BIT(PG_referenced)))
return 0;
/*
@@ -155,11 +192,40 @@ static inline int folio_lru_refs(const struct folio *folio)
return ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + 1;
}
-static inline int folio_lru_gen(const struct folio *folio)
+/**
+ * lru_refs_set_flags - Set the LRU referenced / access count to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.
+ */
+static inline void lru_refs_set_flags(unsigned long *flags, unsigned int refs)
+{
+ VM_WARN_ON_ONCE(refs > LRU_REFS_MAX);
+ BUILD_BUG_ON(LRU_REFS_MAX != (LRU_REFS_MASK >> LRU_REFS_PGOFF) + 1);
+
+ *flags &= ~LRU_REFS_FLAGS;
+ if (!refs)
+ return;
+ *flags |= (BIT(PG_referenced) | ((refs - 1UL) << LRU_REFS_PGOFF));
+}
+
+static inline int folio_lru_refs(const struct folio *folio)
+{
+ return lru_refs_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
+}
+
+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
- return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ do {
+ new_flags = old_flags;
+ lru_refs_set_flags(&new_flags, refs);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
+}
+
+static inline int folio_lru_gen(const struct folio *folio)
+{
+ return lru_gen_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
}
static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
@@ -270,7 +336,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,
gen = lru_gen_from_seq(seq);
flags = (gen + 1UL) << LRU_GEN_PGOFF;
/* see the comment on MIN_NR_GENS about PG_active */
- set_mask_bits(&folio->flags.f, LRU_GEN_MASK | BIT(PG_active), flags);
+ set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);
lru_gen_update_size(lruvec, folio, -1, gen);
/* for folio_rotate_reclaimable() */
@@ -295,7 +361,7 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
/* for folio_migrate_flags() */
flags = !reclaiming && lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;
- flags = set_mask_bits(&folio->flags.f, LRU_GEN_MASK, flags);
+ flags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);
gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
lru_gen_update_size(lruvec, folio, gen, -1);
@@ -304,11 +370,19 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return true;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+/**
+ * folio_migrate_lru_refs - copy the reference state to a new folio
+ * @new: the destination folio
+ * @old: the source folio
+ *
+ * Transfer the reference state to @new during migration: the MGLRU
+ * refs count, including PG_referenced, or just PG_referenced for the
+ * active/inactive LRU.
+ */
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
- unsigned long refs = READ_ONCE(old->flags.f) & LRU_REFS_MASK;
-
- set_mask_bits(&new->flags.f, LRU_REFS_MASK, refs);
+ BUILD_BUG_ON(LRU_REFS_MASK & BIT(PG_referenced));
+ folio_set_lru_refs(new, folio_lru_refs(old));
}
#else /* !CONFIG_LRU_GEN */
@@ -337,9 +411,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return false;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
-
+ if (folio_test_referenced(old))
+ folio_set_referenced(new);
}
#endif /* CONFIG_LRU_GEN */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 94f9c3ff54160..9b27cf53bdbc7 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -492,11 +492,14 @@ enum lruvec_flags {
* folio->flags, masked by LRU_REFS_MASK.
*/
#define MAX_NR_TIERS 4U
+#define LRU_TIER_MIN 0U
+#define LRU_TIER_MAX (MAX_NR_TIERS - 1)
#ifndef __GENERATING_BOUNDS_H
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
+#define LRU_REFS_MAX BIT(LRU_REFS_WIDTH)
/*
* For folios accessed multiple times through file descriptors,
diff --git a/mm/folio.c b/mm/folio.c
index c02dcea9c03c2..a932059057ac1 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -353,26 +353,28 @@ static void __lru_cache_activate_folio(struct folio *folio)
static void lru_gen_inc_refs(struct folio *folio)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int refs;
if (folio_test_unevictable(folio))
return;
/* see the comment on LRU_REFS_FLAGS */
- if (!folio_test_referenced(folio)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ if (!folio_lru_refs(folio)) {
+ folio_set_lru_refs(folio, 1);
return;
}
do {
- if ((old_flags & LRU_REFS_MASK) == LRU_REFS_MASK) {
+ new_flags = old_flags;
+ refs = lru_refs_from_flags(old_flags);
+ if (refs == LRU_REFS_MAX) {
if (!folio_test_workingset(folio))
folio_set_workingset(folio);
return;
}
-
- new_flags = old_flags + BIT(LRU_REFS_PGOFF);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_refs_set_flags(&new_flags, refs + 1);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
}
static bool lru_gen_clear_refs(struct folio *folio)
@@ -384,7 +386,8 @@ static bool lru_gen_clear_refs(struct folio *folio)
if (gen < 0)
return true;
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0);
+ folio_set_lru_refs(folio, 0);
+ folio_clear_workingset(folio);
rcu_read_lock();
seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 11b85f4b6828b..a7572ded56c9d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
int zid, long nr_pages)
{
struct mem_cgroup_per_node *mz;
- unsigned long *lru_size;
- long size;
if (mem_cgroup_disabled())
return;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- lru_size = &mz->lru_zone_size[zid][lru];
-
- if (nr_pages < 0)
- *lru_size += nr_pages;
-
- size = *lru_size;
- if (WARN_ONCE(size < 0,
- "%s(%p, %d, %ld): lru_size %ld\n",
- __func__, lruvec, lru, nr_pages, size)) {
- VM_BUG_ON(1);
- *lru_size = 0;
- }
-
- if (nr_pages > 0)
- *lru_size += nr_pages;
+ atomic_long_add(nr_pages, &mz->lru_zone_size[zid][lru]);
}
/**
diff --git a/mm/migrate.c b/mm/migrate.c
index 15b45832bcfa7..a369d0c95c386 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -776,8 +776,9 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
{
int cpupid;
- if (folio_test_referenced(folio))
- folio_set_referenced(newfolio);
+ /* Copy the reference state, including PG_referenced */
+ folio_migrate_lru_refs(newfolio, folio);
+
if (folio_test_uptodate(folio))
folio_mark_uptodate(newfolio);
if (folio_test_clear_active(folio)) {
@@ -807,7 +808,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
if (folio_test_idle(folio))
folio_set_idle(newfolio);
- folio_migrate_refs(newfolio, folio);
/*
* Copy NUMA information to the new page, to prevent over-eager
* future migrations of this same page.
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 73a81b4a3e164..6fda948aecf2c 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -843,19 +843,22 @@ static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)
if (!folio_test_referenced(folio) && !folio_test_workingset(folio)) {
/* Activate file-backed executable folios after first usage. */
if (is_exec_file_folio(folio, vma_flags)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_workingset));
+ folio_set_workingset(folio);
+ folio_set_lru_refs(folio, 0);
return true;
}
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ folio_set_lru_refs(folio, 1);
return false;
}
/* Promote on second access */
- if (folio_lru_refs(folio) > 1)
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_workingset));
- else
+ if (folio_lru_refs(folio) > 1) {
+ folio_set_workingset(folio);
+ folio_set_lru_refs(folio, 0);
+ } else {
folio_mark_accessed(folio);
+ }
return true;
}
#else
@@ -3195,8 +3198,8 @@ struct ctrl_pos {
int gain;
};
-static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
- struct ctrl_pos *pos)
+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,
+ int tier_max, int gain, struct ctrl_pos *pos)
{
int i;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
@@ -3205,7 +3208,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
pos->gain = gain;
pos->refaulted = pos->total = 0;
- for (i = tier % MAX_NR_TIERS; i <= min(tier, MAX_NR_TIERS - 1); i++) {
+ for (i = tier_min; i <= tier_max; i++) {
pos->refaulted += lrugen->avg_refaulted[type][i] +
atomic_long_read(&lrugen->refaulted[hist][type][i]);
pos->total += lrugen->avg_total[type][i] +
@@ -3266,11 +3269,11 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
******************************************************************************/
/* promote pages accessed through page tables */
-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)
+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,
+ const vma_flags_t *vma_flags)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int old_gen;
/*
* See the comment on LRU_REFS_FLAGS, and activate file-backed
@@ -3279,20 +3282,25 @@ static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma
*/
if (!folio_test_referenced(folio) && !folio_test_workingset(folio) &&
!is_exec_file_folio(folio, vma_flags)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ folio_set_lru_refs(folio, 1);
return -1;
}
do {
+ old_gen = lru_gen_from_flags(old_flags);
+ new_flags = old_flags;
+
/* lru_gen_del_folio() has isolated this page? */
- if (!(old_flags & LRU_GEN_MASK))
- return -1;
+ if (old_gen < 0)
+ break;
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= ((gen + 1UL) << LRU_GEN_PGOFF) | BIT(PG_workingset);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ new_flags |= BIT(PG_workingset);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
- return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ *is_file = folio_flags_is_file_lru(&old_flags);
+ return old_gen;
}
/* protect pages accessed multiple times through file descriptors */
@@ -3301,21 +3309,20 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
int type = folio_is_file_lru(folio);
struct lru_gen_folio *lrugen = &lruvec->lrugen;
int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE_FOLIO(!(old_flags & LRU_GEN_MASK), folio);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
do {
- new_gen = ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ new_gen = lru_gen_from_flags(old_flags);
+
/* folio_update_gen() has promoted this page? */
if (new_gen >= 0 && new_gen != old_gen)
return new_gen;
+ new_flags = old_flags;
new_gen = (old_gen + 1) % MAX_NR_GENS;
-
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF;
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
lru_gen_update_size(lruvec, folio, old_gen, new_gen);
@@ -3323,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
}
static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
- int old_gen, int new_gen)
+ int old_gen, int new_gen, int type)
{
- int type = folio_is_file_lru(folio);
int zone = folio_zonenum(folio);
int delta = folio_nr_pages(folio);
@@ -3512,22 +3518,24 @@ static bool suitable_to_scan(int total, int young)
}
static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
- struct folio *folio, int new_gen, bool dirty)
+ struct lruvec *lruvec, struct folio *folio, bool dirty)
{
- int old_gen;
+ int new_gen, old_gen, file;
if (!folio)
return;
+ new_gen = lru_gen_from_seq(READ_ONCE(lruvec->lrugen.max_seq));
+
if (dirty && !folio_test_dirty(folio) &&
!(folio_test_anon(folio) && folio_test_swapbacked(folio) &&
!folio_test_swapcache(folio)))
folio_mark_dirty(folio);
if (walk) {
- old_gen = folio_update_gen(folio, new_gen, &vma->flags);
+ old_gen = folio_update_gen(folio, new_gen, &file, &vma->flags);
if (old_gen >= 0 && old_gen != new_gen)
- update_batch_size(walk, folio, old_gen, new_gen);
+ update_batch_size(walk, folio, old_gen, new_gen, file);
} else if (lru_gen_set_refs(folio, &vma->flags)) {
old_gen = folio_lru_gen(folio);
if (old_gen >= 0 && old_gen != new_gen)
@@ -3549,8 +3557,6 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
unsigned int nr;
pmd_t pmdval;
@@ -3601,7 +3607,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
continue;
if (last != folio) {
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3614,7 +3620,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
walk->mm_stats[MM_LEAF_YOUNG] += nr;
}
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = NULL;
if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end))
@@ -3637,8 +3643,6 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
VM_WARN_ON_ONCE(pud_leaf(*pud));
@@ -3692,7 +3696,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
goto next;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3706,7 +3710,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
i = i > MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;
} while (i <= MIN_LRU_BATCH);
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
lazy_mmu_mode_disable();
spin_unlock(ptl);
@@ -4270,8 +4274,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
struct pglist_data *pgdat = folio_pgdat(folio);
struct lruvec *lruvec;
struct lru_gen_mm_state *mm_state;
- unsigned long max_seq;
- int gen;
lockdep_assert_held(pvmw->ptl);
VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio);
@@ -4308,8 +4310,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
memcg = get_mem_cgroup_from_folio(folio);
lruvec = mem_cgroup_lruvec(memcg, pgdat);
- max_seq = READ_ONCE((lruvec)->lrugen.max_seq);
- gen = lru_gen_from_seq(max_seq);
mm_state = get_mm_state(lruvec);
lazy_mmu_mode_enable();
@@ -4341,7 +4341,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
continue;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
last = folio;
dirty = false;
@@ -4353,13 +4353,14 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
young += nr;
}
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
lazy_mmu_mode_disable();
/* feedback from rmap walkers to page table walkers */
if (mm_state && suitable_to_scan(i, young))
- update_bloom_filter(mm_state, max_seq, pvmw->pmd);
+ update_bloom_filter(mm_state, READ_ONCE(lruvec->lrugen.max_seq),
+ pvmw->pmd);
mem_cgroup_put(memcg);
@@ -4716,7 +4717,7 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca
/* see the comment on LRU_REFS_FLAGS */
if (!folio_test_referenced(folio))
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, 0);
+ folio_set_lru_refs(folio, 0);
success = lru_gen_del_folio(lruvec, folio, true);
VM_WARN_ON_ONCE_FOLIO(!success, folio);
@@ -4809,9 +4810,9 @@ static int get_tier_idx(struct lruvec *lruvec, int type)
* This value is chosen because any other tier would have at least twice
* as many refaults as the first tier.
*/
- read_ctrl_pos(lruvec, type, 0, 2, &sp);
- for (tier = 1; tier < MAX_NR_TIERS; tier++) {
- read_ctrl_pos(lruvec, type, tier, 3, &pv);
+ read_ctrl_pos(lruvec, type, LRU_TIER_MIN, LRU_TIER_MIN, 2, &sp);
+ for (tier = LRU_TIER_MIN + 1; tier <= LRU_TIER_MAX; tier++) {
+ read_ctrl_pos(lruvec, type, tier, tier, 3, &pv);
if (!positive_ctrl_err(&sp, &pv))
break;
}
@@ -4832,8 +4833,10 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)
* Compare the sum of all tiers of anon with that of file to determine
* which type to scan.
*/
- read_ctrl_pos(lruvec, LRU_GEN_ANON, MAX_NR_TIERS, swappiness, &sp);
- read_ctrl_pos(lruvec, LRU_GEN_FILE, MAX_NR_TIERS, MAX_SWAPPINESS - swappiness, &pv);
+ read_ctrl_pos(lruvec, LRU_GEN_ANON, LRU_TIER_MIN, LRU_TIER_MAX,
+ swappiness, &sp);
+ read_ctrl_pos(lruvec, LRU_GEN_FILE, LRU_TIER_MIN, LRU_TIER_MAX,
+ MAX_SWAPPINESS - swappiness, &pv);
return positive_ctrl_err(&sp, &pv);
}
@@ -4932,8 +4935,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
}
/* don't add rejected folios to the oldest generation */
- if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_active));
+ if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {
+ folio_set_lru_refs(folio, 0);
+ folio_set_active(folio);
+ }
}
move_folios_to_lru(&list);
|
| 29/3 | 2026/08/25 19:05 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11405 output: 195 thoughts: 1185 Thoughts: **Patch Analysis: Memory Control and LRU Generation Refactor**
Okay, let's break this down. My initial assessment is that this patch primarily focuses on refactoring the memory control and LRU generation code within the Linux kernel. It seems like a core internal change, specifically targeting `lru_zone_size` and its associated folio flag manipulation.
My immediate concerns are always centered on potential vulnerabilities: uninitialized memory usage, information leaks, and any changes that might introduce unexpected behavior in user-space interactions. Let's dig in. I see that `lru_zone_size` is changing from a simple array of `unsigned long` to an array of `atomic_long_t`. This is a pretty straightforward refactoring and, by itself, shouldn't pose any immediate risks.
I'm seeing helper functions introduced for setting and getting LRU generation and references from folio flags, replacing direct bit manipulations. Good. This often leads to more robust and less error-prone code. I'm also noticing the modifications to functions like `folio_update_gen`, `folio_inc_gen`, and others. The changes to `read_ctrl_pos` look reasonable. I am going to see if any user-space changes are being made.
I've carefully examined the code for any new structures being sent to user space, the presence of conditional logic that might depend on uninitialized variables, the allocation of complex data structures, and any bounds checks that could expose uninitialized memory. I'm not seeing anything that raises immediate red flags. No direct user-space copies, no complex allocations, and no obvious memory safety issues.
However, I did catch that the `mem_cgroup_get_zone_lru_size` now returns a `long val` and includes a `WARN_ON_ONCE` check. This is standard good practice, but I need to make sure this is the only use of this and that it's being properly handled. The use of `atomic_long_read` and the `lru_gen_*_flags` helper functions look like standard and safe operations.
I'm now focusing on potential information leaks or uninitialized memory use. The patch appears to be entirely internal to memory management - LRU generation, folio flags, and memcg LRU size tracking. No user-space interaction. Given this context, KASAN and standard bug detectors are sufficient for checking common issues like out-of-bounds access or use-after-free, and I don't see any changes that would require KMSAN to detect uninitialized memory.
Let's do a deeper dive into the `folio_update_gen` change. Specifically, the introduction of the `is_file` pointer:
```c
-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)
+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,
+ const vma_flags_t *vma_flags)
```
I am now making sure `is_file` is initialized and used correctly. My scan for its use in `walk_update_folio` is what I'm looking at.
```c
-static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
- struct folio *folio, int new_gen, bool dirty)
+static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
+ struct lruvec *lruvec, struct folio *folio, bool dirty)
{
- int old_gen;
+ int new_gen, old_gen, file;
...
if (walk) {
- old_gen = folio_update_gen(folio, new_gen, &vma->flags);
+ old_gen = folio_update_gen(folio, new_gen, &file, &vma->flags);
if (old_gen >= 0 && old_gen != new_gen)
- update_batch_size(walk, folio, old_gen, new_gen);
+ update_batch_size(walk, folio, old_gen, new_gen, file);
```
The new `file` variable is introduced and passed as the `is_file` parameter, so I need to know where it's being set.
If `folio_update_gen` returns `< 0`, `old_gen >= 0` is false, and `file` is never used so it should not be a problem. If `folio_update_gen`
|
| 30/3 | 2026/08/25 19:05 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch refactors the internal memory management code, specifically LRU generation and folio flags manipulation, and changes `lru_zone_size` to use `atomic_long_t`. It does not introduce any new structures that are copied to user space, nor does it allocate complex data structures or modify bounds checks in a way that could expose uninitialized memory. The newly introduced out-parameter `is_file` in `folio_update_gen` is always initialized before returning. Any potential bugs introduced by these changes (such as race conditions, use-after-free, or out-of-bounds accesses) would be effectively caught by KASAN, LOCKDEP, and other standard kernel debugging tools. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch refactors the internal memory management code, specifically LRU generation and folio flags manipulation, and changes `lru_zone_size` to use `atomic_long_t`. It does not introduce any new structures that are copied to user space, nor does it allocate complex data structures or modify bounds checks in a way that could expose uninitialized memory. The newly introduced out-parameter `is_file` in `folio_update_gen` is always initialized before returning. Any potential bugs introduced by these changes (such as race conditions, use-after-free, or out-of-bounds accesses) would be effectively caught by KASAN, LOCKDEP, and other standard kernel debugging tools. There is no risk of uninitialized memory usage or info-leaks that would necessitate a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|