AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/21 00:06 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "MEMCG",
    "LRU_GEN",
    "LRU_GEN_ENABLED"
  ],
  "FocusSymbols": [
    "prepare_scan_control",
    "shrink_inactive_list",
    "shrink_active_list",
    "evict_folios",
    "lruvec_page_state_monotonic",
    "node_page_state_monotonic",
    "workingset_refault",
    "pageout"
  ],
  "Reasoning": "The patch modifies the LRU cost model and statistics tracking in the core memory management subsystem. It introduces monotonic page state tracking for LRU vectors and nodes, and updates how `anon_cost` and `file_cost` are calculated during memory reclaim. It also modifies both the traditional LRU and Multi-Gen LRU (MGLRU) reclaim paths to use the new cost model and statistics. These are functional changes to reachable core kernel logic that should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/21 00:06 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 1cca8549e97de5e36710969e7597332c5d64fe15\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jul 21 00:06:41 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h\nindex e1f46a0016fcfd..b40bc4f6fe4a0e 100644\n--- a/include/linux/memcontrol.h\n+++ b/include/linux/memcontrol.h\n@@ -931,6 +931,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item);\n bool memcg_stat_item_valid(int idx);\n bool memcg_vm_event_item_valid(enum vm_event_item idx);\n unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx);\n+unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\n+\t\t\t\t\t  enum node_stat_item idx);\n unsigned long lruvec_page_state_local(struct lruvec *lruvec,\n \t\t\t\t      enum node_stat_item idx);\n \n@@ -1378,6 +1380,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec,\n \treturn node_page_state(lruvec_pgdat(lruvec), idx);\n }\n \n+static inline unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\n+\t\t\t\t\t\t\tenum node_stat_item idx)\n+{\n+\treturn node_page_state_monotonic(lruvec_pgdat(lruvec), idx);\n+}\n+\n static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,\n \t\t\t\t\t\t    enum node_stat_item idx)\n {\ndiff --git a/include/linux/mmzone.h b/include/linux/mmzone.h\nindex ca27121871475c..85303c5867c84b 100644\n--- a/include/linux/mmzone.h\n+++ b/include/linux/mmzone.h\n@@ -323,6 +323,8 @@ enum node_stat_item {\n \tPGSCAN_PROACTIVE,\n \tPGSCAN_ANON,\n \tPGSCAN_FILE,\n+\tPGROTATE_ANON,\n+\tPGROTATE_FILE,\n \tPGREFILL,\n #ifdef CONFIG_HUGETLB_PAGE\n \tNR_HUGETLB,\n@@ -755,6 +757,12 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent,\n \n #endif /* CONFIG_LRU_GEN */\n \n+struct lru_cost {\n+\tunsigned long\t\tcount;\n+\tunsigned long\t\tlast_rotated;\n+\tunsigned long\t\tlast_io;\n+};\n+\n struct lruvec {\n \tstruct list_head\t\tlists[NR_LRU_LISTS];\n \t/* per lruvec lru_lock for memcg */\n@@ -763,9 +771,12 @@ struct lruvec {\n \t * These track the cost of reclaiming one LRU - file or anon -\n \t * over the other. As the observed cost of reclaiming one LRU\n \t * increases, the reclaim scan balance tips toward the other.\n+\t * Updated and decayed at prepare_scan_control() time; cost_lock\n+\t * serialises that update.\n \t */\n-\tunsigned long\t\t\tanon_cost;\n-\tunsigned long\t\t\tfile_cost;\n+\tstruct lru_cost\t\t\tcost[ANON_AND_FILE];\n+\t/* Protects cost[]. */\n+\tspinlock_t\t\t\tcost_lock;\n \t/* Non-resident age, driven by LRU movement */\n \tatomic_long_t\t\t\tnonresident_age;\n \t/* Refaults at the time of last reclaim cycle */\ndiff --git a/include/linux/swap.h b/include/linux/swap.h\nindex 8f0f68e245baa3..b8e2e1af2b1425 100644\n--- a/include/linux/swap.h\n+++ b/include/linux/swap.h\n@@ -309,9 +309,6 @@ extern unsigned long totalreserve_pages;\n \n \n /* linux/mm/swap.c */\n-void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,\n-\t\tunsigned int nr_io, unsigned int nr_rotated);\n-void lru_note_cost_refault(struct folio *);\n void folio_add_lru(struct folio *);\n void folio_add_lru_vma(struct folio *, struct vm_area_struct *);\n void mark_page_accessed(struct page *);\ndiff --git a/include/linux/vmstat.h b/include/linux/vmstat.h\nindex 3c9c266cf782ad..5b31d8e7ae4058 100644\n--- a/include/linux/vmstat.h\n+++ b/include/linux/vmstat.h\n@@ -20,7 +20,6 @@ struct reclaim_stat {\n \tunsigned nr_congested;\n \tunsigned nr_writeback;\n \tunsigned nr_immediate;\n-\tunsigned nr_pageout;\n \tunsigned nr_activate[ANON_AND_FILE];\n \tunsigned nr_ref_keep;\n \tunsigned nr_unmap_fail;\n@@ -194,6 +193,19 @@ unsigned long global_node_page_state_pages(enum node_stat_item item)\n \treturn x;\n }\n \n+/*\n+ * Non-clamping variant of global_node_page_state() intended for callers that\n+ * snapshot a monotonically-incremented counter and subtract two samples.\n+ * Returns the raw wrapping value so that unsigned modular subtraction stays\n+ * correct across a signed-long overflow (a real hazard on 32-bit) that the\n+ * clamp in global_node_page_state() would otherwise turn into a huge spurious\n+ * delta. Do NOT use for non-monotonic page-count reads.\n+ */\n+static inline unsigned long global_node_page_state_monotonic(enum node_stat_item item)\n+{\n+\treturn (unsigned long)atomic_long_read(\u0026vm_node_stat[item]);\n+}\n+\n static inline unsigned long global_node_page_state(enum node_stat_item item)\n {\n \tVM_WARN_ON_ONCE(vmstat_item_in_bytes(item));\n@@ -259,11 +271,14 @@ extern unsigned long node_page_state(struct pglist_data *pgdat,\n \t\t\t\t\t\tenum node_stat_item item);\n extern unsigned long node_page_state_pages(struct pglist_data *pgdat,\n \t\t\t\t\t   enum node_stat_item item);\n+extern unsigned long node_page_state_monotonic(struct pglist_data *pgdat,\n+\t\t\t\t\t       enum node_stat_item item);\n extern void fold_vm_numa_events(void);\n #else\n #define sum_zone_node_page_state(node, item) global_zone_page_state(item)\n #define node_page_state(node, item) global_node_page_state(item)\n #define node_page_state_pages(node, item) global_node_page_state_pages(item)\n+#define node_page_state_monotonic(node, item) global_node_page_state_monotonic(item)\n static inline void fold_vm_numa_events(void)\n {\n }\ndiff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c\nindex 7650692115678c..091bc9ffee44a0 100644\n--- a/mm/memcontrol-v1.c\n+++ b/mm/memcontrol-v1.c\n@@ -1988,8 +1988,8 @@ void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)\n \t\tfor_each_online_pgdat(pgdat) {\n \t\t\tmz = memcg-\u003enodeinfo[pgdat-\u003enode_id];\n \n-\t\t\tanon_cost += mz-\u003elruvec.anon_cost;\n-\t\t\tfile_cost += mz-\u003elruvec.file_cost;\n+\t\t\tanon_cost += mz-\u003elruvec.cost[WORKINGSET_ANON].count;\n+\t\t\tfile_cost += mz-\u003elruvec.cost[WORKINGSET_FILE].count;\n \t\t}\n \t\tseq_buf_printf(s, \"anon_cost %lu\\n\", anon_cost);\n \t\tseq_buf_printf(s, \"file_cost %lu\\n\", file_cost);\ndiff --git a/mm/memcontrol.c b/mm/memcontrol.c\nindex 6dc4888a90f3fd..c97c9bc3f8ddb8 100644\n--- a/mm/memcontrol.c\n+++ b/mm/memcontrol.c\n@@ -393,6 +393,7 @@ static const unsigned int memcg_node_stat_items[] = {\n \tNR_SHMEM_THPS,\n \tNR_FILE_THPS,\n \tNR_ANON_THPS,\n+\tNR_VMSCAN_WRITE,\n \tNR_VMALLOC,\n \tNR_KERNEL_STACK_KB,\n \tNR_PAGETABLE,\n@@ -419,6 +420,8 @@ static const unsigned int memcg_node_stat_items[] = {\n \tPGSCAN_PROACTIVE,\n \tPGSCAN_ANON,\n \tPGSCAN_FILE,\n+\tPGROTATE_ANON,\n+\tPGROTATE_FILE,\n \tPGREFILL,\n #ifdef CONFIG_HUGETLB_PAGE\n \tNR_HUGETLB,\n@@ -502,6 +505,42 @@ unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx)\n \treturn x;\n }\n \n+/**\n+ * lruvec_page_state_monotonic - non-clamping lruvec stat read for delta sampling\n+ * @lruvec: the LRU vector to read from\n+ * @idx: the node_stat_item to read\n+ *\n+ * Returns the raw state[idx] value cast to unsigned long, skipping the\n+ * clamp-negative-to-zero step in lruvec_page_state(). Intended for callers\n+ * that snapshot a monotonically-incremented counter and subtract two\n+ * samples: unsigned modular arithmetic then yields the correct delta across\n+ * a signed-long wraparound (a real hazard on 32-bit) that the clamp would\n+ * otherwise turn into a huge spurious delta.\n+ *\n+ * Do NOT use for non-monotonic page-count reads where a transient negative\n+ * reading from per-CPU delta skew must present as zero.\n+ *\n+ * XXX: This helper (and its node/global peers) exists because we place\n+ * monotonically-incremented event counters (NR_VMSCAN_WRITE and PGROTATE_*)\n+ * into enum node_stat_item.\n+ */\n+unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\n+\t\t\t\t\t  enum node_stat_item idx)\n+{\n+\tstruct mem_cgroup_per_node *pn;\n+\tint i;\n+\n+\tif (mem_cgroup_disabled())\n+\t\treturn node_page_state_monotonic(lruvec_pgdat(lruvec), idx);\n+\n+\ti = memcg_stats_index(idx);\n+\tif (WARN_ONCE(BAD_STAT_IDX(i), \"%s: missing stat item %d\\n\", __func__, idx))\n+\t\treturn 0;\n+\n+\tpn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);\n+\treturn (unsigned long)READ_ONCE(pn-\u003elruvec_stats-\u003estate[i]);\n+}\n+\n unsigned long lruvec_page_state_local(struct lruvec *lruvec,\n \t\t\t\t      enum node_stat_item idx)\n {\ndiff --git a/mm/mmzone.c b/mm/mmzone.c\nindex 0c8f181d9d5006..17139db4d291ce 100644\n--- a/mm/mmzone.c\n+++ b/mm/mmzone.c\n@@ -78,6 +78,7 @@ void lruvec_init(struct lruvec *lruvec)\n \n \tmemset(lruvec, 0, sizeof(struct lruvec));\n \tspin_lock_init(\u0026lruvec-\u003elru_lock);\n+\tspin_lock_init(\u0026lruvec-\u003ecost_lock);\n \tzswap_lruvec_state_init(lruvec);\n \n \tfor_each_lru(lru)\ndiff --git a/mm/swap.c b/mm/swap.c\nindex 588f50d8f1a8c9..74b281778cbc92 100644\n--- a/mm/swap.c\n+++ b/mm/swap.c\n@@ -272,73 +272,6 @@ void folio_rotate_reclaimable(struct folio *folio)\n \tfolio_batch_add_and_move(folio, lru_move_tail);\n }\n \n-void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,\n-\t\tunsigned int nr_io, unsigned int nr_rotated)\n-\t\t__releases(lruvec-\u003elru_lock)\n-\t\t__releases(rcu)\n-{\n-\tunsigned long cost;\n-\n-\t/*\n-\t * Reflect the relative cost of incurring IO and spending CPU\n-\t * time on rotations. This doesn't attempt to make a precise\n-\t * comparison, it just says: if reloads are about comparable\n-\t * between the LRU lists, or rotations are overwhelmingly\n-\t * different between them, adjust scan balance for CPU work.\n-\t */\n-\tcost = nr_io * SWAP_CLUSTER_MAX + nr_rotated;\n-\tif (!cost) {\n-\t\tspin_unlock_irq(\u0026lruvec-\u003elru_lock);\n-\t\trcu_read_unlock();\n-\t\treturn;\n-\t}\n-\n-\tfor (;;) {\n-\t\tunsigned long lrusize;\n-\n-\t\t/* Record cost event */\n-\t\tif (file)\n-\t\t\tlruvec-\u003efile_cost += cost;\n-\t\telse\n-\t\t\tlruvec-\u003eanon_cost += cost;\n-\n-\t\t/*\n-\t\t * Decay previous events\n-\t\t *\n-\t\t * Because workloads change over time (and to avoid\n-\t\t * overflow) we keep these statistics as a floating\n-\t\t * average, which ends up weighing recent refaults\n-\t\t * more than old ones.\n-\t\t */\n-\t\tlrusize = lruvec_page_state(lruvec, NR_INACTIVE_ANON) +\n-\t\t\t  lruvec_page_state(lruvec, NR_ACTIVE_ANON) +\n-\t\t\t  lruvec_page_state(lruvec, NR_INACTIVE_FILE) +\n-\t\t\t  lruvec_page_state(lruvec, NR_ACTIVE_FILE);\n-\n-\t\tif (lruvec-\u003efile_cost + lruvec-\u003eanon_cost \u003e lrusize / 4) {\n-\t\t\tlruvec-\u003efile_cost /= 2;\n-\t\t\tlruvec-\u003eanon_cost /= 2;\n-\t\t}\n-\n-\t\tspin_unlock_irq(\u0026lruvec-\u003elru_lock);\n-\t\tlruvec = parent_lruvec(lruvec);\n-\t\tif (!lruvec) {\n-\t\t\trcu_read_unlock();\n-\t\t\tbreak;\n-\t\t}\n-\t\tspin_lock_irq(\u0026lruvec-\u003elru_lock);\n-\t}\n-}\n-\n-void lru_note_cost_refault(struct folio *folio)\n-{\n-\tstruct lruvec *lruvec;\n-\n-\tlruvec = folio_lruvec_lock_irq(folio);\n-\tlru_note_cost_unlock_irq(lruvec, folio_is_file_lru(folio),\n-\t\t\t\tfolio_nr_pages(folio), 0);\n-}\n-\n static void lru_activate(struct lruvec *lruvec, struct folio *folio)\n {\n \tlong nr_pages = folio_nr_pages(folio);\n@@ -1164,8 +1097,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int\n \n \tchild_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));\n \tparent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid));\n-\tparent_lruvec-\u003eanon_cost += child_lruvec-\u003eanon_cost;\n-\tparent_lruvec-\u003efile_cost += child_lruvec-\u003efile_cost;\n \n \tfor_each_lru(lru)\n \t\tlruvec_reparent_lru(child_lruvec, parent_lruvec, lru, nid);\ndiff --git a/mm/vmscan.c b/mm/vmscan.c\nindex 35c3bb15ae96a3..07545095b352cc 100644\n--- a/mm/vmscan.c\n+++ b/mm/vmscan.c\n@@ -668,7 +668,7 @@ static pageout_t pageout(struct folio *folio, struct address_space *mapping,\n \t\tfolio_clear_reclaim(folio);\n \n \ttrace_mm_vmscan_write_folio(folio);\n-\tnode_stat_add_folio(folio, NR_VMSCAN_WRITE);\n+\tlruvec_stat_mod_folio(folio, NR_VMSCAN_WRITE, folio_nr_pages(folio));\n \treturn PAGE_SUCCESS;\n }\n \n@@ -1413,8 +1413,6 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,\n \t\t\t\t\tsc-\u003enr_scanned -= (nr_pages - 1);\n \t\t\t\t\tnr_pages = 1;\n \t\t\t\t}\n-\t\t\t\tstat-\u003enr_pageout += nr_pages;\n-\n \t\t\t\tif (folio_test_writeback(folio))\n \t\t\t\t\tgoto keep;\n \t\t\t\tif (folio_test_dirty(folio))\n@@ -2038,10 +2036,10 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan,\n \titem = PGSTEAL_KSWAPD + reclaimer_offset(sc);\n \tmod_lruvec_state(lruvec, item, nr_reclaimed);\n \tmod_lruvec_state(lruvec, PGSTEAL_ANON + file, nr_reclaimed);\n+\tif (nr_scanned \u003e nr_reclaimed)\n+\t\tmod_lruvec_state(lruvec, PGROTATE_ANON + file,\n+\t\t\t\t nr_scanned - nr_reclaimed);\n \n-\tlruvec_lock_irq(lruvec);\n-\tlru_note_cost_unlock_irq(lruvec, file, stat.nr_pageout,\n-\t\t\t\t\tnr_scanned - nr_reclaimed);\n \thandle_reclaim_writeback(nr_taken, pgdat, sc, \u0026stat);\n \ttrace_mm_vmscan_lru_shrink_inactive(pgdat-\u003enode_id,\n \t\t\tnr_scanned, nr_reclaimed, \u0026stat, sc-\u003epriority, file);\n@@ -2147,9 +2145,9 @@ static void shrink_active_list(unsigned long nr_to_scan,\n \tcount_vm_events(PGDEACTIVATE, nr_deactivate);\n \tcount_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_deactivate);\n \tmod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken);\n+\tif (nr_rotated)\n+\t\tmod_lruvec_state(lruvec, PGROTATE_ANON + file, nr_rotated);\n \n-\tlruvec_lock_irq(lruvec);\n-\tlru_note_cost_unlock_irq(lruvec, file, 0, nr_rotated);\n \ttrace_mm_vmscan_lru_shrink_active(pgdat-\u003enode_id, nr_taken, nr_activate,\n \t\t\tnr_deactivate, nr_rotated, sc-\u003epriority, file);\n }\n@@ -2282,8 +2280,10 @@ enum scan_balance {\n \n static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)\n {\n-\tunsigned long file;\n+\tstruct lru_cost *anon_cost, *file_cost;\n \tstruct lruvec *target_lruvec;\n+\tunsigned long lrusize;\n+\tunsigned long file;\n \n \tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching())\n \t\treturn;\n@@ -2299,11 +2299,69 @@ static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)\n \n \t/*\n \t * Determine the scan balance between anon and file LRUs.\n+\t *\n+\t * The cost model is based on rotations, refaults and\n+\t * reclaim-driven writes (anon only) on each side.\n+\t *\n+\t * These event counters are monotonic, so each reclaim cycle\n+\t * the delta since the last scan is extracted and incorporated\n+\t * into a decaying average. This ensures currency, as workloads\n+\t * change over time, and avoids overflow in the calculations.\n+\t *\n+\t * Use lruvec_page_state_monotonic() so unsigned subtraction\n+\t * yields the correct delta across a signed-long wraparound of\n+\t * the underlying counter (a real hazard on 32-bit that the\n+\t * clamp in lruvec_page_state() would otherwise turn into a huge\n+\t * spurious delta).\n \t */\n-\tspin_lock_irq(\u0026target_lruvec-\u003elru_lock);\n-\tsc-\u003eanon_cost = target_lruvec-\u003eanon_cost;\n-\tsc-\u003efile_cost = target_lruvec-\u003efile_cost;\n-\tspin_unlock_irq(\u0026target_lruvec-\u003elru_lock);\n+\tspin_lock(\u0026target_lruvec-\u003ecost_lock);\n+\n+\tfor (int f = 0; f \u003c= 1; f++) {\n+\t\tstruct lru_cost *cost = \u0026target_lruvec-\u003ecost[f];\n+\t\tunsigned long rotated, io, nr_rotated, nr_io;\n+\n+\t\trotated = lruvec_page_state_monotonic(target_lruvec,\n+\t\t\t\t\t\t      PGROTATE_ANON + f);\n+\t\tio = lruvec_page_state_monotonic(target_lruvec,\n+\t\t\t\t\t\t WORKINGSET_RESTORE_BASE + f);\n+\t\tif (f == WORKINGSET_ANON)\n+\t\t\tio += lruvec_page_state_monotonic(target_lruvec,\n+\t\t\t\t\t\t\t  NR_VMSCAN_WRITE);\n+\n+\t\tnr_rotated = rotated - cost-\u003elast_rotated;\n+\t\tnr_io = io - cost-\u003elast_io;\n+\n+\t\t/*\n+\t\t * Reflect the relative cost of incurring IO and spending\n+\t\t * CPU time on rotations. This doesn't attempt to make a\n+\t\t * precise comparison, it just says: if reloads are about\n+\t\t * comparable between the LRU lists, or rotations are\n+\t\t * overwhelmingly different between them, adjust scan\n+\t\t * balance for CPU work.\n+\t\t */\n+\t\tcost-\u003ecount += nr_io * SWAP_CLUSTER_MAX + nr_rotated;\n+\n+\t\tcost-\u003elast_rotated = rotated;\n+\t\tcost-\u003elast_io = io;\n+\t}\n+\n+\tanon_cost = \u0026target_lruvec-\u003ecost[WORKINGSET_ANON];\n+\tfile_cost = \u0026target_lruvec-\u003ecost[WORKINGSET_FILE];\n+\n+\tlrusize = lruvec_page_state(target_lruvec, NR_INACTIVE_ANON) +\n+\t\t  lruvec_page_state(target_lruvec, NR_ACTIVE_ANON) +\n+\t\t  lruvec_page_state(target_lruvec, NR_INACTIVE_FILE) +\n+\t\t  lruvec_page_state(target_lruvec, NR_ACTIVE_FILE);\n+\n+\twhile (anon_cost-\u003ecount + file_cost-\u003ecount \u003e lrusize / 4) {\n+\t\tanon_cost-\u003ecount /= 2;\n+\t\tfile_cost-\u003ecount /= 2;\n+\t}\n+\n+\tsc-\u003eanon_cost = anon_cost-\u003ecount;\n+\tsc-\u003efile_cost = file_cost-\u003ecount;\n+\n+\tspin_unlock(\u0026target_lruvec-\u003ecost_lock);\n \n \t/*\n \t * Target desirable inactive:active list ratios for the anon\n@@ -4810,7 +4868,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \tstruct reclaim_stat stat;\n \tstruct lru_gen_mm_walk *walk;\n \tint scanned, reclaimed;\n-\tint isolated = 0, type, type_scanned;\n+\tint isolated = 0, nr_isolated = 0, type, type_scanned;\n+\tunsigned long total_reclaimed = 0;\n \tbool skip_retry = false;\n \tstruct mem_cgroup *memcg = lruvec_memcg(lruvec);\n \tstruct pglist_data *pgdat = lruvec_pgdat(lruvec);\n@@ -4822,6 +4881,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \n \tscanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,\n \t\t\t\t \u0026list, \u0026isolated, \u0026type, \u0026type_scanned);\n+\tnr_isolated = isolated;\n \n \t/* Scanning may have emptied the oldest gen, flush it */\n \tif (scanned)\n@@ -4834,6 +4894,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n retry:\n \treclaimed = shrink_folio_list(\u0026list, pgdat, sc, \u0026stat, false, memcg);\n \tsc-\u003enr_reclaimed += reclaimed;\n+\ttotal_reclaimed += reclaimed;\n \t/* Retry pass is only meant for clean folios without new isolation */\n \tif (isolated)\n \t\thandle_reclaim_writeback(isolated, pgdat, sc, \u0026stat);\n@@ -4887,6 +4948,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \t\tgoto retry;\n \t}\n \n+\tif (nr_isolated \u003e total_reclaimed)\n+\t\tmod_lruvec_state(lruvec, PGROTATE_ANON + type,\n+\t\t\t\t nr_isolated - total_reclaimed);\n+\n \treturn scanned;\n }\n \ndiff --git a/mm/vmstat.c b/mm/vmstat.c\nindex f534972f517de0..87d4a678136767 100644\n--- a/mm/vmstat.c\n+++ b/mm/vmstat.c\n@@ -1024,6 +1024,17 @@ unsigned long node_page_state(struct pglist_data *pgdat,\n \n \treturn node_page_state_pages(pgdat, item);\n }\n+\n+/*\n+ * Non-clamping variant of node_page_state() intended for callers that\n+ * snapshot a monotonically-incremented counter and subtract two samples.\n+ * See global_node_page_state_monotonic() for the rationale.\n+ */\n+unsigned long node_page_state_monotonic(struct pglist_data *pgdat,\n+\t\t\t\t\tenum node_stat_item item)\n+{\n+\treturn (unsigned long)atomic_long_read(\u0026pgdat-\u003evm_stat[item]);\n+}\n #endif\n \n /*\n@@ -1289,6 +1300,8 @@ const char * const vmstat_text[] = {\n \t[I(PGSCAN_PROACTIVE)]\t\t\t= \"pgscan_proactive\",\n \t[I(PGSCAN_ANON)]\t\t\t= \"pgscan_anon\",\n \t[I(PGSCAN_FILE)]\t\t\t= \"pgscan_file\",\n+\t[I(PGROTATE_ANON)]\t\t\t= \"pgrotate_anon\",\n+\t[I(PGROTATE_FILE)]\t\t\t= \"pgrotate_file\",\n \t[I(PGREFILL)]\t\t\t\t= \"pgrefill\",\n #ifdef CONFIG_HUGETLB_PAGE\n \t[I(NR_HUGETLB)]\t\t\t\t= \"nr_hugetlb\",\ndiff --git a/mm/workingset.c b/mm/workingset.c\nindex f351798e723acd..7ac2b88c80ae56 100644\n--- a/mm/workingset.c\n+++ b/mm/workingset.c\n@@ -584,11 +584,6 @@ void workingset_refault(struct folio *folio, void *shadow)\n \t/* Folio was active prior to eviction */\n \tif (workingset) {\n \t\tfolio_set_workingset(folio);\n-\t\t/*\n-\t\t * XXX: Move to folio_add_lru() when it supports new vs\n-\t\t * putback\n-\t\t */\n-\t\tlru_note_cost_refault(folio);\n \t\tmod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + file, nr);\n \t}\n out:\n"
}

2/1 2026/07/21 00:06 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "MEMCG",
    "LRU_GEN",
    "LRU_GEN_ENABLED"
  ],
  "FocusSymbols": [
    "prepare_scan_control",
    "shrink_inactive_list",
    "shrink_active_list",
    "evict_folios",
    "lruvec_page_state_monotonic",
    "node_page_state_monotonic",
    "workingset_refault",
    "pageout"
  ],
  "Reasoning": "The patch modifies the LRU cost model and statistics tracking in the core memory management subsystem. It introduces monotonic page state tracking for LRU vectors and nodes, and updates how `anon_cost` and `file_cost` are calculated during memory reclaim. It also modifies both the traditional LRU and Multi-Gen LRU (MGLRU) reclaim paths to use the new cost model and statistics. These are functional changes to reachable core kernel logic that should be fuzzed.",
  "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).

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.
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.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. 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 1cca8549e97de5e36710969e7597332c5d64fe15
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jul 21 00:06:41 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index e1f46a0016fcfd..b40bc4f6fe4a0e 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -931,6 +931,8 @@ unsigned long memcg_page_state_output(struct mem_cgroup *memcg, int item);
 bool memcg_stat_item_valid(int idx);
 bool memcg_vm_event_item_valid(enum vm_event_item idx);
 unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx);
+unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
+					  enum node_stat_item idx);
 unsigned long lruvec_page_state_local(struct lruvec *lruvec,
 				      enum node_stat_item idx);
 
@@ -1378,6 +1380,12 @@ static inline unsigned long lruvec_page_state(struct lruvec *lruvec,
 	return node_page_state(lruvec_pgdat(lruvec), idx);
 }
 
+static inline unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
+							enum node_stat_item idx)
+{
+	return node_page_state_monotonic(lruvec_pgdat(lruvec), idx);
+}
+
 static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,
 						    enum node_stat_item idx)
 {
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ca27121871475c..85303c5867c84b 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -323,6 +323,8 @@ enum node_stat_item {
 	PGSCAN_PROACTIVE,
 	PGSCAN_ANON,
 	PGSCAN_FILE,
+	PGROTATE_ANON,
+	PGROTATE_FILE,
 	PGREFILL,
 #ifdef CONFIG_HUGETLB_PAGE
 	NR_HUGETLB,
@@ -755,6 +757,12 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent,
 
 #endif /* CONFIG_LRU_GEN */
 
+struct lru_cost {
+	unsigned long		count;
+	unsigned long		last_rotated;
+	unsigned long		last_io;
+};
+
 struct lruvec {
 	struct list_head		lists[NR_LRU_LISTS];
 	/* per lruvec lru_lock for memcg */
@@ -763,9 +771,12 @@ struct lruvec {
 	 * These track the cost of reclaiming one LRU - file or anon -
 	 * over the other. As the observed cost of reclaiming one LRU
 	 * increases, the reclaim scan balance tips toward the other.
+	 * Updated and decayed at prepare_scan_control() time; cost_lock
+	 * serialises that update.
 	 */
-	unsigned long			anon_cost;
-	unsigned long			file_cost;
+	struct lru_cost			cost[ANON_AND_FILE];
+	/* Protects cost[]. */
+	spinlock_t			cost_lock;
 	/* Non-resident age, driven by LRU movement */
 	atomic_long_t			nonresident_age;
 	/* Refaults at the time of last reclaim cycle */
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 8f0f68e245baa3..b8e2e1af2b1425 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -309,9 +309,6 @@ extern unsigned long totalreserve_pages;
 
 
 /* linux/mm/swap.c */
-void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,
-		unsigned int nr_io, unsigned int nr_rotated);
-void lru_note_cost_refault(struct folio *);
 void folio_add_lru(struct folio *);
 void folio_add_lru_vma(struct folio *, struct vm_area_struct *);
 void mark_page_accessed(struct page *);
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index 3c9c266cf782ad..5b31d8e7ae4058 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -20,7 +20,6 @@ struct reclaim_stat {
 	unsigned nr_congested;
 	unsigned nr_writeback;
 	unsigned nr_immediate;
-	unsigned nr_pageout;
 	unsigned nr_activate[ANON_AND_FILE];
 	unsigned nr_ref_keep;
 	unsigned nr_unmap_fail;
@@ -194,6 +193,19 @@ unsigned long global_node_page_state_pages(enum node_stat_item item)
 	return x;
 }
 
+/*
+ * Non-clamping variant of global_node_page_state() intended for callers that
+ * snapshot a monotonically-incremented counter and subtract two samples.
+ * Returns the raw wrapping value so that unsigned modular subtraction stays
+ * correct across a signed-long overflow (a real hazard on 32-bit) that the
+ * clamp in global_node_page_state() would otherwise turn into a huge spurious
+ * delta. Do NOT use for non-monotonic page-count reads.
+ */
+static inline unsigned long global_node_page_state_monotonic(enum node_stat_item item)
+{
+	return (unsigned long)atomic_long_read(&vm_node_stat[item]);
+}
+
 static inline unsigned long global_node_page_state(enum node_stat_item item)
 {
 	VM_WARN_ON_ONCE(vmstat_item_in_bytes(item));
@@ -259,11 +271,14 @@ extern unsigned long node_page_state(struct pglist_data *pgdat,
 						enum node_stat_item item);
 extern unsigned long node_page_state_pages(struct pglist_data *pgdat,
 					   enum node_stat_item item);
+extern unsigned long node_page_state_monotonic(struct pglist_data *pgdat,
+					       enum node_stat_item item);
 extern void fold_vm_numa_events(void);
 #else
 #define sum_zone_node_page_state(node, item) global_zone_page_state(item)
 #define node_page_state(node, item) global_node_page_state(item)
 #define node_page_state_pages(node, item) global_node_page_state_pages(item)
+#define node_page_state_monotonic(node, item) global_node_page_state_monotonic(item)
 static inline void fold_vm_numa_events(void)
 {
 }
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index 7650692115678c..091bc9ffee44a0 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -1988,8 +1988,8 @@ void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
 		for_each_online_pgdat(pgdat) {
 			mz = memcg->nodeinfo[pgdat->node_id];
 
-			anon_cost += mz->lruvec.anon_cost;
-			file_cost += mz->lruvec.file_cost;
+			anon_cost += mz->lruvec.cost[WORKINGSET_ANON].count;
+			file_cost += mz->lruvec.cost[WORKINGSET_FILE].count;
 		}
 		seq_buf_printf(s, "anon_cost %lu\n", anon_cost);
 		seq_buf_printf(s, "file_cost %lu\n", file_cost);
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 6dc4888a90f3fd..c97c9bc3f8ddb8 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -393,6 +393,7 @@ static const unsigned int memcg_node_stat_items[] = {
 	NR_SHMEM_THPS,
 	NR_FILE_THPS,
 	NR_ANON_THPS,
+	NR_VMSCAN_WRITE,
 	NR_VMALLOC,
 	NR_KERNEL_STACK_KB,
 	NR_PAGETABLE,
@@ -419,6 +420,8 @@ static const unsigned int memcg_node_stat_items[] = {
 	PGSCAN_PROACTIVE,
 	PGSCAN_ANON,
 	PGSCAN_FILE,
+	PGROTATE_ANON,
+	PGROTATE_FILE,
 	PGREFILL,
 #ifdef CONFIG_HUGETLB_PAGE
 	NR_HUGETLB,
@@ -502,6 +505,42 @@ unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx)
 	return x;
 }
 
+/**
+ * lruvec_page_state_monotonic - non-clamping lruvec stat read for delta sampling
+ * @lruvec: the LRU vector to read from
+ * @idx: the node_stat_item to read
+ *
+ * Returns the raw state[idx] value cast to unsigned long, skipping the
+ * clamp-negative-to-zero step in lruvec_page_state(). Intended for callers
+ * that snapshot a monotonically-incremented counter and subtract two
+ * samples: unsigned modular arithmetic then yields the correct delta across
+ * a signed-long wraparound (a real hazard on 32-bit) that the clamp would
+ * otherwise turn into a huge spurious delta.
+ *
+ * Do NOT use for non-monotonic page-count reads where a transient negative
+ * reading from per-CPU delta skew must present as zero.
+ *
+ * XXX: This helper (and its node/global peers) exists because we place
+ * monotonically-incremented event counters (NR_VMSCAN_WRITE and PGROTATE_*)
+ * into enum node_stat_item.
+ */
+unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
+					  enum node_stat_item idx)
+{
+	struct mem_cgroup_per_node *pn;
+	int i;
+
+	if (mem_cgroup_disabled())
+		return node_page_state_monotonic(lruvec_pgdat(lruvec), idx);
+
+	i = memcg_stats_index(idx);
+	if (WARN_ONCE(BAD_STAT_IDX(i), "%s: missing stat item %d\n", __func__, idx))
+		return 0;
+
+	pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
+	return (unsigned long)READ_ONCE(pn->lruvec_stats->state[i]);
+}
+
 unsigned long lruvec_page_state_local(struct lruvec *lruvec,
 				      enum node_stat_item idx)
 {
diff --git a/mm/mmzone.c b/mm/mmzone.c
index 0c8f181d9d5006..17139db4d291ce 100644
--- a/mm/mmzone.c
+++ b/mm/mmzone.c
@@ -78,6 +78,7 @@ void lruvec_init(struct lruvec *lruvec)
 
 	memset(lruvec, 0, sizeof(struct lruvec));
 	spin_lock_init(&lruvec->lru_lock);
+	spin_lock_init(&lruvec->cost_lock);
 	zswap_lruvec_state_init(lruvec);
 
 	for_each_lru(lru)
diff --git a/mm/swap.c b/mm/swap.c
index 588f50d8f1a8c9..74b281778cbc92 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -272,73 +272,6 @@ void folio_rotate_reclaimable(struct folio *folio)
 	folio_batch_add_and_move(folio, lru_move_tail);
 }
 
-void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,
-		unsigned int nr_io, unsigned int nr_rotated)
-		__releases(lruvec->lru_lock)
-		__releases(rcu)
-{
-	unsigned long cost;
-
-	/*
-	 * Reflect the relative cost of incurring IO and spending CPU
-	 * time on rotations. This doesn't attempt to make a precise
-	 * comparison, it just says: if reloads are about comparable
-	 * between the LRU lists, or rotations are overwhelmingly
-	 * different between them, adjust scan balance for CPU work.
-	 */
-	cost = nr_io * SWAP_CLUSTER_MAX + nr_rotated;
-	if (!cost) {
-		spin_unlock_irq(&lruvec->lru_lock);
-		rcu_read_unlock();
-		return;
-	}
-
-	for (;;) {
-		unsigned long lrusize;
-
-		/* Record cost event */
-		if (file)
-			lruvec->file_cost += cost;
-		else
-			lruvec->anon_cost += cost;
-
-		/*
-		 * Decay previous events
-		 *
-		 * Because workloads change over time (and to avoid
-		 * overflow) we keep these statistics as a floating
-		 * average, which ends up weighing recent refaults
-		 * more than old ones.
-		 */
-		lrusize = lruvec_page_state(lruvec, NR_INACTIVE_ANON) +
-			  lruvec_page_state(lruvec, NR_ACTIVE_ANON) +
-			  lruvec_page_state(lruvec, NR_INACTIVE_FILE) +
-			  lruvec_page_state(lruvec, NR_ACTIVE_FILE);
-
-		if (lruvec->file_cost + lruvec->anon_cost > lrusize / 4) {
-			lruvec->file_cost /= 2;
-			lruvec->anon_cost /= 2;
-		}
-
-		spin_unlock_irq(&lruvec->lru_lock);
-		lruvec = parent_lruvec(lruvec);
-		if (!lruvec) {
-			rcu_read_unlock();
-			break;
-		}
-		spin_lock_irq(&lruvec->lru_lock);
-	}
-}
-
-void lru_note_cost_refault(struct folio *folio)
-{
-	struct lruvec *lruvec;
-
-	lruvec = folio_lruvec_lock_irq(folio);
-	lru_note_cost_unlock_irq(lruvec, folio_is_file_lru(folio),
-				folio_nr_pages(folio), 0);
-}
-
 static void lru_activate(struct lruvec *lruvec, struct folio *folio)
 {
 	long nr_pages = folio_nr_pages(folio);
@@ -1164,8 +1097,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int
 
 	child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
 	parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid));
-	parent_lruvec->anon_cost += child_lruvec->anon_cost;
-	parent_lruvec->file_cost += child_lruvec->file_cost;
 
 	for_each_lru(lru)
 		lruvec_reparent_lru(child_lruvec, parent_lruvec, lru, nid);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 35c3bb15ae96a3..07545095b352cc 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -668,7 +668,7 @@ static pageout_t pageout(struct folio *folio, struct address_space *mapping,
 		folio_clear_reclaim(folio);
 
 	trace_mm_vmscan_write_folio(folio);
-	node_stat_add_folio(folio, NR_VMSCAN_WRITE);
+	lruvec_stat_mod_folio(folio, NR_VMSCAN_WRITE, folio_nr_pages(folio));
 	return PAGE_SUCCESS;
 }
 
@@ -1413,8 +1413,6 @@ static unsigned int shrink_folio_list(struct list_head *folio_list,
 					sc->nr_scanned -= (nr_pages - 1);
 					nr_pages = 1;
 				}
-				stat->nr_pageout += nr_pages;
-
 				if (folio_test_writeback(folio))
 					goto keep;
 				if (folio_test_dirty(folio))
@@ -2038,10 +2036,10 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan,
 	item = PGSTEAL_KSWAPD + reclaimer_offset(sc);
 	mod_lruvec_state(lruvec, item, nr_reclaimed);
 	mod_lruvec_state(lruvec, PGSTEAL_ANON + file, nr_reclaimed);
+	if (nr_scanned > nr_reclaimed)
+		mod_lruvec_state(lruvec, PGROTATE_ANON + file,
+				 nr_scanned - nr_reclaimed);
 
-	lruvec_lock_irq(lruvec);
-	lru_note_cost_unlock_irq(lruvec, file, stat.nr_pageout,
-					nr_scanned - nr_reclaimed);
 	handle_reclaim_writeback(nr_taken, pgdat, sc, &stat);
 	trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id,
 			nr_scanned, nr_reclaimed, &stat, sc->priority, file);
@@ -2147,9 +2145,9 @@ static void shrink_active_list(unsigned long nr_to_scan,
 	count_vm_events(PGDEACTIVATE, nr_deactivate);
 	count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_deactivate);
 	mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken);
+	if (nr_rotated)
+		mod_lruvec_state(lruvec, PGROTATE_ANON + file, nr_rotated);
 
-	lruvec_lock_irq(lruvec);
-	lru_note_cost_unlock_irq(lruvec, file, 0, nr_rotated);
 	trace_mm_vmscan_lru_shrink_active(pgdat->node_id, nr_taken, nr_activate,
 			nr_deactivate, nr_rotated, sc->priority, file);
 }
@@ -2282,8 +2280,10 @@ enum scan_balance {
 
 static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)
 {
-	unsigned long file;
+	struct lru_cost *anon_cost, *file_cost;
 	struct lruvec *target_lruvec;
+	unsigned long lrusize;
+	unsigned long file;
 
 	if (lru_gen_enabled() && !lru_gen_switching())
 		return;
@@ -2299,11 +2299,69 @@ static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)
 
 	/*
 	 * Determine the scan balance between anon and file LRUs.
+	 *
+	 * The cost model is based on rotations, refaults and
+	 * reclaim-driven writes (anon only) on each side.
+	 *
+	 * These event counters are monotonic, so each reclaim cycle
+	 * the delta since the last scan is extracted and incorporated
+	 * into a decaying average. This ensures currency, as workloads
+	 * change over time, and avoids overflow in the calculations.
+	 *
+	 * Use lruvec_page_state_monotonic() so unsigned subtraction
+	 * yields the correct delta across a signed-long wraparound of
+	 * the underlying counter (a real hazard on 32-bit that the
+	 * clamp in lruvec_page_state() would otherwise turn into a huge
+	 * spurious delta).
 	 */
-	spin_lock_irq(&target_lruvec->lru_lock);
-	sc->anon_cost = target_lruvec->anon_cost;
-	sc->file_cost = target_lruvec->file_cost;
-	spin_unlock_irq(&target_lruvec->lru_lock);
+	spin_lock(&target_lruvec->cost_lock);
+
+	for (int f = 0; f <= 1; f++) {
+		struct lru_cost *cost = &target_lruvec->cost[f];
+		unsigned long rotated, io, nr_rotated, nr_io;
+
+		rotated = lruvec_page_state_monotonic(target_lruvec,
+						      PGROTATE_ANON + f);
+		io = lruvec_page_state_monotonic(target_lruvec,
+						 WORKINGSET_RESTORE_BASE + f);
+		if (f == WORKINGSET_ANON)
+			io += lruvec_page_state_monotonic(target_lruvec,
+							  NR_VMSCAN_WRITE);
+
+		nr_rotated = rotated - cost->last_rotated;
+		nr_io = io - cost->last_io;
+
+		/*
+		 * Reflect the relative cost of incurring IO and spending
+		 * CPU time on rotations. This doesn't attempt to make a
+		 * precise comparison, it just says: if reloads are about
+		 * comparable between the LRU lists, or rotations are
+		 * overwhelmingly different between them, adjust scan
+		 * balance for CPU work.
+		 */
+		cost->count += nr_io * SWAP_CLUSTER_MAX + nr_rotated;
+
+		cost->last_rotated = rotated;
+		cost->last_io = io;
+	}
+
+	anon_cost = &target_lruvec->cost[WORKINGSET_ANON];
+	file_cost = &target_lruvec->cost[WORKINGSET_FILE];
+
+	lrusize = lruvec_page_state(target_lruvec, NR_INACTIVE_ANON) +
+		  lruvec_page_state(target_lruvec, NR_ACTIVE_ANON) +
+		  lruvec_page_state(target_lruvec, NR_INACTIVE_FILE) +
+		  lruvec_page_state(target_lruvec, NR_ACTIVE_FILE);
+
+	while (anon_cost->count + file_cost->count > lrusize / 4) {
+		anon_cost->count /= 2;
+		file_cost->count /= 2;
+	}
+
+	sc->anon_cost = anon_cost->count;
+	sc->file_cost = file_cost->count;
+
+	spin_unlock(&target_lruvec->cost_lock);
 
 	/*
 	 * Target desirable inactive:active list ratios for the anon
@@ -4810,7 +4868,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
 	struct reclaim_stat stat;
 	struct lru_gen_mm_walk *walk;
 	int scanned, reclaimed;
-	int isolated = 0, type, type_scanned;
+	int isolated = 0, nr_isolated = 0, type, type_scanned;
+	unsigned long total_reclaimed = 0;
 	bool skip_retry = false;
 	struct mem_cgroup *memcg = lruvec_memcg(lruvec);
 	struct pglist_data *pgdat = lruvec_pgdat(lruvec);
@@ -4822,6 +4881,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
 
 	scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,
 				 &list, &isolated, &type, &type_scanned);
+	nr_isolated = isolated;
 
 	/* Scanning may have emptied the oldest gen, flush it */
 	if (scanned)
@@ -4834,6 +4894,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
 retry:
 	reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg);
 	sc->nr_reclaimed += reclaimed;
+	total_reclaimed += reclaimed;
 	/* Retry pass is only meant for clean folios without new isolation */
 	if (isolated)
 		handle_reclaim_writeback(isolated, pgdat, sc, &stat);
@@ -4887,6 +4948,10 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
 		goto retry;
 	}
 
+	if (nr_isolated > total_reclaimed)
+		mod_lruvec_state(lruvec, PGROTATE_ANON + type,
+				 nr_isolated - total_reclaimed);
+
 	return scanned;
 }
 
diff --git a/mm/vmstat.c b/mm/vmstat.c
index f534972f517de0..87d4a678136767 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1024,6 +1024,17 @@ unsigned long node_page_state(struct pglist_data *pgdat,
 
 	return node_page_state_pages(pgdat, item);
 }
+
+/*
+ * Non-clamping variant of node_page_state() intended for callers that
+ * snapshot a monotonically-incremented counter and subtract two samples.
+ * See global_node_page_state_monotonic() for the rationale.
+ */
+unsigned long node_page_state_monotonic(struct pglist_data *pgdat,
+					enum node_stat_item item)
+{
+	return (unsigned long)atomic_long_read(&pgdat->vm_stat[item]);
+}
 #endif
 
 /*
@@ -1289,6 +1300,8 @@ const char * const vmstat_text[] = {
 	[I(PGSCAN_PROACTIVE)]			= "pgscan_proactive",
 	[I(PGSCAN_ANON)]			= "pgscan_anon",
 	[I(PGSCAN_FILE)]			= "pgscan_file",
+	[I(PGROTATE_ANON)]			= "pgrotate_anon",
+	[I(PGROTATE_FILE)]			= "pgrotate_file",
 	[I(PGREFILL)]				= "pgrefill",
 #ifdef CONFIG_HUGETLB_PAGE
 	[I(NR_HUGETLB)]				= "nr_hugetlb",
diff --git a/mm/workingset.c b/mm/workingset.c
index f351798e723acd..7ac2b88c80ae56 100644
--- a/mm/workingset.c
+++ b/mm/workingset.c
@@ -584,11 +584,6 @@ void workingset_refault(struct folio *folio, void *shadow)
 	/* Folio was active prior to eviction */
 	if (workingset) {
 		folio_set_workingset(folio);
-		/*
-		 * XXX: Move to folio_add_lru() when it supports new vs
-		 * putback
-		 */
-		lru_note_cost_refault(folio);
 		mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + file, nr);
 	}
 out:

3/2 2026/07/21 00:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9136
							output: 19
							thoughts: 436
						

Thoughts:
**Analyzing the Memory Management Patch**

Okay, so I've got this patch in front of me that's tweaking the LRU cost model and its associated stats – that's some serious core memory management stuff. It's introducing these new monotonic counters, `lruvec_page_state_monotonic` and `node_page_state_monotonic`. And it's also making some changes to how `prepare_scan_control` calculates the cost of scanning anonymous and file pages. Essentially, this patch is changing the reclaim path, specifically the balance between scanning anon and file LRUs, so this is definitely worth fuzzing.

The patch also seems to be cleaning up some older functions, like removing `lru_note_cost_unlock_irq` and `lru_note_cost_refault`, and adding two new items `PGROTATE_ANON` and `PGROTATE_FILE`. It looks like `shrink_inactive_list`, `shrink_active_list`, and `evict_folios` are all being updated to account for the new items. This is all centered on how the kernel decides what to evict, so I need to dig into this further.

The modified functions are a key target: `lruvec_page_state_monotonic`, `node_page_state_monotonic`, `prepare_scan_control`, `shrink_inactive_list`, `shrink_active_list`, `evict_folios`, `pageout`, and `shrink_folio_list`. Looks like `prepare_scan_control`, `shrink_inactive_list`, `shrink_active_list`, and `evict_folios` are all part of the reclaim path. These are definitely where I should focus the fuzzing efforts.

I'll need to check the relevant configuration options. The code is in the main MM subsystems: `mm/vmscan.c`, `mm/swap.c`, `mm/workingset.c`, `mm/memcontrol.c`, and `mm/vmstat.c`, so I should probably focus on `CONFIG_MEMCG` which is related to `mm/memcontrol.c`. I need to review `mm/Makefile` and `mm/Kconfig` to see if there are any specific configurations that could influence how this code behaves.



4/2 2026/07/21 00:06 tool grepper
0m Args:
{
  "Expression": "prepare_scan_control"
}

Results:
{
  "Output": "include/linux/mmzone.h=766=struct lruvec {\n--\ninclude/linux/mmzone.h-773-\t * increases, the reclaim scan balance tips toward the other.\ninclude/linux/mmzone.h:774:\t * Updated and decayed at prepare_scan_control() time; cost_lock\ninclude/linux/mmzone.h-775-\t * serialises that update.\n--\nmm/vmscan.c=2274=enum scan_balance {\n--\nmm/vmscan.c-2280-\nmm/vmscan.c:2281:static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)\nmm/vmscan.c-2282-{\n--\nmm/vmscan.c=6211=static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-6233-\nmm/vmscan.c:6234:\tprepare_scan_control(pgdat, sc);\nmm/vmscan.c-6235-\n"
}

5/2 2026/07/21 00:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10363
							output: 27
							thoughts: 52
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Diving into the Memory Management Code**

Alright, so the modifications are definitely centered in `mm/vmscan.c`. That's good, at least it's a known area. That file is always compiled, which is useful when tracking down a specific issue. I can use my experience to immediately think about the changes made there and how they may influence my work.

Now, if memory serves, `CONFIG_MEMCG` comes into play with `mm/memcontrol.c`. I need to figure out how closely the current modification is tied to memory control groups. To verify the relationship, let's take a quick look at `mm/Makefile` to confirm the compilation of `memcontrol.c`. This will give me an understanding of the relationship between this file and the compilation process, helping me build a mental model of the changes. I'll need to double check the dependencies.



6/2 2026/07/21 00:06 tool grepper
0m Args:
{
  "Expression": "memcontrol.o",
  "PathPrefix": "mm/Makefile"
}

Results:
{
  "Output": "mm/Makefile=27=KCOV_INSTRUMENT_kmemleak.o := n\nmm/Makefile:28:KCOV_INSTRUMENT_memcontrol.o := n\nmm/Makefile-29-KCOV_INSTRUMENT_memcontrol-v1.o := n\n--\nmm/Makefile=104=obj-$(CONFIG_MEMCG_V1) += memcontrol-v1.o\nmm/Makefile:105:obj-$(CONFIG_MEMCG) += memcontrol.o vmpressure.o\nmm/Makefile-106-ifdef CONFIG_BPF_SYSCALL\nmm/Makefile:107:obj-$(CONFIG_MEMCG) += bpf_memcontrol.o\nmm/Makefile-108-endif\n"
}

7/2 2026/07/21 00:06 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10838
							output: 18
							thoughts: 174
						

Thoughts:
**My Line of Inquiry: Memory Management and LRU Generation**

Okay, so I'm diving into this memory management code, and it looks like `CONFIG_MEMCG` is definitely a prerequisite for `memcontrol.c`. That's not surprising. Now, I need to understand the connection to `CONFIG_LRU_GEN`. Let's see... looking at `mm/vmscan.c`, I spot this function, `prepare_scan_control`. Hmm, inside it, there's a check: `lru_gen_enabled()`. This is interesting; it suggests that LRU generation is somehow integrated into the scanning process itself. The logic suggests, that, if LRU generation is enabled and it is not switching, then the function `prepare_scan_control` is going to return. Alright, seems I need to understand that. Now, let's look at where `evict_folios` fits into all this.



8/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "evict_folios"
}

Results:
{
  "Output": "mm/vmscan.c=4826=static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4859-\nmm/vmscan.c:4860:static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\nmm/vmscan.c-4861-\t\t\tstruct scan_control *sc, int swappiness)\n--\nmm/vmscan.c=5024=static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5047-\t\tnr_batch = min(nr_to_scan, MIN_LRU_BATCH);\nmm/vmscan.c:5048:\t\tdelta = evict_folios(nr_batch, lruvec, sc, swappiness);\nmm/vmscan.c-5049-\t\tif (!delta)\n--\nmm/vmscan.c=5651=static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,\n--\nmm/vmscan.c-5671-\t\tnr_batch = min(nr_to_reclaim - sc-\u003enr_reclaimed, MAX_LRU_BATCH);\nmm/vmscan.c:5672:\t\tif (!evict_folios(nr_batch, lruvec, sc, swappiness))\nmm/vmscan.c-5673-\t\t\treturn 0;\n"
}

9/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11624
							output: 32
							thoughts: 41
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own reasoning:

**Checking the MGLRU Connection to Eviction Logic**

Alright, so `evict_folios` is part of the MGLRU implementation. My initial concern is to understand its place within the broader picture. My immediate thought is to check if this function is only compiled if the `CONFIG_LRU_GEN` configuration option is enabled. That's a critical detail because it ties `evict_folios` directly to the multi-generational LRU mechanism. Ensuring this dependency will help me understand its purpose and behavior in the larger context of memory management.



10/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "CONFIG_LRU_GEN",
  "PathPrefix": "mm/vmscan.c"
}

Results:
{
  "Output": "mm/vmscan.c=824=enum folio_references {\n--\nmm/vmscan.c-830-\nmm/vmscan.c:831:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-832-/*\n--\nmm/vmscan.c=854=static bool lru_gen_set_refs(struct folio *folio)\n--\nmm/vmscan.c-857-}\nmm/vmscan.c:858:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-859-\n--\nmm/vmscan.c=2671=static bool can_age_anon_pages(struct lruvec *lruvec,\n--\nmm/vmscan.c-2682-\nmm/vmscan.c:2683:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-2684-\nmm/vmscan.c=2685=DEFINE_STATIC_KEY_FALSE(lru_switch);\nmm/vmscan.c:2686:#ifdef CONFIG_LRU_GEN_ENABLED\nmm/vmscan.c-2687-DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);\n--\nmm/vmscan.c=2867=static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\n--\nmm/vmscan.c-2886-\nmm/vmscan.c:2887:#ifdef CONFIG_LRU_GEN_WALKS_MMU\nmm/vmscan.c-2888-\n--\nmm/vmscan.c=2996=void lru_gen_migrate_mm(struct mm_struct *mm)\n--\nmm/vmscan.c-3024-\nmm/vmscan.c:3025:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\nmm/vmscan.c-3026-\n--\nmm/vmscan.c=5920=late_initcall(init_lru_gen);\nmm/vmscan.c-5921-\nmm/vmscan.c:5922:#else /* !CONFIG_LRU_GEN */\nmm/vmscan.c-5923-\n--\nmm/vmscan.c=5934=static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5938-\nmm/vmscan.c:5939:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-5940-\n"
}

11/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12505
							output: 30
							thoughts: 148
						

Thoughts:
**Analyzing the VMscan Patch**

Okay, so I'm seeing that `evict_folios` is definitely nestled within that `#ifdef CONFIG_LRU_GEN` block. That's a key observation. Now, let's just make sure I have this right... wait a second, `shrink_inactive_list`, `shrink_active_list`, and `prepare_scan_control` are all core components of the traditional active/inactive LRU mechanism. This patch is touching both the MGLRU via `evict_folios`, and the classic LRU with those three functions. So it's affecting *both* implementations, not just the new one. I should probably just double-check that by looking at `mm/vmscan.c` to confirm. It is not just one LRU that is being modified.



12/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "evict_folios",
  "PathPrefix": "mm/vmscan.c"
}

Results:
{
  "Output": "mm/vmscan.c=4826=static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4859-\nmm/vmscan.c:4860:static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\nmm/vmscan.c-4861-\t\t\tstruct scan_control *sc, int swappiness)\n--\nmm/vmscan.c=5024=static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5047-\t\tnr_batch = min(nr_to_scan, MIN_LRU_BATCH);\nmm/vmscan.c:5048:\t\tdelta = evict_folios(nr_batch, lruvec, sc, swappiness);\nmm/vmscan.c-5049-\t\tif (!delta)\n--\nmm/vmscan.c=5651=static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,\n--\nmm/vmscan.c-5671-\t\tnr_batch = min(nr_to_reclaim - sc-\u003enr_reclaimed, MAX_LRU_BATCH);\nmm/vmscan.c:5672:\t\tif (!evict_folios(nr_batch, lruvec, sc, swappiness))\nmm/vmscan.c-5673-\t\t\treturn 0;\n"
}

13/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13253
							output: 31
							thoughts: 19
						

Thoughts:
Okay, let's dive into this code. Here's what I'm thinking as I analyze these functions:

**Analyzing `shrink_inactive_list` and `shrink_active_list`**

Right, let's get down to brass tacks. I need to understand how these lists are being managed and how memory is potentially being optimized. So, I'm going to start by looking at `shrink_inactive_list` and `shrink_active_list`. These function names are pretty descriptive, so my initial hypothesis is that they're related to memory management and probably involve reducing the size of some data structures to reclaim resources. I need to see *how* this shrinking is accomplished – are we freeing memory, coalescing segments, or just reallocating to smaller blocks? What are the criteria for triggering this shrinkage? I need to understand the impact of the `shrink_inactive_list` and `shrink_active_list` on performance, particularly the trade-off between memory footprint and execution speed. Are there any potential concurrency issues I should be aware of, especially if the lists are being accessed by multiple threads? Ultimately, I need a very clear picture of their functionality and the invariants they maintain.



14/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "shrink_inactive_list",
  "PathPrefix": "mm/vmscan.c"
}

Results:
{
  "Output": "mm/vmscan.c=1942=static void handle_reclaim_writeback(unsigned long nr_taken,\n--\nmm/vmscan.c-1980-/*\nmm/vmscan.c:1981: * shrink_inactive_list() is a helper for shrink_node().  It returns the number\nmm/vmscan.c-1982- * of reclaimed pages\nmm/vmscan.c-1983- */\nmm/vmscan.c:1984:static unsigned long shrink_inactive_list(unsigned long nr_to_scan,\nmm/vmscan.c-1985-\t\tstruct lruvec *lruvec, struct scan_control *sc,\n--\nmm/vmscan.c=2213=static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,\n--\nmm/vmscan.c-2223-\nmm/vmscan.c:2224:\treturn shrink_inactive_list(nr_to_scan, lruvec, sc, lru);\nmm/vmscan.c-2225-}\n"
}

15/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13838
							output: 35
							thoughts: 115
						

Thoughts:
**Analyzing the Patch's Impact on LRU and MGLRU Implementations**

Okay, so this patch is a bit more involved than I initially thought. It looks like the changes touch both the traditional LRU algorithm *and* the MGLRU (Multi-Generational LRU) implementation. The classic LRU is always baked into the kernel, that’s no surprise, but MGLRU is conditional, based on the `CONFIG_LRU_GEN` configuration option. Therefore, to get a handle on the overall effect of this code, I'm thinking we need to explicitly enable `CONFIG_LRU_GEN`.

Hold on a second, though. I'm also seeing some potential interaction with `CONFIG_MEMCG`, specifically within the `lruvec_page_state_monotonic` function. That function has specific logic that hinges on `mem_cgroup_disabled()`. This is going to matter, so I really need to examine the details there. I’ll navigate to `mm/memcontrol.c` and take a closer look at the implementation of `lruvec_page_state_monotonic` to understand how this all fits together and how the memory cgroup setting changes the behavior.



16/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "lruvec_page_state_monotonic",
  "PathPrefix": "mm/memcontrol.c"
}

Results:
{
  "Output": "mm/memcontrol.c=486=unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx)\n--\nmm/memcontrol.c-508-/**\nmm/memcontrol.c:509: * lruvec_page_state_monotonic - non-clamping lruvec stat read for delta sampling\nmm/memcontrol.c-510- * @lruvec: the LRU vector to read from\n--\nmm/memcontrol.c-526- */\nmm/memcontrol.c:527:unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\nmm/memcontrol.c-528-\t\t\t\t\t  enum node_stat_item idx)\n"
}

17/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14439
							output: 29
							thoughts: 29
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Kconfig Dependency Confirmation**

Right, so we're absolutely going to need `CONFIG_MEMCG`. No question about that. Now, let's see, what's next? Hmm, the problem I'm trying to solve seems to hinge on something related to `LRU_GEN`. I should go directly to `mm/Kconfig` to see if that option is enabled, and under what circumstances. Gotta make sure we're not missing a critical dependency or something.



18/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "config LRU_GEN",
  "PathPrefix": "mm/Kconfig"
}

Results:
{
  "Output": "mm/Kconfig=1406=endif # USERFAULTFD\n--\nmm/Kconfig-1408-# multi-gen LRU {\nmm/Kconfig:1409:config LRU_GEN\nmm/Kconfig-1410-\tbool \"Multi-Gen LRU\"\n--\nmm/Kconfig-1417-\nmm/Kconfig:1418:config LRU_GEN_ENABLED\nmm/Kconfig-1419-\tbool \"Enable by default\"\n--\nmm/Kconfig-1423-\nmm/Kconfig:1424:config LRU_GEN_STATS\nmm/Kconfig-1425-\tbool \"Full stats for debugging\"\n--\nmm/Kconfig-1432-\nmm/Kconfig:1433:config LRU_GEN_WALKS_MMU\nmm/Kconfig-1434-\tdef_bool y\n"
}

19/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14851
							output: 31
							thoughts: 331
						

Thoughts:
**My Analysis of MGLRU Testing & Key Functions**

Okay, so I'm thinking, to really put the MGLRU path through its paces, enabling `CONFIG_LRU_GEN` and `CONFIG_LRU_GEN_ENABLED` seems like the right move. However, wait a minute, if we enable `CONFIG_LRU_GEN_ENABLED` outright, we might inadvertently bypass the traditional LRU path in default configurations, unless we explicitly force a runtime switch. That's not ideal, since the patch modifies both approaches. So, I'll be sure to make note of these concerns. I can simplify things by listing `MEMCG` and `LRU_GEN`.

Now, I need to pinpoint the critical functions where this patch will have the greatest impact and where testing should be focused. I've got to consider:

*   `prepare_scan_control`
*   `shrink_inactive_list`
*   `shrink_active_list`
*   `evict_folios`
*   `lruvec_page_state_monotonic`
*   `node_page_state_monotonic`

I'm evaluating whether `shrink_inactive_list` and `shrink_active_list` are hot, critical paths. I know they're invoked during memory reclaim, which can happen frequently when the system is under memory pressure, but they're not quite as ubiquitous as functions like `kmalloc` or `schedule`. `prepare_scan_control` is also part of that reclaim process. `evict_folios` definitely looks key since it's directly involved in the MGLRU reclaim process. `lruvec_page_state_monotonic` is called from `prepare_scan_control`, and `node_page_state_monotonic` is in turn called from `lruvec_page_state_monotonic`. I'll want to check the callers of `prepare_scan_control` next.



20/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "prepare_scan_control",
  "PathPrefix": "mm/vmscan.c"
}

Results:
{
  "Output": "mm/vmscan.c=2274=enum scan_balance {\n--\nmm/vmscan.c-2280-\nmm/vmscan.c:2281:static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)\nmm/vmscan.c-2282-{\n--\nmm/vmscan.c=6211=static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-6233-\nmm/vmscan.c:6234:\tprepare_scan_control(pgdat, sc);\nmm/vmscan.c-6235-\n"
}

21/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15812
							output: 35
							thoughts: 100
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own:

**Analyzing the Memory Shrinkage Process**

Alright, let's break this down. The entry point we're working with here is `shrink_node`. That's where it all begins. And from my experience, `shrink_node` gets invoked from a few key places: `do_try_to_free_pages`, `kswapd_shrink_node`, `shrink_all_memory`, all core components of the kernel's memory management system. Knowing that, focusing in on `prepare_scan_control`, `shrink_inactive_list`, `shrink_active_list`, and `evict_folios` seems like the right approach. That's where the real work of reclamation is happening. We're talking about the active and inactive list manipulation. I'm especially interested in `evict_folios`, because that touches directly on the data structures. I think it is important to be comprehensive here.

Before I go further, I should probably double-check how `lruvec_page_state_monotonic` works again. It's often the root of subtle bugs related to the bookkeeping.



22/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "lruvec_page_state_monotonic",
  "PathPrefix": "mm/memcontrol.c"
}

Results:
{
  "Output": "mm/memcontrol.c=486=unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx)\n--\nmm/memcontrol.c-508-/**\nmm/memcontrol.c:509: * lruvec_page_state_monotonic - non-clamping lruvec stat read for delta sampling\nmm/memcontrol.c-510- * @lruvec: the LRU vector to read from\n--\nmm/memcontrol.c-526- */\nmm/memcontrol.c:527:unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\nmm/memcontrol.c-528-\t\t\t\t\t  enum node_stat_item idx)\n"
}

23/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16396
							output: 33
							thoughts: 23
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Analyzing the New Function**

Alright, a new function, got it. My initial thought is to dive right in and understand its implications. First, I'm going to start with a quick check on `node_page_state_monotonic`. That specific function or variable name immediately rings a bell – it's likely a key element or dependency here. I'll need to assess how this new function interacts with, or modifies, whatever behavior `node_page_state_monotonic` is responsible for. Understanding that relationship is crucial before I can fully grasp the intent and potential impact of the introduction. I'm already anticipating that the interaction with this core concept will reveal the function's true purpose and likely areas for potential issues.



24/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "node_page_state_monotonic",
  "PathPrefix": "mm/vmstat.c"
}

Results:
{
  "Output": "mm/vmstat.c=1020=unsigned long node_page_state(struct pglist_data *pgdat,\n--\nmm/vmstat.c-1030- * snapshot a monotonically-incremented counter and subtract two samples.\nmm/vmstat.c:1031: * See global_node_page_state_monotonic() for the rationale.\nmm/vmstat.c-1032- */\nmm/vmstat.c:1033:unsigned long node_page_state_monotonic(struct pglist_data *pgdat,\nmm/vmstat.c-1034-\t\t\t\t\tenum node_stat_item item)\n"
}

25/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16788
							output: 28
							thoughts: 113
						

Thoughts:
**Analyzing Recent Kernel Memory Management Changes**

Okay, so this is new information. I'm focusing on the recent modifications, and I need to get a handle on the scope of the changes. Specifically, I need to understand what's been touched in the memory management subsystem.

It seems we've got a list of modified functions. Alright, let's see, we have `pageout` in `mm/vmscan.c`, `shrink_folio_list` in `mm/vmscan.c`, `workingset_refault` in `mm/workingset.c`, `lru_reparent_memcg` in `mm/swap.c`, and `memcg1_stat_format` in `mm/memcontrol-v1.c`.

Right. My immediate instinct is to start digging into these. I'll begin by focusing on `pageout`. It's always a critical function.



26/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "pageout",
  "PathPrefix": "mm/vmscan.c"
}

Results:
{
  "Output": "mm/vmscan.c=582=void __acct_reclaim_writeback(pg_data_t *pgdat, struct folio *folio,\n--\nmm/vmscan.c-602-\nmm/vmscan.c:603:/* possible outcome of pageout() */\nmm/vmscan.c-604-typedef enum {\n--\nmm/vmscan.c-612-\tPAGE_CLEAN,\nmm/vmscan.c:613:} pageout_t;\nmm/vmscan.c-614-\nmm/vmscan.c-615-/*\nmm/vmscan.c:616: * pageout is called by shrink_folio_list() for each dirty folio.\nmm/vmscan.c-617- */\nmm/vmscan.c:618:static pageout_t pageout(struct folio *folio, struct address_space *mapping,\nmm/vmscan.c-619-\t\t\t struct swap_iocb **plug, struct list_head *folio_list)\n--\nmm/vmscan.c=1055=static unsigned int shrink_folio_list(struct list_head *folio_list,\n--\nmm/vmscan.c-1396-\t\t\ttry_to_unmap_flush_dirty();\nmm/vmscan.c:1397:\t\t\tswitch (pageout(folio, mapping, \u0026plug, folio_list)) {\nmm/vmscan.c-1398-\t\t\tcase PAGE_KEEP:\n--\nmm/vmscan.c=2213=static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,\n--\nmm/vmscan.c-2240- * The inactive_ratio is the target ratio of ACTIVE to INACTIVE folios\nmm/vmscan.c:2241: * on this LRU, maintained by the pageout code. An inactive_ratio\nmm/vmscan.c-2242- * of 3 means 3:1 or 25% of the folios are kept on the inactive list.\n--\nmm/vmscan.c=7367=static void kswapd_try_to_sleep(pg_data_t *pgdat, int alloc_order, int reclaim_order,\n--\nmm/vmscan.c-7451-/*\nmm/vmscan.c:7452: * The background pageout daemon, started as a kernel thread\nmm/vmscan.c-7453- * from the init process.\n"
}

27/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17745
							output: 33
							thoughts: 46
						

Thoughts:
**Analyzing Memory Management Functions**

Okay, so I'm looking at these functions here. Interesting, `pageout` is defined as a static function. That usually means it's limited in scope and can only be called from within the same source file. Good to know for understanding dependencies and potential side effects. Then there's `shrink_folio_list`, also static. Again, reinforces the idea of limited visibility and a specific purpose within its compilation unit.

But now, `workingset_refault` has been exported. That suggests it's designed to be used by other parts of the system, potentially by the kernel itself or maybe even user-space processes if the API is exposed correctly. That's the one I need to focus on. Let's dig in and figure out how `workingset_refault` is used and how it interacts with the static `pageout` and `shrink_folio_list`. This is where the interesting stuff usually is. Let's see what's what.



28/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "workingset_refault",
  "PathPrefix": "mm/workingset.c"
}

Results:
{
  "Output": "mm/workingset.c=430=bool workingset_test_recent(void *shadow, bool file, bool *workingset,\n--\nmm/workingset.c-539-/**\nmm/workingset.c:540: * workingset_refault - Evaluate the refault of a previously evicted folio.\nmm/workingset.c-541- * @folio: The freshly allocated replacement folio.\n--\nmm/workingset.c-547- */\nmm/workingset.c:548:void workingset_refault(struct folio *folio, void *shadow)\nmm/workingset.c-549-{\n"
}

29/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18211
							output: 31
							thoughts: 562
						

Thoughts:
**My Analysis of Key Functions and Configurations**

Okay, let's break this down. My focus right now is pinpointing the critical functions and configurations. Based on what I've seen, I've identified several functions that are central to the work I'm doing. I need to understand their scope and how they're used. My initial list includes: `prepare_scan_control`, `shrink_inactive_list`, `shrink_active_list`, `evict_folios`, `lruvec_page_state_monotonic`, `node_page_state_monotonic`, and `workingset_refault`.

Now, I need to confirm the scope of these functions. Are they globally accessible, or are they static within a specific module? It looks like `lruvec_page_state_monotonic` is globally declared in `mm/memcontrol.c` and `include/linux/memcontrol.h` and `node_page_state_monotonic` is in `mm/vmstat.c` and `include/linux/vmstat.h`. I confirm that `evict_folios` is the correct function name within `mm/vmscan.c`. Similarly, `shrink_inactive_list` and `shrink_active_list`, and `prepare_scan_control` are static within `mm/vmscan.c`. And `workingset_refault` is global in `mm/workingset.c`. I confirm `pageout` and `shrink_folio_list` are static within `mm/vmscan.c`.

Okay, so my confirmed list of FocusSymbols is:
`prepare_scan_control`
`shrink_inactive_list`
`shrink_active_list`
`evict_folios`
`lruvec_page_state_monotonic`
`node_page_state_monotonic`
`workingset_refault`

Next, I need to consider any relevant configurations. I've noted three key ones: `CONFIG_MEMCG`, `CONFIG_LRU_GEN`, and `CONFIG_LRU_GEN_ENABLED`. The presence of `CONFIG_LRU_GEN` means `evict_folios` *could* be compiled. But the execution of `evict_folios` may depend on runtime or compile-time options. So to ensure I'm covering all bases, I should include `LRU_GEN_ENABLED`.



30/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "config LRU_GEN_ENABLED",
  "PathPrefix": "mm/Kconfig"
}

Results:
{
  "Output": "mm/Kconfig=1409=config LRU_GEN\n--\nmm/Kconfig-1417-\nmm/Kconfig:1418:config LRU_GEN_ENABLED\nmm/Kconfig-1419-\tbool \"Enable by default\"\n"
}

31/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19396
							output: 26
							thoughts: 63
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience:

**Analyzing Swap and LRU Generation Configuration**

Right, so `LRU_GEN_ENABLED` is indeed a configuration option, that's clear enough. Now, I need to understand the dependencies around this change. My immediate concern is whether or not we need to consider `CONFIG_SWAP` in this equation. The patch touches both `mm/swap.c` and `include/linux/swap.h`. That's the key. Although `mm/swap.c` is always compiled.



32/2 2026/07/21 00:07 tool grepper
0m Args:
{
  "Expression": "swap.o",
  "PathPrefix": "mm/Makefile"
}

Results:
{
  "Output": "mm/Makefile=51=obj-y\t\t\t:= filemap.o mempool.o oom_kill.o fadvise.o \\\nmm/Makefile-52-\t\t\t   maccess.o page-writeback.o folio-compat.o \\\nmm/Makefile:53:\t\t\t   readahead.o swap.o truncate.o vmscan.o shrinker.o \\\nmm/Makefile-54-\t\t\t   shmem.o util.o mmzone.o vmstat.o backing-dev.o \\\n--\nmm/Makefile=78=obj-$(CONFIG_SWAP)\t+= page_io.o swap_state.o swapfile.o\nmm/Makefile:79:obj-$(CONFIG_ZSWAP)\t+= zswap.o\nmm/Makefile-80-obj-$(CONFIG_HAS_DMA)\t+= dmapool.o\n"
}

33/2 2026/07/21 00:07 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19821
							output: 37
							thoughts: 108
						

Thoughts:
**Checking the Dependencies of `swap.o`**

Okay, so I've confirmed that `swap.o` is indeed included in `obj-y`. My next step is to nail down the exact configuration options involved here. It looks like we're dealing with `MEMCG`, `LRU_GEN`, and `LRU_GEN_ENABLED`. Now, I need to be absolutely sure about the behavior of `lruvec_page_state_monotonic`. I'm going to quickly check `mm/memcontrol.c`.

Right, if `CONFIG_MEMCG` *isn't* set, `lruvec_page_state_monotonic` is defined as an inline function within `include/linux/memcontrol.h`. That's a crucial detail to remember. I'll make sure to note this difference in behavior depending on whether MEMCG is enabled or disabled.



34/2 2026/07/21 00:08 tool grepper
0m Args:
{
  "Expression": "lruvec_page_state_monotonic",
  "PathPrefix": "include/linux/memcontrol.h"
}

Results:
{
  "Output": "include/linux/memcontrol.h=933=unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx);\ninclude/linux/memcontrol.h:934:unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\ninclude/linux/memcontrol.h-935-\t\t\t\t\t  enum node_stat_item idx);\n--\ninclude/linux/memcontrol.h=1377=static inline unsigned long lruvec_page_state(struct lruvec *lruvec,\n--\ninclude/linux/memcontrol.h-1382-\ninclude/linux/memcontrol.h:1383:static inline unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,\ninclude/linux/memcontrol.h-1384-\t\t\t\t\t\t\tenum node_stat_item idx)\n"
}

35/2 2026/07/21 00:08 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20384
							output: 35
							thoughts: 79
						

Thoughts:
Okay, here's my interpretation of these thoughts, putting myself in the position of needing to understand this code:

**Analyzing Memory Control with CONFIG_MEMCG**

Right, so we're looking at memory control code. First, the question is, is this `mm/memcontrol.c` functionality inline or out-of-line? The note clearly states that if `!CONFIG_MEMCG` is true, then it's inline. That's straightforward enough. Therefore, to *really* test the out-of-line implementation, we're going to need to ensure `MEMCG` is defined and active. That's the baseline, the core condition.

Now, where do we go from there? The next place to check is `mm/vmstat.c`, specifically the `node_page_state_monotonic` function. I have a hunch, and this needs to be confirmed, that if `CONFIG_SMP` is *not* defined, then *that* portion of the code is likely to be inline as well. This, I need to verify.



36/2 2026/07/21 00:08 tool grepper
0m Args:
{
  "Expression": "node_page_state_monotonic",
  "PathPrefix": "include/linux/vmstat.h"
}

Results:
{
  "Output": "include/linux/vmstat.h=186=unsigned long global_node_page_state_pages(enum node_stat_item item)\n--\ninclude/linux/vmstat.h-203- */\ninclude/linux/vmstat.h:204:static inline unsigned long global_node_page_state_monotonic(enum node_stat_item item)\ninclude/linux/vmstat.h-205-{\n--\ninclude/linux/vmstat.h=272=extern unsigned long node_page_state_pages(struct pglist_data *pgdat,\ninclude/linux/vmstat.h-273-\t\t\t\t\t   enum node_stat_item item);\ninclude/linux/vmstat.h:274:extern unsigned long node_page_state_monotonic(struct pglist_data *pgdat,\ninclude/linux/vmstat.h-275-\t\t\t\t\t       enum node_stat_item item);\ninclude/linux/vmstat.h=276=extern void fold_vm_numa_events(void);\n--\ninclude/linux/vmstat.h-280-#define node_page_state_pages(node, item) global_node_page_state_pages(item)\ninclude/linux/vmstat.h:281:#define node_page_state_monotonic(node, item) global_node_page_state_monotonic(item)\ninclude/linux/vmstat.h-282-static inline void fold_vm_numa_events(void)\n"
}

37/2 2026/07/21 00:08 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21092
							output: 27
							thoughts: 866
						

Thoughts:
**Analyzing the Impact of the LRU Cost Model and Statistics Patch**

Okay, let's break this down. The first thing I'm seeing is how `global_node_page_state_monotonic` is conditional. It's defined when `!CONFIG_SMP`, meaning that to test the out-of-line version within `mm/vmstat.c`, SMP (Symmetric Multiprocessing) needs to be enabled. However, SMP is almost always enabled these days, so whether we explicitly list it or not is essentially irrelevant for coverage.

Now, let's get down to the meat of the functionality. I need to summarize the core functions affected: `prepare_scan_control`, `shrink_inactive_list`, `shrink_active_list`, `evict_folios`, `lruvec_page_state_monotonic`, `node_page_state_monotonic`, and `workingset_refault`. These are the key entry points and core logic impacted by the changes to the LRU cost model and statistics.

I should also check if `shrink_folio_list` should be included. It looks like it only has a minor modification, just removing a `stat->nr_pageout += nr_pages;` statement. This isn't a significant functional change, so I'm not immediately concerned. On the other hand, the `pageout` function *does* involve a change from a node stat to an lruvec stat, as indicated by the change:

```c
-	node_stat_add_folio(folio, NR_VMSCAN_WRITE);
+	lruvec_stat_mod_folio(folio, NR_VMSCAN_WRITE, folio_nr_pages(folio));
```

So `pageout` probably *should* be considered.

Let's check `lru_reparent_memcg`. It's only removing lines:

```c
-	parent_lruvec->anon_cost += child_lruvec->anon_cost;
-	parent_lruvec->file_cost += child_lruvec->file_cost;
```

Similarly, `memcg1_stat_format` is just reading from a new struct:

```c
-			anon_cost += mz->lruvec.anon_cost;
-			file_cost += mz->lruvec.file_cost;
+			anon_cost += mz->lruvec.cost[WORKINGSET_ANON].count;
+			file_cost += mz->lruvec.cost[WORKINGSET_FILE].count;
```

So the significant functional changes are focused in: `prepare_scan_control`, `shrink_inactive_list`, `shrink_active_list`, `evict_folios`, `lruvec_page_state_monotonic`, `node_page_state_monotonic`, `workingset_refault`, and `pageout`. I'm updating my initial list to reflect this analysis:

```json
[
  "prepare_scan_control",
  "shrink_inactive_list",
  "shrink_active_list",
  "evict_folios",
  "lruvec_page_state_monotonic",
  "node_page_state_monotonic",
  "workingset_refault",
  "pageout"
]
```

Finally, I need to identify the relevant configurations. Based on my understanding of the memory management subsystem, the relevant configurations here should include `MEMCG`, `LRU_GEN`, and `LRU_GEN_ENABLED`. These should be the minimal necessary for fuzzing.

```json
[
  "MEMCG",
  "LRU_GEN",
  "LRU_GEN_ENABLED"
]
```

In summary, this patch profoundly alters the LRU cost model and statistics tracking within the core memory management. It introduces monotonic page state tracking for both LRU vectors and nodes, and changes how `anon_cost` and `file_cost` are computed during memory reclaim. It also modifies both traditional LRU and Multi-Gen LRU (MGLRU) reclaim paths to use the updated cost model and statistics. These are functional changes which cover reachable core kernel logic and need to be fuzzed. I've double checked that the config names (`LRU_GEN`, `LRU_GEN_ENABLED` and `MEMCG`) are correct.



38/2 2026/07/21 00:08 tool grepper
0m Args:
{
  "Expression": "config MEMCG",
  "PathPrefix": "init/Kconfig"
}

Results:
{
  "Output": "init/Kconfig=1063=config CGROUP_FAVOR_DYNMODS\n--\ninit/Kconfig-1072-\ninit/Kconfig:1073:config MEMCG\ninit/Kconfig-1074-\tbool \"Memory controller\"\n--\ninit/Kconfig-1081-\ninit/Kconfig:1082:config MEMCG_NMI_UNSAFE\ninit/Kconfig-1083-\tbool\n--\ninit/Kconfig-1088-\ninit/Kconfig:1089:config MEMCG_NMI_SAFETY_REQUIRES_ATOMIC\ninit/Kconfig-1090-\tbool\n--\ninit/Kconfig-1095-\ninit/Kconfig:1096:config MEMCG_V1\ninit/Kconfig-1097-\tbool \"Legacy cgroup v1 memory controller\"\n"
}

39/2 2026/07/21 00:08 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23163
							output: 214
							thoughts: 19
						

Thoughts:
Okay, here's my summary, in the first person, as requested:

**Navigating the Code**

Yes, that's it! Looking back, `MEMCG` is definitely the correct component to be focusing on. That's the key. Now that I've established that, let's move forward and call `set-results`. It's the logical next step.



40/2 2026/07/21 00:08 tool set-results
0m Args:
{
  "EnableConfigs": [
    "MEMCG",
    "LRU_GEN",
    "LRU_GEN_ENABLED"
  ],
  "FocusSymbols": [
    "prepare_scan_control",
    "shrink_inactive_list",
    "shrink_active_list",
    "evict_folios",
    "lruvec_page_state_monotonic",
    "node_page_state_monotonic",
    "workingset_refault",
    "pageout"
  ],
  "Reasoning": "The patch modifies the LRU cost model and statistics tracking in the core memory management subsystem. It introduces monotonic page state tracking for LRU vectors and nodes, and updates how `anon_cost` and `file_cost` are calculated during memory reclaim. It also modifies both the traditional LRU and Multi-Gen LRU (MGLRU) reclaim paths to use the new cost model and statistics. These are functional changes to reachable core kernel logic that should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "MEMCG",
    "LRU_GEN",
    "LRU_GEN_ENABLED"
  ],
  "FocusSymbols": [
    "prepare_scan_control",
    "shrink_inactive_list",
    "shrink_active_list",
    "evict_folios",
    "lruvec_page_state_monotonic",
    "node_page_state_monotonic",
    "workingset_refault",
    "pageout"
  ],
  "Reasoning": "The patch modifies the LRU cost model and statistics tracking in the core memory management subsystem. It introduces monotonic page state tracking for LRU vectors and nodes, and updates how `anon_cost` and `file_cost` are calculated during memory reclaim. It also modifies both the traditional LRU and Multi-Gen LRU (MGLRU) reclaim paths to use the new cost model and statistics. These are functional changes to reachable core kernel logic that should be fuzzed.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)