| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/03 04:40 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"mem_cgroup_update_lru_size",
"lru_gen_reparent_memcg",
"lru_gen_soft_reclaim"
],
"KMSANReasoning": "The patch series refactors Multi-Gen LRU (MGLRU) folio flag operations (introducing helpers like lru_set_gen_flags, lru_set_refs_flags, folio_set_lru_refs), updates folio reference/generation tracking across reclaim and migration paths, and changes memcg lru_zone_size tracking from unsigned long to signed long with bounds checking.\n\nNo kernel-to-user data copies, ioctls, or user-facing structs are modified. Stack variables introduced (e.g., `type` in `walk_update_folio`) are initialized before being read (as `type` is only passed to `update_batch_size` when `old_gen \u003e= 0`, which guarantees assignment in `folio_update_gen`). No uninitialized heap, stack, or page allocations are exposed. Potential memory safety issues in these MM paths (such as concurrency issues or invalid folio dereferences) fall under KASAN/LOCKDEP rather than uninitialized memory detectors. Thus, dedicated KMSAN fuzzing is not needed.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies core memory management logic in Multi-Gen LRU (MGLRU), page reference counting, folio flags management, folio migration, and memcg LRU size accounting. These functional changes affect page reclamation, aging, and migration behavior in reachable core mm code paths.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/03 04:40 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 4d70d1cb7571342286f46c3b5a88d49ef649adb7\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Sep 3 04:40:48 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h\nindex 7d1c0ce189a88..86780ef65eafa 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+\tlong\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 = READ_ONCE(mz-\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..ab69b9930893f 100644\n--- a/include/linux/mm_inline.h\n+++ b/include/linux/mm_inline.h\n@@ -30,6 +30,17 @@ static inline int folio_is_file_lru(const struct folio *folio)\n \treturn !folio_test_swapbacked(folio);\n }\n \n+/**\n+ * folio_flags_is_file_lru - Should the folio be on a file LRU or anon LRU?\n+ * @flags: The folio's flags.\n+ *\n+ * Just like folio_is_file_lru but take the folio flags directly instead.\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 static __always_inline void __update_lru_size(struct lruvec *lruvec,\n \t\t\t\tenum lru_list lru, enum zone_type zid,\n \t\t\t\tlong nr_pages)\n@@ -142,10 +153,66 @@ 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_set_gen_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_set_gen_flags(unsigned long *flags, int gen)\n+{\n+\tBUILD_BUG_ON(LRU_GEN_MASK \u0026 LRU_REFS_MASK);\n+\tVM_WARN_ON_ONCE(gen \u003e= MAX_NR_GENS || gen \u003c 0);\n+\t/* Store gen offset by 1, zero means the folio is off-list. */\n+\t*flags \u0026= ~LRU_GEN_MASK;\n+\t*flags |= (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n+}\n+\n+/**\n+ * lru_get_gen_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_get_gen_flags(unsigned long flags)\n+{\n+\tint gen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\n+\t/* Exclude the legal -1 from the unsigned MAX_NR_GENS comparison */\n+\tVM_WARN_ON_ONCE(gen != -1 \u0026\u0026 gen \u003e= MAX_NR_GENS);\n+\treturn gen;\n+}\n+\n+/**\n+ * lru_set_refs_flags - Set the LRU referenced count to folio flags.\n+ * @flags: pointer to the folio flags\n+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.\n+ *\n+ * For MGLRU, PG_referenced holds the first ref, and the extra bits hold the\n+ * remaining refs. For classical LRU the extra bits are not used, so it can\n+ * also be seen as the refs count never exceeds 1. In both cases, refs == 1\n+ * means PG_referenced is set and the extra bits are zero, and refs == 0 means\n+ * PG_referenced and the extra bits are all unset.\n+ */\n+static inline void lru_set_refs_flags(unsigned long *flags, unsigned int refs)\n {\n-\tunsigned long flags = READ_ONCE(folio-\u003eflags.f);\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+/**\n+ * lru_get_refs_flags - Return LRU referenced / access count from folio flags.\n+ * @flags: folio flags\n+ *\n+ * Reads the LRU referenced count set by lru_set_refs_flags().\n+ */\n+static inline int lru_get_refs_flags(unsigned long flags)\n+{\n \tif (!(flags \u0026 BIT(PG_referenced)))\n \t\treturn 0;\n \t/*\n@@ -155,11 +222,24 @@ 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+static inline int folio_lru_refs(const struct folio *folio)\n {\n-\tunsigned long flags = READ_ONCE(folio-\u003eflags.f);\n+\treturn lru_get_refs_flags(READ_ONCE(*const_folio_flags(folio, 0)));\n+}\n \n-\treturn ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)\n+{\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n+\n+\tdo {\n+\t\tnew_flags = old_flags;\n+\t\tlru_set_refs_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_get_gen_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 +350,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 +375,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 +384,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 +425,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..fb874fe492b2b 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_get_refs_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_set_refs_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 856a7d07586cc..0a65ab8df27a9 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+\tmz-\u003elru_zone_size[zid][lru] += nr_pages;\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 fdd13299a04a9..e7d379c6e703a 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 *type,\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_get_gen_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_set_gen_flags(\u0026new_flags, new_gen);\n+\t\tlru_set_refs_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*type = 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_get_gen_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_set_gen_flags(\u0026new_flags, new_gen);\n+\t\tlru_set_refs_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, type;\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, \u0026type, \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, type);\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/09/03 04:40 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"mem_cgroup_update_lru_size",
"lru_gen_reparent_memcg",
"lru_gen_soft_reclaim"
],
"Reasoning": "The patch modifies core memory management logic in Multi-Gen LRU (MGLRU), page reference counting, folio flags management, folio migration, and memcg LRU size accounting. These functional changes affect page reclamation, aging, and migration behavior in reachable core mm code paths.",
"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 4d70d1cb7571342286f46c3b5a88d49ef649adb7
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Sep 3 04:40:48 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 7d1c0ce189a88..86780ef65eafa 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];
+ long 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 = READ_ONCE(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..ab69b9930893f 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -30,6 +30,17 @@ static inline int folio_is_file_lru(const struct folio *folio)
return !folio_test_swapbacked(folio);
}
+/**
+ * folio_flags_is_file_lru - Should the folio be on a file LRU or anon LRU?
+ * @flags: The folio's flags.
+ *
+ * Just like folio_is_file_lru but take the folio flags directly instead.
+ */
+static inline int folio_flags_is_file_lru(const unsigned long *flags)
+{
+ return !test_bit(PG_swapbacked, flags);
+}
+
static __always_inline void __update_lru_size(struct lruvec *lruvec,
enum lru_list lru, enum zone_type zid,
long nr_pages)
@@ -142,10 +153,66 @@ 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_set_gen_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_set_gen_flags(unsigned long *flags, int gen)
+{
+ BUILD_BUG_ON(LRU_GEN_MASK & LRU_REFS_MASK);
+ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS || gen < 0);
+ /* Store gen offset by 1, zero means the folio is off-list. */
+ *flags &= ~LRU_GEN_MASK;
+ *flags |= (gen + 1UL) << LRU_GEN_PGOFF;
+}
+
+/**
+ * lru_get_gen_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_get_gen_flags(unsigned long flags)
+{
+ int gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+
+ /* Exclude the legal -1 from the unsigned MAX_NR_GENS comparison */
+ VM_WARN_ON_ONCE(gen != -1 && gen >= MAX_NR_GENS);
+ return gen;
+}
+
+/**
+ * lru_set_refs_flags - Set the LRU referenced count to folio flags.
+ * @flags: pointer to the folio flags
+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.
+ *
+ * For MGLRU, PG_referenced holds the first ref, and the extra bits hold the
+ * remaining refs. For classical LRU the extra bits are not used, so it can
+ * also be seen as the refs count never exceeds 1. In both cases, refs == 1
+ * means PG_referenced is set and the extra bits are zero, and refs == 0 means
+ * PG_referenced and the extra bits are all unset.
+ */
+static inline void lru_set_refs_flags(unsigned long *flags, unsigned int refs)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ 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));
+}
+
+/**
+ * lru_get_refs_flags - Return LRU referenced / access count from folio flags.
+ * @flags: folio flags
+ *
+ * Reads the LRU referenced count set by lru_set_refs_flags().
+ */
+static inline int lru_get_refs_flags(unsigned long flags)
+{
if (!(flags & BIT(PG_referenced)))
return 0;
/*
@@ -155,11 +222,24 @@ 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)
+static inline int folio_lru_refs(const struct folio *folio)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ return lru_get_refs_flags(READ_ONCE(*const_folio_flags(folio, 0)));
+}
- return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)
+{
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+
+ do {
+ new_flags = old_flags;
+ lru_set_refs_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_get_gen_flags(READ_ONCE(*const_folio_flags(folio, 0)));
}
static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
@@ -270,7 +350,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 +375,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 +384,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 +425,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..fb874fe492b2b 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_get_refs_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_set_refs_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 856a7d07586cc..0a65ab8df27a9 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;
+ mz->lru_zone_size[zid][lru] += nr_pages;
}
/**
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 fdd13299a04a9..e7d379c6e703a 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 *type,
+ 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_get_gen_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_set_gen_flags(&new_flags, new_gen);
+ lru_set_refs_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;
+ *type = 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_get_gen_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_set_gen_flags(&new_flags, new_gen);
+ lru_set_refs_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, type;
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, &type, &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, type);
} 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/09/03 04:40 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 11622 output: 27 thoughts: 559 Thoughts: **Analyzing the Patch for Fuzzing Potential** Okay, let's break down this patch and see if it warrants some fuzzing efforts. My initial assessment is focused on identifying functional changes, figuring out the required configurations, and pinpointing key symbols that might be interesting targets. Looking at the code, it looks like this patch touches a fair number of files related to memory management and control. The primary files that stand out are `memcontrol.h`, `mm_inline.h`, `mmzone.h`, `folio.c`, `memcontrol.c`, `migrate.c` and `vmscan.c`. Specifically, I see a change in `memcontrol.h` where `lru_zone_size` in the `struct mem_cgroup_per_node` is changed from `unsigned long` to `long`. There's also a `WARN_ON_ONCE` check that might be interesting. The updates in `mm_inline.h` look like refactoring, replacing direct struct field access with `folio_flags(folio, 0)`. We also see updates to things like LRU tiers and references (`LRU_TIER_MIN`, `LRU_REFS_MAX`) defined in `mmzone.h`. `folio.c` has some changes related to LRU reference counting, and `memcontrol.c` has a simplification of `mem_cgroup_update_lru_size`. We see that `migrate.c` also calls `folio_migrate_lru_refs`. `vmscan.c` appears to be getting the most attention in terms of new functions, with a lot of LRU-related functions. Now, I'll need to dig deeper. I'll probably fire up `grepper` or a similar tool to determine the Kconfig dependencies and understand the broader context of these modifications. That will help me zero in on the parts of the code most likely to be susceptible to fuzzing. I'll need to find functions that are exposed or accessible. |
| 4/2 | 2026/09/03 04:40 | tool | grepper |
0mArgs:{
"Expression": "config LRU_GEN",
"PathPrefix": "mm/"
}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/09/03 04:40 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 12875 output: 33 thoughts: 18 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into the Kernel Configuration Landscape** Okay, so I need to get a handle on how this particular feature is being configured within the kernel. My initial instinct is to dig into the `mm/Kconfig` file. Specifically, I'm thinking around line 1400. That's usually a good starting point for memory management-related configurations, at least based on my past experience. I'm expecting to find the relevant `config` options, dependencies, maybe even some help text that outlines the purpose and implications of the feature I'm interested in. I'm going to scan that section for clues, looking for keywords that align with the specific area I'm trying to understand. From there, I'll trace through any dependencies and relationships the options might have with other parts of the configuration system, using my understanding of the Kconfig language to infer the logic. My goal is to quickly build a mental map of where this setting fits into the broader memory management framework. |
| 6/2 | 2026/09/03 04:40 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 1400,
"LineCount": 40
}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\n1420:\t\tbool \"Full stats for debugging\"\n1421:\t\tdepends on LRU_GEN\n1422:\t\thelp\n1423:\t\t Do not enable this option unless you plan to look at historical stats\n1424:\t\t from evicted generations for debugging purpose.\n1425:\t\n1426:\t\t This option has a per-memcg and per-node memory overhead.\n1427:\t\n1428:\tconfig LRU_GEN_WALKS_MMU\n1429:\t\tdef_bool y\n1430:\t\tdepends on LRU_GEN \u0026\u0026 ARCH_HAS_HW_PTE_YOUNG\n1431:\t# }\n1432:\t\n1433:\tconfig ARCH_SUPPORTS_PER_VMA_LOCK\n1434:\t def_bool n\n1435:\t\n1436:\tconfig PER_VMA_LOCK\n1437:\t\tdef_bool y\n1438:\t\tdepends on ARCH_SUPPORTS_PER_VMA_LOCK \u0026\u0026 MMU \u0026\u0026 SMP\n1439:\t\thelp\n"
} |
| 7/2 | 2026/09/03 04:40 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 13734 output: 27 thoughts: 50 Thoughts: Here's my take on the thought process: **Diving Deeper into Memory Management in the Kernel** Okay, so we're looking at these `mem_cgroup` functions, specifically `mem_cgroup_update_lru_size` and `mem_cgroup_get_zone_lru_size`. They're right there in `mm/memcontrol.c`. Since they are related to cgroups and memory, I should really circle back and make sure to double-check the Kconfig options that control MEMCG functionality. It's too easy to miss something like a dependency or a configuration flag that might be relevant here. Let's make sure we aren't overlooking any crucial settings before going further. This is a critical area, and a deep dive can be the difference between robust memory management and a subtle bug. |
| 8/2 | 2026/09/03 04:40 | tool | grepper |
0mArgs:{
"Expression": "config MEMCG",
"PathPrefix": "init/Kconfig"
}Results: {
"Output": "init/Kconfig=1075=config CGROUP_FAVOR_DYNMODS\n--\ninit/Kconfig-1084-\ninit/Kconfig:1085:config MEMCG\ninit/Kconfig-1086-\tbool \"Memory controller\"\n--\ninit/Kconfig-1093-\ninit/Kconfig:1094:config MEMCG_NMI_UNSAFE\ninit/Kconfig-1095-\tbool\n--\ninit/Kconfig-1100-\ninit/Kconfig:1101:config MEMCG_NMI_SAFETY_REQUIRES_ATOMIC\ninit/Kconfig-1102-\tbool\n--\ninit/Kconfig-1107-\ninit/Kconfig:1108:config MEMCG_V1\ninit/Kconfig-1109-\tbool \"Legacy cgroup v1 memory controller\"\n"
} |
| 9/2 | 2026/09/03 04:40 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 14218 output: 27 thoughts: 13 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `mm/migrate.c` Configurations** Okay, time to dig into the details. I need to refresh my memory on the migration configurations within `mm/migrate.c`. Specifically, I'm thinking about the parameters and flags that govern how pages are moved between zones and nodes, and the various strategies employed. I need to be sure the current configuration is optimal for the workload and hardware I'm dealing with. I'm going to pay close attention to the heuristics and thresholds related to NUMA balancing, zone reclaim, and any potential interactions with the page allocator. I'll probably start by reviewing the core data structures and functions that set these parameters and then trace their usage. I'm especially interested in understanding the trade-offs between performance and memory utilization under different migration scenarios. I also want to make sure there are no obvious lock contention issues or potential race conditions within the migration code paths. Let's see what we can find. |
| 10/2 | 2026/09/03 04:41 | tool | grepper |
0mArgs:{
"Expression": "MIGRATION",
"PathPrefix": "mm/Kconfig"
}Results: {
"Output": "mm/Kconfig=582=config MEMORY_HOTREMOVE\nmm/Kconfig-583-\tbool \"Allow for memory hot remove\"\nmm/Kconfig:584:\tselect MIGRATION\nmm/Kconfig-585-\n--\nmm/Kconfig=625=config BALLOON\n--\nmm/Kconfig-629-# support for memory balloon page migration\nmm/Kconfig:630:config BALLOON_MIGRATION\nmm/Kconfig-631-\tbool \"Allow for balloon memory migration\"\nmm/Kconfig-632-\tdefault y\nmm/Kconfig:633:\tdepends on MIGRATION \u0026\u0026 BALLOON\nmm/Kconfig-634-\thelp\n--\nmm/Kconfig=642=config COMPACTION\n--\nmm/Kconfig-644-\tdefault y\nmm/Kconfig:645:\tselect MIGRATION\nmm/Kconfig-646-\tdepends on MMU\n--\nmm/Kconfig=665=config PAGE_REPORTING\n--\nmm/Kconfig-672-\nmm/Kconfig:673:config NUMA_MIGRATION\nmm/Kconfig-674-\tbool \"NUMA page migration\"\n--\nmm/Kconfig-676-\tdepends on NUMA \u0026\u0026 MMU\nmm/Kconfig:677:\tselect MIGRATION\nmm/Kconfig-678-\thelp\n--\nmm/Kconfig-683-\nmm/Kconfig:684:config MIGRATION\nmm/Kconfig-685-\tbool\n--\nmm/Kconfig-687-\nmm/Kconfig:688:config DEVICE_MIGRATION\nmm/Kconfig:689:\tdef_bool MIGRATION \u0026\u0026 ZONE_DEVICE\nmm/Kconfig-690-\nmm/Kconfig:691:config ARCH_ENABLE_HUGEPAGE_MIGRATION\nmm/Kconfig-692-\tbool\n--\nmm/Kconfig=765=config MEMORY_FAILURE\n--\nmm/Kconfig-769-\tselect INTERVAL_TREE\nmm/Kconfig:770:\tselect MIGRATION\nmm/Kconfig-771-\thelp\n--\nmm/Kconfig=1056=config CMA\n--\nmm/Kconfig-1058-\tdepends on MMU\nmm/Kconfig:1059:\tselect MIGRATION\nmm/Kconfig-1060-\tselect MEMORY_ISOLATION\n"
} |
| 11/2 | 2026/09/03 04:41 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 15093 output: 30 thoughts: 394 Thoughts: **Fuzzing the Multi-Gen LRU Subsystem: A Deep Dive** Okay, so LRU_GEN is the Multi-Gen LRU subsystem – that’s the thing in Linux that deals with memory management, specifically in `mm/vmscan.c`, `mm/folio.c`, `include/linux/mm_inline.h`, `include/linux/mmzone.h`, and `mm/memcontrol.c`. The question is, is this functional change significant enough to warrant fuzzing? Let's break down these changes to see if they are worth the fuzzing effort. It looks like the patch is refactoring and fixing how the multi-gen LRU reference bit manipulation, generation tracking, and tier calculations are done. Specifically, this touches code related to `read_ctrl_pos`, `folio_update_gen`, `folio_inc_gen`, `walk_update_folio`, `lru_gen_look_around`, `folio_migrate_flags`, and `mem_cgroup_update_lru_size`. These are core functions that I, as an expert in this area, recognize as being at the heart of memory reclaim/page aging/migration logic in MGLRU (Multi-Gen LRU), and memcg LRU accounting. Essentially, this directly modifies critical logic for page reclamation, generation advancement, tier classification, folio flag bit compare-and-swap loops, and memcg LRU zone size calculation. This is core kernel logic that's hit during *standard* kernel execution – memory allocation, reclamation, paging, memory cgroups, and migration, all of which are common operations. So yes, absolutely, this is **definitely** worth fuzzing! Now, for the next phase: Let's focus our efforts. To determine the most impactful symbols to target, I need to figure out which functions were modified or, more importantly, are global entrypoints for this logic. I need to search through `mm/vmscan.c`, `mm/folio.c`, `mm/memcontrol.c`, and `mm/migrate.c` for any non-static functions – or any functions that call directly into the modified code. Let me take a look at `lru_gen_look_around` in `mm/vmscan.c`. Is that a global symbol? Let's check its definition and declaration to confirm. |
| 12/2 | 2026/09/03 04:41 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_look_around",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/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"
} |
| 13/2 | 2026/09/03 04:41 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16497 output: 27 thoughts: 69 Thoughts: Here's my summary, as the expert, rendered in the first person: **Diving into `lru_gen_look_around` and Migration Flags** Okay, so I'm looking at `lru_gen_look_around`. It's a non-static function, meaning it's meant to be used externally, and it's declared in `include/linux/mm_inline.h` and/or `mm/internal.h`, and, more importantly, it's called from `mm/rmap.c`. That places it firmly within the realm of the page replacement algorithm and reverse mapping. I need to understand its role within the broader context of memory management and how it interacts with the rmap code. Now, given that it's related to LRU and likely involved in some sort of page scanning or look-around mechanism for generating LRU lists, I also want to refresh my understanding of how migrations interact with the LRU. Therefore, I need to check how `folio_migrate_flags` is implemented, especially in `mm/migrate.c`. I have a feeling that `lru_gen_look_around` and the migration flags will be interwoven, especially if the code is dealing with pages in the process of migration or those potentially being targeted for migration due to the LRU eviction. Understanding this connection is critical to seeing the whole picture. |
| 14/2 | 2026/09/03 04:41 | tool | grepper |
0mArgs:{
"Expression": "folio_migrate_flags",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/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"
} |
| 15/2 | 2026/09/03 04:41 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 17201 output: 33 thoughts: 26 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into `mem_cgroup_update_lru_size`** Okay, time to dig into the guts of memory cgroup management. I need to understand how the Least Recently Used (LRU) size is updated within a memory cgroup. Let's start with `mem_cgroup_update_lru_size` in `mm/memcontrol.c`. This function is likely the central point for tracking and reflecting changes to the LRU lists used for memory reclamation within each cgroup. I'll need to see what arguments it takes, what data structures it manipulates, and where it's called from. I'm especially interested in understanding the conditions that trigger an update and how the LRU size is calculated or adjusted. Is this a global update, or specific to particular memory zones? How does it interact with the various memory stats and thresholds? Hopefully, the surrounding code will give me some clues about its purpose in the overall memory management scheme. I'm expecting this to be a core element in the memory cgroup eviction strategy. Time to get to the code. |
| 16/2 | 2026/09/03 04:41 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_update_lru_size",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/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"
} |
| 17/2 | 2026/09/03 04:41 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 18139 output: 31 thoughts: 52 Thoughts: **Investigating MGLRU Integration Points** Okay, so I need to get a better handle on how MGLRU is actually being used and integrated within the kernel's memory management system. My first step is to methodically identify any potential entry points. I'm going to scan through `mm/vmscan.c` and other files within the `mm` directory to see if there are any *non-static* functions that might be acting as access points for MGLRU. I need to be sure I'm capturing all the potential call sites. Then, I'll focus specifically on `mm/vmscan.c` and zero in on what calls *into* MGLRU are made from there. This will give me a good handle on its control flow. |
| 18/2 | 2026/09/03 04:41 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_",
"PathPrefix": "mm/vmscan.c"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 718 lines.\nUse more precise expression if possible.\n\nmm/vmscan.c=827=enum folio_references {\n--\nmm/vmscan.c-839- */\nmm/vmscan.c:840:static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\nmm/vmscan.c-841-{\n--\nmm/vmscan.c-864-#else\nmm/vmscan.c:865:static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\nmm/vmscan.c-866-{\n--\nmm/vmscan.c=871=static enum folio_references folio_check_references(struct folio *folio,\n--\nmm/vmscan.c-895-\nmm/vmscan.c:896:\tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching()) {\nmm/vmscan.c-897-\t\tif (!referenced_ptes)\n--\nmm/vmscan.c-899-\nmm/vmscan.c:900:\t\treturn lru_gen_set_refs(folio, \u0026vma_flags) ? FOLIOREF_ACTIVATE : FOLIOREF_KEEP;\nmm/vmscan.c-901-\t}\n--\nmm/vmscan.c=2283=static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-2289-\nmm/vmscan.c:2290:\tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching())\nmm/vmscan.c-2291-\t\treturn;\n--\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);\nmm/vmscan.c:2700:#define get_cap(cap)\tstatic_branch_likely(\u0026lru_gen_caps[cap])\nmm/vmscan.c-2701-#else\nmm/vmscan.c:2702:DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS);\nmm/vmscan.c:2703:#define get_cap(cap)\tstatic_branch_unlikely(\u0026lru_gen_caps[cap])\nmm/vmscan.c-2704-#endif\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=2838=static void get_item_key(void *item, int *key)\n--\nmm/vmscan.c-2847-\nmm/vmscan.c:2848:static bool test_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq,\nmm/vmscan.c-2849-\t\t\t void *item)\n--\nmm/vmscan.c-2863-\nmm/vmscan.c:2864:static void update_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq,\nmm/vmscan.c-2865-\t\t\t\tvoid *item)\n--\nmm/vmscan.c-2882-\nmm/vmscan.c:2883:static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\nmm/vmscan.c-2884-{\n--\nmm/vmscan.c-2904-\nmm/vmscan.c:2905:static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg)\nmm/vmscan.c-2906-{\nmm/vmscan.c:2907:\tstatic struct lru_gen_mm_list mm_list = {\nmm/vmscan.c-2908-\t\t.fifo = LIST_HEAD_INIT(mm_list.fifo),\n--\nmm/vmscan.c-2920-\nmm/vmscan.c:2921:static struct lru_gen_mm_state *get_mm_state(struct lruvec *lruvec)\nmm/vmscan.c-2922-{\n--\nmm/vmscan.c-2925-\nmm/vmscan.c:2926:static struct mm_struct *get_next_mm(struct lru_gen_mm_walk *walk)\nmm/vmscan.c-2927-{\n--\nmm/vmscan.c-2930-\tstruct pglist_data *pgdat = lruvec_pgdat(walk-\u003elruvec);\nmm/vmscan.c:2931:\tstruct lru_gen_mm_state *mm_state = get_mm_state(walk-\u003elruvec);\nmm/vmscan.c-2932-\n--\nmm/vmscan.c-2944-\nmm/vmscan.c:2945:void lru_gen_add_mm(struct mm_struct *mm)\nmm/vmscan.c-2946-{\n--\nmm/vmscan.c-2948-\tstruct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm);\nmm/vmscan.c:2949:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c-2950-\n--\nmm/vmscan.c-2959-\t\tstruct lruvec *lruvec = get_lruvec(memcg, nid);\nmm/vmscan.c:2960:\t\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-2961-\n--\nmm/vmscan.c-2971-\nmm/vmscan.c:2972:void lru_gen_del_mm(struct mm_struct *mm)\nmm/vmscan.c-2973-{\nmm/vmscan.c-2974-\tint nid;\nmm/vmscan.c:2975:\tstruct lru_gen_mm_list *mm_list;\nmm/vmscan.c-2976-\tstruct mem_cgroup *memcg = NULL;\n--\nmm/vmscan.c-2989-\t\tstruct lruvec *lruvec = get_lruvec(memcg, nid);\nmm/vmscan.c:2990:\t\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-2991-\n--\nmm/vmscan.c-3011-#ifdef CONFIG_MEMCG\nmm/vmscan.c:3012:void lru_gen_migrate_mm(struct mm_struct *mm)\nmm/vmscan.c-3013-{\n--\nmm/vmscan.c-3035-\nmm/vmscan.c:3036:\tlru_gen_del_mm(mm);\nmm/vmscan.c:3037:\tlru_gen_add_mm(mm);\nmm/vmscan.c-3038-}\n--\nmm/vmscan.c-3042-\nmm/vmscan.c:3043:static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg)\nmm/vmscan.c-3044-{\n--\nmm/vmscan.c-3047-\nmm/vmscan.c:3048:static struct lru_gen_mm_state *get_mm_state(struct lruvec *lruvec)\nmm/vmscan.c-3049-{\n--\nmm/vmscan.c-3052-\nmm/vmscan.c:3053:static struct mm_struct *get_next_mm(struct lru_gen_mm_walk *walk)\nmm/vmscan.c-3054-{\n--\nmm/vmscan.c-3059-\nmm/vmscan.c:3060:static void reset_mm_stats(struct lru_gen_mm_walk *walk, bool last)\nmm/vmscan.c-3061-{\n--\nmm/vmscan.c-3064-\tstruct lruvec *lruvec = walk-\u003elruvec;\nmm/vmscan.c:3065:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-3066-\n--\nmm/vmscan.c-3084-\nmm/vmscan.c:3085:static bool iterate_mm_list(struct lru_gen_mm_walk *walk, struct mm_struct **iter)\nmm/vmscan.c-3086-{\n--\nmm/vmscan.c-3091-\tstruct mem_cgroup *memcg = lruvec_memcg(lruvec);\nmm/vmscan.c:3092:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c:3093:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-3094-\n--\nmm/vmscan.c=3149=static bool iterate_mm_list_nowalk(struct lruvec *lruvec, unsigned long seq)\n--\nmm/vmscan.c-3152-\tstruct mem_cgroup *memcg = lruvec_memcg(lruvec);\nmm/vmscan.c:3153:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c:3154:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-3155-\n--\nmm/vmscan.c=3201=static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,\n--\nmm/vmscan.c-3204-\tint i;\nmm/vmscan.c:3205:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3206-\tint hist = lru_hist_from_seq(lrugen-\u003emin_seq[type]);\n--\nmm/vmscan.c=3220=static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)\n--\nmm/vmscan.c-3222-\tint hist, tier;\nmm/vmscan.c:3223:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3224-\tbool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS \u003e 1;\n--\nmm/vmscan.c=3272=static int folio_update_gen(struct folio *folio, int new_gen, int *type,\n--\nmm/vmscan.c-3292-\nmm/vmscan.c:3293:\t\t/* lru_gen_del_folio() has isolated this page? */\nmm/vmscan.c-3294-\t\tif (old_gen \u003c 0)\n--\nmm/vmscan.c=3307=static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n--\nmm/vmscan.c-3309-\tint type = folio_is_file_lru(folio);\nmm/vmscan.c:3310:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c:3311:\tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c-3312-\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n--\nmm/vmscan.c-3326-\nmm/vmscan.c:3327:\tlru_gen_update_size(lruvec, folio, old_gen, new_gen);\nmm/vmscan.c-3328-\n--\nmm/vmscan.c-3331-\nmm/vmscan.c:3332:static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,\nmm/vmscan.c-3333-\t\t\t int old_gen, int new_gen, int type)\n--\nmm/vmscan.c-3346-\nmm/vmscan.c:3347:static void reset_batch_size(struct lru_gen_mm_walk *walk)\nmm/vmscan.c-3348-{\n--\nmm/vmscan.c-3350-\tstruct lruvec *lruvec = lruvec_live_lock_irq(walk-\u003elruvec);\nmm/vmscan.c:3351:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3352-\n--\nmm/vmscan.c-3365-\nmm/vmscan.c:3366:\t\tif (lru_gen_is_active(lruvec, gen))\nmm/vmscan.c-3367-\t\t\tlru += LRU_ACTIVE;\n--\nmm/vmscan.c=3374=static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *args)\n--\nmm/vmscan.c-3377-\tstruct vm_area_struct *vma = args-\u003evma;\nmm/vmscan.c:3378:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3379-\n--\nmm/vmscan.c=3512=static bool suitable_to_scan(int total, int young)\n--\nmm/vmscan.c-3519-\nmm/vmscan.c:3520:static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,\nmm/vmscan.c-3521-\t\tstruct lruvec *lruvec, struct folio *folio, bool dirty)\n--\nmm/vmscan.c-3527-\nmm/vmscan.c:3528:\tnew_gen = lru_gen_from_seq(READ_ONCE(lruvec-\u003elrugen.max_seq));\nmm/vmscan.c-3529-\n--\nmm/vmscan.c-3538-\t\t\tupdate_batch_size(walk, folio, old_gen, new_gen, type);\nmm/vmscan.c:3539:\t} else if (lru_gen_set_refs(folio, \u0026vma-\u003eflags)) {\nmm/vmscan.c-3540-\t\told_gen = folio_lru_gen(folio);\n--\nmm/vmscan.c=3546=static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n--\nmm/vmscan.c-3556-\tstruct folio *last = NULL;\nmm/vmscan.c:3557:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3558-\tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n--\nmm/vmscan.c=3635=static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,\n--\nmm/vmscan.c-3642-\tstruct folio *last = NULL;\nmm/vmscan.c:3643:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3644-\tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n--\nmm/vmscan.c=3721=static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,\n--\nmm/vmscan.c-3730-\tunsigned long first = -1;\nmm/vmscan.c:3731:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c:3732:\tstruct lru_gen_mm_state *mm_state = get_mm_state(walk-\u003elruvec);\nmm/vmscan.c-3733-\n--\nmm/vmscan.c=3794=static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end,\n--\nmm/vmscan.c-3800-\tunsigned long next;\nmm/vmscan.c:3801:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3802-\n--\nmm/vmscan.c-3835-\nmm/vmscan.c:3836:static void walk_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk)\nmm/vmscan.c-3837-{\n--\nmm/vmscan.c-3870-\nmm/vmscan.c:3871:static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat, bool force_alloc)\nmm/vmscan.c-3872-{\nmm/vmscan.c:3873:\tstruct lru_gen_mm_walk *walk = current-\u003ereclaim_state-\u003emm_walk;\nmm/vmscan.c-3874-\n--\nmm/vmscan.c=3891=static void clear_mm_walk(void)\nmm/vmscan.c-3892-{\nmm/vmscan.c:3893:\tstruct lru_gen_mm_walk *walk = current-\u003ereclaim_state-\u003emm_walk;\nmm/vmscan.c-3894-\n--\nmm/vmscan.c=3904=static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)\n--\nmm/vmscan.c-3907-\tint remaining = MAX_LRU_BATCH;\nmm/vmscan.c:3908:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3909-\tint hist = lru_hist_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c:3910:\tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c-3911-\n--\nmm/vmscan.c=3957=static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-3960-\tbool seq_inc_flag = false;\nmm/vmscan.c:3961:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3962-\tDEFINE_MIN_SEQ(lruvec);\n--\nmm/vmscan.c-3968-\t\twhile (min_seq[type] + MIN_NR_GENS \u003c= lrugen-\u003emax_seq) {\nmm/vmscan.c:3969:\t\t\tgen = lru_gen_from_seq(min_seq[type]);\nmm/vmscan.c-3970-\n--\nmm/vmscan.c-3989-\nmm/vmscan.c:3990:\t/* see the comment on lru_gen_folio */\nmm/vmscan.c-3991-\tif (swappiness \u0026\u0026 swappiness \u003c= MAX_SWAPPINESS) {\n--\nmm/vmscan.c=4009=static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness)\n--\nmm/vmscan.c-4013-\tint type, zone;\nmm/vmscan.c:4014:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4015-restart:\n--\nmm/vmscan.c-4044-\t */\nmm/vmscan.c:4045:\tprev = lru_gen_from_seq(lrugen-\u003emax_seq - 1);\nmm/vmscan.c:4046:\tnext = lru_gen_from_seq(lrugen-\u003emax_seq + 1);\nmm/vmscan.c-4047-\n--\nmm/vmscan.c=4074=static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long seq,\n--\nmm/vmscan.c-4077-\tbool success;\nmm/vmscan.c:4078:\tstruct lru_gen_mm_walk *walk;\nmm/vmscan.c-4079-\tstruct mm_struct *mm = NULL;\nmm/vmscan.c:4080:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c:4081:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-4082-\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=4158=static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-4161-\tunsigned long seq, total = 0;\nmm/vmscan.c:4162:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4163-\tDEFINE_MAX_SEQ(lruvec);\n--\nmm/vmscan.c-4167-\t\tfor (seq = min_seq[type]; seq \u003c= max_seq; seq++) {\nmm/vmscan.c:4168:\t\t\tgen = lru_gen_from_seq(seq);\nmm/vmscan.c-4169-\t\t\tfor (zone = 0; zone \u003c MAX_NR_ZONES; zone++)\n--\nmm/vmscan.c=4189=static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,\n--\nmm/vmscan.c-4203-\nmm/vmscan.c:4204:\tgen = lru_gen_from_seq(evictable_min_seq(min_seq, swappiness));\nmm/vmscan.c-4205-\tbirth = READ_ONCE(lruvec-\u003elrugen.timestamps[gen]);\n--\nmm/vmscan.c-4210-/* to protect the working set of the last N jiffies */\nmm/vmscan.c:4211:static unsigned long lru_gen_min_ttl __read_mostly;\nmm/vmscan.c-4212-\nmm/vmscan.c:4213:static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-4214-{\nmm/vmscan.c-4215-\tstruct mem_cgroup *memcg;\nmm/vmscan.c:4216:\tunsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);\nmm/vmscan.c-4217-\tbool reclaimable = !min_ttl;\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--\nmm/vmscan.c-4265-\tunsigned long end;\nmm/vmscan.c:4266:\tstruct lru_gen_mm_walk *walk;\nmm/vmscan.c-4267-\tstruct folio *last = NULL;\n--\nmm/vmscan.c-4275-\tstruct lruvec *lruvec;\nmm/vmscan.c:4276:\tstruct lru_gen_mm_state *mm_state;\nmm/vmscan.c-4277-\n--\nmm/vmscan.c=4375=enum {\n--\nmm/vmscan.c-4382-\nmm/vmscan.c:4383:static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)\nmm/vmscan.c-4384-{\n--\nmm/vmscan.c-4430-\nmm/vmscan.c:4431:void lru_gen_online_memcg(struct mem_cgroup *memcg)\nmm/vmscan.c-4432-{\n--\nmm/vmscan.c-4455-\nmm/vmscan.c:4456:void lru_gen_offline_memcg(struct mem_cgroup *memcg)\nmm/vmscan.c-4457-{\n--\nmm/vmscan.c-4462-\nmm/vmscan.c:4463:\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);\nmm/vmscan.c-4464-\t}\n--\nmm/vmscan.c-4466-\nmm/vmscan.c:4467:void lru_gen_release_memcg(struct mem_cgroup *memcg)\nmm/vmscan.c-4468-{\n--\nmm/vmscan.c-4492-\nmm/vmscan.c:4493:void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid)\nmm/vmscan.c-4494-{\n--\nmm/vmscan.c-4498-\tif (READ_ONCE(lruvec-\u003elrugen.seg) != MEMCG_LRU_HEAD)\nmm/vmscan.c:4499:\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);\nmm/vmscan.c-4500-}\nmm/vmscan.c-4501-\nmm/vmscan.c:4502:bool recheck_lru_gen_max_memcg(struct mem_cgroup *memcg, int nid)\nmm/vmscan.c-4503-{\n--\nmm/vmscan.c=4515=static void try_to_inc_max_seq_nowalk(struct mem_cgroup *memcg,\n--\nmm/vmscan.c-4517-{\nmm/vmscan.c:4518:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c:4519:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-4520-\tint swappiness = mem_cgroup_swappiness(memcg);\n--\nmm/vmscan.c-4548- */\nmm/vmscan.c:4549:void max_lru_gen_memcg(struct mem_cgroup *memcg, int nid)\nmm/vmscan.c-4550-{\n--\nmm/vmscan.c-4575- * generations on an LRU list.\nmm/vmscan.c:4576: * 4. In lru_gen_del_folio(), the generation to which the folio belongs is\nmm/vmscan.c-4577- * found based on the generation information in folio-\u003eflags, and the\n--\nmm/vmscan.c-4579- * the lru size correctly during reparenting, otherwise the lru size may\nmm/vmscan.c:4580: * be updated incorrectly in lru_gen_del_folio().\nmm/vmscan.c-4581- *\n--\nmm/vmscan.c-4589- */\nmm/vmscan.c:4590:static void __lru_gen_reparent_memcg(struct lruvec *child_lruvec, struct lruvec *parent_lruvec,\nmm/vmscan.c-4591-\t\t\t\t int zone, int type)\nmm/vmscan.c-4592-{\nmm/vmscan.c:4593:\tstruct lru_gen_folio *child_lrugen, *parent_lrugen;\nmm/vmscan.c-4594-\tenum lru_list lru = type * LRU_INACTIVE_FILE;\n--\nmm/vmscan.c-4600-\tfor (i = 0; i \u003c get_nr_gens(child_lruvec, type); i++) {\nmm/vmscan.c:4601:\t\tint gen = lru_gen_from_seq(child_lrugen-\u003emax_seq - i);\nmm/vmscan.c-4602-\t\tlong nr_pages = child_lrugen-\u003enr_pages[gen][type][zone];\nmm/vmscan.c:4603:\t\tint child_lru_active = lru_gen_is_active(child_lruvec, gen) ? LRU_ACTIVE : 0;\nmm/vmscan.c:4604:\t\tint parent_lru_active = lru_gen_is_active(parent_lruvec, gen) ? LRU_ACTIVE : 0;\nmm/vmscan.c-4605-\n--\nmm/vmscan.c-4613-\nmm/vmscan.c:4614:\t\tif (lru_gen_is_active(child_lruvec, gen) != lru_gen_is_active(parent_lruvec, gen)) {\nmm/vmscan.c-4615-\t\t\t__update_lru_size(child_lruvec, lru + child_lru_active, zone, -nr_pages);\n--\nmm/vmscan.c-4620-\nmm/vmscan.c:4621:void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid)\nmm/vmscan.c-4622-{\n--\nmm/vmscan.c-4632-\t\tfor (type = 0; type \u003c ANON_AND_FILE; type++)\nmm/vmscan.c:4633:\t\t\t__lru_gen_reparent_memcg(child_lruvec, parent_lruvec, zid, type);\nmm/vmscan.c-4634-\n--\nmm/vmscan.c=4655=static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc,\n--\nmm/vmscan.c-4664-\tint tier = lru_tier_from_refs(refs, workingset);\nmm/vmscan.c:4665:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4666-\n--\nmm/vmscan.c-4673-\t/* promoted */\nmm/vmscan.c:4674:\tif (gen != lru_gen_from_seq(lrugen-\u003emin_seq[type])) {\nmm/vmscan.c-4675-\t\tlist_move(\u0026folio-\u003elru, \u0026lrugen-\u003efolios[gen][type][zone]);\n--\nmm/vmscan.c=4704=static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc)\n--\nmm/vmscan.c-4721-\nmm/vmscan.c:4722:\tsuccess = lru_gen_del_folio(lruvec, folio, true);\nmm/vmscan.c-4723-\tVM_WARN_ON_ONCE_FOLIO(!success, folio);\n--\nmm/vmscan.c=4728=static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4739-\tunsigned long remaining = nr_to_scan;\nmm/vmscan.c:4740:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4741-\n--\nmm/vmscan.c-4747-\nmm/vmscan.c:4748:\tgen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c-4749-\n--\nmm/vmscan.c=4878=static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4886-\tstruct reclaim_stat stat;\nmm/vmscan.c:4887:\tstruct lru_gen_mm_walk *walk;\nmm/vmscan.c-4888-\tint scanned, reclaimed;\n--\nmm/vmscan.c-4937-\t\t/* don't add rejected folios to the oldest generation */\nmm/vmscan.c:4938:\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {\nmm/vmscan.c-4939-\t\t\tfolio_set_lru_refs(folio, 0);\n--\nmm/vmscan.c=5102=static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5109-\nmm/vmscan.c:5110:\t/* lru_gen_age_node() called mem_cgroup_calculate_protection() */\nmm/vmscan.c-5111-\tif (mem_cgroup_below_min(NULL, memcg))\n--\nmm/vmscan.c=5143=static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5149-\tstruct lruvec *lruvec;\nmm/vmscan.c:5150:\tstruct lru_gen_folio *lrugen;\nmm/vmscan.c-5151-\tstruct mem_cgroup *memcg;\n--\nmm/vmscan.c-5163-\t\tif (op) {\nmm/vmscan.c:5164:\t\t\tlru_gen_rotate_memcg(lruvec, op);\nmm/vmscan.c-5165-\t\t\top = 0;\n--\nmm/vmscan.c-5177-\t\tif (!mem_cgroup_tryget(memcg)) {\nmm/vmscan.c:5178:\t\t\tlru_gen_release_memcg(memcg);\nmm/vmscan.c-5179-\t\t\tmemcg = NULL;\n--\nmm/vmscan.c-5195-\tif (op)\nmm/vmscan.c:5196:\t\tlru_gen_rotate_memcg(lruvec, op);\nmm/vmscan.c-5197-\n--\nmm/vmscan.c-5202-\nmm/vmscan.c:5203:\t/* restart if raced with lru_gen_rotate_memcg() */\nmm/vmscan.c-5204-\tif (gen != get_nulls_value(pos))\n--\nmm/vmscan.c-5212-\nmm/vmscan.c:5213:static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\nmm/vmscan.c-5214-{\n--\nmm/vmscan.c-5226-\tif (try_to_shrink_lruvec(lruvec, sc))\nmm/vmscan.c:5227:\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);\nmm/vmscan.c-5228-\n--\nmm/vmscan.c-5233-\nmm/vmscan.c:5234:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-5235-{\n--\nmm/vmscan.c=5280=static bool __maybe_unused state_is_valid(struct lruvec *lruvec)\nmm/vmscan.c-5281-{\nmm/vmscan.c:5282:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-5283-\n--\nmm/vmscan.c=5303=static bool fill_evictable(struct lruvec *lruvec)\n--\nmm/vmscan.c-5317-\t\t\t/*\nmm/vmscan.c:5318:\t\t\t * lru_gen_add_folio() uses list_add_tail() rather\nmm/vmscan.c-5319-\t\t\t * than list_add() when reclaiming is true. Match\n--\nmm/vmscan.c-5336-\t\t\t */\nmm/vmscan.c:5337:\t\t\tsuccess = lru_gen_add_folio(lruvec, folio, !active);\nmm/vmscan.c-5338-\t\t\tVM_WARN_ON_ONCE(!success);\n--\nmm/vmscan.c=5348=static bool drain_evictable(struct lruvec *lruvec)\n--\n\n"
} |
| 19/2 | 2026/09/03 04:41 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28276 output: 170 thoughts: 812 Thoughts: **Focusing on Multi-Gen LRU Functionality** Okay, here's what I've got so far. I need to identify key kernel functions for fuzzing related to this Multi-Gen LRU work, and also figure out the necessary configurations. I'm avoiding generic hot-path functions to keep the testing focused. Based on the requirements, I'm focusing on non-static, non-inlined API entrypoints. First, let's look at the functions that are relevant and meet those criteria. I identified the following functions which are likely entrypoints to the multi-gen LRU functionality: * `lru_gen_look_around` (in `mm/vmscan.c`) - Looks promising. * `folio_migrate_flags` (in `mm/migrate.c`) - Seems relevant to page migration. * `mem_cgroup_update_lru_size` (in `mm/memcontrol.c`) - This looks important for memory cgroup integration. * `lru_gen_reparent_memcg` (in `mm/vmscan.c`) - Likely relevant to memcg operations. * `lru_gen_soft_reclaim` (in `mm/vmscan.c`) - This is probably a good entrypoint. Now, regarding configurations, it seems clear that `CONFIG_LRU_GEN` is essential, as is `CONFIG_LRU_GEN_ENABLED` because it's enabled by default. Given the functions I've identified, `CONFIG_MEMCG` is required for the memory cgroup aspect and also `CONFIG_MIGRATION` is needed for `folio_migrate_flags` and related functions. I need to make sure I don't miss anything that these depend on. Looking at the Kconfig files, I see that: * `LRU_GEN` depends on `MMU`, and also `64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP` (I'll need to remember those for building configurations). * `LRU_GEN_ENABLED` depends on `LRU_GEN`. * `MEMCG` depends on `CGROUPS`. * And finally, `NUMA_MIGRATION` or `MIGRATION` is needed. `COMPACTION` is generally selected. So, I will add these functions into the `FocusSymbols` array: * `lru_gen_look_around` * `folio_migrate_flags` * `mem_cgroup_update_lru_size` * `lru_gen_reparent_memcg` * `lru_gen_soft_reclaim` And these configurations to the `EnableConfigs` array: * LRU_GEN * LRU_GEN_ENABLED * MEMCG * MIGRATION |
| 20/2 | 2026/09/03 04:41 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"mem_cgroup_update_lru_size",
"lru_gen_reparent_memcg",
"lru_gen_soft_reclaim"
],
"Reasoning": "The patch modifies core memory management logic in Multi-Gen LRU (MGLRU), page reference counting, folio flags management, folio migration, and memcg LRU size accounting. These functional changes affect page reclamation, aging, and migration behavior in reachable core mm code paths.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"mem_cgroup_update_lru_size",
"lru_gen_reparent_memcg",
"lru_gen_soft_reclaim"
],
"Reasoning": "The patch modifies core memory management logic in Multi-Gen LRU (MGLRU), page reference counting, folio flags management, folio migration, and memcg LRU size accounting. These functional changes affect page reclamation, aging, and migration behavior in reachable core mm code paths.",
"WorthFuzzing": true
} |
| 21/1 | 2026/09/03 04:41 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 22/2 | 2026/09/03 04:41 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch series refactors Multi-Gen LRU (MGLRU) folio flag operations (introducing helpers like lru_set_gen_flags, lru_set_refs_flags, folio_set_lru_refs), updates folio reference/generation tracking across reclaim and migration paths, and changes memcg lru_zone_size tracking from unsigned long to signed long with bounds checking.\n\nNo kernel-to-user data copies, ioctls, or user-facing structs are modified. Stack variables introduced (e.g., `type` in `walk_update_folio`) are initialized before being read (as `type` is only passed to `update_batch_size` when `old_gen \u003e= 0`, which guarantees assignment in `folio_update_gen`). No uninitialized heap, stack, or page allocations are exposed. Potential memory safety issues in these MM paths (such as concurrency issues or invalid folio dereferences) fall under KASAN/LOCKDEP rather than uninitialized memory detectors. Thus, dedicated KMSAN fuzzing is not needed.",
"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 4d70d1cb7571342286f46c3b5a88d49ef649adb7
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Sep 3 04:40:48 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 7d1c0ce189a88..86780ef65eafa 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];
+ long 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 = READ_ONCE(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..ab69b9930893f 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -30,6 +30,17 @@ static inline int folio_is_file_lru(const struct folio *folio)
return !folio_test_swapbacked(folio);
}
+/**
+ * folio_flags_is_file_lru - Should the folio be on a file LRU or anon LRU?
+ * @flags: The folio's flags.
+ *
+ * Just like folio_is_file_lru but take the folio flags directly instead.
+ */
+static inline int folio_flags_is_file_lru(const unsigned long *flags)
+{
+ return !test_bit(PG_swapbacked, flags);
+}
+
static __always_inline void __update_lru_size(struct lruvec *lruvec,
enum lru_list lru, enum zone_type zid,
long nr_pages)
@@ -142,10 +153,66 @@ 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_set_gen_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_set_gen_flags(unsigned long *flags, int gen)
+{
+ BUILD_BUG_ON(LRU_GEN_MASK & LRU_REFS_MASK);
+ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS || gen < 0);
+ /* Store gen offset by 1, zero means the folio is off-list. */
+ *flags &= ~LRU_GEN_MASK;
+ *flags |= (gen + 1UL) << LRU_GEN_PGOFF;
+}
+
+/**
+ * lru_get_gen_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_get_gen_flags(unsigned long flags)
+{
+ int gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+
+ /* Exclude the legal -1 from the unsigned MAX_NR_GENS comparison */
+ VM_WARN_ON_ONCE(gen != -1 && gen >= MAX_NR_GENS);
+ return gen;
+}
+
+/**
+ * lru_set_refs_flags - Set the LRU referenced count to folio flags.
+ * @flags: pointer to the folio flags
+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.
+ *
+ * For MGLRU, PG_referenced holds the first ref, and the extra bits hold the
+ * remaining refs. For classical LRU the extra bits are not used, so it can
+ * also be seen as the refs count never exceeds 1. In both cases, refs == 1
+ * means PG_referenced is set and the extra bits are zero, and refs == 0 means
+ * PG_referenced and the extra bits are all unset.
+ */
+static inline void lru_set_refs_flags(unsigned long *flags, unsigned int refs)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ 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));
+}
+
+/**
+ * lru_get_refs_flags - Return LRU referenced / access count from folio flags.
+ * @flags: folio flags
+ *
+ * Reads the LRU referenced count set by lru_set_refs_flags().
+ */
+static inline int lru_get_refs_flags(unsigned long flags)
+{
if (!(flags & BIT(PG_referenced)))
return 0;
/*
@@ -155,11 +222,24 @@ 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)
+static inline int folio_lru_refs(const struct folio *folio)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ return lru_get_refs_flags(READ_ONCE(*const_folio_flags(folio, 0)));
+}
- return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)
+{
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+
+ do {
+ new_flags = old_flags;
+ lru_set_refs_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_get_gen_flags(READ_ONCE(*const_folio_flags(folio, 0)));
}
static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
@@ -270,7 +350,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 +375,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 +384,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 +425,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..fb874fe492b2b 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_get_refs_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_set_refs_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 856a7d07586cc..0a65ab8df27a9 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;
+ mz->lru_zone_size[zid][lru] += nr_pages;
}
/**
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 fdd13299a04a9..e7d379c6e703a 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 *type,
+ 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_get_gen_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_set_gen_flags(&new_flags, new_gen);
+ lru_set_refs_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;
+ *type = 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_get_gen_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_set_gen_flags(&new_flags, new_gen);
+ lru_set_refs_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, type;
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, &type, &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, type);
} 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);
|
| 23/3 | 2026/09/03 04:41 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 11619 output: 248 thoughts: 3020 Thoughts: **KMSAN Evaluation: Patch Series Analysis** Alright, let's break down this patch series and determine if it warrants a dedicated KMSAN fuzzing session. My focus is always on potential uninitialized memory issues, especially those that could lead to information leaks or exploitable vulnerabilities. First, I've meticulously examined the file modifications. The changes are concentrated in `memcontrol.h`, `mm_inline.h`, `mmzone.h`, `folio.c`, `memcontrol.c`, `migrate.c`, and `vmscan.c`. It's a mix of data structure adjustments and function refactoring related to the Multi-Gen LRU (MGLRU) implementation, folio flags, and memory control groups. The key structural change is in `mem_cgroup_per_node`, where `lru_zone_size` is changed from `unsigned long` to `long`. There are additions of inline functions for flag management and updates to several LRU-related functions. I've specifically scrutinized `walk_update_folio` and its call to `folio_update_gen`. The potential issue was whether the `type` variable, passed to `update_batch_size`, might be uninitialized if `folio_update_gen` returned an early exit. However, after careful review, I've confirmed that this isn't the case. The `if` condition correctly handles the scenario where the early return of `-1` occurs, preventing the use of an uninitialized `type`. I've also checked whether there are further usages of other variables within `walk_update_folio`, finding that none present any risk. I followed the same principle for `read_ctrl_pos` and for `mem_cgroup_get_zone_lru_size`. The `lru_set_refs_flags`, `lru_get_refs_flags`, `folio_set_lru_refs`, `lru_set_gen_flags`, and `lru_get_gen_flags` functions are all manipulating the `folio->flags`, which are `unsigned long` fields. I've confirmed that there's no risk of any uninitialized reads. Crucially, I've found no evidence of any data structures being sent to user space or any new kernel interfaces being exposed or modified. There are no allocations or initializations of complex data structures where uninitialized fields could later be read. The changes deal with the LRU logic, which is critical, but I don't see any direct information leakage risks. Therefore, after thoroughly analyzing these changes, I'm confident that this patch series does not introduce any risks that warrant a dedicated KMSAN fuzzing session. Standard kernel sanitizers like KASAN, and lockdep, should catch any memory safety issues in this area. KMSAN is overkill for this specific set of changes. This patch is a refactoring, and a change to a variable's data type, and this should be tested in the normal manner. |
| 24/3 | 2026/09/03 04:41 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series refactors Multi-Gen LRU (MGLRU) folio flag operations (introducing helpers like lru_set_gen_flags, lru_set_refs_flags, folio_set_lru_refs), updates folio reference/generation tracking across reclaim and migration paths, and changes memcg lru_zone_size tracking from unsigned long to signed long with bounds checking.\n\nNo kernel-to-user data copies, ioctls, or user-facing structs are modified. Stack variables introduced (e.g., `type` in `walk_update_folio`) are initialized before being read (as `type` is only passed to `update_batch_size` when `old_gen \u003e= 0`, which guarantees assignment in `folio_update_gen`). No uninitialized heap, stack, or page allocations are exposed. Potential memory safety issues in these MM paths (such as concurrency issues or invalid folio dereferences) fall under KASAN/LOCKDEP rather than uninitialized memory detectors. Thus, dedicated KMSAN fuzzing is not needed.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series refactors Multi-Gen LRU (MGLRU) folio flag operations (introducing helpers like lru_set_gen_flags, lru_set_refs_flags, folio_set_lru_refs), updates folio reference/generation tracking across reclaim and migration paths, and changes memcg lru_zone_size tracking from unsigned long to signed long with bounds checking.\n\nNo kernel-to-user data copies, ioctls, or user-facing structs are modified. Stack variables introduced (e.g., `type` in `walk_update_folio`) are initialized before being read (as `type` is only passed to `update_batch_size` when `old_gen \u003e= 0`, which guarantees assignment in `folio_update_gen`). No uninitialized heap, stack, or page allocations are exposed. Potential memory safety issues in these MM paths (such as concurrency issues or invalid folio dereferences) fall under KASAN/LOCKDEP rather than uninitialized memory detectors. Thus, dedicated KMSAN fuzzing is not needed.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|