| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/24 17:58 | flow | patch-triage |
43mArgs:null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"LRU_GEN_STATS",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"try_to_free_pages",
"try_to_free_mem_cgroup_pages",
"folio_deactivate",
"deactivate_file_folio",
"folio_mark_lazyfree",
"migrate_pages"
],
"KMSANReasoning": "The patch modifies internal memory management code related to LRU generation and references tracking in folio flags, and changes `lru_zone_size` to use atomic operations. It does not introduce any new structures exposed to user space, nor does it alter control flow in a way that could expose uninitialized memory. The local variable `file` in `walk_update_folio` is passed by reference to `folio_update_gen`, and although it might not be initialized on early returns, it is only accessed when `old_gen \u003e= 0`, which guarantees it has been initialized. Therefore, there is no risk of uninitialized memory usage or info-leaks, and standard KASAN/LOCKDEP testing is sufficient.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies the Multi-Gen LRU (MGLRU) implementation in the core memory management subsystem. It changes how LRU zone sizes are updated (using atomic operations) and refactors LRU generation and reference flag manipulation into inline helpers. It also updates several core MGLRU functions like `folio_update_gen`, `folio_inc_gen`, `lru_gen_look_around`, and `evict_folios`. These changes affect page reclaim, page migration, and memory cgroup accounting. The code is reachable through memory pressure (reclaim), page migration, and madvise/fadvise syscalls.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/24 17:58 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 5dd612268d61060285ff77f66c43f609c311f380\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Aug 24 17:58:23 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h\nindex 215e2e87f42b2..7b89d0cb5f6c6 100644\n--- a/include/linux/memcontrol.h\n+++ b/include/linux/memcontrol.h\n@@ -113,7 +113,7 @@ struct mem_cgroup_per_node {\n \t/* Fields which get updated often at the end. */\n \tstruct lruvec\t\tlruvec;\n \tCACHELINE_PADDING(_pad2_);\n-\tunsigned long\t\tlru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];\n+\tatomic_long_t\t\tlru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];\n \tstruct mem_cgroup_reclaim_iter\titer;\n \n \t/*\n@@ -902,10 +902,15 @@ static inline\n unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,\n \t\tenum lru_list lru, int zone_idx)\n {\n+\tlong val;\n \tstruct mem_cgroup_per_node *mz;\n \n \tmz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);\n-\treturn READ_ONCE(mz-\u003elru_zone_size[zone_idx][lru]);\n+\tval = atomic_long_read(\u0026mz-\u003elru_zone_size[zone_idx][lru]);\n+\tif (WARN_ON_ONCE(val \u003c 0))\n+\t\treturn 0;\n+\n+\treturn val;\n }\n \n void __mem_cgroup_handle_over_high(gfp_t gfp_mask);\ndiff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h\nindex 621c8653d8f7e..8cf82989ec263 100644\n--- a/include/linux/mm_inline.h\n+++ b/include/linux/mm_inline.h\n@@ -10,6 +10,11 @@\n #include \u003clinux/userfaultfd_k.h\u003e\n #include \u003clinux/leafops.h\u003e\n \n+static inline int folio_flags_is_file_lru(const unsigned long *flags)\n+{\n+\treturn !test_bit(PG_swapbacked, flags);\n+}\n+\n /**\n * folio_is_file_lru - Should the folio be on a file LRU or anon LRU?\n * @folio: The folio to test.\n@@ -27,7 +32,7 @@\n */\n static inline int folio_is_file_lru(const struct folio *folio)\n {\n-\treturn !folio_test_swapbacked(folio);\n+\treturn folio_flags_is_file_lru(const_folio_flags(folio, 0));\n }\n \n static __always_inline void __update_lru_size(struct lruvec *lruvec,\n@@ -142,10 +147,42 @@ static inline int lru_tier_from_refs(int refs, bool workingset)\n \treturn workingset ? MAX_NR_TIERS - 1 : order_base_2(refs);\n }\n \n-static inline int folio_lru_refs(const struct folio *folio)\n+/**\n+ * lru_gen_from_flags - Return the LRU generation number from folio flags.\n+ * @flags: folio flags\n+ *\n+ * Returns: A number between 0 and (MAX_NR_GENS - 1), inclusive. Returns\n+ * -1 if the flags indicate the folio is off the list (e.g., isolated).\n+ */\n+static inline int lru_gen_from_flags(unsigned long flags)\n+{\n+\tint gen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF);\n+\n+\tBUILD_BUG_ON(LRU_GEN_MASK \u0026 LRU_REFS_MASK);\n+\tgen -= 1;\n+\tVM_WARN_ON_ONCE(gen != -1 \u0026\u0026 gen \u003e= MAX_NR_GENS);\n+\treturn gen;\n+}\n+\n+/**\n+ * lru_gen_set_flags - Set the LRU generation number to specified folio flags.\n+ * @flags: pointer to the folio flags\n+ * @gen: generation number, between 0 and (MAX_NR_GENS - 1), inclusive.\n+ */\n+static inline void lru_gen_set_flags(unsigned long *flags, int gen)\n {\n-\tunsigned long flags = READ_ONCE(folio-\u003eflags.f);\n+\tVM_WARN_ON_ONCE(gen \u003e= MAX_NR_GENS || gen \u003c 0);\n+\n+\t*flags \u0026= ~LRU_GEN_MASK;\n+\t*flags |= (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n+}\n \n+/**\n+ * lru_refs_from_flags - Return LRU referenced / access count from folio flags.\n+ * @flags: folio flags\n+ */\n+static inline int lru_refs_from_flags(unsigned long flags)\n+{\n \tif (!(flags \u0026 BIT(PG_referenced)))\n \t\treturn 0;\n \t/*\n@@ -155,11 +192,40 @@ static inline int folio_lru_refs(const struct folio *folio)\n \treturn ((flags \u0026 LRU_REFS_MASK) \u003e\u003e LRU_REFS_PGOFF) + 1;\n }\n \n-static inline int folio_lru_gen(const struct folio *folio)\n+/**\n+ * lru_refs_set_flags - Set the LRU referenced / access count to specified folio flags.\n+ * @flags: pointer to the folio flags\n+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.\n+ */\n+static inline void lru_refs_set_flags(unsigned long *flags, unsigned int refs)\n+{\n+\tVM_WARN_ON_ONCE(refs \u003e LRU_REFS_MAX);\n+\tBUILD_BUG_ON(LRU_REFS_MAX != (LRU_REFS_MASK \u003e\u003e LRU_REFS_PGOFF) + 1);\n+\n+\t*flags \u0026= ~LRU_REFS_FLAGS;\n+\tif (!refs)\n+\t\treturn;\n+\t*flags |= (BIT(PG_referenced) | ((refs - 1UL) \u003c\u003c LRU_REFS_PGOFF));\n+}\n+\n+static inline int folio_lru_refs(const struct folio *folio)\n+{\n+\treturn lru_refs_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));\n+}\n+\n+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)\n {\n-\tunsigned long flags = READ_ONCE(folio-\u003eflags.f);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n \n-\treturn ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\tdo {\n+\t\tnew_flags = old_flags;\n+\t\tlru_refs_set_flags(\u0026new_flags, refs);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n+}\n+\n+static inline int folio_lru_gen(const struct folio *folio)\n+{\n+\treturn lru_gen_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));\n }\n \n static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)\n@@ -270,7 +336,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,\n \tgen = lru_gen_from_seq(seq);\n \tflags = (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n \t/* see the comment on MIN_NR_GENS about PG_active */\n-\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_GEN_MASK | BIT(PG_active), flags);\n+\tset_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);\n \n \tlru_gen_update_size(lruvec, folio, -1, gen);\n \t/* for folio_rotate_reclaimable() */\n@@ -295,7 +361,7 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,\n \n \t/* for folio_migrate_flags() */\n \tflags = !reclaiming \u0026\u0026 lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;\n-\tflags = set_mask_bits(\u0026folio-\u003eflags.f, LRU_GEN_MASK, flags);\n+\tflags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);\n \tgen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n \n \tlru_gen_update_size(lruvec, folio, gen, -1);\n@@ -304,11 +370,19 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,\n \treturn true;\n }\n \n-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)\n+/**\n+ * folio_migrate_lru_refs - copy the reference state to a new folio\n+ * @new: the destination folio\n+ * @old: the source folio\n+ *\n+ * Transfer the reference state to @new during migration: the MGLRU\n+ * refs count, including PG_referenced, or just PG_referenced for the\n+ * active/inactive LRU.\n+ */\n+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n {\n-\tunsigned long refs = READ_ONCE(old-\u003eflags.f) \u0026 LRU_REFS_MASK;\n-\n-\tset_mask_bits(\u0026new-\u003eflags.f, LRU_REFS_MASK, refs);\n+\tBUILD_BUG_ON(LRU_REFS_MASK \u0026 BIT(PG_referenced));\n+\tfolio_set_lru_refs(new, folio_lru_refs(old));\n }\n #else /* !CONFIG_LRU_GEN */\n \n@@ -337,9 +411,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,\n \treturn false;\n }\n \n-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)\n+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n {\n-\n+\tif (folio_test_referenced(old))\n+\t\tfolio_set_referenced(new);\n }\n #endif /* CONFIG_LRU_GEN */\n \ndiff --git a/include/linux/mmzone.h b/include/linux/mmzone.h\nindex 94f9c3ff54160..9b27cf53bdbc7 100644\n--- a/include/linux/mmzone.h\n+++ b/include/linux/mmzone.h\n@@ -492,11 +492,14 @@ enum lruvec_flags {\n * folio-\u003eflags, masked by LRU_REFS_MASK.\n */\n #define MAX_NR_TIERS\t\t4U\n+#define LRU_TIER_MIN\t\t0U\n+#define LRU_TIER_MAX\t\t(MAX_NR_TIERS - 1)\n \n #ifndef __GENERATING_BOUNDS_H\n \n #define LRU_GEN_MASK\t\t((BIT(LRU_GEN_WIDTH) - 1) \u003c\u003c LRU_GEN_PGOFF)\n #define LRU_REFS_MASK\t\t((BIT(LRU_REFS_WIDTH) - 1) \u003c\u003c LRU_REFS_PGOFF)\n+#define LRU_REFS_MAX\t\tBIT(LRU_REFS_WIDTH)\n \n /*\n * For folios accessed multiple times through file descriptors,\ndiff --git a/mm/folio.c b/mm/folio.c\nindex 59c477120b9a8..0adfe4f5ef72f 100644\n--- a/mm/folio.c\n+++ b/mm/folio.c\n@@ -353,26 +353,28 @@ static void __lru_cache_activate_folio(struct folio *folio)\n \n static void lru_gen_inc_refs(struct folio *folio)\n {\n-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n+\tint refs;\n \n \tif (folio_test_unevictable(folio))\n \t\treturn;\n \n \t/* see the comment on LRU_REFS_FLAGS */\n-\tif (!folio_test_referenced(folio)) {\n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_MASK, BIT(PG_referenced));\n+\tif (!folio_lru_refs(folio)) {\n+\t\tfolio_set_lru_refs(folio, 1);\n \t\treturn;\n \t}\n \n \tdo {\n-\t\tif ((old_flags \u0026 LRU_REFS_MASK) == LRU_REFS_MASK) {\n+\t\tnew_flags = old_flags;\n+\t\trefs = lru_refs_from_flags(old_flags);\n+\t\tif (refs == LRU_REFS_MAX) {\n \t\t\tif (!folio_test_workingset(folio))\n \t\t\t\tfolio_set_workingset(folio);\n \t\t\treturn;\n \t\t}\n-\n-\t\tnew_flags = old_flags + BIT(LRU_REFS_PGOFF);\n-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n+\t\tlru_refs_set_flags(\u0026new_flags, refs + 1);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n }\n \n static bool lru_gen_clear_refs(struct folio *folio)\n@@ -384,7 +386,8 @@ static bool lru_gen_clear_refs(struct folio *folio)\n \tif (gen \u003c 0)\n \t\treturn true;\n \n-\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0);\n+\tfolio_set_lru_refs(folio, 0);\n+\tfolio_clear_workingset(folio);\n \n \trcu_read_lock();\n \tseq = READ_ONCE(folio_lruvec(folio)-\u003elrugen.min_seq[type]);\ndiff --git a/mm/memcontrol.c b/mm/memcontrol.c\nindex 11b85f4b6828b..a7572ded56c9d 100644\n--- a/mm/memcontrol.c\n+++ b/mm/memcontrol.c\n@@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\n \t\t\t\tint zid, long nr_pages)\n {\n \tstruct mem_cgroup_per_node *mz;\n-\tunsigned long *lru_size;\n-\tlong size;\n \n \tif (mem_cgroup_disabled())\n \t\treturn;\n \n \tmz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);\n-\tlru_size = \u0026mz-\u003elru_zone_size[zid][lru];\n-\n-\tif (nr_pages \u003c 0)\n-\t\t*lru_size += nr_pages;\n-\n-\tsize = *lru_size;\n-\tif (WARN_ONCE(size \u003c 0,\n-\t\t\"%s(%p, %d, %ld): lru_size %ld\\n\",\n-\t\t__func__, lruvec, lru, nr_pages, size)) {\n-\t\tVM_BUG_ON(1);\n-\t\t*lru_size = 0;\n-\t}\n-\n-\tif (nr_pages \u003e 0)\n-\t\t*lru_size += nr_pages;\n+\tatomic_long_add(nr_pages, \u0026mz-\u003elru_zone_size[zid][lru]);\n }\n \n /**\ndiff --git a/mm/migrate.c b/mm/migrate.c\nindex 15b45832bcfa7..96eda490ba435 100644\n--- a/mm/migrate.c\n+++ b/mm/migrate.c\n@@ -776,8 +776,6 @@ 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 \tif (folio_test_uptodate(folio))\n \t\tfolio_mark_uptodate(newfolio);\n \tif (folio_test_clear_active(folio)) {\n@@ -807,7 +805,9 @@ 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/* Copy the reference state, including PG_referenced */\n+\tfolio_migrate_lru_refs(newfolio, folio);\n+\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 c1404a59523d6..a1497ad61ad70 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_lru_refs(folio, 0);\n+\t\t\tfolio_set_workingset(folio);\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_lru_refs(folio, 0);\n+\t\tfolio_set_workingset(folio);\n+\t} else {\n \t\tfolio_mark_accessed(folio);\n+\t}\n \treturn true;\n }\n #else\n@@ -3195,8 +3198,8 @@ struct ctrl_pos {\n \tint gain;\n };\n \n-static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,\n-\t\t\t struct ctrl_pos *pos)\n+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,\n+\t\t\t int tier_max, int gain, struct ctrl_pos *pos)\n {\n \tint i;\n \tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\n@@ -3205,7 +3208,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,\n \tpos-\u003egain = gain;\n \tpos-\u003erefaulted = pos-\u003etotal = 0;\n \n-\tfor (i = tier % MAX_NR_TIERS; i \u003c= min(tier, MAX_NR_TIERS - 1); i++) {\n+\tfor (i = tier_min; i \u003c= tier_max; i++) {\n \t\tpos-\u003erefaulted += lrugen-\u003eavg_refaulted[type][i] +\n \t\t\t\t atomic_long_read(\u0026lrugen-\u003erefaulted[hist][type][i]);\n \t\tpos-\u003etotal += lrugen-\u003eavg_total[type][i] +\n@@ -3266,11 +3269,11 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)\n ******************************************************************************/\n \n /* promote pages accessed through page tables */\n-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)\n+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,\n+\t\t\t const vma_flags_t *vma_flags)\n {\n-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n-\n-\tVM_WARN_ON_ONCE(gen \u003e= MAX_NR_GENS);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n+\tint old_gen;\n \n \t/*\n \t * See the comment on LRU_REFS_FLAGS, and activate file-backed\n@@ -3279,20 +3282,25 @@ static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma\n \t */\n \tif (!folio_test_referenced(folio) \u0026\u0026 !folio_test_workingset(folio) \u0026\u0026\n \t !is_exec_file_folio(folio, vma_flags)) {\n-\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_MASK, BIT(PG_referenced));\n+\t\tfolio_set_lru_refs(folio, 1);\n \t\treturn -1;\n \t}\n \n \tdo {\n+\t\told_gen = lru_gen_from_flags(old_flags);\n+\t\tnew_flags = old_flags;\n+\n \t\t/* lru_gen_del_folio() has isolated this page? */\n-\t\tif (!(old_flags \u0026 LRU_GEN_MASK))\n-\t\t\treturn -1;\n+\t\tif (old_gen \u003c 0)\n+\t\t\tbreak;\n \n-\t\tnew_flags = old_flags \u0026 ~(LRU_GEN_MASK | LRU_REFS_FLAGS);\n-\t\tnew_flags |= ((gen + 1UL) \u003c\u003c LRU_GEN_PGOFF) | BIT(PG_workingset);\n-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n+\t\tlru_gen_set_flags(\u0026new_flags, new_gen);\n+\t\tlru_refs_set_flags(\u0026new_flags, 0);\n+\t\tnew_flags |= BIT(PG_workingset);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n \n-\treturn ((old_flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\t*is_file = folio_flags_is_file_lru(\u0026old_flags);\n+\treturn old_gen;\n }\n \n /* protect pages accessed multiple times through file descriptors */\n@@ -3301,21 +3309,20 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n \tint type = folio_is_file_lru(folio);\n \tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\n \tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\n-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n-\n-\tVM_WARN_ON_ONCE_FOLIO(!(old_flags \u0026 LRU_GEN_MASK), folio);\n+\tunsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));\n \n \tdo {\n-\t\tnew_gen = ((old_flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\n+\t\tnew_gen = lru_gen_from_flags(old_flags);\n+\n \t\t/* folio_update_gen() has promoted this page? */\n \t\tif (new_gen \u003e= 0 \u0026\u0026 new_gen != old_gen)\n \t\t\treturn new_gen;\n \n+\t\tnew_flags = old_flags;\n \t\tnew_gen = (old_gen + 1) % MAX_NR_GENS;\n-\n-\t\tnew_flags = old_flags \u0026 ~(LRU_GEN_MASK | LRU_REFS_FLAGS);\n-\t\tnew_flags |= (new_gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\n-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n+\t\tlru_gen_set_flags(\u0026new_flags, new_gen);\n+\t\tlru_refs_set_flags(\u0026new_flags, 0);\n+\t} while (!try_cmpxchg(folio_flags(folio, 0), \u0026old_flags, new_flags));\n \n \tlru_gen_update_size(lruvec, folio, old_gen, new_gen);\n \n@@ -3323,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n }\n \n static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,\n-\t\t\t int old_gen, int new_gen)\n+\t\t\t int old_gen, int new_gen, int type)\n {\n-\tint type = folio_is_file_lru(folio);\n \tint zone = folio_zonenum(folio);\n \tint delta = folio_nr_pages(folio);\n \n@@ -3512,22 +3518,24 @@ static bool suitable_to_scan(int total, int young)\n }\n \n static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,\n-\t\tstruct folio *folio, int new_gen, bool dirty)\n+\t\tstruct lruvec *lruvec, struct folio *folio, bool dirty)\n {\n-\tint old_gen;\n+\tint new_gen, old_gen, file;\n \n \tif (!folio)\n \t\treturn;\n \n+\tnew_gen = lru_gen_from_seq(READ_ONCE(lruvec-\u003elrugen.max_seq));\n+\n \tif (dirty \u0026\u0026 !folio_test_dirty(folio) \u0026\u0026\n \t !(folio_test_anon(folio) \u0026\u0026 folio_test_swapbacked(folio) \u0026\u0026\n \t !folio_test_swapcache(folio)))\n \t\tfolio_mark_dirty(folio);\n \n \tif (walk) {\n-\t\told_gen = folio_update_gen(folio, new_gen, \u0026vma-\u003eflags);\n+\t\told_gen = folio_update_gen(folio, new_gen, \u0026file, \u0026vma-\u003eflags);\n \t\tif (old_gen \u003e= 0 \u0026\u0026 old_gen != new_gen)\n-\t\t\tupdate_batch_size(walk, folio, old_gen, new_gen);\n+\t\t\tupdate_batch_size(walk, folio, old_gen, new_gen, file);\n \t} else if (lru_gen_set_refs(folio, \u0026vma-\u003eflags)) {\n \t\told_gen = folio_lru_gen(folio);\n \t\tif (old_gen \u003e= 0 \u0026\u0026 old_gen != new_gen)\n@@ -3549,8 +3557,6 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n \tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\n \tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n \tstruct pglist_data *pgdat = lruvec_pgdat(walk-\u003elruvec);\n-\tDEFINE_MAX_SEQ(walk-\u003elruvec);\n-\tint gen = lru_gen_from_seq(max_seq);\n \tunsigned int nr;\n \tpmd_t pmdval;\n \n@@ -3601,7 +3607,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n \t\t\tcontinue;\n \n \t\tif (last != folio) {\n-\t\t\twalk_update_folio(walk, args-\u003evma, last, gen, dirty);\n+\t\t\twalk_update_folio(walk, args-\u003evma, walk-\u003elruvec, last, dirty);\n \n \t\t\tlast = folio;\n \t\t\tdirty = false;\n@@ -3614,7 +3620,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n \t\twalk-\u003emm_stats[MM_LEAF_YOUNG] += nr;\n \t}\n \n-\twalk_update_folio(walk, args-\u003evma, last, gen, dirty);\n+\twalk_update_folio(walk, args-\u003evma, walk-\u003elruvec, last, dirty);\n \tlast = NULL;\n \n \tif (i \u003c PTRS_PER_PTE \u0026\u0026 get_next_vma(PMD_MASK, PAGE_SIZE, args, \u0026start, \u0026end))\n@@ -3637,8 +3643,6 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area\n \tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\n \tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n \tstruct pglist_data *pgdat = lruvec_pgdat(walk-\u003elruvec);\n-\tDEFINE_MAX_SEQ(walk-\u003elruvec);\n-\tint gen = lru_gen_from_seq(max_seq);\n \n \tVM_WARN_ON_ONCE(pud_leaf(*pud));\n \n@@ -3692,7 +3696,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area\n \t\t\tgoto next;\n \n \t\tif (last != folio) {\n-\t\t\twalk_update_folio(walk, vma, last, gen, dirty);\n+\t\t\twalk_update_folio(walk, vma, walk-\u003elruvec, last, dirty);\n \n \t\t\tlast = folio;\n \t\t\tdirty = false;\n@@ -3706,7 +3710,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area\n \t\ti = i \u003e MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;\n \t} while (i \u003c= MIN_LRU_BATCH);\n \n-\twalk_update_folio(walk, vma, last, gen, dirty);\n+\twalk_update_folio(walk, vma, walk-\u003elruvec, last, dirty);\n \n \tlazy_mmu_mode_disable();\n \tspin_unlock(ptl);\n@@ -4270,8 +4274,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \tstruct pglist_data *pgdat = folio_pgdat(folio);\n \tstruct lruvec *lruvec;\n \tstruct lru_gen_mm_state *mm_state;\n-\tunsigned long max_seq;\n-\tint gen;\n \n \tlockdep_assert_held(pvmw-\u003eptl);\n \tVM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio);\n@@ -4308,8 +4310,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \n \tmemcg = get_mem_cgroup_from_folio(folio);\n \tlruvec = mem_cgroup_lruvec(memcg, pgdat);\n-\tmax_seq = READ_ONCE((lruvec)-\u003elrugen.max_seq);\n-\tgen = lru_gen_from_seq(max_seq);\n \tmm_state = get_mm_state(lruvec);\n \n \tlazy_mmu_mode_enable();\n@@ -4341,7 +4341,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \t\t\tcontinue;\n \n \t\tif (last != folio) {\n-\t\t\twalk_update_folio(walk, vma, last, gen, dirty);\n+\t\t\twalk_update_folio(walk, vma, lruvec, last, dirty);\n \n \t\t\tlast = folio;\n \t\t\tdirty = false;\n@@ -4353,13 +4353,14 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\n \t\tyoung += nr;\n \t}\n \n-\twalk_update_folio(walk, vma, last, gen, dirty);\n+\twalk_update_folio(walk, vma, lruvec, last, dirty);\n \n \tlazy_mmu_mode_disable();\n \n \t/* feedback from rmap walkers to page table walkers */\n \tif (mm_state \u0026\u0026 suitable_to_scan(i, young))\n-\t\tupdate_bloom_filter(mm_state, max_seq, pvmw-\u003epmd);\n+\t\tupdate_bloom_filter(mm_state, READ_ONCE(lruvec-\u003elrugen.max_seq),\n+\t\t\t\t pvmw-\u003epmd);\n \n \tmem_cgroup_put(memcg);\n \n@@ -4711,7 +4712,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@@ -4804,9 +4805,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@@ -4827,8 +4828,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@@ -4927,8 +4930,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \t\t}\n \n \t\t/* don't add rejected folios to the oldest generation */\n-\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])\n-\t\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS, BIT(PG_active));\n+\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {\n+\t\t\tfolio_set_lru_refs(folio, 0);\n+\t\t\tfolio_set_active(folio);\n+\t\t}\n \t}\n \n \tmove_folios_to_lru(\u0026list);\n"
} |
| 2/1 | 2026/08/24 17:58 | agent | patch-evaluator |
38mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"LRU_GEN_STATS",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"try_to_free_pages",
"try_to_free_mem_cgroup_pages",
"folio_deactivate",
"deactivate_file_folio",
"folio_mark_lazyfree",
"migrate_pages"
],
"Reasoning": "The patch modifies the Multi-Gen LRU (MGLRU) implementation in the core memory management subsystem. It changes how LRU zone sizes are updated (using atomic operations) and refactors LRU generation and reference flag manipulation into inline helpers. It also updates several core MGLRU functions like `folio_update_gen`, `folio_inc_gen`, `lru_gen_look_around`, and `evict_folios`. These changes affect page reclaim, page migration, and memory cgroup accounting. The code is reachable through memory pressure (reclaim), page migration, and madvise/fadvise syscalls.",
"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 5dd612268d61060285ff77f66c43f609c311f380
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Aug 24 17:58:23 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 215e2e87f42b2..7b89d0cb5f6c6 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -113,7 +113,7 @@ struct mem_cgroup_per_node {
/* Fields which get updated often at the end. */
struct lruvec lruvec;
CACHELINE_PADDING(_pad2_);
- unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
+ atomic_long_t lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
struct mem_cgroup_reclaim_iter iter;
/*
@@ -902,10 +902,15 @@ static inline
unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,
enum lru_list lru, int zone_idx)
{
+ long val;
struct mem_cgroup_per_node *mz;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- return READ_ONCE(mz->lru_zone_size[zone_idx][lru]);
+ val = atomic_long_read(&mz->lru_zone_size[zone_idx][lru]);
+ if (WARN_ON_ONCE(val < 0))
+ return 0;
+
+ return val;
}
void __mem_cgroup_handle_over_high(gfp_t gfp_mask);
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index 621c8653d8f7e..8cf82989ec263 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -10,6 +10,11 @@
#include <linux/userfaultfd_k.h>
#include <linux/leafops.h>
+static inline int folio_flags_is_file_lru(const unsigned long *flags)
+{
+ return !test_bit(PG_swapbacked, flags);
+}
+
/**
* folio_is_file_lru - Should the folio be on a file LRU or anon LRU?
* @folio: The folio to test.
@@ -27,7 +32,7 @@
*/
static inline int folio_is_file_lru(const struct folio *folio)
{
- return !folio_test_swapbacked(folio);
+ return folio_flags_is_file_lru(const_folio_flags(folio, 0));
}
static __always_inline void __update_lru_size(struct lruvec *lruvec,
@@ -142,10 +147,42 @@ static inline int lru_tier_from_refs(int refs, bool workingset)
return workingset ? MAX_NR_TIERS - 1 : order_base_2(refs);
}
-static inline int folio_lru_refs(const struct folio *folio)
+/**
+ * lru_gen_from_flags - Return the LRU generation number from folio flags.
+ * @flags: folio flags
+ *
+ * Returns: A number between 0 and (MAX_NR_GENS - 1), inclusive. Returns
+ * -1 if the flags indicate the folio is off the list (e.g., isolated).
+ */
+static inline int lru_gen_from_flags(unsigned long flags)
+{
+ int gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF);
+
+ BUILD_BUG_ON(LRU_GEN_MASK & LRU_REFS_MASK);
+ gen -= 1;
+ VM_WARN_ON_ONCE(gen != -1 && gen >= MAX_NR_GENS);
+ return gen;
+}
+
+/**
+ * lru_gen_set_flags - Set the LRU generation number to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @gen: generation number, between 0 and (MAX_NR_GENS - 1), inclusive.
+ */
+static inline void lru_gen_set_flags(unsigned long *flags, int gen)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS || gen < 0);
+
+ *flags &= ~LRU_GEN_MASK;
+ *flags |= (gen + 1UL) << LRU_GEN_PGOFF;
+}
+/**
+ * lru_refs_from_flags - Return LRU referenced / access count from folio flags.
+ * @flags: folio flags
+ */
+static inline int lru_refs_from_flags(unsigned long flags)
+{
if (!(flags & BIT(PG_referenced)))
return 0;
/*
@@ -155,11 +192,40 @@ static inline int folio_lru_refs(const struct folio *folio)
return ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + 1;
}
-static inline int folio_lru_gen(const struct folio *folio)
+/**
+ * lru_refs_set_flags - Set the LRU referenced / access count to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.
+ */
+static inline void lru_refs_set_flags(unsigned long *flags, unsigned int refs)
+{
+ VM_WARN_ON_ONCE(refs > LRU_REFS_MAX);
+ BUILD_BUG_ON(LRU_REFS_MAX != (LRU_REFS_MASK >> LRU_REFS_PGOFF) + 1);
+
+ *flags &= ~LRU_REFS_FLAGS;
+ if (!refs)
+ return;
+ *flags |= (BIT(PG_referenced) | ((refs - 1UL) << LRU_REFS_PGOFF));
+}
+
+static inline int folio_lru_refs(const struct folio *folio)
+{
+ return lru_refs_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
+}
+
+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
- return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ do {
+ new_flags = old_flags;
+ lru_refs_set_flags(&new_flags, refs);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
+}
+
+static inline int folio_lru_gen(const struct folio *folio)
+{
+ return lru_gen_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
}
static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
@@ -270,7 +336,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,
gen = lru_gen_from_seq(seq);
flags = (gen + 1UL) << LRU_GEN_PGOFF;
/* see the comment on MIN_NR_GENS about PG_active */
- set_mask_bits(&folio->flags.f, LRU_GEN_MASK | BIT(PG_active), flags);
+ set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);
lru_gen_update_size(lruvec, folio, -1, gen);
/* for folio_rotate_reclaimable() */
@@ -295,7 +361,7 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
/* for folio_migrate_flags() */
flags = !reclaiming && lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;
- flags = set_mask_bits(&folio->flags.f, LRU_GEN_MASK, flags);
+ flags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);
gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
lru_gen_update_size(lruvec, folio, gen, -1);
@@ -304,11 +370,19 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return true;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+/**
+ * folio_migrate_lru_refs - copy the reference state to a new folio
+ * @new: the destination folio
+ * @old: the source folio
+ *
+ * Transfer the reference state to @new during migration: the MGLRU
+ * refs count, including PG_referenced, or just PG_referenced for the
+ * active/inactive LRU.
+ */
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
- unsigned long refs = READ_ONCE(old->flags.f) & LRU_REFS_MASK;
-
- set_mask_bits(&new->flags.f, LRU_REFS_MASK, refs);
+ BUILD_BUG_ON(LRU_REFS_MASK & BIT(PG_referenced));
+ folio_set_lru_refs(new, folio_lru_refs(old));
}
#else /* !CONFIG_LRU_GEN */
@@ -337,9 +411,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return false;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
-
+ if (folio_test_referenced(old))
+ folio_set_referenced(new);
}
#endif /* CONFIG_LRU_GEN */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 94f9c3ff54160..9b27cf53bdbc7 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -492,11 +492,14 @@ enum lruvec_flags {
* folio->flags, masked by LRU_REFS_MASK.
*/
#define MAX_NR_TIERS 4U
+#define LRU_TIER_MIN 0U
+#define LRU_TIER_MAX (MAX_NR_TIERS - 1)
#ifndef __GENERATING_BOUNDS_H
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
+#define LRU_REFS_MAX BIT(LRU_REFS_WIDTH)
/*
* For folios accessed multiple times through file descriptors,
diff --git a/mm/folio.c b/mm/folio.c
index 59c477120b9a8..0adfe4f5ef72f 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -353,26 +353,28 @@ static void __lru_cache_activate_folio(struct folio *folio)
static void lru_gen_inc_refs(struct folio *folio)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int refs;
if (folio_test_unevictable(folio))
return;
/* see the comment on LRU_REFS_FLAGS */
- if (!folio_test_referenced(folio)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ if (!folio_lru_refs(folio)) {
+ folio_set_lru_refs(folio, 1);
return;
}
do {
- if ((old_flags & LRU_REFS_MASK) == LRU_REFS_MASK) {
+ new_flags = old_flags;
+ refs = lru_refs_from_flags(old_flags);
+ if (refs == LRU_REFS_MAX) {
if (!folio_test_workingset(folio))
folio_set_workingset(folio);
return;
}
-
- new_flags = old_flags + BIT(LRU_REFS_PGOFF);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_refs_set_flags(&new_flags, refs + 1);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
}
static bool lru_gen_clear_refs(struct folio *folio)
@@ -384,7 +386,8 @@ static bool lru_gen_clear_refs(struct folio *folio)
if (gen < 0)
return true;
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0);
+ folio_set_lru_refs(folio, 0);
+ folio_clear_workingset(folio);
rcu_read_lock();
seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 11b85f4b6828b..a7572ded56c9d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
int zid, long nr_pages)
{
struct mem_cgroup_per_node *mz;
- unsigned long *lru_size;
- long size;
if (mem_cgroup_disabled())
return;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- lru_size = &mz->lru_zone_size[zid][lru];
-
- if (nr_pages < 0)
- *lru_size += nr_pages;
-
- size = *lru_size;
- if (WARN_ONCE(size < 0,
- "%s(%p, %d, %ld): lru_size %ld\n",
- __func__, lruvec, lru, nr_pages, size)) {
- VM_BUG_ON(1);
- *lru_size = 0;
- }
-
- if (nr_pages > 0)
- *lru_size += nr_pages;
+ atomic_long_add(nr_pages, &mz->lru_zone_size[zid][lru]);
}
/**
diff --git a/mm/migrate.c b/mm/migrate.c
index 15b45832bcfa7..96eda490ba435 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -776,8 +776,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
{
int cpupid;
- if (folio_test_referenced(folio))
- folio_set_referenced(newfolio);
if (folio_test_uptodate(folio))
folio_mark_uptodate(newfolio);
if (folio_test_clear_active(folio)) {
@@ -807,7 +805,9 @@ 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 the reference state, including PG_referenced */
+ folio_migrate_lru_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 c1404a59523d6..a1497ad61ad70 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_lru_refs(folio, 0);
+ folio_set_workingset(folio);
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_lru_refs(folio, 0);
+ folio_set_workingset(folio);
+ } else {
folio_mark_accessed(folio);
+ }
return true;
}
#else
@@ -3195,8 +3198,8 @@ struct ctrl_pos {
int gain;
};
-static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
- struct ctrl_pos *pos)
+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,
+ int tier_max, int gain, struct ctrl_pos *pos)
{
int i;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
@@ -3205,7 +3208,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
pos->gain = gain;
pos->refaulted = pos->total = 0;
- for (i = tier % MAX_NR_TIERS; i <= min(tier, MAX_NR_TIERS - 1); i++) {
+ for (i = tier_min; i <= tier_max; i++) {
pos->refaulted += lrugen->avg_refaulted[type][i] +
atomic_long_read(&lrugen->refaulted[hist][type][i]);
pos->total += lrugen->avg_total[type][i] +
@@ -3266,11 +3269,11 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
******************************************************************************/
/* promote pages accessed through page tables */
-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)
+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,
+ const vma_flags_t *vma_flags)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int old_gen;
/*
* See the comment on LRU_REFS_FLAGS, and activate file-backed
@@ -3279,20 +3282,25 @@ static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma
*/
if (!folio_test_referenced(folio) && !folio_test_workingset(folio) &&
!is_exec_file_folio(folio, vma_flags)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ folio_set_lru_refs(folio, 1);
return -1;
}
do {
+ old_gen = lru_gen_from_flags(old_flags);
+ new_flags = old_flags;
+
/* lru_gen_del_folio() has isolated this page? */
- if (!(old_flags & LRU_GEN_MASK))
- return -1;
+ if (old_gen < 0)
+ break;
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= ((gen + 1UL) << LRU_GEN_PGOFF) | BIT(PG_workingset);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ new_flags |= BIT(PG_workingset);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
- return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ *is_file = folio_flags_is_file_lru(&old_flags);
+ return old_gen;
}
/* protect pages accessed multiple times through file descriptors */
@@ -3301,21 +3309,20 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
int type = folio_is_file_lru(folio);
struct lru_gen_folio *lrugen = &lruvec->lrugen;
int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE_FOLIO(!(old_flags & LRU_GEN_MASK), folio);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
do {
- new_gen = ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ new_gen = lru_gen_from_flags(old_flags);
+
/* folio_update_gen() has promoted this page? */
if (new_gen >= 0 && new_gen != old_gen)
return new_gen;
+ new_flags = old_flags;
new_gen = (old_gen + 1) % MAX_NR_GENS;
-
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF;
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
lru_gen_update_size(lruvec, folio, old_gen, new_gen);
@@ -3323,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
}
static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
- int old_gen, int new_gen)
+ int old_gen, int new_gen, int type)
{
- int type = folio_is_file_lru(folio);
int zone = folio_zonenum(folio);
int delta = folio_nr_pages(folio);
@@ -3512,22 +3518,24 @@ static bool suitable_to_scan(int total, int young)
}
static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
- struct folio *folio, int new_gen, bool dirty)
+ struct lruvec *lruvec, struct folio *folio, bool dirty)
{
- int old_gen;
+ int new_gen, old_gen, file;
if (!folio)
return;
+ new_gen = lru_gen_from_seq(READ_ONCE(lruvec->lrugen.max_seq));
+
if (dirty && !folio_test_dirty(folio) &&
!(folio_test_anon(folio) && folio_test_swapbacked(folio) &&
!folio_test_swapcache(folio)))
folio_mark_dirty(folio);
if (walk) {
- old_gen = folio_update_gen(folio, new_gen, &vma->flags);
+ old_gen = folio_update_gen(folio, new_gen, &file, &vma->flags);
if (old_gen >= 0 && old_gen != new_gen)
- update_batch_size(walk, folio, old_gen, new_gen);
+ update_batch_size(walk, folio, old_gen, new_gen, file);
} else if (lru_gen_set_refs(folio, &vma->flags)) {
old_gen = folio_lru_gen(folio);
if (old_gen >= 0 && old_gen != new_gen)
@@ -3549,8 +3557,6 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
unsigned int nr;
pmd_t pmdval;
@@ -3601,7 +3607,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
continue;
if (last != folio) {
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3614,7 +3620,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
walk->mm_stats[MM_LEAF_YOUNG] += nr;
}
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = NULL;
if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end))
@@ -3637,8 +3643,6 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
VM_WARN_ON_ONCE(pud_leaf(*pud));
@@ -3692,7 +3696,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
goto next;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3706,7 +3710,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
i = i > MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;
} while (i <= MIN_LRU_BATCH);
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
lazy_mmu_mode_disable();
spin_unlock(ptl);
@@ -4270,8 +4274,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
struct pglist_data *pgdat = folio_pgdat(folio);
struct lruvec *lruvec;
struct lru_gen_mm_state *mm_state;
- unsigned long max_seq;
- int gen;
lockdep_assert_held(pvmw->ptl);
VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio);
@@ -4308,8 +4310,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
memcg = get_mem_cgroup_from_folio(folio);
lruvec = mem_cgroup_lruvec(memcg, pgdat);
- max_seq = READ_ONCE((lruvec)->lrugen.max_seq);
- gen = lru_gen_from_seq(max_seq);
mm_state = get_mm_state(lruvec);
lazy_mmu_mode_enable();
@@ -4341,7 +4341,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
continue;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
last = folio;
dirty = false;
@@ -4353,13 +4353,14 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
young += nr;
}
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
lazy_mmu_mode_disable();
/* feedback from rmap walkers to page table walkers */
if (mm_state && suitable_to_scan(i, young))
- update_bloom_filter(mm_state, max_seq, pvmw->pmd);
+ update_bloom_filter(mm_state, READ_ONCE(lruvec->lrugen.max_seq),
+ pvmw->pmd);
mem_cgroup_put(memcg);
@@ -4711,7 +4712,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);
@@ -4804,9 +4805,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;
}
@@ -4827,8 +4828,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);
}
@@ -4927,8 +4930,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
}
/* don't add rejected folios to the oldest generation */
- if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS, BIT(PG_active));
+ if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {
+ folio_set_lru_refs(folio, 0);
+ folio_set_active(folio);
+ }
}
move_folios_to_lru(&list);
|
| 3/2 | 2026/08/24 17:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11407 output: 18 thoughts: 238 Thoughts: **My Assessment of the MGLRU Patch** Okay, so I'm looking at this patch for the Multi-Gen LRU implementation in the Linux kernel, and immediately I see some interesting changes. They're making a pretty fundamental shift – updating the `lru_zone_size` in `mem_cgroup_per_node` from an `unsigned long` array to an `atomic_long_t`. That's a definite concurrency improvement, right off the bat. Good move, assuming the performance impact is negligible, and it seems like it probably is. Next, I see they've refactored LRU flag manipulation into these inline functions: `lru_gen_from_flags`, `lru_gen_set_flags`, `lru_refs_from_flags`, and `lru_refs_set_flags`. That's clean, more maintainable, and likely safer. Centralizing that logic is always a plus. They've also updated `folio_update_gen` and `folio_inc_gen` to leverage these new helpers, which should lead to more consistent behavior and probably fixes some subtle bugs or logic flaws. The change to `read_ctrl_pos` to take `tier_min` and `tier_max` instead of just `tier` is potentially significant too, depending on how it's used elsewhere. It hints at a more flexible or granular control over the LRU tiers. I'll need to dig into the context there to fully understand the implications. Now, the crucial question is: is this even *reachable* code? And, if so, is it worth targeting for fuzzing? MGLRU itself is a core memory management feature. The presence of `CONFIG_LRU_GEN` indicates it's definitely enabled in some builds. But I need to check for other relevant configs that might affect the reachability of these *specific* code paths. It's a critical area, so I'm already thinking about potential fuzz targets. Let's see... I'll need to consider how this interacts with memory cgroups, NUMA, and other memory management subsystems. This has my attention. |
| 4/2 | 2026/08/24 17:58 | tool | grepper |
0mArgs:{
"Expression": "LRU_GEN"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 547 lines.\nUse more precise expression if possible.\n\nDocumentation/admin-guide/mm/multigen_lru.rst=13=Build the kernel with the following configurations.\nDocumentation/admin-guide/mm/multigen_lru.rst-14-\nDocumentation/admin-guide/mm/multigen_lru.rst:15:* ``CONFIG_LRU_GEN=y``\nDocumentation/admin-guide/mm/multigen_lru.rst:16:* ``CONFIG_LRU_GEN_ENABLED=y``\nDocumentation/admin-guide/mm/multigen_lru.rst-17-\n--\nDocumentation/admin-guide/mm/multigen_lru.rst=28=following components. Its default value depends on\nDocumentation/admin-guide/mm/multigen_lru.rst:29:``CONFIG_LRU_GEN_ENABLED``. All the components should be enabled\nDocumentation/admin-guide/mm/multigen_lru.rst-30-unless some of them have unforeseen side effects. Writing to\n--\nDocumentation/admin-guide/mm/multigen_lru.rst=88=concatenation with delimiters ``,`` and ``;``.\n--\nDocumentation/admin-guide/mm/multigen_lru.rst-90-``/sys/kernel/debug/lru_gen_full`` provides additional stats for\nDocumentation/admin-guide/mm/multigen_lru.rst:91:debugging. ``CONFIG_LRU_GEN_STATS=y`` keeps historical stats from\nDocumentation/admin-guide/mm/multigen_lru.rst-92-evicted generations in this file.\n--\nfs/fuse/dev.c=1087=static int fuse_check_folio(struct folio *folio)\n--\nfs/fuse/dev.c-1098-\t 1 \u003c\u003c PG_waiters |\nfs/fuse/dev.c:1099:\t LRU_GEN_MASK | LRU_REFS_MASK))) {\nfs/fuse/dev.c-1100-\t\tdump_page(\u0026folio-\u003epage, \"fuse: trying to steal weird page\");\n--\ninclude/linux/memcontrol.h=202=struct mem_cgroup {\n--\ninclude/linux/memcontrol.h-283-\ninclude/linux/memcontrol.h:284:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/memcontrol.h-285-\t/* per-memcg mm_struct list */\n--\ninclude/linux/mm_inline.h=87=static __always_inline enum lru_list folio_lru_list(const struct folio *folio)\n--\ninclude/linux/mm_inline.h-102-\ninclude/linux/mm_inline.h:103:#ifdef CONFIG_LRU_GEN\ninclude/linux/mm_inline.h-104-\ninclude/linux/mm_inline.h=105=static inline bool lru_gen_switching(void)\n--\ninclude/linux/mm_inline.h-110-}\ninclude/linux/mm_inline.h:111:#ifdef CONFIG_LRU_GEN_ENABLED\ninclude/linux/mm_inline.h-112-static inline bool lru_gen_enabled(void)\ninclude/linux/mm_inline.h-113-{\ninclude/linux/mm_inline.h:114:\tDECLARE_STATIC_KEY_TRUE(lru_gen_caps[NR_LRU_GEN_CAPS]);\ninclude/linux/mm_inline.h-115-\ninclude/linux/mm_inline.h:116:\treturn static_branch_likely(\u0026lru_gen_caps[LRU_GEN_CORE]);\ninclude/linux/mm_inline.h-117-}\n--\ninclude/linux/mm_inline.h=119=static inline bool lru_gen_enabled(void)\ninclude/linux/mm_inline.h-120-{\ninclude/linux/mm_inline.h:121:\tDECLARE_STATIC_KEY_FALSE(lru_gen_caps[NR_LRU_GEN_CAPS]);\ninclude/linux/mm_inline.h-122-\ninclude/linux/mm_inline.h:123:\treturn static_branch_unlikely(\u0026lru_gen_caps[LRU_GEN_CORE]);\ninclude/linux/mm_inline.h-124-}\n--\ninclude/linux/mm_inline.h=157=static inline int lru_gen_from_flags(unsigned long flags)\ninclude/linux/mm_inline.h-158-{\ninclude/linux/mm_inline.h:159:\tint gen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF);\ninclude/linux/mm_inline.h-160-\ninclude/linux/mm_inline.h:161:\tBUILD_BUG_ON(LRU_GEN_MASK \u0026 LRU_REFS_MASK);\ninclude/linux/mm_inline.h-162-\tgen -= 1;\n--\ninclude/linux/mm_inline.h=172=static inline void lru_gen_set_flags(unsigned long *flags, int gen)\n--\ninclude/linux/mm_inline.h-175-\ninclude/linux/mm_inline.h:176:\t*flags \u0026= ~LRU_GEN_MASK;\ninclude/linux/mm_inline.h:177:\t*flags |= (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\ninclude/linux/mm_inline.h-178-}\n--\ninclude/linux/mm_inline.h=321=static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)\n--\ninclude/linux/mm_inline.h-336-\tgen = lru_gen_from_seq(seq);\ninclude/linux/mm_inline.h:337:\tflags = (gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\ninclude/linux/mm_inline.h-338-\t/* see the comment on MIN_NR_GENS about PG_active */\ninclude/linux/mm_inline.h:339:\tset_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);\ninclude/linux/mm_inline.h-340-\n--\ninclude/linux/mm_inline.h=351=static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)\n--\ninclude/linux/mm_inline.h-363-\tflags = !reclaiming \u0026\u0026 lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;\ninclude/linux/mm_inline.h:364:\tflags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);\ninclude/linux/mm_inline.h:365:\tgen = ((flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\ninclude/linux/mm_inline.h-366-\n--\ninclude/linux/mm_inline.h=382=static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n--\ninclude/linux/mm_inline.h-386-}\ninclude/linux/mm_inline.h:387:#else /* !CONFIG_LRU_GEN */\ninclude/linux/mm_inline.h-388-\n--\ninclude/linux/mm_inline.h=414=static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)\n--\ninclude/linux/mm_inline.h-418-}\ninclude/linux/mm_inline.h:419:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mm_inline.h-420-\n--\ninclude/linux/mm_types.h=1175=struct mm_struct {\n--\ninclude/linux/mm_types.h-1402-#endif /* CONFIG_KSM */\ninclude/linux/mm_types.h:1403:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/mm_types.h-1404-\t\tstruct {\n--\ninclude/linux/mm_types.h-1417-\t\t} lru_gen;\ninclude/linux/mm_types.h:1418:#endif /* CONFIG_LRU_GEN_WALKS_MMU */\ninclude/linux/mm_types.h-1419-#ifdef CONFIG_MM_ID\n--\ninclude/linux/mm_types.h=1480=static inline cpumask_t *mm_cpumask(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1484-\ninclude/linux/mm_types.h:1485:#ifdef CONFIG_LRU_GEN\ninclude/linux/mm_types.h-1486-\ninclude/linux/mm_types.h=1487=struct lru_gen_mm_list {\n--\ninclude/linux/mm_types.h-1493-\ninclude/linux/mm_types.h:1494:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mm_types.h-1495-\ninclude/linux/mm_types.h:1496:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/mm_types.h-1497-\n--\ninclude/linux/mm_types.h=1511=static inline void lru_gen_use_mm(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1520-\ninclude/linux/mm_types.h:1521:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\ninclude/linux/mm_types.h-1522-\n--\ninclude/linux/mm_types.h=1539=static inline void lru_gen_use_mm(struct mm_struct *mm)\n--\ninclude/linux/mm_types.h-1542-\ninclude/linux/mm_types.h:1543:#endif /* CONFIG_LRU_GEN_WALKS_MMU */\ninclude/linux/mm_types.h-1544-\n--\ninclude/linux/mmzone.h=424=enum lruvec_flags {\n--\ninclude/linux/mmzone.h-468- * accesses through page tables. This requires order_base_2(MAX_NR_GENS+1) bits\ninclude/linux/mmzone.h:469: * in folio-\u003eflags, masked by LRU_GEN_MASK.\ninclude/linux/mmzone.h-470- */\n--\ninclude/linux/mmzone.h-499-\ninclude/linux/mmzone.h:500:#define LRU_GEN_MASK\t\t((BIT(LRU_GEN_WIDTH) - 1) \u003c\u003c LRU_GEN_PGOFF)\ninclude/linux/mmzone.h-501-#define LRU_REFS_MASK\t\t((BIT(LRU_REFS_WIDTH) - 1) \u003c\u003c LRU_REFS_PGOFF)\n--\ninclude/linux/mmzone.h=528=struct page_vma_mapped_walk;\ninclude/linux/mmzone.h-529-\ninclude/linux/mmzone.h:530:#ifdef CONFIG_LRU_GEN\ninclude/linux/mmzone.h-531-\ninclude/linux/mmzone.h=532=enum {\ninclude/linux/mmzone.h:533:\tLRU_GEN_ANON,\ninclude/linux/mmzone.h:534:\tLRU_GEN_FILE,\ninclude/linux/mmzone.h-535-};\n--\ninclude/linux/mmzone.h=537=enum {\ninclude/linux/mmzone.h:538:\tLRU_GEN_CORE,\ninclude/linux/mmzone.h:539:\tLRU_GEN_MM_WALK,\ninclude/linux/mmzone.h:540:\tLRU_GEN_NONLEAF_YOUNG,\ninclude/linux/mmzone.h:541:\tNR_LRU_GEN_CAPS\ninclude/linux/mmzone.h-542-};\n--\ninclude/linux/mmzone.h-547-/* whether to keep historical stats from evicted generations */\ninclude/linux/mmzone.h:548:#ifdef CONFIG_LRU_GEN_STATS\ninclude/linux/mmzone.h-549-#define NR_HIST_GENS\t\tMAX_NR_GENS\n--\ninclude/linux/mmzone.h=705=void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid);\ninclude/linux/mmzone.h-706-\ninclude/linux/mmzone.h:707:#else /* !CONFIG_LRU_GEN */\ninclude/linux/mmzone.h-708-\n--\ninclude/linux/mmzone.h=757=void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid)\n--\ninclude/linux/mmzone.h-760-\ninclude/linux/mmzone.h:761:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mmzone.h-762-\n--\ninclude/linux/mmzone.h=769=struct lruvec {\n--\ninclude/linux/mmzone.h-788-\tunsigned long\t\t\tflags;\ninclude/linux/mmzone.h:789:#ifdef CONFIG_LRU_GEN\ninclude/linux/mmzone.h-790-\t/* evictable pages divided into generations */\ninclude/linux/mmzone.h-791-\tstruct lru_gen_folio\t\tlrugen;\ninclude/linux/mmzone.h:792:#ifdef CONFIG_LRU_GEN_WALKS_MMU\ninclude/linux/mmzone.h-793-\t/* to concurrently iterate lru_gen_mm_list */\n--\ninclude/linux/mmzone.h-795-#endif\ninclude/linux/mmzone.h:796:#endif /* CONFIG_LRU_GEN */\ninclude/linux/mmzone.h-797-#ifdef CONFIG_MEMCG\n--\ninclude/linux/mmzone.h=1238=static inline bool zone_is_empty(const struct zone *zone)\n--\ninclude/linux/mmzone.h-1254-#define KASAN_TAG_PGOFF\t\t(LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH)\ninclude/linux/mmzone.h:1255:#define LRU_GEN_PGOFF\t\t(KASAN_TAG_PGOFF - LRU_GEN_WIDTH)\ninclude/linux/mmzone.h:1256:#define LRU_REFS_PGOFF\t\t(LRU_GEN_PGOFF - LRU_REFS_WIDTH)\ninclude/linux/mmzone.h-1257-\n--\ninclude/linux/mmzone.h=1481=typedef struct pglist_data {\n--\ninclude/linux/mmzone.h-1598-\ninclude/linux/mmzone.h:1599:#ifdef CONFIG_LRU_GEN\ninclude/linux/mmzone.h-1600-\t/* kswap mm walk data */\n--\ninclude/linux/page-flags-layout.h-57-\ninclude/linux/page-flags-layout.h:58:#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_SHIFT \\\ninclude/linux/page-flags-layout.h-59-\t\u003c= BITS_PER_LONG - NR_PAGEFLAGS\n--\ninclude/linux/page-flags-layout.h-94-\ninclude/linux/page-flags-layout.h:95:#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \\\ninclude/linux/page-flags-layout.h-96-\tKASAN_TAG_WIDTH + LAST_CPUPID_SHIFT \u003c= BITS_PER_LONG - NR_PAGEFLAGS\n--\ninclude/linux/page-flags-layout.h-105-\ninclude/linux/page-flags-layout.h:106:#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \\\ninclude/linux/page-flags-layout.h-107-\tKASAN_TAG_WIDTH + LAST_CPUPID_WIDTH \u003e BITS_PER_LONG - NR_PAGEFLAGS\n--\ninclude/linux/page-flags-layout.h-112-#define LRU_REFS_WIDTH\tmin(__LRU_REFS_WIDTH, BITS_PER_LONG - NR_PAGEFLAGS - \\\ninclude/linux/page-flags-layout.h:113:\t\t\t ZONES_WIDTH - LRU_GEN_WIDTH - SECTIONS_WIDTH - \\\ninclude/linux/page-flags-layout.h-114-\t\t\t NODES_WIDTH - KASAN_TAG_WIDTH - LAST_CPUPID_WIDTH)\n--\ninclude/linux/page-flags-layout.h-117-\t\t\t\tLAST_CPUPID_SHIFT + KASAN_TAG_WIDTH + \\\ninclude/linux/page-flags-layout.h:118:\t\t\t\tLRU_GEN_WIDTH + LRU_REFS_WIDTH)\ninclude/linux/page-flags-layout.h-119-\n--\ninclude/linux/page-flags.h=1154=static __always_inline void __ClearPageAnonExclusive(struct page *page)\n--\ninclude/linux/page-flags.h-1175-\t 1UL \u003c\u003c PG_active \t|\t\t\t\t\\\ninclude/linux/page-flags.h:1176:\t 1UL \u003c\u003c PG_unevictable\t| __PG_MLOCKED | LRU_GEN_MASK)\ninclude/linux/page-flags.h-1177-\n--\ninclude/linux/page-flags.h-1186-#define PAGE_FLAGS_CHECK_AT_PREP\t\\\ninclude/linux/page-flags.h:1187:\t((PAGEFLAGS_MASK \u0026 ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK)\ninclude/linux/page-flags.h-1188-\n--\ninclude/linux/sched.h=826=struct task_struct {\n--\ninclude/linux/sched.h-1024-#endif\ninclude/linux/sched.h:1025:#ifdef CONFIG_LRU_GEN\ninclude/linux/sched.h-1026-\t/* whether the LRU algorithm may apply to this access */\n--\ninclude/linux/swap.h=146=struct reclaim_state {\n--\ninclude/linux/swap.h-148-\tunsigned long reclaimed;\ninclude/linux/swap.h:149:#ifdef CONFIG_LRU_GEN\ninclude/linux/swap.h-150-\t/* per-thread mm walk data */\n--\nkernel/bounds.c=17=int main(void)\n--\nkernel/bounds.c-25-\tDEFINE(SPINLOCK_SIZE, sizeof(spinlock_t));\nkernel/bounds.c:26:#ifdef CONFIG_LRU_GEN\nkernel/bounds.c:27:\tDEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1));\nkernel/bounds.c-28-\tDEFINE(__LRU_REFS_WIDTH, MAX_NR_TIERS - 2);\nkernel/bounds.c-29-#else\nkernel/bounds.c:30:\tDEFINE(LRU_GEN_WIDTH, 0);\nkernel/bounds.c-31-\tDEFINE(__LRU_REFS_WIDTH, 0);\n--\nkernel/fork.c=2694=pid_t kernel_clone(struct kernel_clone_args *args)\n--\nkernel/fork.c-2771-\nkernel/fork.c:2772:\tif (IS_ENABLED(CONFIG_LRU_GEN_WALKS_MMU) \u0026\u0026 !(clone_flags \u0026 CLONE_VM)) {\nkernel/fork.c-2773-\t\t/* lock the task to synchronize with memcg migration */\n--\nmm/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\"\nmm/Kconfig:1415:\tdepends on LRU_GEN\nmm/Kconfig-1416-\thelp\n--\nmm/Kconfig-1418-\nmm/Kconfig:1419:config LRU_GEN_STATS\nmm/Kconfig-1420-\tbool \"Full stats for debugging\"\nmm/Kconfig:1421:\tdepends on LRU_GEN\nmm/Kconfig-1422-\thelp\n--\nmm/Kconfig-1427-\nmm/Kconfig:1428:config LRU_GEN_WALKS_MMU\nmm/Kconfig-1429-\tdef_bool y\nmm/Kconfig:1430:\tdepends on LRU_GEN \u0026\u0026 ARCH_HAS_HW_PTE_YOUNG\nmm/Kconfig-1431-# }\n--\nmm/folio.c=322=static void __lru_cache_activate_folio(struct folio *folio)\n--\nmm/folio.c-351-\nmm/folio.c:352:#ifdef CONFIG_LRU_GEN\nmm/folio.c-353-\n--\nmm/folio.c=380=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-398-\nmm/folio.c:399:#else /* !CONFIG_LRU_GEN */\nmm/folio.c-400-\n--\nmm/folio.c=405=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-409-\nmm/folio.c:410:#endif /* CONFIG_LRU_GEN */\nmm/folio.c-411-\n--\nmm/huge_memory.c=3643=static void __split_folio_to_order(struct folio *folio, int old_order,\n--\nmm/huge_memory.c-3703-\t\t\t\t (1L \u003c\u003c PG_dropbehind) |\nmm/huge_memory.c:3704:\t\t\t\t LRU_GEN_MASK | LRU_REFS_MASK));\nmm/huge_memory.c-3705-\n--\nmm/memcontrol.c=4610=static void mem_cgroup_exit(struct task_struct *task)\n--\nmm/memcontrol.c-4626-\nmm/memcontrol.c:4627:#ifdef CONFIG_LRU_GEN\nmm/memcontrol.c-4628-static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset)\n--\nmm/memcontrol.c=4646=static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset) {}\nmm/memcontrol.c:4647:#endif /* CONFIG_LRU_GEN */\nmm/memcontrol.c-4648-\n--\nmm/memory.c=6725=static inline void mm_account_fault(struct mm_struct *mm, struct pt_regs *regs,\n--\nmm/memory.c-6777-\nmm/memory.c:6778:#ifdef CONFIG_LRU_GEN\nmm/memory.c-6779-static void lru_gen_enter_fault(struct vm_area_struct *vma)\n--\nmm/memory.c=6794=static void lru_gen_exit_fault(void)\n--\nmm/memory.c-6796-}\nmm/memory.c:6797:#endif /* CONFIG_LRU_GEN */\nmm/memory.c-6798-\n--\nmm/mm_init.c=112=void __init mminit_verify_pageflags_layout(void)\n--\nmm/mm_init.c-125-\t\tKASAN_TAG_WIDTH,\nmm/mm_init.c:126:\t\tLRU_GEN_WIDTH,\nmm/mm_init.c-127-\t\tLRU_REFS_WIDTH,\n--\nmm/vmscan.c=827=enum folio_references {\n--\nmm/vmscan.c-832-\nmm/vmscan.c:833:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-834-/*\n--\nmm/vmscan.c=865=static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\n--\nmm/vmscan.c-868-}\nmm/vmscan.c:869:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-870-\n--\nmm/vmscan.c=2683=static bool can_age_anon_pages(struct lruvec *lruvec,\n--\nmm/vmscan.c-2694-\nmm/vmscan.c:2695:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-2696-\nmm/vmscan.c=2697=DEFINE_STATIC_KEY_FALSE(lru_switch);\nmm/vmscan.c:2698:#ifdef CONFIG_LRU_GEN_ENABLED\nmm/vmscan.c:2699:DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);\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])\n--\nmm/vmscan.c=2706=static bool should_walk_mmu(void)\nmm/vmscan.c-2707-{\nmm/vmscan.c:2708:\treturn arch_has_hw_pte_young() \u0026\u0026 get_cap(LRU_GEN_MM_WALK);\nmm/vmscan.c-2709-}\n--\nmm/vmscan.c=2711=static bool should_clear_pmd_young(void)\nmm/vmscan.c-2712-{\nmm/vmscan.c:2713:\treturn arch_has_hw_nonleaf_pmd_young() \u0026\u0026 get_cap(LRU_GEN_NONLEAF_YOUNG);\nmm/vmscan.c-2714-}\n--\nmm/vmscan.c-2724-\tunsigned long min_seq[ANON_AND_FILE] = {\t\t\t\\\nmm/vmscan.c:2725:\t\tREAD_ONCE((lruvec)-\u003elrugen.min_seq[LRU_GEN_ANON]),\t\\\nmm/vmscan.c:2726:\t\tREAD_ONCE((lruvec)-\u003elrugen.min_seq[LRU_GEN_FILE]),\t\\\nmm/vmscan.c-2727-\t}\n--\nmm/vmscan.c=2883=static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\n--\nmm/vmscan.c-2902-\nmm/vmscan.c:2903:#ifdef CONFIG_LRU_GEN_WALKS_MMU\nmm/vmscan.c-2904-\n--\nmm/vmscan.c=3012=void lru_gen_migrate_mm(struct mm_struct *mm)\n--\nmm/vmscan.c-3040-\nmm/vmscan.c:3041:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\nmm/vmscan.c-3042-\n--\nmm/vmscan.c=3957=static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-3993-\nmm/vmscan.c:3994:\t\tif (min_seq[LRU_GEN_ANON] \u003e seq \u0026\u0026 min_seq[LRU_GEN_FILE] \u003c seq)\nmm/vmscan.c:3995:\t\t\tmin_seq[LRU_GEN_ANON] = seq;\nmm/vmscan.c:3996:\t\telse if (min_seq[LRU_GEN_FILE] \u003e seq \u0026\u0026 min_seq[LRU_GEN_ANON] \u003c seq)\nmm/vmscan.c:3997:\t\t\tmin_seq[LRU_GEN_FILE] = seq;\nmm/vmscan.c-3998-\t}\n--\nmm/vmscan.c=4009=static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness)\n--\nmm/vmscan.c-4041-\t * the current max_seq need to be covered, since max_seq+1 can overlap\nmm/vmscan.c:4042:\t * with min_seq[LRU_GEN_ANON] if swapping is constrained. And if they do\nmm/vmscan.c-4043-\t * overlap, cold/hot inversion happens.\n--\nmm/vmscan.c=4818=static int get_type_to_scan(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-4822-\tif (swappiness \u003c= MIN_SWAPPINESS + 1)\nmm/vmscan.c:4823:\t\treturn LRU_GEN_FILE;\nmm/vmscan.c-4824-\nmm/vmscan.c-4825-\tif (swappiness \u003e= MAX_SWAPPINESS)\nmm/vmscan.c:4826:\t\treturn LRU_GEN_ANON;\nmm/vmscan.c-4827-\t/*\n--\nmm/vmscan.c-4830-\t */\nmm/vmscan.c:4831:\tread_ctrl_pos(lruvec, LRU_GEN_ANON, LRU_TIER_MIN, LRU_TIER_MAX,\nmm/vmscan.c-4832-\t\t swappiness, \u0026sp);\nmm/vmscan.c:4833:\tread_ctrl_pos(lruvec, LRU_GEN_FILE, LRU_TIER_MIN, LRU_TIER_MAX,\nmm/vmscan.c-4834-\t\t MAX_SWAPPINESS - swappiness, \u0026pv);\n--\nmm/vmscan.c=5358=static void lru_gen_change_state(bool enabled)\n--\nmm/vmscan.c-5374-\tif (enabled)\nmm/vmscan.c:5375:\t\tstatic_branch_enable_cpuslocked(\u0026lru_gen_caps[LRU_GEN_CORE]);\nmm/vmscan.c-5376-\telse\nmm/vmscan.c:5377:\t\tstatic_branch_disable_cpuslocked(\u0026lru_gen_caps[LRU_GEN_CORE]);\nmm/vmscan.c-5378-\n--\nmm/vmscan.c=5439=static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n--\nmm/vmscan.c-5442-\nmm/vmscan.c:5443:\tif (get_cap(LRU_GEN_CORE))\nmm/vmscan.c:5444:\t\tcaps |= BIT(LRU_GEN_CORE);\nmm/vmscan.c-5445-\nmm/vmscan.c-5446-\tif (should_walk_mmu())\nmm/vmscan.c:5447:\t\tcaps |= BIT(LRU_GEN_MM_WALK);\nmm/vmscan.c-5448-\nmm/vmscan.c-5449-\tif (should_clear_pmd_young())\nmm/vmscan.c:5450:\t\tcaps |= BIT(LRU_GEN_NONLEAF_YOUNG);\nmm/vmscan.c-5451-\n--\nmm/vmscan.c=5456=static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,\n--\nmm/vmscan.c-5468-\nmm/vmscan.c:5469:\tfor (i = 0; i \u003c NR_LRU_GEN_CAPS; i++) {\nmm/vmscan.c-5470-\t\tbool enabled = caps \u0026 BIT(i);\nmm/vmscan.c-5471-\nmm/vmscan.c:5472:\t\tif (i == LRU_GEN_CORE)\nmm/vmscan.c-5473-\t\t\tlru_gen_change_state(enabled);\n--\nmm/vmscan.c=5933=static int __init init_lru_gen(void)\n--\nmm/vmscan.c-5935-\tBUILD_BUG_ON(MIN_NR_GENS + 1 \u003e= MAX_NR_GENS);\nmm/vmscan.c:5936:\tBUILD_BUG_ON(BIT(LRU_GEN_WIDTH) \u003c= MAX_NR_GENS);\nmm/vmscan.c-5937-\n--\nmm/vmscan.c=5948=late_initcall(init_lru_gen);\nmm/vmscan.c-5949-\nmm/vmscan.c:5950:#else /* !CONFIG_LRU_GEN */\nmm/vmscan.c-5951-\n--\nmm/vmscan.c=5962=static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5966-\nmm/vmscan.c:5967:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-5968-\n--\nmm/workingset.c=213=static void unpack_shadow(void *shadow, int *memcgidp, pg_data_t **pgdat,\n--\nmm/workingset.c-232-\nmm/workingset.c:233:#ifdef CONFIG_LRU_GEN\nmm/workingset.c-234-\nmm/workingset.c=235=static void *lru_gen_eviction(struct folio *folio)\n--\nmm/workingset.c-250-\nmm/workingset.c:251:\tBUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH \u003e\nmm/workingset.c-252-\t\t BITS_PER_LONG - max(EVICTION_SHIFT, EVICTION_SHIFT_ANON));\n--\nmm/workingset.c=292=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-336-\nmm/workingset.c:337:#else /* !CONFIG_LRU_GEN */\nmm/workingset.c-338-\n--\nmm/workingset.c=350=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-353-\nmm/workingset.c:354:#endif /* CONFIG_LRU_GEN */\nmm/workingset.c-355-\n--\ntools/testing/selftests/kvm/include/lru_gen_util.h-6- */\ntools/testing/selftests/kvm/include/lru_gen_util.h:7:#ifndef SELFTEST_KVM_LRU_GEN_UTIL_H\ntools/testing/selftests/kvm/include/lru_gen_util.h:8:#define SELFTEST_KVM_LRU_GEN_UTIL_H\ntools/testing/selftests/kvm/include/lru_gen_util.h-9-\n--\ntools/testing/selftests/kvm/include/lru_gen_util.h-18-\ntools/testing/selftests/kvm/include/lru_gen_util.h:19:#define LRU_GEN_DEBUGFS \"/sys/kernel/debug/lru_gen\"\ntools/testing/selftests/kvm/include/lru_gen_util.h:20:#define LRU_GEN_ENABLED_PATH \"/sys/kernel/mm/lru_gen/enabled\"\ntools/testing/selftests/kvm/include/lru_gen_util.h:21:#define LRU_GEN_ENABLED 1\ntools/testing/selftests/kvm/include/lru_gen_util.h:22:#define LRU_GEN_MM_WALK 2\ntools/testing/selftests/kvm/include/lru_gen_util.h-23-\n--\ntools/testing/selftests/kvm/include/lru_gen_util.h=49=bool lru_gen_usable(void);\ntools/testing/selftests/kvm/include/lru_gen_util.h-50-\ntools/testing/selftests/kvm/include/lru_gen_util.h:51:#endif /* SELFTEST_KVM_LRU_GEN_UTIL_H */\n--\ntools/testing/selftests/kvm/lib/lru_gen_util.c=201=void lru_gen_read_memcg_stats(struct memcg_stats *stats, const char *memcg)\n--\ntools/testing/selftests/kvm/lib/lru_gen_util.c-213-\ntools/testing/selftests/kvm/lib/lru_gen_util.c:214:\tf = fopen(LRU_GEN_DEBUGFS, \"r\");\n\n"
} |
| 5/2 | 2026/08/24 17:58 | llm | patch-evaluator |
5mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21738 output: 19 thoughts: 228 Thoughts: **Analyzing a Kernel Memory Management Patch** Okay, so this patch is touching some pretty fundamental memory management code. I see changes in `mm/folio.c`, `mm/memcontrol.c`, `mm/migrate.c`, and `mm/vmscan.c`. These are all files I'm very familiar with – the core of how the kernel handles memory, specifically with the folio abstraction, memory control groups, migration, and the virtual memory scanner. I recognize functions like `lru_gen_inc_refs`, `folio_migrate_flags`, and `isolate_folio`. This is definitely in the heart of the LRU (Least Recently Used) and page reclaim logic, which is critical for system performance and stability. The function names give me a clear picture of what's happening. `lru_gen_*` functions suggest something is changing with the LRU generation counters. We're updating LRU sizes with `mem_cgroup_update_lru_size` and migrating folios with `folio_migrate_flags`. I see functions like `walk_update_folio`, `walk_pte_range`, and `walk_pmd_range_locked` which suggest that we're dealing with page table manipulation. These functions are responsible for the core memory accounting operations. This has to be related to `CONFIG_LRU_GEN`. I'm definitely going to check `mm/Kconfig` to confirm what that's all about and see how this feature is enabled. I need to understand this `LRU_GEN` concept and how these changes impact the overall memory management strategy. It's a key part of this patch, I need to know how the pages are being treated and how that effects memory allocation and deallocation. |
| 6/2 | 2026/08/24 18:03 | tool | grepper |
0mArgs:{
"Expression": "config LRU_GEN"
}Results: {
"Output": "mm/Kconfig=1393=config PTE_MARKER_UFFD_WP\n--\nmm/Kconfig-1403-# multi-gen LRU {\nmm/Kconfig:1404:config LRU_GEN\nmm/Kconfig-1405-\tbool \"Multi-Gen LRU\"\n--\nmm/Kconfig-1412-\nmm/Kconfig:1413:config LRU_GEN_ENABLED\nmm/Kconfig-1414-\tbool \"Enable by default\"\n--\nmm/Kconfig-1418-\nmm/Kconfig:1419:config LRU_GEN_STATS\nmm/Kconfig-1420-\tbool \"Full stats for debugging\"\n--\nmm/Kconfig-1427-\nmm/Kconfig:1428:config LRU_GEN_WALKS_MMU\nmm/Kconfig-1429-\tdef_bool y\n"
} |
| 7/2 | 2026/08/24 18:03 | llm | patch-evaluator |
4mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22592 output: 33 thoughts: 25 Thoughts: Here's my thought process, as it went down: **Investigating `LRU_GEN` in `mm/Kconfig`** Okay, so I need to understand the dependencies around `LRU_GEN`. Let's dive straight into `mm/Kconfig` and zoom in on that specific symbol. I need to quickly identify any other config options that might influence or require `LRU_GEN`. Knowing its relationships will help me understand its implications, potential conflicts, or any preconditions that need to be met. I'm looking for the "why" and "when" this is enabled, not just "what" it is. Let's see what the Kconfig file reveals... |
| 8/2 | 2026/08/24 18:08 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 1400,
"LineCount": 50
}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\n1440:\t\t Allow per-vma locking during page fault handling.\n1441:\t\n1442:\t\t This feature allows locking each virtual memory area separately when\n1443:\t\t handling page faults instead of taking mmap_lock.\n1444:\t\n1445:\tconfig LOCK_MM_AND_FIND_VMA\n1446:\t\tbool\n1447:\t\tdepends on !STACK_GROWSUP\n1448:\t\n1449:\tconfig IOMMU_MM_DATA\n"
} |
| 9/2 | 2026/08/24 18:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23514 output: 130 thoughts: 350 Thoughts: **My Initial Assessment of the LRU_GEN Patch** Okay, so we're enabling `LRU_GEN` and `LRU_GEN_ENABLED`. Good. Always good to see this feature being brought online. Now, let's dive into the core of this patch and see what's being touched. We need to understand the scope of these changes. Alright, the patch modifies a bunch of functions. First up, `mem_cgroup_update_lru_size` in `mm/memcontrol.c`. That's expected; it's the obvious place for LRU updates to interface with memory cgroup accounting. Then, we have `folio_migrate_flags` in `mm/migrate.c`. Interesting. This likely means LRU generation is integrated into page migration logic. Good to know. Next, a slew of functions in `mm/vmscan.c`: `lru_gen_look_around`, `evict_folios`, `get_type_to_scan`, `get_tier_idx`, `isolate_folio`, `walk_pmd_range_locked`, `walk_pte_range`, `folio_inc_gen`, `folio_update_gen`, `lru_gen_set_refs`. Lots of core VM scanning and eviction logic is being updated. That's the heart of the matter, so this makes sense. This includes functions that directly manipulate LRU generation counters. Finally, we see some updates in `mm/folio.c`: `lru_gen_clear_refs` and `lru_gen_inc_refs`. These are likely to manage reference counts. Now, a critical point: while many of these functions might be static, we need to identify *which* of these modifications are to externally-visible functions, not just internal details. We need to check which functions are non-static and not inlined because those are the interfaces that might be used by other parts of the kernel, potentially impacting other subsystems. Let's make sure we understand the *extent* of these changes and potential downstream impact. |
| 10/2 | 2026/08/24 18:08 | tool | grepper |
0mArgs:{
"Expression": "^(void|int|bool|unsigned long).* (mem_cgroup_update_lru_size|folio_migrate_flags|lru_gen_look_around|evict_folios|get_type_to_scan|get_tier_idx|isolate_folio|walk_pmd_range_locked|walk_pte_range|folio_inc_gen|folio_update_gen|lru_gen_set_refs|lru_gen_clear_refs|lru_gen_inc_refs)\\("
}Results: {
"Output": "include/linux/memcontrol.h=891=static inline bool mem_cgroup_online(struct mem_cgroup *memcg)\n--\ninclude/linux/memcontrol.h-897-\ninclude/linux/memcontrol.h:898:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\ninclude/linux/memcontrol.h-899-\t\tint zid, long nr_pages);\n--\ninclude/linux/migrate.h=69=void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)\ninclude/linux/migrate.h-70-\t\t__releases(ptl);\ninclude/linux/migrate.h:71:void folio_migrate_flags(struct folio *newfolio, struct folio *folio);\ninclude/linux/migrate.h-72-int folio_migrate_mapping(struct address_space *mapping,\n--\ninclude/linux/mmzone.h=694=void lru_gen_init_lruvec(struct lruvec *lruvec);\ninclude/linux/mmzone.h:695:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr);\ninclude/linux/mmzone.h-696-\n--\nmm/memcontrol.c=1501=struct lruvec *folio_lruvec_lock_irqsave(struct folio *folio,\n--\nmm/memcontrol.c-1527- */\nmm/memcontrol.c:1528:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\nmm/memcontrol.c-1529-\t\t\t\tint zid, long nr_pages)\n--\nmm/migrate.c=739=int migrate_huge_page_move_mapping(struct address_space *mapping,\n--\nmm/migrate.c-774- */\nmm/migrate.c:775:void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\nmm/migrate.c-776-{\n--\nmm/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"
} |
| 11/2 | 2026/08/24 18:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25086 output: 22 thoughts: 65 Thoughts: **Investigating Potential Non-Static Function Usage** Okay, so I'm looking at these functions: `mem_cgroup_update_lru_size`, `folio_migrate_flags`, and `lru_gen_look_around`. The key thing here is that they *aren't* declared as static. This means they're potentially being used outside of the file they're defined in, which could have implications for maintainability and modularity, and potentially make debugging more challenging. Specifically, I want to confirm whether `lru_gen_look_around` is actually used outside of the `mm/vmscan.c` file. It's good practice to ensure the scope of functions is as limited as possible. I'll need to do a code search to verify this. If it's *only* used within `mm/vmscan.c`, then declaring it static would be a good optimization and cleaner design. If it *is* called elsewhere, I need to understand why and assess if it's necessary to maintain that external dependency. This is all about ensuring the code's structure aligns with its intended use and maintaining a clear understanding of its interactions. |
| 12/2 | 2026/08/24 18:08 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_look_around"
}Results: {
"Output": "Documentation/mm/multigen_lru.rst=187=can incur the highest CPU cost in the reclaim path.\nDocumentation/mm/multigen_lru.rst-188-\nDocumentation/mm/multigen_lru.rst:189:``lru_gen_look_around()`` exploits spatial locality to reduce the\nDocumentation/mm/multigen_lru.rst-190-trips into the rmap. It scans the adjacent PTEs of a young PTE and\n--\nDocumentation/mm/multigen_lru.rst=199=in the set.\nDocumentation/mm/multigen_lru.rst-200-\nDocumentation/mm/multigen_lru.rst:201:In the eviction path, specifically, in ``lru_gen_look_around()``, if a\nDocumentation/mm/multigen_lru.rst-202-PMD has a sufficient number of hot pages, its address is placed in the\n--\ninclude/linux/mmzone.h=694=void lru_gen_init_lruvec(struct lruvec *lruvec);\ninclude/linux/mmzone.h:695:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr);\ninclude/linux/mmzone.h-696-\n--\ninclude/linux/mmzone.h=713=static inline void lru_gen_init_lruvec(struct lruvec *lruvec)\n--\ninclude/linux/mmzone.h-716-\ninclude/linux/mmzone.h:717:static inline bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw,\ninclude/linux/mmzone.h-718-\t\tunsigned int nr)\n--\nmm/rmap.c=918=static bool folio_referenced_one(struct folio *folio,\n--\nmm/rmap.c-982-\t\tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching() \u0026\u0026 pvmw.pte) {\nmm/rmap.c:983:\t\t\tif (lru_gen_look_around(\u0026pvmw, nr))\nmm/rmap.c-984-\t\t\t\treferenced++;\n--\nmm/vmscan.c=2791=static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)\n--\nmm/vmscan.c-2820- * walk_pmd_range(); the eviction also report them when walking the rmap\nmm/vmscan.c:2821: * in lru_gen_look_around().\nmm/vmscan.c-2822- *\n--\nmm/vmscan.c=4074=static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long seq,\n--\nmm/vmscan.c-4093-\t * If the hardware doesn't automatically set the accessed bit, fallback\nmm/vmscan.c:4094:\t * to lru_gen_look_around(), which only clears the accessed bit in a\nmm/vmscan.c-4095-\t * handful of PTEs. Spreading the work out over a period of time usually\n--\nmm/vmscan.c=4213=static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-4259- */\nmm/vmscan.c:4260:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\nmm/vmscan.c-4261-{\n"
} |
| 13/2 | 2026/08/24 18:08 | llm | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 26330 output: 19 thoughts: 66 Thoughts: Here's my take on those thoughts: **Analyzing Kernel Memory Management Function Calls** Okay, so I'm digging into how the kernel manages memory references and migrations. Specifically, I'm thinking about the call graph for these functions. I've confirmed that `lru_gen_look_around` *is* indeed invoked from within `mm/rmap.c`, specifically during the `folio_referenced_one` routine. That makes sense, given the role of the LRU in tracking referenced pages. Now, I'm shifting my focus to `folio_migrate_flags`. I believe that function gets called from both `mm/migrate.c`, which is the core of page migration functionality, and also from `mm/huge_memory.c`, which handles huge pages. I'm reasonably confident in this, but I should probably confirm that hunch before I move on. Just to be sure, I should check the call sites for `folio_migrate_flags` again to verify and see if there are other callers. |
| 14/2 | 2026/08/24 18:09 | tool | grepper |
0mArgs:{
"Expression": "folio_migrate_flags"
}Results: {
"Output": "fs/aio.c=460=static int aio_migrate_folio(struct address_space *mapping, struct folio *dst,\n--\nfs/aio.c-512-\tfolio_copy(dst, src);\nfs/aio.c:513:\tfolio_migrate_flags(dst, src);\nfs/aio.c-514-\tBUG_ON(ctx-\u003ering_folios[idx] != src);\n--\nfs/hugetlbfs/inode.c=1024=static int hugetlbfs_migrate_folio(struct address_space *mapping,\n--\nfs/hugetlbfs/inode.c-1039-\nfs/hugetlbfs/inode.c:1040:\tfolio_migrate_flags(dst, src);\nfs/hugetlbfs/inode.c-1041-\n--\ninclude/linux/migrate.h=69=void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)\ninclude/linux/migrate.h-70-\t\t__releases(ptl);\ninclude/linux/migrate.h:71:void folio_migrate_flags(struct folio *newfolio, struct folio *folio);\ninclude/linux/migrate.h-72-int folio_migrate_mapping(struct address_space *mapping,\n--\ninclude/linux/mm_inline.h=351=static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)\n--\ninclude/linux/mm_inline.h-361-\ninclude/linux/mm_inline.h:362:\t/* for folio_migrate_flags() */\ninclude/linux/mm_inline.h-363-\tflags = !reclaiming \u0026\u0026 lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;\n--\nmm/migrate.c=739=int migrate_huge_page_move_mapping(struct address_space *mapping,\n--\nmm/migrate.c-774- */\nmm/migrate.c:775:void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\nmm/migrate.c-776-{\n--\nmm/migrate.c-863-}\nmm/migrate.c:864:EXPORT_SYMBOL(folio_migrate_flags);\nmm/migrate.c-865-\n--\nmm/migrate.c=870=static int __migrate_folio(struct address_space *mapping, struct folio *dst,\n--\nmm/migrate.c-890-\nmm/migrate.c:891:\tfolio_migrate_flags(dst, src);\nmm/migrate.c-892-\treturn 0;\n--\nmm/migrate_device.c=1109=static void __migrate_device_pages(unsigned long *src_pfns,\n--\nmm/migrate_device.c-1251-\t\t\telse\nmm/migrate_device.c:1252:\t\t\t\tfolio_migrate_flags(newfolio, folio);\nmm/migrate_device.c-1253-\t\t}\n"
} |
| 15/2 | 2026/08/24 18:09 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27404 output: 25 thoughts: 54 Thoughts: Here's my take on those thoughts: **Analyzing Code Usage** Okay, so `folio_migrate_flags` is clearly a key component, showing up in a bunch of different modules. That's good to know, means I need to understand its impact broadly. I need to make sure I grasp its implications across the codebase before making any changes. Now, onto `mem_cgroup_update_lru_size`. This one's used in `mm/memcontrol.c` and `mm/vmscan.c`. Alright, so memory control and the virtual memory scanner, that's interesting. I should probably dive deeper into those specific files now to understand how it's being *used* in those two contexts. I need to understand its role in LRU size updates and how it might interact with other memory management functions. Gotta track down those instances to get the full picture. |
| 16/2 | 2026/08/24 18:10 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_update_lru_size"
}Results: {
"Output": "include/linux/memcontrol.h=891=static inline bool mem_cgroup_online(struct mem_cgroup *memcg)\n--\ninclude/linux/memcontrol.h-897-\ninclude/linux/memcontrol.h:898:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\ninclude/linux/memcontrol.h-899-\t\tint zid, long nr_pages);\n--\ninclude/linux/mm_inline.h=52=static __always_inline void update_lru_size(struct lruvec *lruvec,\n--\ninclude/linux/mm_inline.h-57-#ifdef CONFIG_MEMCG\ninclude/linux/mm_inline.h:58:\tmem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages);\ninclude/linux/mm_inline.h-59-#endif\n--\nmm/folio.c=1123=static void lruvec_reparent_lru(struct lruvec *child_lruvec,\n--\nmm/folio.c-1135-\nmm/folio.c:1136:\t\tmem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);\nmm/folio.c-1137-\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-4638-\nmm/vmscan.c:4639:\t\t\tmem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);\nmm/vmscan.c-4640-\t\t}\n"
} |
| 17/2 | 2026/08/24 18:10 | llm | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28439 output: 22 thoughts: 96 Thoughts: Here's my understanding of the situation, from an expert's perspective: **LRU Size Updates and Folio Reference Counts** Okay, so I'm tracking this LRU size update mechanism. It looks like `mem_cgroup_update_lru_size` is the core function, invoked through `update_lru_size` which lives in `include/linux/mm_inline.h`. That makes sense; it's designed to be inlined and called from all the key places where pages are moved to or from the Least Recently Used (LRU) lists. This is the critical update path. Now, this particular patch appears to focus on modifications within `mm/folio.c`, specifically around the functions `lru_gen_inc_refs` and `lru_gen_clear_refs`. Since the LRU and Folio code is being looked at, I need to immediately confirm whether those functions are static or not. Knowing that will be fundamental to understanding the scope and potential impact of these changes. If they're non-static, they're likely exported and are core pieces of the memory management infrastructure, making the patch even more significant. If they're static, they're more localized to `folio.c`, but could still impact other functions within that file. I need to dig deeper into the call graphs from there. |
| 18/2 | 2026/08/24 18:12 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_inc_refs"
}Results: {
"Output": "include/linux/mmzone.h=424=enum lruvec_flags {\n--\ninclude/linux/mmzone.h-505- * For folios accessed multiple times through file descriptors,\ninclude/linux/mmzone.h:506: * lru_gen_inc_refs() sets additional bits of LRU_REFS_WIDTH in folio-\u003eflags\ninclude/linux/mmzone.h-507- * after PG_referenced, then PG_workingset after LRU_REFS_WIDTH. After all its\n--\ninclude/linux/mmzone.h-510- * folio_inc_gen() does that, it clears LRU_REFS_FLAGS so that\ninclude/linux/mmzone.h:511: * lru_gen_inc_refs() can start over. Note that for this case, LRU_REFS_MASK is\ninclude/linux/mmzone.h-512- * only valid when PG_referenced is set.\n--\nmm/folio.c=322=static void __lru_cache_activate_folio(struct folio *folio)\n--\nmm/folio.c-353-\nmm/folio.c:354:static void lru_gen_inc_refs(struct folio *folio)\nmm/folio.c-355-{\n--\nmm/folio.c=380=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-400-\nmm/folio.c:401:static void lru_gen_inc_refs(struct folio *folio)\nmm/folio.c-402-{\n--\nmm/folio.c=425=void folio_mark_accessed(struct folio *folio)\n--\nmm/folio.c-429-\tif (lru_gen_enabled()) {\nmm/folio.c:430:\t\tlru_gen_inc_refs(folio);\nmm/folio.c-431-\t\treturn;\n"
} |
| 19/2 | 2026/08/24 18:12 | llm | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 29331 output: 22 thoughts: 60 Thoughts: Here's my summary, as if I were thinking these thoughts: **Analyzing LRU Reference Management in the Kernel** Okay, so I'm looking at these `lru_gen_*` functions. I see `lru_gen_inc_refs` is declared static, which is usually fine for internal functions. But, and this is important, it's called from `folio_mark_accessed`. Now, `folio_mark_accessed` is a performance-critical function, extremely high-use. That static declaration could become a significant bottleneck if the function isn't inlined or the compiler can't optimize it effectively. I need to make sure the code path there is as efficient as possible. Time to dig into the generated assembly and see what's actually happening. Now, I also see `lru_gen_clear_refs` is static. I *think* that's used by `folio_clear_workingset`, or perhaps something similar related to clearing workingset. I'd better confirm that assumption, just to be sure. I'll need to trace the call graph and see exactly where it's being used. If it's as performance critical as I suspect, I will need to be extremely careful with how this function behaves when used. |
| 20/2 | 2026/08/24 18:14 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_clear_refs"
}Results: {
"Output": "include/linux/mmzone.h=424=enum lruvec_flags {\n--\ninclude/linux/mmzone.h-521- * For both cases above, after PG_workingset is set on a folio, it remains until\ninclude/linux/mmzone.h:522: * this folio is either reclaimed, or \"deactivated\" by lru_gen_clear_refs(). It\ninclude/linux/mmzone.h-523- * can be set again if lru_gen_test_recent() returns true upon a refault.\n--\nmm/folio.c=354=static void lru_gen_inc_refs(struct folio *folio)\n--\nmm/folio.c-379-\nmm/folio.c:380:static bool lru_gen_clear_refs(struct folio *folio)\nmm/folio.c-381-{\n--\nmm/folio.c=401=static void lru_gen_inc_refs(struct folio *folio)\n--\nmm/folio.c-404-\nmm/folio.c:405:static bool lru_gen_clear_refs(struct folio *folio)\nmm/folio.c-406-{\n--\nmm/folio.c=591=static void lru_lazyfree(struct lruvec *lruvec, struct folio *folio)\n--\nmm/folio.c-601-\tif (lru_gen_enabled())\nmm/folio.c:602:\t\tlru_gen_clear_refs(folio);\nmm/folio.c-603-\telse\n--\nmm/folio.c=669=void deactivate_file_folio(struct folio *folio)\n--\nmm/folio.c-674-\nmm/folio.c:675:\tif (lru_gen_enabled() \u0026\u0026 lru_gen_clear_refs(folio))\nmm/folio.c-676-\t\treturn;\n--\nmm/folio.c=689=void folio_deactivate(struct folio *folio)\n--\nmm/folio.c-693-\nmm/folio.c:694:\tif (lru_gen_enabled() ? lru_gen_clear_refs(folio) : !folio_test_active(folio))\nmm/folio.c-695-\t\treturn;\n"
} |
| 21/2 | 2026/08/24 18:14 | llm | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 30259 output: 19 thoughts: 64 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Analyzing `lru_gen_clear_refs` Usage** Alright, so `lru_gen_clear_refs` *is* called from `folio_deactivate` and `deactivate_file_folio`, which is what I suspected. Good. Now, this patch specifically touches `folio_update_gen` and `folio_inc_gen`. The real question is: are these functions used internally only, or are they exposed in some way? I need to quickly determine if they are called from outside their immediate scope. If they are, then this has far-reaching implications, and I need to consider those non-static call sites. Otherwise, if they are internal, I can probably stick to the primary call-sites mentioned, and work back from there. |
| 22/2 | 2026/08/24 18:16 | tool | grepper |
0mArgs:{
"Expression": "folio_update_gen"
}Results: {
"Output": "include/linux/mm_inline.h=287=static inline unsigned long lru_gen_folio_seq(const struct lruvec *lruvec,\n--\ninclude/linux/mm_inline.h-297-\t * | Accessed through page tables and | Accessed through file descriptors |\ninclude/linux/mm_inline.h:298:\t * | promoted by folio_update_gen() | and protected by folio_inc_gen() |\ninclude/linux/mm_inline.h-299-\t * +-----------------------------------+-----------------------------------+\n--\ninclude/linux/mmzone.h=424=enum lruvec_flags {\n--\ninclude/linux/mmzone.h-513- *\ninclude/linux/mmzone.h:514: * For folios accessed multiple times through page tables, folio_update_gen()\ninclude/linux/mmzone.h-515- * from a page table walk or lru_gen_set_refs() from a rmap walk sets\n--\ninclude/linux/mmzone.h-517- * Thereafter, those two paths set PG_workingset and promote folios to the\ninclude/linux/mmzone.h:518: * youngest generation. Like folio_inc_gen(), folio_update_gen() also clears\ninclude/linux/mmzone.h-519- * PG_referenced. Note that for this case, LRU_REFS_MASK is not used.\n--\nmm/vmscan.c=827=enum folio_references {\n--\nmm/vmscan.c-837- * with PG_active set. In contrast, the aging (page table walk) path uses\nmm/vmscan.c:838: * folio_update_gen().\nmm/vmscan.c-839- */\n--\nmm/vmscan.c=3256=static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)\n--\nmm/vmscan.c-3271-/* promote pages accessed through page tables */\nmm/vmscan.c:3272:static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,\nmm/vmscan.c-3273-\t\t\t const vma_flags_t *vma_flags)\n--\nmm/vmscan.c=3307=static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n--\nmm/vmscan.c-3316-\nmm/vmscan.c:3317:\t\t/* folio_update_gen() has promoted this page? */\nmm/vmscan.c-3318-\t\tif (new_gen \u003e= 0 \u0026\u0026 new_gen != old_gen)\n--\nmm/vmscan.c=3520=static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,\n--\nmm/vmscan.c-3535-\tif (walk) {\nmm/vmscan.c:3536:\t\told_gen = folio_update_gen(folio, new_gen, \u0026file, \u0026vma-\u003eflags);\nmm/vmscan.c-3537-\t\tif (old_gen \u003e= 0 \u0026\u0026 old_gen != new_gen)\n"
} |
| 23/2 | 2026/08/24 18:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 31331 output: 19 thoughts: 121 Thoughts: **Analyzing the Folio Generation Update Functions** Okay, so I'm tracing the calls related to folio generation updates. My initial thought is that `folio_update_gen` is triggered from `walk_update_folio`, which in turn gets called from a few key places: `walk_pte_range`, `walk_pmd_range_locked`, and `lru_gen_look_around`. The first two, `walk_pte_range` and `walk_pmd_range_locked`, immediately ping my radar as they're foundational for page table walking within `mm/vmscan.c`. This suggests a strong connection to memory management and particularly the scanning processes. Then, there's `folio_inc_gen`. This seems to be another crucial function for folio generation. My hunch is that this is likely called from the `mm/vmscan.c` code, specifically during folio eviction or isolation, likely from `evict_folios` or `isolate_folio`. I'll need to confirm that, but it seems very probable given the context. I'll take a quick look at the code to verify those assumptions. |
| 24/2 | 2026/08/24 18:16 | tool | grepper |
0mArgs:{
"Expression": "folio_inc_gen"
}Results: {
"Output": "include/linux/mm_inline.h=287=static inline unsigned long lru_gen_folio_seq(const struct lruvec *lruvec,\n--\ninclude/linux/mm_inline.h-297-\t * | Accessed through page tables and | Accessed through file descriptors |\ninclude/linux/mm_inline.h:298:\t * | promoted by folio_update_gen() | and protected by folio_inc_gen() |\ninclude/linux/mm_inline.h-299-\t * +-----------------------------------+-----------------------------------+\n--\ninclude/linux/mmzone.h=424=enum lruvec_flags {\n--\ninclude/linux/mmzone.h-509- * promoted into the second oldest generation in the eviction path. And when\ninclude/linux/mmzone.h:510: * folio_inc_gen() does that, it clears LRU_REFS_FLAGS so that\ninclude/linux/mmzone.h-511- * lru_gen_inc_refs() can start over. Note that for this case, LRU_REFS_MASK is\n--\ninclude/linux/mmzone.h-517- * Thereafter, those two paths set PG_workingset and promote folios to the\ninclude/linux/mmzone.h:518: * youngest generation. Like folio_inc_gen(), folio_update_gen() also clears\ninclude/linux/mmzone.h-519- * PG_referenced. Note that for this case, LRU_REFS_MASK is not used.\n--\nmm/vmscan.c=3272=static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,\n--\nmm/vmscan.c-3306-/* protect pages accessed multiple times through file descriptors */\nmm/vmscan.c:3307:static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\nmm/vmscan.c-3308-{\n--\nmm/vmscan.c=3904=static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)\n--\nmm/vmscan.c-3933-\nmm/vmscan.c:3934:\t\t\tnew_gen = folio_inc_gen(lruvec, folio);\nmm/vmscan.c-3935-\t\t\tlist_move_tail(\u0026folio-\u003elru, \u0026lrugen-\u003efolios[new_gen][type][zone]);\n--\nmm/vmscan.c=4650=static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc,\n--\nmm/vmscan.c-4675-\tif (tier \u003e tier_idx || refs + workingset == BIT(LRU_REFS_WIDTH) + 1) {\nmm/vmscan.c:4676:\t\tgen = folio_inc_gen(lruvec, folio);\nmm/vmscan.c-4677-\t\tlist_move(\u0026folio-\u003elru, \u0026lrugen-\u003efolios[gen][type][zone]);\n--\nmm/vmscan.c-4690-\tif (zone \u003e sc-\u003ereclaim_idx) {\nmm/vmscan.c:4691:\t\tgen = folio_inc_gen(lruvec, folio);\nmm/vmscan.c-4692-\t\tlist_move_tail(\u0026folio-\u003elru, \u0026lrugen-\u003efolios[gen][type][zone]);\n"
} |
| 25/2 | 2026/08/24 18:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32572 output: 17 thoughts: 1210 Thoughts: **MGLRU Core Implementation Changes & Fuzzing Strategy** Okay, so I've been digging into these changes and it's clear we're dealing with modifications deep within the core MGLRU implementation. We have `folio_inc_gen` being used by `inc_min_seq` and `sort_folio`, and `sort_folio` in turn is called by `evict_folios`, that's a key piece of the puzzle. It looks like the patch is touching several critical areas. Specifically, I see: * `mem_cgroup_update_lru_size` is now using `atomic_long_add`, which makes sense for concurrency. * `folio_migrate_flags` is being updated to use `folio_migrate_lru_refs`. * `lru_gen_look_around` now passes `lruvec` to `walk_update_folio`. * `evict_folios` now uses `folio_set_lru_refs(folio, 0)` instead of `set_mask_bits`, which is a change in the reference counting mechanism. * `get_type_to_scan` and `get_tier_idx` are now passing `LRU_TIER_MIN` and `LRU_TIER_MAX` to `read_ctrl_pos`. * `isolate_folio` is now using `folio_set_lru_refs(folio, 0)`. * `walk_pmd_range_locked` and `walk_pte_range` are passing `walk->lruvec` to `walk_update_folio`. * `folio_inc_gen` and `folio_update_gen` are using `lru_gen_from_flags`, `lru_gen_set_flags`, `lru_refs_set_flags`. * `lru_gen_set_refs` uses `folio_set_lru_refs`, and * `lru_gen_clear_refs` and `lru_gen_inc_refs` are also leveraging `folio_set_lru_refs` and `lru_refs_from_flags`. Given the scope and the core nature of these changes, this is *definitely* worth a solid fuzzing campaign. The required config is `LRU_GEN`, of course, and we should consider enabling `LRU_GEN_ENABLED` to activate it by default, or maybe `LRU_GEN_STATS` to enable more code paths. Good to see that `LRU_GEN_WALKS_MMU` is enabled by default. Now, let's nail down the critical symbols we should be focusing on: * `mem_cgroup_update_lru_size` * `folio_migrate_flags` * `lru_gen_look_around` * `folio_mark_accessed` (important as it calls `lru_gen_inc_refs`) * `folio_deactivate` (calling `lru_gen_clear_refs`) * `deactivate_file_folio` (calls `lru_gen_clear_refs`) * `lru_lazyfree` (calls `lru_gen_clear_refs`) Hmm, `mem_cgroup_update_lru_size` is called frequently – could be a hot-path function. `folio_migrate_flags` is clearly relevant to page migration. `lru_gen_look_around` is used during page reclaim/rmap walking. `folio_mark_accessed` is also a hot-path for sure. `folio_deactivate`, `deactivate_file_folio`, and `lru_lazyfree` are all called during page reclaim or with MADV_FREE, good to keep in mind. We need to make sure we consider any non-static functions modified or directly calling the modified static functions. `evict_folios` is called from `lru_gen_shrink_lruvec` through to `shrink_node`, `get_type_to_scan` and `get_tier_idx` are called from `evict_folios`, `isolate_folio` is also called from `evict_folios`, `walk_pmd_range_locked` is called from `lru_gen_shrink_node` through other static functions, `walk_pte_range` is related to `walk_pmd_range_locked`, `folio_inc_gen` called from `inc_min_seq` and `sort_folio`, `folio_update_gen` called from `walk_update_folio`, and `lru_gen |
| 26/2 | 2026/08/24 18:17 | tool | grepper |
0mArgs:{
"Expression": "shrink_node"
}Results: {
"Output": "mm/internal.h=83=unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,\n--\nmm/internal.h-87-\t\t\t\t\t int *swappiness);\nmm/internal.h:88:unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,\nmm/internal.h-89-\t\t\t\t gfp_t gfp_mask, bool noswap,\n--\nmm/memcontrol-v1.c=272=static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,\n--\nmm/memcontrol-v1.c-311-\t\t}\nmm/memcontrol-v1.c:312:\t\ttotal += mem_cgroup_shrink_node(victim, gfp_mask, false,\nmm/memcontrol-v1.c-313-\t\t\t\t\tpgdat, \u0026nr_scanned);\n--\nmm/page_isolation.c=19=bool page_is_unmovable(struct zone *zone, struct page *page,\n--\nmm/page_isolation.c-114-\t * it. But now, memory offline itself doesn't call\nmm/page_isolation.c:115:\t * shrink_node_slabs() and it still to be fixed.\nmm/page_isolation.c-116-\t */\n--\nmm/vmpressure.c=57=enum vmpressure_levels vmpressure_calc_level(unsigned long scanned,\n--\nmm/vmpressure.c-64-\t * reclaimed can be greater than scanned for things such as reclaimed\nmm/vmpressure.c:65:\t * slab pages. shrink_node() just adds reclaimed pages without a\nmm/vmpressure.c-66-\t * related increment to scanned pages.\n--\nmm/vmscan.c=1944=static void handle_reclaim_writeback(unsigned long nr_taken,\n--\nmm/vmscan.c-1982-/*\nmm/vmscan.c:1983: * shrink_inactive_list() is a helper for shrink_node(). It returns the number\nmm/vmscan.c-1984- * of reclaimed pages\n--\nmm/vmscan.c=5208=static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5228-\nmm/vmscan.c:5229:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-5230-{\n--\nmm/vmscan.c=5957=static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5961-\nmm/vmscan.c:5962:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-5963-{\n--\nmm/vmscan.c=6108=static inline bool should_continue_reclaim(struct pglist_data *pgdat,\n--\nmm/vmscan.c-6159-\nmm/vmscan.c:6160:static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)\nmm/vmscan.c-6161-{\n--\nmm/vmscan.c-6238-\nmm/vmscan.c:6239:static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)\nmm/vmscan.c-6240-{\n--\nmm/vmscan.c-6246-\t\tmemset(\u0026sc-\u003enr, 0, sizeof(sc-\u003enr));\nmm/vmscan.c:6247:\t\tlru_gen_shrink_node(pgdat, sc);\nmm/vmscan.c-6248-\n--\nmm/vmscan.c-6263-\nmm/vmscan.c:6264:\tshrink_node_memcgs(pgdat, sc);\nmm/vmscan.c-6265-\n--\nmm/vmscan.c=6424=static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc)\n--\nmm/vmscan.c-6502-\t\tlast_pgdat = zone-\u003ezone_pgdat;\nmm/vmscan.c:6503:\t\tshrink_node(zone-\u003ezone_pgdat, sc);\nmm/vmscan.c-6504-\t}\n--\nmm/vmscan.c=6769=unsigned long try_to_free_pages(struct zonelist *zonelist, int order,\n--\nmm/vmscan.c-6814-/* Only used by soft limit reclaim. Do not reuse for anything else. */\nmm/vmscan.c:6815:unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,\nmm/vmscan.c-6816-\t\t\t\t\t\tgfp_t gfp_mask, bool noswap,\n--\nmm/vmscan.c-6841-\t * here is not a good idea, since it limits the pages we can scan.\nmm/vmscan.c:6842:\t * if we don't reclaim here, the shrink_node from balance_pgdat\nmm/vmscan.c-6843-\t * will pick up pages from other mem cgroup's as well. We hack\n--\nmm/vmscan.c=6963=static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)\n--\nmm/vmscan.c-6987-\t\t * its own and needs kcompactd support. Once it's\nmm/vmscan.c:6988:\t\t * reclaimed a compaction gap, and kswapd_shrink_node\nmm/vmscan.c-6989-\t\t * has dropped order, simply ensure there are enough\n--\nmm/vmscan.c=7045=static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order,\n--\nmm/vmscan.c-7083- */\nmm/vmscan.c:7084:static bool kswapd_shrink_node(pg_data_t *pgdat,\nmm/vmscan.c-7085-\t\t\t struct scan_control *sc)\n--\nmm/vmscan.c-7100-\t */\nmm/vmscan.c:7101:\tshrink_node(pgdat, sc);\nmm/vmscan.c-7102-\n--\nmm/vmscan.c=7157=static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx)\n--\nmm/vmscan.c-7266-\nmm/vmscan.c:7267:\t\t/* Call soft limit reclaim before calling shrink_node. */\nmm/vmscan.c-7268-\t\tsc.nr_scanned = 0;\n--\nmm/vmscan.c-7278-\t\t */\nmm/vmscan.c:7279:\t\tif (kswapd_shrink_node(pgdat, \u0026sc))\nmm/vmscan.c-7280-\t\t\traise_priority = false;\n--\nmm/vmscan.c=7833=static unsigned long __node_reclaim(struct pglist_data *pgdat,\n--\nmm/vmscan.c-7860-\t\tdo {\nmm/vmscan.c:7861:\t\t\tshrink_node(pgdat, sc);\nmm/vmscan.c-7862-\t\t} while (sc-\u003enr_reclaimed \u003c nr_pages \u0026\u0026 --sc-\u003epriority \u003e= 0);\n"
} |
| 27/2 | 2026/08/24 18:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36787 output: 19 thoughts: 539 Thoughts: **My Fuzzing Strategy for Memory Reclaim** Okay, so I'm thinking about how to approach fuzzing the memory reclaim mechanisms, and I've got a good starting point. I can see that `shrink_node` is a really promising entry point because it kicks off MGLRU eviction paths through `lru_gen_shrink_node`, `lru_gen_shrink_lruvec`, and ultimately `evict_folios`. That's a solid chain to target. Now, `lru_gen_look_around` is triggered via `folio_referenced_one`, which is itself called by `rmap_walk` during page reclaim or other rmap operations. That's another interesting avenue to explore. We also have `folio_migrate_flags`, which comes into play during page migration, and `folio_mark_accessed` which is called during page access like read/write syscalls and page faults. Initially, I had a slightly longer list, but I need to focus on avoiding generic hot-path functions to keep my tests from becoming skewed. I realized that `mem_cgroup_update_lru_size` is modified, which is a key part of the lru size update, but it's called so often by things like `lruvec_add_folio` and `lruvec_del_folio` that I should really exclude it. And I'm pretty sure `folio_mark_accessed` will be way too hot as well. So, I'm going to refine my list of focus symbols to the following: * `lru_gen_look_around` - This is triggered by reclaim, which is not a generic hot path. * `folio_migrate_flags` - This happens during page migration, which is not a hot path. * `shrink_node` - Great entry point, also not a hot path. * `folio_deactivate` and `deactivate_file_folio` - These are called from things like `madvise(MADV_COLD)` and `fadvise(FADV_DONTNEED)`, which isn't generic. * `lru_lazyfree` - This is used in `madvise(MADV_FREE)`. Now, I'm going to quickly double check that `lru_lazyfree` isn't static. That will make it easier to hook into. I'm feeling confident about this approach for now. |
| 28/2 | 2026/08/24 18:17 | tool | grepper |
0mArgs:{
"Expression": "lru_lazyfree"
}Results: {
"Output": "mm/folio.c=43=struct cpu_fbatches {\n--\nmm/folio.c-51-\tstruct folio_batch lru_deactivate;\nmm/folio.c:52:\tstruct folio_batch lru_lazyfree;\nmm/folio.c-53-#ifdef CONFIG_SMP\n--\nmm/folio.c=575=static void lru_deactivate(struct lruvec *lruvec, struct folio *folio)\n--\nmm/folio.c-590-\nmm/folio.c:591:static void lru_lazyfree(struct lruvec *lruvec, struct folio *folio)\nmm/folio.c-592-{\n--\nmm/folio.c=622=void lru_add_drain_cpu(int cpu)\n--\nmm/folio.c-651-\nmm/folio.c:652:\tfbatch = \u0026fbatches-\u003elru_lazyfree;\nmm/folio.c-653-\tif (folio_batch_count(fbatch))\nmm/folio.c:654:\t\tfolio_batch_move_lru(fbatch, lru_lazyfree);\nmm/folio.c-655-\n--\nmm/folio.c=707=void folio_mark_lazyfree(struct folio *folio)\n--\nmm/folio.c-713-\nmm/folio.c:714:\tfolio_batch_add_and_move(folio, lru_lazyfree);\nmm/folio.c-715-}\n--\nmm/folio.c=758=static bool cpu_needs_drain(unsigned int cpu)\n--\nmm/folio.c-766-\t\t\t folio_batch_count(\u0026fbatches-\u003elru_deactivate) ||\nmm/folio.c:767:\t\t\t folio_batch_count(\u0026fbatches-\u003elru_lazyfree) ||\nmm/folio.c-768-\t\t\t folio_batch_count(\u0026fbatches-\u003elru_activate) ||\n"
} |
| 29/2 | 2026/08/24 18:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38370 output: 18 thoughts: 44 Thoughts: Here's my take on the thought process, as if it were my own: **Investigating Lazy Freeing in Folio Management** Okay, so `lru_lazyfree` is declared as a `static` function. That immediately makes me think, "Alright, where's the non-static counterpart?" And sure enough, `folio_mark_lazyfree` is the one that's designed to be externally accessible. I'm going to want to understand the interaction between these two functions and how they collaborate. Now, to get a better grasp on the flow, I should dig into the functions which use the methods, which are `folio_deactivate` and `deactivate_file_folio`. These functions will give me more context about when lazy freeing is invoked and in what scenarios. I'll need to follow the execution paths of these deactivation routines to see how the folio is managed across the file system operations. I'm especially keen on finding the points where the folio is placed onto the LRU or where the lazy free process is signaled. |
| 30/2 | 2026/08/24 18:17 | tool | grepper |
0mArgs:{
"Expression": "folio_deactivate"
}Results: {
"Output": "mm/damon/paddr.c=278=static inline unsigned long damon_pa_de_activate(\n--\nmm/damon/paddr.c-301-\t\telse\nmm/damon/paddr.c:302:\t\t\tfolio_deactivate(folio);\nmm/damon/paddr.c-303-\t\tapplied += folio_nr_pages(folio);\n--\nmm/folio.c=669=void deactivate_file_folio(struct folio *folio)\n--\nmm/folio.c-681-/*\nmm/folio.c:682: * folio_deactivate - deactivate a folio\nmm/folio.c-683- * @folio: folio to deactivate\nmm/folio.c-684- *\nmm/folio.c:685: * folio_deactivate() moves @folio to the inactive list if @folio was on the\nmm/folio.c-686- * active list and was not unevictable. This is done to accelerate the\n--\nmm/folio.c-688- */\nmm/folio.c:689:void folio_deactivate(struct folio *folio)\nmm/folio.c-690-{\n--\nmm/internal.h=64=void lru_add_drain_cpu_zone(struct zone *zone);\nmm/internal.h:65:void folio_deactivate(struct folio *folio);\nmm/internal.h-66-void folio_mark_lazyfree(struct folio *folio);\n--\nmm/madvise.c=362=static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,\n--\nmm/madvise.c-447-\t\t} else\nmm/madvise.c:448:\t\t\tfolio_deactivate(folio);\nmm/madvise.c-449-huge_unlock:\n--\nmm/madvise.c-562-\t\t} else\nmm/madvise.c:563:\t\t\tfolio_deactivate(folio);\nmm/madvise.c-564-\t}\n--\nmm/page-writeback.c=2773=bool folio_mark_dirty(struct folio *folio)\n--\nmm/page-writeback.c-2778-\t\t/*\nmm/page-writeback.c:2779:\t\t * readahead/folio_deactivate could remain\nmm/page-writeback.c-2780-\t\t * PG_readahead/PG_reclaim due to race with folio_end_writeback\n--\nmm/page-writeback.c-2782-\t\t * reset. So no problem.\nmm/page-writeback.c:2783:\t\t * About folio_deactivate, if the folio is redirtied,\nmm/page-writeback.c-2784-\t\t * the flag will be reset. So no problem. but if the\n--\nmm/vmscan.c=1061=static unsigned int shrink_folio_list(struct list_head *folio_list,\n--\nmm/vmscan.c-1376-\t\t\t\t * Immediately reclaim when written back.\nmm/vmscan.c:1377:\t\t\t\t * Similar in principle to folio_deactivate()\nmm/vmscan.c-1378-\t\t\t\t * except we already have the folio isolated\n"
} |
| 31/2 | 2026/08/24 18:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39478 output: 25 thoughts: 247 Thoughts: **My Assessment of LRU Generation and Related Configurations** Okay, so I've been looking at some key symbols related to LRU management. I've confirmed that `folio_deactivate`, `deactivate_file_folio`, and `folio_mark_lazyfree` are non-static and thankfully not on the hot path; their calls are tied to `madvise`/`fadvise` or specific reclaim processes, which makes sense. I see that `shrink_node`, `lru_gen_look_around`, and `folio_migrate_flags` are also strong candidates for my current investigation. Now, I need to make sure I haven't missed any crucial configuration options. I'm focusing on `CONFIG_LRU_GEN`, of course – that's the core. `CONFIG_LRU_GEN_ENABLED` seems to enable it by default, as expected. Then there's `CONFIG_LRU_GEN_STATS`, which is useful. I noted that `CONFIG_LRU_GEN_WALKS_MMU` is enabled by default if `ARCH_HAS_HW_PTE_YOUNG` is also present. And finally, I've got to consider `CONFIG_MEMCG`. I need to verify that `mem_cgroup_update_lru_size` is only compiled in *when* `CONFIG_MEMCG` is set. Just need to double-check that dependency to be sure everything is as I anticipate. |
| 32/2 | 2026/08/24 18:17 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_update_lru_size"
}Results: {
"Output": "include/linux/memcontrol.h=891=static inline bool mem_cgroup_online(struct mem_cgroup *memcg)\n--\ninclude/linux/memcontrol.h-897-\ninclude/linux/memcontrol.h:898:void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,\ninclude/linux/memcontrol.h-899-\t\tint zid, long nr_pages);\n--\ninclude/linux/mm_inline.h=52=static __always_inline void update_lru_size(struct lruvec *lruvec,\n--\ninclude/linux/mm_inline.h-57-#ifdef CONFIG_MEMCG\ninclude/linux/mm_inline.h:58:\tmem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages);\ninclude/linux/mm_inline.h-59-#endif\n--\nmm/folio.c=1123=static void lruvec_reparent_lru(struct lruvec *child_lruvec,\n--\nmm/folio.c-1135-\nmm/folio.c:1136:\t\tmem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);\nmm/folio.c-1137-\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-4638-\nmm/vmscan.c:4639:\t\t\tmem_cgroup_update_lru_size(parent_lruvec, lru, zid, size);\nmm/vmscan.c-4640-\t\t}\n"
} |
| 33/2 | 2026/08/24 18:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 40832 output: 19 thoughts: 47 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person: **Kernel Configuration Dependencies** Alright, so I'm thinking about these kernel configuration options and their dependencies. I'm pretty certain that `CONFIG_MEMCG` is definitely required for `mem_cgroup_update_lru_size`. That's a fundamental part of how memory cgroups operate, so it's a given. Now, I'm shifting gears to `folio_migrate_flags`. I need to verify whether `CONFIG_MIGRATION` is a prerequisite here. It seems logical, given that flags related to migration would hinge on the migration infrastructure itself being enabled. I'll need to double-check the code paths and any related documentation to confirm, but my initial impression is that `CONFIG_MIGRATION` is likely a requirement for `folio_migrate_flags` to function correctly. I'll make sure to explore those interdependencies thoroughly. |
| 34/2 | 2026/08/24 18:17 | tool | grepper |
0mArgs:{
"Expression": "folio_migrate_flags"
}Results: {
"Output": "fs/aio.c=460=static int aio_migrate_folio(struct address_space *mapping, struct folio *dst,\n--\nfs/aio.c-512-\tfolio_copy(dst, src);\nfs/aio.c:513:\tfolio_migrate_flags(dst, src);\nfs/aio.c-514-\tBUG_ON(ctx-\u003ering_folios[idx] != src);\n--\nfs/hugetlbfs/inode.c=1024=static int hugetlbfs_migrate_folio(struct address_space *mapping,\n--\nfs/hugetlbfs/inode.c-1039-\nfs/hugetlbfs/inode.c:1040:\tfolio_migrate_flags(dst, src);\nfs/hugetlbfs/inode.c-1041-\n--\ninclude/linux/migrate.h=69=void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl)\ninclude/linux/migrate.h-70-\t\t__releases(ptl);\ninclude/linux/migrate.h:71:void folio_migrate_flags(struct folio *newfolio, struct folio *folio);\ninclude/linux/migrate.h-72-int folio_migrate_mapping(struct address_space *mapping,\n--\ninclude/linux/mm_inline.h=351=static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)\n--\ninclude/linux/mm_inline.h-361-\ninclude/linux/mm_inline.h:362:\t/* for folio_migrate_flags() */\ninclude/linux/mm_inline.h-363-\tflags = !reclaiming \u0026\u0026 lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;\n--\nmm/migrate.c=739=int migrate_huge_page_move_mapping(struct address_space *mapping,\n--\nmm/migrate.c-774- */\nmm/migrate.c:775:void folio_migrate_flags(struct folio *newfolio, struct folio *folio)\nmm/migrate.c-776-{\n--\nmm/migrate.c-863-}\nmm/migrate.c:864:EXPORT_SYMBOL(folio_migrate_flags);\nmm/migrate.c-865-\n--\nmm/migrate.c=870=static int __migrate_folio(struct address_space *mapping, struct folio *dst,\n--\nmm/migrate.c-890-\nmm/migrate.c:891:\tfolio_migrate_flags(dst, src);\nmm/migrate.c-892-\treturn 0;\n--\nmm/migrate_device.c=1109=static void __migrate_device_pages(unsigned long *src_pfns,\n--\nmm/migrate_device.c-1251-\t\t\telse\nmm/migrate_device.c:1252:\t\t\t\tfolio_migrate_flags(newfolio, folio);\nmm/migrate_device.c-1253-\t\t}\n"
} |
| 35/2 | 2026/08/24 18:17 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 41879 output: 20 thoughts: 179 Thoughts: **My Initial Thoughts on Migration Configuration** Okay, so looking at `mm/migrate.c`, it's compiled conditionally. The critical point here is that `CONFIG_MIGRATION` needs to be enabled for that file to even exist in the build. Makes sense. Let's briefly recap the relevant configuration options that come to mind related to memory migration and related features: `LRU_GEN`, `LRU_GEN_ENABLED`, `LRU_GEN_STATS`, `MEMCG`, and of course `MIGRATION`. Gotta keep those straight as they relate to migration's overall functionality. Now, let's zero in on some key symbols, potential hot spots within the code related to migration: `lru_gen_look_around`, `folio_migrate_flags`, `shrink_node`, `folio_deactivate`, `deactivate_file_folio`, and `folio_mark_lazyfree`. These seem like the core building blocks of the migration process. Hmm, wait a second. `folio_mark_lazyfree`... isn't that function non-static? Now I need to quickly determine whether that function is exported or internal. I will check for this because if it's exported it may be exposed in other modules. |
| 36/2 | 2026/08/24 18:17 | tool | grepper |
0mArgs:{
"Expression": "folio_mark_lazyfree"
}Results: {
"Output": "mm/folio.c=689=void folio_deactivate(struct folio *folio)\n--\nmm/folio.c-700-/**\nmm/folio.c:701: * folio_mark_lazyfree - make an anon folio lazyfree\nmm/folio.c-702- * @folio: folio to deactivate\nmm/folio.c-703- *\nmm/folio.c:704: * folio_mark_lazyfree() moves @folio to the inactive file list.\nmm/folio.c-705- * This is done to accelerate the reclaim of @folio.\nmm/folio.c-706- */\nmm/folio.c:707:void folio_mark_lazyfree(struct folio *folio)\nmm/folio.c-708-{\n--\nmm/huge_memory.c=2358=bool madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,\n--\nmm/huge_memory.c-2419-\nmm/huge_memory.c:2420:\tfolio_mark_lazyfree(folio);\nmm/huge_memory.c-2421-\tret = true;\n--\nmm/internal.h=65=void folio_deactivate(struct folio *folio);\nmm/internal.h:66:void folio_mark_lazyfree(struct folio *folio);\nmm/internal.h-67-\n--\nmm/madvise.c=661=static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,\n--\nmm/madvise.c-779-\t\t}\nmm/madvise.c:780:\t\tfolio_mark_lazyfree(folio);\nmm/madvise.c-781-\t}\n"
} |
| 37/2 | 2026/08/24 18:17 | llm | patch-evaluator |
2mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 42798 output: 17 thoughts: 42 Thoughts: Okay, here's my summarized thought process, presented as if I'm the one thinking it: **Digging into Memory Management** Alright, so `folio_mark_lazyfree`... Yes, I'm pretty sure I recall seeing that function used within `madvise.c` and `huge_memory.c`. I need to confirm its role in those specific files, but the memory management landscape feels familiar. Next, I need to check something related to `shrink_node`. I'm curious if that function is exported. Knowing its visibility is important to understanding how it interacts with other parts of the system and how it's potentially utilized beyond its immediate context. Let me quickly verify that. |
| 38/2 | 2026/08/24 18:20 | tool | grepper |
0mArgs:{
"Expression": "shrink_node"
}Results: {
"Output": "mm/internal.h=83=unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,\n--\nmm/internal.h-87-\t\t\t\t\t int *swappiness);\nmm/internal.h:88:unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,\nmm/internal.h-89-\t\t\t\t gfp_t gfp_mask, bool noswap,\n--\nmm/memcontrol-v1.c=272=static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,\n--\nmm/memcontrol-v1.c-311-\t\t}\nmm/memcontrol-v1.c:312:\t\ttotal += mem_cgroup_shrink_node(victim, gfp_mask, false,\nmm/memcontrol-v1.c-313-\t\t\t\t\tpgdat, \u0026nr_scanned);\n--\nmm/page_isolation.c=19=bool page_is_unmovable(struct zone *zone, struct page *page,\n--\nmm/page_isolation.c-114-\t * it. But now, memory offline itself doesn't call\nmm/page_isolation.c:115:\t * shrink_node_slabs() and it still to be fixed.\nmm/page_isolation.c-116-\t */\n--\nmm/vmpressure.c=57=enum vmpressure_levels vmpressure_calc_level(unsigned long scanned,\n--\nmm/vmpressure.c-64-\t * reclaimed can be greater than scanned for things such as reclaimed\nmm/vmpressure.c:65:\t * slab pages. shrink_node() just adds reclaimed pages without a\nmm/vmpressure.c-66-\t * related increment to scanned pages.\n--\nmm/vmscan.c=1944=static void handle_reclaim_writeback(unsigned long nr_taken,\n--\nmm/vmscan.c-1982-/*\nmm/vmscan.c:1983: * shrink_inactive_list() is a helper for shrink_node(). It returns the number\nmm/vmscan.c-1984- * of reclaimed pages\n--\nmm/vmscan.c=5208=static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5228-\nmm/vmscan.c:5229:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-5230-{\n--\nmm/vmscan.c=5957=static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5961-\nmm/vmscan.c:5962:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-5963-{\n--\nmm/vmscan.c=6108=static inline bool should_continue_reclaim(struct pglist_data *pgdat,\n--\nmm/vmscan.c-6159-\nmm/vmscan.c:6160:static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)\nmm/vmscan.c-6161-{\n--\nmm/vmscan.c-6238-\nmm/vmscan.c:6239:static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)\nmm/vmscan.c-6240-{\n--\nmm/vmscan.c-6246-\t\tmemset(\u0026sc-\u003enr, 0, sizeof(sc-\u003enr));\nmm/vmscan.c:6247:\t\tlru_gen_shrink_node(pgdat, sc);\nmm/vmscan.c-6248-\n--\nmm/vmscan.c-6263-\nmm/vmscan.c:6264:\tshrink_node_memcgs(pgdat, sc);\nmm/vmscan.c-6265-\n--\nmm/vmscan.c=6424=static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc)\n--\nmm/vmscan.c-6502-\t\tlast_pgdat = zone-\u003ezone_pgdat;\nmm/vmscan.c:6503:\t\tshrink_node(zone-\u003ezone_pgdat, sc);\nmm/vmscan.c-6504-\t}\n--\nmm/vmscan.c=6769=unsigned long try_to_free_pages(struct zonelist *zonelist, int order,\n--\nmm/vmscan.c-6814-/* Only used by soft limit reclaim. Do not reuse for anything else. */\nmm/vmscan.c:6815:unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,\nmm/vmscan.c-6816-\t\t\t\t\t\tgfp_t gfp_mask, bool noswap,\n--\nmm/vmscan.c-6841-\t * here is not a good idea, since it limits the pages we can scan.\nmm/vmscan.c:6842:\t * if we don't reclaim here, the shrink_node from balance_pgdat\nmm/vmscan.c-6843-\t * will pick up pages from other mem cgroup's as well. We hack\n--\nmm/vmscan.c=6963=static bool pgdat_balanced(pg_data_t *pgdat, int order, int highest_zoneidx)\n--\nmm/vmscan.c-6987-\t\t * its own and needs kcompactd support. Once it's\nmm/vmscan.c:6988:\t\t * reclaimed a compaction gap, and kswapd_shrink_node\nmm/vmscan.c-6989-\t\t * has dropped order, simply ensure there are enough\n--\nmm/vmscan.c=7045=static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order,\n--\nmm/vmscan.c-7083- */\nmm/vmscan.c:7084:static bool kswapd_shrink_node(pg_data_t *pgdat,\nmm/vmscan.c-7085-\t\t\t struct scan_control *sc)\n--\nmm/vmscan.c-7100-\t */\nmm/vmscan.c:7101:\tshrink_node(pgdat, sc);\nmm/vmscan.c-7102-\n--\nmm/vmscan.c=7157=static int balance_pgdat(pg_data_t *pgdat, int order, int highest_zoneidx)\n--\nmm/vmscan.c-7266-\nmm/vmscan.c:7267:\t\t/* Call soft limit reclaim before calling shrink_node. */\nmm/vmscan.c-7268-\t\tsc.nr_scanned = 0;\n--\nmm/vmscan.c-7278-\t\t */\nmm/vmscan.c:7279:\t\tif (kswapd_shrink_node(pgdat, \u0026sc))\nmm/vmscan.c-7280-\t\t\traise_priority = false;\n--\nmm/vmscan.c=7833=static unsigned long __node_reclaim(struct pglist_data *pgdat,\n--\nmm/vmscan.c-7860-\t\tdo {\nmm/vmscan.c:7861:\t\t\tshrink_node(pgdat, sc);\nmm/vmscan.c-7862-\t\t} while (sc-\u003enr_reclaimed \u003c nr_pages \u0026\u0026 --sc-\u003epriority \u003e= 0);\n"
} |
| 39/2 | 2026/08/24 18:20 | llm | patch-evaluator |
5mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 44970 output: 21 thoughts: 107 Thoughts: **Refactoring Node Shrinkage** Okay, so `shrink_node` is a static function, which means it's likely being called in a bunch of places internally. I need to consolidate my approach to memory reclamation. Let's see... the non-static functions that currently call this static function are `try_to_free_pages`, `balance_pgdat` (which itself calls `kswapd_shrink_node`, which in turn calls `shrink_node`), `__node_reclaim`, and `mem_cgroup_shrink_node`. My intuition here is to favor the non-static callers directly, especially the ones that offer some context, instead of the static `shrink_node`. `try_to_free_pages` and `mem_cgroup_shrink_node` stand out as the most promising replacements. They probably offer a clearer abstraction level and might be more easily adapted to my refactoring goal. I'll start by taking a closer look at `try_to_free_pages`. This is where I'll focus my efforts. |
| 40/2 | 2026/08/24 18:25 | tool | grepper |
0mArgs:{
"Expression": "try_to_free_pages"
}Results: {
"Output": "fs/fuse/file.c=2351=static void fuse_vma_close(struct vm_area_struct *vma)\n--\nfs/fuse/file.c-2371- * - sync(2)\nfs/fuse/file.c:2372: * - try_to_free_pages() with order \u003e PAGE_ALLOC_COSTLY_ORDER\nfs/fuse/file.c-2373- */\n--\nmm/internal.h=69=unsigned long zone_reclaimable_pages(struct zone *zone);\nmm/internal.h:70:unsigned long try_to_free_pages(struct zonelist *zonelist, int order,\nmm/internal.h-71-\t\t\t\tgfp_t gfp_mask, const nodemask_t *mask);\n--\nmm/page_alloc.c=4438=__perform_reclaim(gfp_t gfp_mask, unsigned int order,\n--\nmm/page_alloc.c-4450-\nmm/page_alloc.c:4451:\tprogress = try_to_free_pages(ac-\u003ezonelist, order, gfp_mask,\nmm/page_alloc.c-4452-\t\t\t\t\t\t\t\tac-\u003enodemask);\n--\nmm/vmscan.c=6516=static void snapshot_refaults(struct mem_cgroup *target_memcg, pg_data_t *pgdat)\n--\nmm/vmscan.c-6546- */\nmm/vmscan.c:6547:static unsigned long do_try_to_free_pages(struct zonelist *zonelist,\nmm/vmscan.c-6548-\t\t\t\t\t struct scan_control *sc)\n--\nmm/vmscan.c=6689=static bool throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,\n--\nmm/vmscan.c-6768-\nmm/vmscan.c:6769:unsigned long try_to_free_pages(struct zonelist *zonelist, int order,\nmm/vmscan.c-6770-\t\t\t\tgfp_t gfp_mask, const nodemask_t *nodemask)\n--\nmm/vmscan.c-6803-\nmm/vmscan.c:6804:\tnr_reclaimed = do_try_to_free_pages(zonelist, \u0026sc);\nmm/vmscan.c-6805-\n--\nmm/vmscan.c=6855=unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,\n--\nmm/vmscan.c-6886-\nmm/vmscan.c:6887:\tnr_reclaimed = do_try_to_free_pages(zonelist, \u0026sc);\nmm/vmscan.c-6888-\n--\nmm/vmscan.c=7653=unsigned long shrink_all_memory(unsigned long nr_to_reclaim)\n--\nmm/vmscan.c-7672-\nmm/vmscan.c:7673:\tnr_reclaimed = do_try_to_free_pages(zonelist, \u0026sc);\nmm/vmscan.c-7674-\n"
} |
| 41/2 | 2026/08/24 18:25 | llm | patch-evaluator |
5mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 46134 output: 17 thoughts: 174 Thoughts: **My Analysis of Memory Management Entry Points** Okay, so I'm looking at these memory management functions, trying to understand the core pathways. The fact that `try_to_free_pages`, `try_to_free_mem_cgroup_pages`, and `shrink_all_memory` are all exported/non-static is immediately interesting – they're clearly meant to be entry points for other parts of the system. I need to focus on these. That means I should keep a close eye on `lru_gen_look_around`, `folio_migrate_flags`, `try_to_free_pages`, `try_to_free_mem_cgroup_pages`, `folio_deactivate`, `deactivate_file_folio`, and `folio_mark_lazyfree`. These seem like the crucial symbols to investigate further. Hold on a second... `folio_migrate_flags` is called from `migrate_pages`. And `migrate_pages` *is* the main entry point to the migration process. Perhaps I should shift my focus and start with `migrate_pages` instead. It might give me a better overview of the entire process. That might be a better starting point. |
| 42/2 | 2026/08/24 18:30 | tool | grepper |
0mArgs:{
"Expression": "migrate_pages"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 523 lines.\nUse more precise expression if possible.\n\nDocumentation/admin-guide/mm/hugetlbpage.rst=390=scenarios: memory offline, memory failure, longterm pinning, syscalls(mbind,\nDocumentation/admin-guide/mm/hugetlbpage.rst:391:migrate_pages and move_pages), alloc_contig_range() and alloc_contig_pages().\nDocumentation/admin-guide/mm/hugetlbpage.rst-392-Now only memory offline, memory failure and syscalls allow fallbacking to allocate\n--\nDocumentation/mm/page_migration.rst=19=a new memory policy via mbind(). The pages of a process can also be relocated\nDocumentation/mm/page_migration.rst:20:from another process using the sys_migrate_pages() function call. The\nDocumentation/mm/page_migration.rst:21:migrate_pages() function call takes two sets of nodes and moves pages of a\nDocumentation/mm/page_migration.rst-22-process that are located on the from nodes to the destination nodes.\n--\nDocumentation/mm/page_migration.rst=58=Page migration occurs in several steps. First a high level\nDocumentation/mm/page_migration.rst:59:description for those trying to use migrate_pages() from the kernel\nDocumentation/mm/page_migration.rst-60-(for userspace usage see the Andi Kleen's numactl package mentioned above)\nDocumentation/mm/page_migration.rst=61=and then a low level description of how the low level details work.\nDocumentation/mm/page_migration.rst-62-\nDocumentation/mm/page_migration.rst:63:In kernel use of migrate_pages()\nDocumentation/mm/page_migration.rst-64-================================\n--\nDocumentation/mm/page_migration.rst-76-2. We need to have a function of type new_folio_t that can be\nDocumentation/mm/page_migration.rst:77: passed to migrate_pages(). This function should figure out\nDocumentation/mm/page_migration.rst-78- how to allocate the correct new folio given the old folio.\nDocumentation/mm/page_migration.rst-79-\nDocumentation/mm/page_migration.rst:80:3. The migrate_pages() function is called which attempts\nDocumentation/mm/page_migration.rst-81- to do the migration. It will call the function to allocate\n--\nDocumentation/mm/page_migration.rst-83-\nDocumentation/mm/page_migration.rst:84:How migrate_pages() works\nDocumentation/mm/page_migration.rst-85-=========================\nDocumentation/mm/page_migration.rst-86-\nDocumentation/mm/page_migration.rst:87:migrate_pages() does several passes over its list of folios. A folio is moved\nDocumentation/mm/page_migration.rst-88-if all references to a folio are removable at the time. The folio has\n--\nDocumentation/translations/zh_CN/mm/page_migration.rst-22-页面迁移允许进程通过MF_MOVE和MF_MOVE_ALL选项手动重新定位其页面所在的节点,同时通过\nDocumentation/translations/zh_CN/mm/page_migration.rst:23:mbind()设置一个新的内存策略。一个进程的页面也可以通过sys_migrate_pages()函数调用从另\nDocumentation/translations/zh_CN/mm/page_migration.rst:24:一个进程重新定位。migrate_pages()函数调用接收两组节点,并将一个进程位于旧节点上的页面移\nDocumentation/translations/zh_CN/mm/page_migration.rst-25-动到目标节点上。页面迁移功能由Andi Kleen的numactl包提供(需要0.9.3以上的版本,其仓库\n--\nDocumentation/translations/zh_CN/mm/page_migration.rst-43-\nDocumentation/translations/zh_CN/mm/page_migration.rst:44:页面迁移分几个步骤进行。首先为那些试图从内核中使用migrate_pages()的进程做一个高层次的\nDocumentation/translations/zh_CN/mm/page_migration.rst-45-描述(对于用户空间的使用,可以参考上面提到的Andi Kleen的numactl包),然后对低水平的细\n--\nDocumentation/translations/zh_CN/mm/page_migration.rst-47-\nDocumentation/translations/zh_CN/mm/page_migration.rst:48:在内核中使用 migrate_pages()\nDocumentation/translations/zh_CN/mm/page_migration.rst-49-============================\n--\nDocumentation/translations/zh_CN/mm/page_migration.rst-57-\nDocumentation/translations/zh_CN/mm/page_migration.rst:58:2. 我们需要有一个new_folio_t类型的函数,可以传递给migrate_pages()。这个函数应该计算\nDocumentation/translations/zh_CN/mm/page_migration.rst-59- 出如何在给定的旧页面中分配正确的新页面。\nDocumentation/translations/zh_CN/mm/page_migration.rst-60-\nDocumentation/translations/zh_CN/mm/page_migration.rst:61:3. migrate_pages()函数被调用,它试图进行迁移。它将调用该函数为每个被考虑迁移的页面分\nDocumentation/translations/zh_CN/mm/page_migration.rst-62- 配新的页面。\nDocumentation/translations/zh_CN/mm/page_migration.rst-63-\nDocumentation/translations/zh_CN/mm/page_migration.rst:64:migrate_pages()如何工作\nDocumentation/translations/zh_CN/mm/page_migration.rst-65-=======================\nDocumentation/translations/zh_CN/mm/page_migration.rst-66-\nDocumentation/translations/zh_CN/mm/page_migration.rst:67:migrate_pages()对它的页面列表进行了多次处理。如果当时对一个页面的所有引用都可以被移除,\nDocumentation/translations/zh_CN/mm/page_migration.rst-68-那么这个页面就会被移动。该页已经通过folio_isolate_lru()从LRU中移除,并且refcount被\n--\narch/alpha/kernel/syscalls/syscall.tbl-379-448\tcommon\tkexec_load\t\t\tsys_kexec_load\narch/alpha/kernel/syscalls/syscall.tbl:380:449\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\narch/alpha/kernel/syscalls/syscall.tbl-381-450\tcommon\topenat\t\t\t\tsys_openat\n--\narch/arm/tools/syscall.tbl-417-399\tcommon\tio_pgetevents\t\tsys_io_pgetevents_time32\narch/arm/tools/syscall.tbl:418:400\tcommon\tmigrate_pages\t\tsys_migrate_pages\narch/arm/tools/syscall.tbl-419-401\tcommon\tkexec_file_load\t\tsys_kexec_file_load\n--\narch/arm64/tools/syscall_32.tbl-414-399\tcommon\tio_pgetevents\t\tsys_io_pgetevents_time32\tcompat_sys_io_pgetevents\narch/arm64/tools/syscall_32.tbl:415:400\tcommon\tmigrate_pages\t\tsys_migrate_pages\narch/arm64/tools/syscall_32.tbl-416-401\tcommon\tkexec_file_load\t\tsys_kexec_file_load\n--\narch/m68k/kernel/syscalls/syscall.tbl-296-286\tcommon\tinotify_rm_watch\t\tsys_inotify_rm_watch\narch/m68k/kernel/syscalls/syscall.tbl:297:287\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\narch/m68k/kernel/syscalls/syscall.tbl-298-288\tcommon\topenat\t\t\t\tsys_openat\n--\narch/microblaze/kernel/syscalls/syscall.tbl-303-293\tcommon\tinotify_rm_watch\t\tsys_inotify_rm_watch\narch/microblaze/kernel/syscalls/syscall.tbl:304:294\tcommon\tmigrate_pages\t\t\tsys_ni_syscall\narch/microblaze/kernel/syscalls/syscall.tbl-305-295\tcommon\topenat\t\t\t\tsys_openat\n--\narch/mips/kernel/syscalls/syscall_n32.tbl-260-249\tn32\tinotify_rm_watch\t\tsys_inotify_rm_watch\narch/mips/kernel/syscalls/syscall_n32.tbl:261:250\tn32\tmigrate_pages\t\t\tsys_migrate_pages\narch/mips/kernel/syscalls/syscall_n32.tbl-262-251\tn32\topenat\t\t\t\tsys_openat\n--\narch/mips/kernel/syscalls/syscall_n64.tbl-256-245\tn64\tinotify_rm_watch\t\tsys_inotify_rm_watch\narch/mips/kernel/syscalls/syscall_n64.tbl:257:246\tn64\tmigrate_pages\t\t\tsys_migrate_pages\narch/mips/kernel/syscalls/syscall_n64.tbl-258-247\tn64\topenat\t\t\t\tsys_openat\n--\narch/mips/kernel/syscalls/syscall_o32.tbl-300-286\to32\tinotify_rm_watch\t\tsys_inotify_rm_watch\narch/mips/kernel/syscalls/syscall_o32.tbl:301:287\to32\tmigrate_pages\t\t\tsys_migrate_pages\narch/mips/kernel/syscalls/syscall_o32.tbl-302-288\to32\topenat\t\t\t\tsys_openat\t\t\tcompat_sys_openat\n--\narch/parisc/kernel/syscalls/syscall.tbl-306-271\tcommon\tinotify_rm_watch\tsys_inotify_rm_watch\narch/parisc/kernel/syscalls/syscall.tbl:307:272\tcommon\tmigrate_pages\t\tsys_migrate_pages\narch/parisc/kernel/syscalls/syscall.tbl-308-273\t32\tpselect6\t\tsys_pselect6_time32\t\tcompat_sys_pselect6_time32\n--\narch/powerpc/kernel/syscalls/syscall.tbl-340-# 257 reserved for vserver\narch/powerpc/kernel/syscalls/syscall.tbl:341:258\tnospu\tmigrate_pages\t\t\tsys_migrate_pages\narch/powerpc/kernel/syscalls/syscall.tbl-342-259\tnospu\tmbind\t\t\t\tsys_mbind\n--\narch/s390/kernel/syscalls/syscall.tbl-240-286\tcommon\tinotify_rm_watch\t\tsys_inotify_rm_watch\narch/s390/kernel/syscalls/syscall.tbl:241:287\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\narch/s390/kernel/syscalls/syscall.tbl-242-288\tcommon\topenat\t\t\t\tsys_openat\n--\narch/sh/kernel/syscalls/syscall.tbl-303-# 293 is unused\narch/sh/kernel/syscalls/syscall.tbl:304:294\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\narch/sh/kernel/syscalls/syscall.tbl-305-295\tcommon\topenat\t\t\t\tsys_openat\n--\narch/sparc/kernel/syscalls/syscall.tbl-367-301\tcommon\tget_robust_list\t\tsys_get_robust_list\t\tcompat_sys_get_robust_list\narch/sparc/kernel/syscalls/syscall.tbl:368:302\tcommon\tmigrate_pages\t\tsys_migrate_pages\narch/sparc/kernel/syscalls/syscall.tbl-369-303\tcommon\tmbind\t\t\tsys_mbind\n--\narch/x86/entry/syscalls/syscall_32.tbl-308-293\ti386\tinotify_rm_watch\tsys_inotify_rm_watch\narch/x86/entry/syscalls/syscall_32.tbl:309:294\ti386\tmigrate_pages\t\tsys_migrate_pages\narch/x86/entry/syscalls/syscall_32.tbl-310-295\ti386\topenat\t\t\tsys_openat\t\t\tcompat_sys_openat\n--\narch/x86/entry/syscalls/syscall_64.tbl-267-255\tcommon\tinotify_rm_watch\tsys_inotify_rm_watch\narch/x86/entry/syscalls/syscall_64.tbl:268:256\tcommon\tmigrate_pages\t\tsys_migrate_pages\narch/x86/entry/syscalls/syscall_64.tbl-269-257\tcommon\topenat\t\t\tsys_openat\n--\narch/xtensa/kernel/syscalls/syscall.tbl-283-261\tcommon\tremap_file_pages\t\tsys_remap_file_pages\narch/xtensa/kernel/syscalls/syscall.tbl:284:262\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\narch/xtensa/kernel/syscalls/syscall.tbl-285-263\tcommon\tmbind\t\t\t\tsys_mbind\n--\ninclude/linux/mempolicy.h=153=static inline void check_highest_zone(enum zone_type k)\n--\ninclude/linux/mempolicy.h-158-\ninclude/linux/mempolicy.h:159:int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,\ninclude/linux/mempolicy.h-160-\t\t const nodemask_t *to, int flags);\n--\ninclude/linux/mempolicy.h=268=static inline bool init_nodemask_of_mempolicy(nodemask_t *m)\n--\ninclude/linux/mempolicy.h-272-\ninclude/linux/mempolicy.h:273:static inline int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,\ninclude/linux/mempolicy.h-274-\t\t\t\t const nodemask_t *to, int flags)\n--\ninclude/linux/migrate.h=57=int migrate_folio(struct address_space *mapping, struct folio *dst,\ninclude/linux/migrate.h-58-\t\tstruct folio *src, enum migrate_mode mode);\ninclude/linux/migrate.h:59:int migrate_pages(struct list_head *l, new_folio_t new, free_folio_t free,\ninclude/linux/migrate.h-60-\t\tunsigned long private, enum migrate_mode mode,\n--\ninclude/linux/migrate.h=78=static inline void putback_movable_pages(struct list_head *l) {}\ninclude/linux/migrate.h:79:static inline int migrate_pages(struct list_head *l, new_folio_t new,\ninclude/linux/migrate.h-80-\t\tfree_folio_t free, unsigned long private,\n--\ninclude/linux/syscalls.h=861=asmlinkage long sys_set_mempolicy(int mode, const unsigned long __user *nmask,\ninclude/linux/syscalls.h-862-\t\t\t\tunsigned long maxnode);\ninclude/linux/syscalls.h:863:asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,\ninclude/linux/syscalls.h-864-\t\t\t\tconst unsigned long __user *from,\n--\ninclude/trace/events/migrate.h=39=MIGRATE_REASON\n--\ninclude/trace/events/migrate.h-49-\ninclude/trace/events/migrate.h:50:TRACE_EVENT(mm_migrate_pages,\ninclude/trace/events/migrate.h-51-\n--\ninclude/trace/events/migrate.h-92-\ninclude/trace/events/migrate.h:93:TRACE_EVENT(mm_migrate_pages_start,\ninclude/trace/events/migrate.h-94-\n--\ninclude/uapi/asm-generic/unistd.h=604=__SYSCALL(__NR_set_mempolicy, sys_set_mempolicy)\ninclude/uapi/asm-generic/unistd.h:605:#define __NR_migrate_pages 238\ninclude/uapi/asm-generic/unistd.h:606:__SYSCALL(__NR_migrate_pages, sys_migrate_pages)\ninclude/uapi/asm-generic/unistd.h-607-#define __NR_move_pages 239\n--\nkernel/cgroup/cpuset.c=2532=static void cpuset_migrate_mm_workfn(struct work_struct *work)\n--\nkernel/cgroup/cpuset.c-2537-\t/* on a wq worker, no need to worry about %current's mems_allowed */\nkernel/cgroup/cpuset.c:2538:\tdo_migrate_pages(mwork-\u003emm, \u0026mwork-\u003efrom, \u0026mwork-\u003eto, MPOL_MF_MOVE_ALL);\nkernel/cgroup/cpuset.c-2539-\tmmput(mwork-\u003emm);\n--\nkernel/sys_ni.c=192=COND_SYSCALL(set_mempolicy);\nkernel/sys_ni.c:193:COND_SYSCALL(migrate_pages);\nkernel/sys_ni.c-194-COND_SYSCALL(move_pages);\n--\nmm/Kconfig=673=config NUMA_MIGRATION\n--\nmm/Kconfig-679-\t Support the migration of pages to other NUMA nodes, available to\nmm/Kconfig:680:\t user space through interfaces like migrate_pages(), move_pages(),\nmm/Kconfig-681-\t and mbind(). Selecting this option also enables support for page\n--\nmm/compaction.c=2562=compact_zone(struct compact_control *cc, struct capture_control *capc)\n--\nmm/compaction.c-2697-\t\tnr_migratepages = cc-\u003enr_migratepages;\nmm/compaction.c:2698:\t\terr = migrate_pages(\u0026cc-\u003emigratepages, compaction_alloc,\nmm/compaction.c-2699-\t\t\t\tcompaction_free, (unsigned long)cc, cc-\u003emode,\n--\nmm/compaction.c-2708-\t\t\t/*\nmm/compaction.c:2709:\t\t\t * migrate_pages() may return -ENOMEM when scanners meet\nmm/compaction.c-2710-\t\t\t * and we want compact_finished() to detect it\n--\nmm/damon/ops-common.c=303=static unsigned int __damon_migrate_folio_list(\n--\nmm/damon/ops-common.c-325-\t/* Migration ignores all cpuset and mempolicy settings */\nmm/damon/ops-common.c:326:\tmigrate_pages(migrate_folios, alloc_migration_target, NULL,\nmm/damon/ops-common.c-327-\t\t (unsigned long)\u0026mtc, MIGRATE_ASYNC, MR_DAMON,\n--\nmm/damon/ops-common.c=333=static unsigned int damon_migrate_folio_list(struct list_head *folio_list,\n--\nmm/damon/ops-common.c-384-\nmm/damon/ops-common.c:385:unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid)\nmm/damon/ops-common.c-386-{\n--\nmm/damon/ops-common.h=20=bool damos_folio_filter_match(struct damos_filter *filter, struct folio *folio);\nmm/damon/ops-common.h:21:unsigned long damon_migrate_pages(struct list_head *folio_list, int target_nid);\nmm/damon/ops-common.h-22-\n--\nmm/damon/paddr.c=326=static unsigned long damon_pa_migrate(struct damon_region *r,\n--\nmm/damon/paddr.c-355-\t}\nmm/damon/paddr.c:356:\tapplied = damon_migrate_pages(\u0026folio_list, s-\u003etarget_nid);\nmm/damon/paddr.c-357-\tcond_resched();\n--\nmm/damon/vaddr.c=738=static unsigned long damos_va_migrate(struct damon_target *target,\n--\nmm/damon/vaddr.c-774-\t\tnid = use_target_nid ? s-\u003etarget_nid : dests-\u003enode_id_arr[i];\nmm/damon/vaddr.c:775:\t\tapplied += damon_migrate_pages(\u0026priv.migration_lists[i], nid);\nmm/damon/vaddr.c-776-\t\tcond_resched();\n--\nmm/gup.c=2316=migrate_longterm_unpinnable_folios(struct list_head *movable_folio_list,\n--\nmm/gup.c-2360-\nmm/gup.c:2361:\t\tif (migrate_pages(movable_folio_list, alloc_migration_target,\nmm/gup.c-2362-\t\t\t\t NULL, (unsigned long)\u0026mtc, MIGRATE_SYNC,\n--\nmm/memory-failure.c=2849=static int soft_offline_in_use_page(struct page *page)\n--\nmm/memory-failure.c-2917-\tif (isolated) {\nmm/memory-failure.c:2918:\t\tret = migrate_pages(\u0026pagelist, alloc_migration_target, NULL,\nmm/memory-failure.c-2919-\t\t\t(unsigned long)\u0026mtc, MIGRATE_SYNC, MR_MEMORY_FAILURE, NULL);\n--\nmm/memory_hotplug.c=1846=static void do_migrate_range(unsigned long start_pfn, unsigned long end_pfn)\n--\nmm/memory_hotplug.c-1919-\t\t\tnode_set(mtc.nid, nmask);\nmm/memory_hotplug.c:1920:\t\tret = migrate_pages(\u0026source, alloc_migration_target, NULL,\nmm/memory_hotplug.c-1921-\t\t\t(unsigned long)\u0026mtc, MIGRATE_SYNC, MR_MEMORY_HOTPLUG, NULL);\n--\nmm/mempolicy.c=687=static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr,\n--\nmm/mempolicy.c-746-\t\t\t *\nmm/mempolicy.c:747:\t\t\t * migrate_pages(\u0026pagelist) returns nr_failed folios, so\nmm/mempolicy.c-748-\t\t\t * check \"large\" now so that queue_pages_range() returns\n--\nmm/mempolicy.c=1285=static long migrate_to_node(struct mm_struct *mm, int source, int dest,\n--\nmm/mempolicy.c-1321-\tif (!list_empty(\u0026pagelist)) {\nmm/mempolicy.c:1322:\t\terr = migrate_pages(\u0026pagelist, alloc_migration_target, NULL,\nmm/mempolicy.c-1323-\t\t\t(unsigned long)\u0026mtc, MIGRATE_SYNC, MR_SYSCALL, NULL);\n--\nmm/mempolicy.c-1338- */\nmm/mempolicy.c:1339:int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,\nmm/mempolicy.c-1340-\t\t const nodemask_t *to, int flags)\n--\nmm/mempolicy.c-1387-\t\t\t/*\nmm/mempolicy.c:1388:\t\t\t * do_migrate_pages() tries to maintain the relative\nmm/mempolicy.c-1389-\t\t\t * node relationship of the pages established between\n--\nmm/mempolicy.c=1470=static bool migrate_folio_add(struct folio *folio, struct list_head *foliolist,\n--\nmm/mempolicy.c-1475-\nmm/mempolicy.c:1476:int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,\nmm/mempolicy.c-1477-\t\t const nodemask_t *to, int flags)\n--\nmm/mempolicy.c=1489=static long do_mbind(unsigned long start, unsigned long len,\n--\nmm/mempolicy.c-1616-\tif (!err \u0026\u0026 !list_empty(\u0026pagelist)) {\nmm/mempolicy.c:1617:\t\tnr_failed |= migrate_pages(\u0026pagelist,\nmm/mempolicy.c-1618-\t\t\t\talloc_migration_target_by_mpol, NULL,\n--\nmm/mempolicy.c=1857=SYSCALL_DEFINE3(set_mempolicy, int, mode, const unsigned long __user *, nmask,\n--\nmm/mempolicy.c-1862-\nmm/mempolicy.c:1863:static int kernel_migrate_pages(pid_t pid, unsigned long maxnode,\nmm/mempolicy.c-1864-\t\t\t\tconst unsigned long __user *old_nodes,\n--\nmm/mempolicy.c-1934-\nmm/mempolicy.c:1935:\terr = do_migrate_pages(mm, old, new,\nmm/mempolicy.c-1936-\t\tcapable(CAP_SYS_NICE) ? MPOL_MF_MOVE_ALL : MPOL_MF_MOVE);\n--\nmm/mempolicy.c-1948-\nmm/mempolicy.c:1949:SYSCALL_DEFINE4(migrate_pages, pid_t, pid, unsigned long, maxnode,\nmm/mempolicy.c-1950-\t\tconst unsigned long __user *, old_nodes,\n--\nmm/mempolicy.c-1952-{\nmm/mempolicy.c:1953:\treturn kernel_migrate_pages(pid, maxnode, old_nodes, new_nodes);\nmm/mempolicy.c-1954-}\n--\nmm/migrate.c=1591=static inline int try_split_folio(struct folio *folio, struct list_head *split_folios,\n--\nmm/migrate.c-1619-\nmm/migrate.c:1620:struct migrate_pages_stats {\nmm/migrate.c-1621-\tint nr_succeeded;\t/* Normal and large folios migrated successfully, in\n--\nmm/migrate.c=1638=static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,\n--\nmm/migrate.c-1640-\t\t\t enum migrate_mode mode, enum migrate_reason reason,\nmm/migrate.c:1641:\t\t\t struct migrate_pages_stats *stats,\nmm/migrate.c-1642-\t\t\t struct list_head *ret_folios)\n--\nmm/migrate.c=1727=static void migrate_folios_move(struct list_head *src_folios,\n--\nmm/migrate.c-1731-\t\tstruct list_head *ret_folios,\nmm/migrate.c:1732:\t\tstruct migrate_pages_stats *stats,\nmm/migrate.c-1733-\t\tint *retry, int *thp_retry, int *nr_failed,\n--\nmm/migrate.c=1779=static void migrate_folios_undo(struct list_head *src_folios,\n--\nmm/migrate.c-1802-/*\nmm/migrate.c:1803: * migrate_pages_batch() first unmaps folios in the from list as many as\nmm/migrate.c-1804- * possible, then move the unmapped folios.\n--\nmm/migrate.c-1810- */\nmm/migrate.c:1811:static int migrate_pages_batch(struct list_head *from,\nmm/migrate.c-1812-\t\tnew_folio_t get_new_folio, free_folio_t put_new_folio,\n--\nmm/migrate.c-1814-\t\tstruct list_head *ret_folios, struct list_head *split_folios,\nmm/migrate.c:1815:\t\tstruct migrate_pages_stats *stats, int nr_pass)\nmm/migrate.c-1816-{\n--\nmm/migrate.c-1848-\t\t\t * but increment nr_failed because, without doing so,\nmm/migrate.c:1849:\t\t\t * migrate_pages() may report success with (split but\nmm/migrate.c-1850-\t\t\t * unmigrated) pages still on its fromlist; whereas it\n--\nmm/migrate.c-1853-\t\t\t * otherwise stats inconsistency will happen when\nmm/migrate.c:1854:\t\t\t * migrate_pages_batch is called via migrate_pages()\nmm/migrate.c-1855-\t\t\t * with MIGRATE_SYNC and MIGRATE_ASYNC.\n--\nmm/migrate.c-2022-\nmm/migrate.c:2023:static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,\nmm/migrate.c-2024-\t\tfree_folio_t put_new_folio, unsigned long private,\n--\nmm/migrate.c-2026-\t\tstruct list_head *ret_folios, struct list_head *split_folios,\nmm/migrate.c:2027:\t\tstruct migrate_pages_stats *stats)\nmm/migrate.c-2028-{\n--\nmm/migrate.c-2030-\tLIST_HEAD(folios);\nmm/migrate.c:2031:\tstruct migrate_pages_stats astats;\nmm/migrate.c-2032-\n--\nmm/migrate.c-2034-\t/* Try to migrate in batch with MIGRATE_ASYNC mode firstly */\nmm/migrate.c:2035:\trc = migrate_pages_batch(from, get_new_folio, put_new_folio, private, MIGRATE_ASYNC,\nmm/migrate.c-2036-\t\t\t\t reason, \u0026folios, split_folios, \u0026astats,\n--\nmm/migrate.c-2061-\t\tlist_move(from-\u003enext, \u0026folios);\nmm/migrate.c:2062:\t\trc = migrate_pages_batch(\u0026folios, get_new_folio, put_new_folio,\nmm/migrate.c-2063-\t\t\t\t\t private, mode, reason, ret_folios,\n--\nmm/migrate.c-2074-/*\nmm/migrate.c:2075: * migrate_pages - migrate the folios specified in a list, to the free folios\nmm/migrate.c-2076- *\t\t supplied as the target for the page migration\n--\nmm/migrate.c-2099- */\nmm/migrate.c:2100:int migrate_pages(struct list_head *from, new_folio_t get_new_folio,\nmm/migrate.c-2101-\t\tfree_folio_t put_new_folio, unsigned long private,\n--\nmm/migrate.c-2109-\tLIST_HEAD(split_folios);\nmm/migrate.c:2110:\tstruct migrate_pages_stats stats;\nmm/migrate.c-2111-\nmm/migrate.c:2112:\ttrace_mm_migrate_pages_start(mode, reason);\nmm/migrate.c-2113-\n--\nmm/migrate.c-2138-\tif (mode == MIGRATE_ASYNC)\nmm/migrate.c:2139:\t\trc = migrate_pages_batch(\u0026folios, get_new_folio, put_new_folio,\nmm/migrate.c-2140-\t\t\t\tprivate, mode, reason, \u0026ret_folios,\n--\nmm/migrate.c-2143-\telse\nmm/migrate.c:2144:\t\trc = migrate_pages_sync(\u0026folios, get_new_folio, put_new_folio,\nmm/migrate.c-2145-\t\t\t\tprivate, mode, reason, \u0026ret_folios,\n--\nmm/migrate.c-2158-\t\t */\nmm/migrate.c:2159:\t\tmigrate_pages_batch(\u0026split_folios, get_new_folio,\nmm/migrate.c-2160-\t\t\t\tput_new_folio, private, MIGRATE_ASYNC, reason,\n--\nmm/migrate.c-2185-\tcount_vm_events(THP_MIGRATION_SPLIT, stats.nr_thp_split);\nmm/migrate.c:2186:\ttrace_mm_migrate_pages(stats.nr_succeeded, stats.nr_failed_pages,\nmm/migrate.c-2187-\t\t\t stats.nr_thp_succeeded, stats.nr_thp_failed,\n--\nmm/migrate.c=2248=static int do_move_pages_to_node(struct list_head *pagelist, int node)\n--\nmm/migrate.c-2256-\nmm/migrate.c:2257:\terr = migrate_pages(pagelist, alloc_migration_target, NULL,\nmm/migrate.c-2258-\t\t(unsigned long)\u0026mtc, MIGRATE_SYNC, MR_SYSCALL, NULL);\n--\nmm/migrate.c=2647=static bool migrate_balanced_pgdat(struct pglist_data *pgdat,\nmm/migrate.c:2648:\t\t\t\t unsigned long nr_migrate_pages)\nmm/migrate.c-2649-{\n--\nmm/migrate.c-2660-\t\t\t\t high_wmark_pages(zone) +\nmm/migrate.c:2661:\t\t\t\t nr_migrate_pages,\nmm/migrate.c-2662-\t\t\t\t ZONE_MOVABLE, ALLOC_CMA))\n--\nmm/migrate.c=2755=int migrate_misplaced_folio(struct folio *folio, int node)\n--\nmm/migrate.c-2764-\tlist_add(\u0026folio-\u003elru, \u0026migratepages);\nmm/migrate.c:2765:\tnr_remaining = migrate_pages(\u0026migratepages, alloc_misplaced_dst_folio,\nmm/migrate.c-2766-\t\t\t\t NULL, node, MIGRATE_ASYNC,\n--\nmm/page_alloc.c=7136=static int __alloc_contig_migrate_range(struct compact_control *cc,\n--\nmm/page_alloc.c-7173-\nmm/page_alloc.c:7174:\t\tret = migrate_pages(\u0026cc-\u003emigratepages, alloc_migration_target,\nmm/page_alloc.c-7175-\t\t\tNULL, (unsigned long)\u0026mtc, cc-\u003emode, MR_CONTIG_RANGE, NULL);\n--\nmm/page_alloc.c-7177-\t\t/*\nmm/page_alloc.c:7178:\t\t * On -ENOMEM, migrate_pages() bails out right away. It is pointless\nmm/page_alloc.c-7179-\t\t * to retry again over this error, so do the same here.\n--\nmm/vmscan.c=1000=static unsigned int demote_folio_list(struct list_head *demote_folios,\n--\nmm/vmscan.c-1035-\t/* Demotion ignores all cpuset and mempolicy settings */\nmm/vmscan.c:1036:\tmigrate_pages(demote_folios, alloc_demote_folio, NULL,\nmm/vmscan.c-1037-\t\t (unsigned long)\u0026mtc, MIGRATE_ASYNC, MR_DEMOTION,\n--\nscripts/checksyscalls.sh=18=cat \u003c\u003c EOF\n--\nscripts/checksyscalls.sh-72-#define __IGNORE_msync\nscripts/checksyscalls.sh:73:#define __IGNORE_migrate_pages\nscripts/checksyscalls.sh-74-#define __IGNORE_move_pages\n--\nscripts/syscall.tbl-283-237\tcommon\tset_mempolicy\t\t\tsys_set_mempolicy\nscripts/syscall.tbl:284:238\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\nscripts/syscall.tbl-285-239\tcommon\tmove_pages\t\t\tsys_move_pages\n--\ntools/include/uapi/asm-generic/unistd.h=604=__SYSCALL(__NR_set_mempolicy, sys_set_mempolicy)\ntools/include/uapi/asm-generic/unistd.h:605:#define __NR_migrate_pages 238\ntools/include/uapi/asm-generic/unistd.h:606:__SYSCALL(__NR_migrate_pages, sys_migrate_pages)\ntools/include/uapi/asm-generic/unistd.h-607-#define __NR_move_pages 239\n--\ntools/perf/arch/alpha/entry/syscalls/syscall.tbl-379-448\tcommon\tkexec_load\t\t\tsys_kexec_load\ntools/perf/arch/alpha/entry/syscalls/syscall.tbl:380:449\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\ntools/perf/arch/alpha/entry/syscalls/syscall.tbl-381-450\tcommon\topenat\t\t\t\tsys_openat\n--\ntools/perf/arch/arm/entry/syscalls/syscall.tbl-417-399\tcommon\tio_pgetevents\t\tsys_io_pgetevents_time32\ntools/perf/arch/arm/entry/syscalls/syscall.tbl:418:400\tcommon\tmigrate_pages\t\tsys_migrate_pages\ntools/perf/arch/arm/entry/syscalls/syscall.tbl-419-401\tcommon\tkexec_file_load\t\tsys_kexec_file_load\n--\ntools/perf/arch/arm64/entry/syscalls/syscall_32.tbl-414-399\tcommon\tio_pgetevents\t\tsys_io_pgetevents_time32\tcompat_sys_io_pgetevents\ntools/perf/arch/arm64/entry/syscalls/syscall_32.tbl:415:400\tcommon\tmigrate_pages\t\tsys_migrate_pages\ntools/perf/arch/arm64/entry/syscalls/syscall_32.tbl-416-401\tcommon\tkexec_file_load\t\tsys_kexec_file_load\n--\ntools/perf/arch/mips/entry/syscalls/syscall_n64.tbl-256-245\tn64\tinotify_rm_watch\t\tsys_inotify_rm_watch\ntools/perf/arch/mips/entry/syscalls/syscall_n64.tbl:257:246\tn64\tmigrate_pages\t\t\tsys_migrate_pages\ntools/perf/arch/mips/entry/syscalls/syscall_n64.tbl-258-247\tn64\topenat\t\t\t\tsys_openat\n--\ntools/perf/arch/parisc/entry/syscalls/syscall.tbl-306-271\tcommon\tinotify_rm_watch\tsys_inotify_rm_watch\ntools/perf/arch/parisc/entry/syscalls/syscall.tbl:307:272\tcommon\tmigrate_pages\t\tsys_migrate_pages\ntools/perf/arch/parisc/entry/syscalls/syscall.tbl-308-273\t32\tpselect6\t\tsys_pselect6_time32\t\tcompat_sys_pselect6_time32\n--\ntools/perf/arch/powerpc/entry/syscalls/syscall.tbl-340-# 257 reserved for vserver\ntools/perf/arch/powerpc/entry/syscalls/syscall.tbl:341:258\tnospu\tmigrate_pages\t\t\tsys_migrate_pages\ntools/perf/arch/powerpc/entry/syscalls/syscall.tbl-342-259\tnospu\tmbind\t\t\t\tsys_mbind\n--\ntools/perf/arch/s390/entry/syscalls/syscall.tbl-240-286\tcommon\tinotify_rm_watch\t\tsys_inotify_rm_watch\ntools/perf/arch/s390/entry/syscalls/syscall.tbl:241:287\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\ntools/perf/arch/s390/entry/syscalls/syscall.tbl-242-288\tcommon\topenat\t\t\t\tsys_openat\n--\ntools/perf/arch/sh/entry/syscalls/syscall.tbl-303-# 293 is unused\ntools/perf/arch/sh/entry/syscalls/syscall.tbl:304:294\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\ntools/perf/arch/sh/entry/syscalls/syscall.tbl-305-295\tcommon\topenat\t\t\t\tsys_openat\n--\ntools/perf/arch/sparc/entry/syscalls/syscall.tbl-367-301\tcommon\tget_robust_list\t\tsys_get_robust_list\t\tcompat_sys_get_robust_list\ntools/perf/arch/sparc/entry/syscalls/syscall.tbl:368:302\tcommon\tmigrate_pages\t\tsys_migrate_pages\ntools/perf/arch/sparc/entry/syscalls/syscall.tbl-369-303\tcommon\tmbind\t\t\tsys_mbind\n--\ntools/perf/arch/x86/entry/syscalls/syscall_32.tbl-308-293\ti386\tinotify_rm_watch\tsys_inotify_rm_watch\ntools/perf/arch/x86/entry/syscalls/syscall_32.tbl:309:294\ti386\tmigrate_pages\t\tsys_migrate_pages\ntools/perf/arch/x86/entry/syscalls/syscall_32.tbl-310-295\ti386\topenat\t\t\tsys_openat\t\t\tcompat_sys_openat\n--\ntools/perf/arch/x86/entry/syscalls/syscall_64.tbl-267-255\tcommon\tinotify_rm_watch\tsys_inotify_rm_watch\ntools/perf/arch/x86/entry/syscalls/syscall_64.tbl:268:256\tcommon\tmigrate_pages\t\tsys_migrate_pages\ntools/perf/arch/x86/entry/syscalls/syscall_64.tbl-269-257\tcommon\topenat\t\t\tsys_openat\n--\ntools/perf/arch/xtensa/entry/syscalls/syscall.tbl-283-261\tcommon\tremap_file_pages\t\tsys_remap_file_pages\ntools/perf/arch/xtensa/entry/syscalls/syscall.tbl:284:262\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\ntools/perf/arch/xtensa/entry/syscalls/syscall.tbl-285-263\tcommon\tmbind\t\t\t\tsys_mbind\n--\ntools/scripts/syscall.tbl-283-237\tcommon\tset_mempolicy\t\t\tsys_set_mempolicy\ntools/scripts/syscall.tbl:284:238\tcommon\tmigrate_pages\t\t\tsys_migrate_pages\ntools/scripts/syscall.tbl-285-239\tcommon\tmove_pages\t\t\tsys_move_pages\n--\ntools/testing/selftests/kvm/include/numaif.h=19=KVM_SYSCALL_DEFINE(set_mempolicy_home_node, 4, unsigned long, start,\n--\ntools/testing/selftests/kvm/include/numaif.h-22-\ntools/testing/selftests/kvm/include/numaif.h:23:KVM_SYSCALL_DEFINE(migrate_pages, 4, int, pid, unsigned long, maxnode,\ntools/testing/selftests/kvm/include/numaif.h-24-\t\t const unsigned long *, frommask, const unsigned long *, tomask);\n--\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-15- * has reentered HLT before sending the next IPI. While the vCPUs are running,\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c:16: * the host continually calls migrate_pages to move all of the process' pages\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-17- * amongst the available numa nodes on the machine.\n--\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-36-\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c:37:/* Default delay between migrate_pages calls (microseconds) */\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-38-#define DEFAULT_DELAY_USECS 500000\n--\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c=250=void do_migrations(struct test_data_page *data, int run_secs, int delay_usecs,\n--\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-265-\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c:266:\tfprintf(stderr, \"Calling migrate_pages every %d microseconds\\n\",\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-267-\t\tdelay_usecs);\n--\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-277-\t/* Init array of masks containing a single-bit in each, one for each\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c:278:\t * available node. migrate_pages called below requires specifying nodes\ntools/testing/selftests/kvm/x86/xapic_ipi_test.c-279-\t * as bit masks.\n\n"
} |
| 43/2 | 2026/08/24 18:30 | llm | patch-evaluator |
5mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 58151 output: 270 thoughts: 782 Thoughts: **My Analysis of the Page Migration Implementation** Okay, let's break this down. First, `migrate_pages` is the key function here; it's the core of page migration and is available as both a syscall (`sys_migrate_pages`, which is what we want) and an exported function. So, we're targeting the right area. The main focus of the patch seems to be centered around these symbols: * `lru_gen_look_around` * `folio_migrate_flags` * `try_to_free_pages` * `try_to_free_mem_cgroup_pages` * `folio_deactivate` * `deactivate_file_folio` * `folio_mark_lazyfree` * `migrate_pages` It looks like the changes are impacting how these functions interact with the Multi-Gen LRU (MGLRU) implementation, particularly how LRU zone sizes are updated (atomically) and the refactoring of LRU generation and reference flag manipulation into inline helpers. It also refactors core MGLRU functions such as `folio_update_gen`, `folio_inc_gen`, `lru_gen_look_around`, and `evict_folios`, which touches page reclaim, page migration and memory cgroup accounting. These changes are triggered by memory pressure (reclaim), page migration, and madvise/fadvise syscalls. Now, let's confirm the relevant kernel configuration options. We need to be sure these are enabled: * `LRU_GEN` * `LRU_GEN_ENABLED` * `LRU_GEN_STATS` * `MEMCG` * `MIGRATION` I was initially considering `LRU_GEN_WALKS_MMU`, but it is automatically defined, depending on `LRU_GEN` and `ARCH_HAS_HW_PTE_YOUNG`, so no need to specify it explicitly. The configuration checks confirm that `MIGRATION`, `MEMCG`, `LRU_GEN_ENABLED`, and `LRU_GEN_STATS` are all user-selectable options, as they are defined as bool options in their respective Kconfig files. Based on the files modified (`mm/folio.c`, `mm/memcontrol.c`, `mm/migrate.c`, and `mm/vmscan.c`), this patch deeply impacts core memory management components. Okay, I'm confident in this summary. Time to call `set-results`. |
| 44/2 | 2026/08/24 18:36 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"LRU_GEN_STATS",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"try_to_free_pages",
"try_to_free_mem_cgroup_pages",
"folio_deactivate",
"deactivate_file_folio",
"folio_mark_lazyfree",
"migrate_pages"
],
"Reasoning": "The patch modifies the Multi-Gen LRU (MGLRU) implementation in the core memory management subsystem. It changes how LRU zone sizes are updated (using atomic operations) and refactors LRU generation and reference flag manipulation into inline helpers. It also updates several core MGLRU functions like `folio_update_gen`, `folio_inc_gen`, `lru_gen_look_around`, and `evict_folios`. These changes affect page reclaim, page migration, and memory cgroup accounting. The code is reachable through memory pressure (reclaim), page migration, and madvise/fadvise syscalls.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED",
"LRU_GEN_STATS",
"MEMCG",
"MIGRATION"
],
"FocusSymbols": [
"lru_gen_look_around",
"folio_migrate_flags",
"try_to_free_pages",
"try_to_free_mem_cgroup_pages",
"folio_deactivate",
"deactivate_file_folio",
"folio_mark_lazyfree",
"migrate_pages"
],
"Reasoning": "The patch modifies the Multi-Gen LRU (MGLRU) implementation in the core memory management subsystem. It changes how LRU zone sizes are updated (using atomic operations) and refactors LRU generation and reference flag manipulation into inline helpers. It also updates several core MGLRU functions like `folio_update_gen`, `folio_inc_gen`, `lru_gen_look_around`, and `evict_folios`. These changes affect page reclaim, page migration, and memory cgroup accounting. The code is reachable through memory pressure (reclaim), page migration, and madvise/fadvise syscalls.",
"WorthFuzzing": true
} |
| 45/1 | 2026/08/24 18:36 | action | If |
5mArgs:{
"WorthFuzzing": true
}Results: null |
| 46/2 | 2026/08/24 18:36 | agent | kmsan-evaluator |
5mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch modifies internal memory management code related to LRU generation and references tracking in folio flags, and changes `lru_zone_size` to use atomic operations. It does not introduce any new structures exposed to user space, nor does it alter control flow in a way that could expose uninitialized memory. The local variable `file` in `walk_update_folio` is passed by reference to `folio_update_gen`, and although it might not be initialized on early returns, it is only accessed when `old_gen \u003e= 0`, which guarantees it has been initialized. Therefore, there is no risk of uninitialized memory usage or info-leaks, and standard KASAN/LOCKDEP testing is sufficient.",
"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 5dd612268d61060285ff77f66c43f609c311f380
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Aug 24 17:58:23 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 215e2e87f42b2..7b89d0cb5f6c6 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -113,7 +113,7 @@ struct mem_cgroup_per_node {
/* Fields which get updated often at the end. */
struct lruvec lruvec;
CACHELINE_PADDING(_pad2_);
- unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
+ atomic_long_t lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
struct mem_cgroup_reclaim_iter iter;
/*
@@ -902,10 +902,15 @@ static inline
unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec,
enum lru_list lru, int zone_idx)
{
+ long val;
struct mem_cgroup_per_node *mz;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- return READ_ONCE(mz->lru_zone_size[zone_idx][lru]);
+ val = atomic_long_read(&mz->lru_zone_size[zone_idx][lru]);
+ if (WARN_ON_ONCE(val < 0))
+ return 0;
+
+ return val;
}
void __mem_cgroup_handle_over_high(gfp_t gfp_mask);
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index 621c8653d8f7e..8cf82989ec263 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -10,6 +10,11 @@
#include <linux/userfaultfd_k.h>
#include <linux/leafops.h>
+static inline int folio_flags_is_file_lru(const unsigned long *flags)
+{
+ return !test_bit(PG_swapbacked, flags);
+}
+
/**
* folio_is_file_lru - Should the folio be on a file LRU or anon LRU?
* @folio: The folio to test.
@@ -27,7 +32,7 @@
*/
static inline int folio_is_file_lru(const struct folio *folio)
{
- return !folio_test_swapbacked(folio);
+ return folio_flags_is_file_lru(const_folio_flags(folio, 0));
}
static __always_inline void __update_lru_size(struct lruvec *lruvec,
@@ -142,10 +147,42 @@ static inline int lru_tier_from_refs(int refs, bool workingset)
return workingset ? MAX_NR_TIERS - 1 : order_base_2(refs);
}
-static inline int folio_lru_refs(const struct folio *folio)
+/**
+ * lru_gen_from_flags - Return the LRU generation number from folio flags.
+ * @flags: folio flags
+ *
+ * Returns: A number between 0 and (MAX_NR_GENS - 1), inclusive. Returns
+ * -1 if the flags indicate the folio is off the list (e.g., isolated).
+ */
+static inline int lru_gen_from_flags(unsigned long flags)
+{
+ int gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF);
+
+ BUILD_BUG_ON(LRU_GEN_MASK & LRU_REFS_MASK);
+ gen -= 1;
+ VM_WARN_ON_ONCE(gen != -1 && gen >= MAX_NR_GENS);
+ return gen;
+}
+
+/**
+ * lru_gen_set_flags - Set the LRU generation number to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @gen: generation number, between 0 and (MAX_NR_GENS - 1), inclusive.
+ */
+static inline void lru_gen_set_flags(unsigned long *flags, int gen)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS || gen < 0);
+
+ *flags &= ~LRU_GEN_MASK;
+ *flags |= (gen + 1UL) << LRU_GEN_PGOFF;
+}
+/**
+ * lru_refs_from_flags - Return LRU referenced / access count from folio flags.
+ * @flags: folio flags
+ */
+static inline int lru_refs_from_flags(unsigned long flags)
+{
if (!(flags & BIT(PG_referenced)))
return 0;
/*
@@ -155,11 +192,40 @@ static inline int folio_lru_refs(const struct folio *folio)
return ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + 1;
}
-static inline int folio_lru_gen(const struct folio *folio)
+/**
+ * lru_refs_set_flags - Set the LRU referenced / access count to specified folio flags.
+ * @flags: pointer to the folio flags
+ * @refs: referenced / access count number, between 0 and LRU_REFS_MAX, inclusive.
+ */
+static inline void lru_refs_set_flags(unsigned long *flags, unsigned int refs)
+{
+ VM_WARN_ON_ONCE(refs > LRU_REFS_MAX);
+ BUILD_BUG_ON(LRU_REFS_MAX != (LRU_REFS_MASK >> LRU_REFS_PGOFF) + 1);
+
+ *flags &= ~LRU_REFS_FLAGS;
+ if (!refs)
+ return;
+ *flags |= (BIT(PG_referenced) | ((refs - 1UL) << LRU_REFS_PGOFF));
+}
+
+static inline int folio_lru_refs(const struct folio *folio)
+{
+ return lru_refs_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
+}
+
+static inline void folio_set_lru_refs(struct folio *folio, unsigned int refs)
{
- unsigned long flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
- return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ do {
+ new_flags = old_flags;
+ lru_refs_set_flags(&new_flags, refs);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
+}
+
+static inline int folio_lru_gen(const struct folio *folio)
+{
+ return lru_gen_from_flags(READ_ONCE(*const_folio_flags(folio, 0)));
}
static inline bool lru_gen_is_active(const struct lruvec *lruvec, int gen)
@@ -270,7 +336,7 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,
gen = lru_gen_from_seq(seq);
flags = (gen + 1UL) << LRU_GEN_PGOFF;
/* see the comment on MIN_NR_GENS about PG_active */
- set_mask_bits(&folio->flags.f, LRU_GEN_MASK | BIT(PG_active), flags);
+ set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK | BIT(PG_active), flags);
lru_gen_update_size(lruvec, folio, -1, gen);
/* for folio_rotate_reclaimable() */
@@ -295,7 +361,7 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
/* for folio_migrate_flags() */
flags = !reclaiming && lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0;
- flags = set_mask_bits(&folio->flags.f, LRU_GEN_MASK, flags);
+ flags = set_mask_bits(folio_flags(folio, 0), LRU_GEN_MASK, flags);
gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
lru_gen_update_size(lruvec, folio, gen, -1);
@@ -304,11 +370,19 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return true;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+/**
+ * folio_migrate_lru_refs - copy the reference state to a new folio
+ * @new: the destination folio
+ * @old: the source folio
+ *
+ * Transfer the reference state to @new during migration: the MGLRU
+ * refs count, including PG_referenced, or just PG_referenced for the
+ * active/inactive LRU.
+ */
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
- unsigned long refs = READ_ONCE(old->flags.f) & LRU_REFS_MASK;
-
- set_mask_bits(&new->flags.f, LRU_REFS_MASK, refs);
+ BUILD_BUG_ON(LRU_REFS_MASK & BIT(PG_referenced));
+ folio_set_lru_refs(new, folio_lru_refs(old));
}
#else /* !CONFIG_LRU_GEN */
@@ -337,9 +411,10 @@ static inline bool lru_gen_del_folio(struct lruvec *lruvec, struct folio *folio,
return false;
}
-static inline void folio_migrate_refs(struct folio *new, const struct folio *old)
+static inline void folio_migrate_lru_refs(struct folio *new, const struct folio *old)
{
-
+ if (folio_test_referenced(old))
+ folio_set_referenced(new);
}
#endif /* CONFIG_LRU_GEN */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 94f9c3ff54160..9b27cf53bdbc7 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -492,11 +492,14 @@ enum lruvec_flags {
* folio->flags, masked by LRU_REFS_MASK.
*/
#define MAX_NR_TIERS 4U
+#define LRU_TIER_MIN 0U
+#define LRU_TIER_MAX (MAX_NR_TIERS - 1)
#ifndef __GENERATING_BOUNDS_H
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
+#define LRU_REFS_MAX BIT(LRU_REFS_WIDTH)
/*
* For folios accessed multiple times through file descriptors,
diff --git a/mm/folio.c b/mm/folio.c
index 59c477120b9a8..0adfe4f5ef72f 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -353,26 +353,28 @@ static void __lru_cache_activate_folio(struct folio *folio)
static void lru_gen_inc_refs(struct folio *folio)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int refs;
if (folio_test_unevictable(folio))
return;
/* see the comment on LRU_REFS_FLAGS */
- if (!folio_test_referenced(folio)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ if (!folio_lru_refs(folio)) {
+ folio_set_lru_refs(folio, 1);
return;
}
do {
- if ((old_flags & LRU_REFS_MASK) == LRU_REFS_MASK) {
+ new_flags = old_flags;
+ refs = lru_refs_from_flags(old_flags);
+ if (refs == LRU_REFS_MAX) {
if (!folio_test_workingset(folio))
folio_set_workingset(folio);
return;
}
-
- new_flags = old_flags + BIT(LRU_REFS_PGOFF);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_refs_set_flags(&new_flags, refs + 1);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
}
static bool lru_gen_clear_refs(struct folio *folio)
@@ -384,7 +386,8 @@ static bool lru_gen_clear_refs(struct folio *folio)
if (gen < 0)
return true;
- set_mask_bits(&folio->flags.f, LRU_REFS_FLAGS | BIT(PG_workingset), 0);
+ folio_set_lru_refs(folio, 0);
+ folio_clear_workingset(folio);
rcu_read_lock();
seq = READ_ONCE(folio_lruvec(folio)->lrugen.min_seq[type]);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 11b85f4b6828b..a7572ded56c9d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1529,28 +1529,12 @@ void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
int zid, long nr_pages)
{
struct mem_cgroup_per_node *mz;
- unsigned long *lru_size;
- long size;
if (mem_cgroup_disabled())
return;
mz = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
- lru_size = &mz->lru_zone_size[zid][lru];
-
- if (nr_pages < 0)
- *lru_size += nr_pages;
-
- size = *lru_size;
- if (WARN_ONCE(size < 0,
- "%s(%p, %d, %ld): lru_size %ld\n",
- __func__, lruvec, lru, nr_pages, size)) {
- VM_BUG_ON(1);
- *lru_size = 0;
- }
-
- if (nr_pages > 0)
- *lru_size += nr_pages;
+ atomic_long_add(nr_pages, &mz->lru_zone_size[zid][lru]);
}
/**
diff --git a/mm/migrate.c b/mm/migrate.c
index 15b45832bcfa7..96eda490ba435 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -776,8 +776,6 @@ void folio_migrate_flags(struct folio *newfolio, struct folio *folio)
{
int cpupid;
- if (folio_test_referenced(folio))
- folio_set_referenced(newfolio);
if (folio_test_uptodate(folio))
folio_mark_uptodate(newfolio);
if (folio_test_clear_active(folio)) {
@@ -807,7 +805,9 @@ 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 the reference state, including PG_referenced */
+ folio_migrate_lru_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 c1404a59523d6..a1497ad61ad70 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_lru_refs(folio, 0);
+ folio_set_workingset(folio);
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_lru_refs(folio, 0);
+ folio_set_workingset(folio);
+ } else {
folio_mark_accessed(folio);
+ }
return true;
}
#else
@@ -3195,8 +3198,8 @@ struct ctrl_pos {
int gain;
};
-static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
- struct ctrl_pos *pos)
+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier_min,
+ int tier_max, int gain, struct ctrl_pos *pos)
{
int i;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
@@ -3205,7 +3208,7 @@ static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
pos->gain = gain;
pos->refaulted = pos->total = 0;
- for (i = tier % MAX_NR_TIERS; i <= min(tier, MAX_NR_TIERS - 1); i++) {
+ for (i = tier_min; i <= tier_max; i++) {
pos->refaulted += lrugen->avg_refaulted[type][i] +
atomic_long_read(&lrugen->refaulted[hist][type][i]);
pos->total += lrugen->avg_total[type][i] +
@@ -3266,11 +3269,11 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
******************************************************************************/
/* promote pages accessed through page tables */
-static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)
+static int folio_update_gen(struct folio *folio, int new_gen, int *is_file,
+ const vma_flags_t *vma_flags)
{
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE(gen >= MAX_NR_GENS);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
+ int old_gen;
/*
* See the comment on LRU_REFS_FLAGS, and activate file-backed
@@ -3279,20 +3282,25 @@ static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma
*/
if (!folio_test_referenced(folio) && !folio_test_workingset(folio) &&
!is_exec_file_folio(folio, vma_flags)) {
- set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
+ folio_set_lru_refs(folio, 1);
return -1;
}
do {
+ old_gen = lru_gen_from_flags(old_flags);
+ new_flags = old_flags;
+
/* lru_gen_del_folio() has isolated this page? */
- if (!(old_flags & LRU_GEN_MASK))
- return -1;
+ if (old_gen < 0)
+ break;
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= ((gen + 1UL) << LRU_GEN_PGOFF) | BIT(PG_workingset);
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ new_flags |= BIT(PG_workingset);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
- return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ *is_file = folio_flags_is_file_lru(&old_flags);
+ return old_gen;
}
/* protect pages accessed multiple times through file descriptors */
@@ -3301,21 +3309,20 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
int type = folio_is_file_lru(folio);
struct lru_gen_folio *lrugen = &lruvec->lrugen;
int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
- unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
-
- VM_WARN_ON_ONCE_FOLIO(!(old_flags & LRU_GEN_MASK), folio);
+ unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
do {
- new_gen = ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1;
+ new_gen = lru_gen_from_flags(old_flags);
+
/* folio_update_gen() has promoted this page? */
if (new_gen >= 0 && new_gen != old_gen)
return new_gen;
+ new_flags = old_flags;
new_gen = (old_gen + 1) % MAX_NR_GENS;
-
- new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_FLAGS);
- new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF;
- } while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ lru_gen_set_flags(&new_flags, new_gen);
+ lru_refs_set_flags(&new_flags, 0);
+ } while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
lru_gen_update_size(lruvec, folio, old_gen, new_gen);
@@ -3323,9 +3330,8 @@ static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
}
static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,
- int old_gen, int new_gen)
+ int old_gen, int new_gen, int type)
{
- int type = folio_is_file_lru(folio);
int zone = folio_zonenum(folio);
int delta = folio_nr_pages(folio);
@@ -3512,22 +3518,24 @@ static bool suitable_to_scan(int total, int young)
}
static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,
- struct folio *folio, int new_gen, bool dirty)
+ struct lruvec *lruvec, struct folio *folio, bool dirty)
{
- int old_gen;
+ int new_gen, old_gen, file;
if (!folio)
return;
+ new_gen = lru_gen_from_seq(READ_ONCE(lruvec->lrugen.max_seq));
+
if (dirty && !folio_test_dirty(folio) &&
!(folio_test_anon(folio) && folio_test_swapbacked(folio) &&
!folio_test_swapcache(folio)))
folio_mark_dirty(folio);
if (walk) {
- old_gen = folio_update_gen(folio, new_gen, &vma->flags);
+ old_gen = folio_update_gen(folio, new_gen, &file, &vma->flags);
if (old_gen >= 0 && old_gen != new_gen)
- update_batch_size(walk, folio, old_gen, new_gen);
+ update_batch_size(walk, folio, old_gen, new_gen, file);
} else if (lru_gen_set_refs(folio, &vma->flags)) {
old_gen = folio_lru_gen(folio);
if (old_gen >= 0 && old_gen != new_gen)
@@ -3549,8 +3557,6 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
unsigned int nr;
pmd_t pmdval;
@@ -3601,7 +3607,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
continue;
if (last != folio) {
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3614,7 +3620,7 @@ static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,
walk->mm_stats[MM_LEAF_YOUNG] += nr;
}
- walk_update_folio(walk, args->vma, last, gen, dirty);
+ walk_update_folio(walk, args->vma, walk->lruvec, last, dirty);
last = NULL;
if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end))
@@ -3637,8 +3643,6 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
struct lru_gen_mm_walk *walk = args->private;
struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec);
struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec);
- DEFINE_MAX_SEQ(walk->lruvec);
- int gen = lru_gen_from_seq(max_seq);
VM_WARN_ON_ONCE(pud_leaf(*pud));
@@ -3692,7 +3696,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
goto next;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
last = folio;
dirty = false;
@@ -3706,7 +3710,7 @@ static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area
i = i > MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1;
} while (i <= MIN_LRU_BATCH);
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, walk->lruvec, last, dirty);
lazy_mmu_mode_disable();
spin_unlock(ptl);
@@ -4270,8 +4274,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
struct pglist_data *pgdat = folio_pgdat(folio);
struct lruvec *lruvec;
struct lru_gen_mm_state *mm_state;
- unsigned long max_seq;
- int gen;
lockdep_assert_held(pvmw->ptl);
VM_WARN_ON_ONCE_FOLIO(folio_test_lru(folio), folio);
@@ -4308,8 +4310,6 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
memcg = get_mem_cgroup_from_folio(folio);
lruvec = mem_cgroup_lruvec(memcg, pgdat);
- max_seq = READ_ONCE((lruvec)->lrugen.max_seq);
- gen = lru_gen_from_seq(max_seq);
mm_state = get_mm_state(lruvec);
lazy_mmu_mode_enable();
@@ -4341,7 +4341,7 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
continue;
if (last != folio) {
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
last = folio;
dirty = false;
@@ -4353,13 +4353,14 @@ bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)
young += nr;
}
- walk_update_folio(walk, vma, last, gen, dirty);
+ walk_update_folio(walk, vma, lruvec, last, dirty);
lazy_mmu_mode_disable();
/* feedback from rmap walkers to page table walkers */
if (mm_state && suitable_to_scan(i, young))
- update_bloom_filter(mm_state, max_seq, pvmw->pmd);
+ update_bloom_filter(mm_state, READ_ONCE(lruvec->lrugen.max_seq),
+ pvmw->pmd);
mem_cgroup_put(memcg);
@@ -4711,7 +4712,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);
@@ -4804,9 +4805,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;
}
@@ -4827,8 +4828,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);
}
@@ -4927,8 +4930,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);
|
| 47/3 | 2026/08/24 18:36 | llm | kmsan-evaluator |
5mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11404 output: 174 thoughts: 1140 Thoughts: **Analysis of Memory Management Patch** Okay, let's break this patch down. My initial assessment is that this is a relatively contained modification, primarily focused on internal memory management, specifically concerning LRU generation and reference tracking. The changes appear to be concentrated in the core memory management files: `memcontrol.h`, `mm_inline.h`, `mmzone.h`, `folio.c`, `memcontrol.c`, `migrate.c`, and `vmscan.c`. The first notable change is the refactoring of `lru_zone_size` from a simple `unsigned long` to an `atomic_long_t` within the `mem_cgroup_per_node` structure. This immediately signals a need for atomic operations, confirmed by the use of `atomic_long_read` and checks for negative values. Several new functions have been introduced for manipulating folio flags directly, such as `folio_flags_is_file_lru`, `lru_gen_from_flags`, etc. This looks like the core of the optimization. We are moving from direct field manipulation to bitwise operations on folio flags, and in my experience, that's often a signal for improved performance and atomicity. The `folio_migrate_lru_refs` function is replacing `folio_migrate_refs`, and all the other functions like `lru_gen_inc_refs` now use the flag manipulation functions. `walk_update_folio` now takes `lruvec` and `file` parameters, and `read_ctrl_pos` takes `tier_min` and `tier_max`. The significant point here is that nothing is exposed to user space. There's no change to any system calls, no new structures, no changes to how we handle user-space interactions. This means the surface area for introducing new bugs from a security perspective is minimized. Also, there's no mention of any new uninitialized memory handling. These changes are all about internal memory management and atomicity, which is a good sign from a functional perspective. My first thought on these changes is that there is no risk of uninitialized memory being exposed here. Folio flags are always initialized at creation, and the `lru_zone_size` array is part of the `mem_cgroup_per_node` structure, which is allocated and initialized by the memcg subsystem, it should also be safely initialized, which is good. This patch seems to be refactoring how LRU generation and references are stored in folio flags and updating the use of `lru_zone_size` to atomic. Okay, let's double-check just to be sure... Let me focus in on the `folio_update_gen` and `walk_update_folio` functions. I need to make sure that the `file` variable is properly initialized. Looking at `folio_update_gen`, the code *does* set `*is_file` (which is `file`) before returning. However, it *can* return early if the folio isn't referenced, isn't part of the workingset, and isn't an executable file. I need to be sure that the early return doesn't cause a problem. Ah, I see it now. If `folio_update_gen` returns -1, then `*is_file` is *not* set. However, back in `walk_update_folio`, the `update_batch_size` function is *only* called if `old_gen >= 0`. Therefore, the `file` variable is only passed as a parameter to `update_batch_size` if it was initialized by `folio_update_gen`. Therefore, there's no way that we can use an uninitialized `file` value. Excellent. That leaves `lru_zone_size`, which, as I mentioned, is an `atomic_long_t` and is manipulated using `atomic_long_read` and `atomic_long_add`. So that should be safe. No need for KMSAN. Standard bug detectors and KASAN should be sufficient to catch any issues related to memory corruption. |
| 48/3 | 2026/08/24 18:42 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch modifies internal memory management code related to LRU generation and references tracking in folio flags, and changes `lru_zone_size` to use atomic operations. It does not introduce any new structures exposed to user space, nor does it alter control flow in a way that could expose uninitialized memory. The local variable `file` in `walk_update_folio` is passed by reference to `folio_update_gen`, and although it might not be initialized on early returns, it is only accessed when `old_gen \u003e= 0`, which guarantees it has been initialized. Therefore, there is no risk of uninitialized memory usage or info-leaks, and standard KASAN/LOCKDEP testing is sufficient.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch modifies internal memory management code related to LRU generation and references tracking in folio flags, and changes `lru_zone_size` to use atomic operations. It does not introduce any new structures exposed to user space, nor does it alter control flow in a way that could expose uninitialized memory. The local variable `file` in `walk_update_folio` is passed by reference to `folio_update_gen`, and although it might not be initialized on early returns, it is only accessed when `old_gen \u003e= 0`, which guarantees it has been initialized. Therefore, there is no risk of uninitialized memory usage or info-leaks, and standard KASAN/LOCKDEP testing is sufficient.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|