| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/03 03:16 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"throttle_evictable_types",
"lru_gen_too_many_isolated",
"isolate_folios",
"evict_folios",
"lru_gen_seq_write"
],
"KMSANReasoning": "The patch adds per-lruvec isolation accounting (`nr_isolated`) and isolation throttling (`lru_gen_too_many_isolated`, `throttle_evictable_types`) for Multi-Gen LRU (MGLRU) page reclaim.\n\nAnalysis against KMSAN applicability:\n1. User-space leaks: No data is copied to user space or exposed via ioctls/sysfs/netlink/BPF buffers.\n2. Uninitialized variables: All added local variables (`allowed`, `fatal`, `stalled`) and struct fields (`nr_isolated`) are properly initialized and managed (e.g., `*fatal` is initialized to false upon entering `throttle_evictable_types()`, `struct lruvec` memory is zero-allocated during node/memcg initialization).\n3. Buffer bounds / memory handling: The patch only adjusts reclaimer scheduling, throttling, and isolation counters without touching uninitialized buffer access, variable-length buffers, or struct padding.\n\nAny potential bugs introduced by this patch would relate to reclaim throttling, concurrency, locking, or counter imbalances, which are handled by standard kernel debugging tools (LOCKDEP, KASAN, WARN_ON). There is no risk of uninitialized memory usage or info-leaks, so a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces isolated folio accounting and reclaim throttling for Multi-Gen LRU (MGLRU) in mm/vmscan.c, implementing lru_gen_too_many_isolated() and throttle_evictable_types() to prevent excessive folio isolation during memory pressure and direct reclaim. This is a functional change in core memory management logic.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/03 03:16 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 836d96433c0ed76d07112897bdf2664b1c287817\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Sep 3 03:16:46 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/mmzone.h b/include/linux/mmzone.h\nindex 94f9c3ff54160..819b6c059116e 100644\n--- a/include/linux/mmzone.h\n+++ b/include/linux/mmzone.h\n@@ -580,6 +580,8 @@ struct lru_gen_folio {\n \t/* can be modified without holding the LRU lock */\n \tatomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];\n \tatomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];\n+\t/* the number of folios isolated from this lruvec and being reclaimed */\n+\tatomic_long_t nr_isolated[ANON_AND_FILE];\n \t/* whether the multi-gen LRU is enabled */\n \tbool enabled;\n \t/* the memcg generation this lru_gen_folio belongs to */\ndiff --git a/mm/vmscan.c b/mm/vmscan.c\nindex f11491ee9ed5c..211839d10909a 100644\n--- a/mm/vmscan.c\n+++ b/mm/vmscan.c\n@@ -4151,25 +4151,34 @@ static void set_initial_priority(struct pglist_data *pgdat, struct scan_control\n \tsc-\u003epriority = clamp(priority, DEF_PRIORITY / 2, DEF_PRIORITY);\n }\n \n-static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)\n+static unsigned long lruvec_type_evictable_size(struct lruvec *lruvec, int type)\n {\n-\tint gen, type, zone;\n+\tint gen, zone;\n \tunsigned long seq, total = 0;\n \tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\n \tDEFINE_MAX_SEQ(lruvec);\n \tDEFINE_MIN_SEQ(lruvec);\n \n-\tfor_each_evictable_type(type, swappiness) {\n-\t\tfor (seq = min_seq[type]; seq \u003c= max_seq; seq++) {\n-\t\t\tgen = lru_gen_from_seq(seq);\n-\t\t\tfor (zone = 0; zone \u003c MAX_NR_ZONES; zone++)\n-\t\t\t\ttotal += max(READ_ONCE(lrugen-\u003enr_pages[gen][type][zone]), 0L);\n-\t\t}\n+\tfor (seq = min_seq[type]; seq \u003c= max_seq; seq++) {\n+\t\tgen = lru_gen_from_seq(seq);\n+\t\tfor (zone = 0; zone \u003c MAX_NR_ZONES; zone++)\n+\t\t\ttotal += max(READ_ONCE(lrugen-\u003enr_pages[gen][type][zone]), 0L);\n \t}\n \n \treturn total;\n }\n \n+static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)\n+{\n+\tunsigned long total = 0;\n+\tint type;\n+\n+\tfor_each_evictable_type(type, swappiness)\n+\t\ttotal += lruvec_type_evictable_size(lruvec, type);\n+\n+\treturn total;\n+}\n+\n static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)\n {\n \tunsigned long total;\n@@ -4838,15 +4847,128 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)\n \treturn positive_ctrl_err(\u0026sp, \u0026pv);\n }\n \n+/*\n+ * The MGLRU counterpart to too_many_isolated().\n+ *\n+ * too_many_isolated() compares node-wide isolated counts against\n+ * node-wide inactive sizes. It predates per-memcg LRU lists, which\n+ * moved isolation to per-lruvec granularity. MGLRU reclaimers contend\n+ * per lruvec, so this check is per lruvec as well, and isolation in\n+ * one memcg does not throttle reclaim in another.\n+ *\n+ * The legacy threshold, the size of the inactive list, is not usable\n+ * here either: in MGLRU the inactive counters are only a compatibility\n+ * shim - they jump around when swap runs out or a memcg hits its swap\n+ * limit, and proactive aging can inflate them. Compare against the\n+ * total number of evictable pages of the type divided by MIN_NR_GENS,\n+ * approximating the size of one generation - the same unit the\n+ * original MGLRU aging heuristics used.\n+ */\n+static bool lru_gen_too_many_isolated(struct lruvec *lruvec, int type,\n+\t\t\t\t struct scan_control *sc)\n+{\n+\tunsigned long isolated, evictable;\n+\n+\tif (current_is_kswapd())\n+\t\treturn false;\n+\n+\tif (!writeback_throttling_sane(sc))\n+\t\treturn false;\n+\n+\tisolated = atomic_long_read(\u0026lruvec-\u003elrugen.nr_isolated[type]);\n+\tevictable = lruvec_type_evictable_size(lruvec, type);\n+\n+\t/*\n+\t * GFP_NOIO/GFP_NOFS callers are allowed to isolate more pages, so\n+\t * they won't be blocked by normal direct-reclaimers, forming a\n+\t * circular deadlock.\n+\t */\n+\tif (gfp_has_io_fs(sc-\u003egfp_mask))\n+\t\tevictable \u003e\u003e= 3;\n+\n+\treturn isolated \u003e evictable / MIN_NR_GENS;\n+}\n+\n+/*\n+ * Unlike the legacy path, where the LRU list to isolate from is known\n+ * before isolation, isolate_folios() picks the type from the refault\n+ * feedback and may fall back to the other one. Therefore, instead of\n+ * throttling on a single type, collect the evictable types that do not\n+ * have too many isolated folios, and only sleep when all of them do.\n+ *\n+ * Returns the mask of the types isolate_folios() may isolate from, or\n+ * 0 if reclaim should stop. Also sets @fatal to tell the caller that\n+ * the task received a fatal signal while waiting.\n+ */\n+static unsigned int throttle_evictable_types(struct lruvec *lruvec,\n+\t\t\t\t\t int swappiness,\n+\t\t\t\t\t struct scan_control *sc,\n+\t\t\t\t\t bool *fatal)\n+{\n+\tunsigned int allowed;\n+\tbool stalled = false;\n+\tstruct pglist_data *pgdat = lruvec_pgdat(lruvec);\n+\tint i;\n+\n+\t*fatal = false;\n+\n+\tfor (;;) {\n+\t\tallowed = 0;\n+\t\tfor_each_evictable_type(i, swappiness) {\n+\t\t\t/*\n+\t\t\t * A type with no evictable folios has nothing to\n+\t\t\t * isolate. Don't allow it, otherwise reclaim can be\n+\t\t\t * redirected to it and spin making no progress, e.g.\n+\t\t\t * when the only type with folios is over-isolated.\n+\t\t\t */\n+\t\t\tif (!lruvec_type_evictable_size(lruvec, i))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (!lru_gen_too_many_isolated(lruvec, i, sc))\n+\t\t\t\tallowed |= BIT(i);\n+\t\t}\n+\n+\t\tif (allowed) {\n+\t\t\t/* Wake up reclaimers waiting on the isolation to go down. */\n+\t\t\twake_throttle_isolated(pgdat);\n+\t\t\treturn allowed;\n+\t\t}\n+\n+\t\t/*\n+\t\t * All evictable types are over-isolated. Like the legacy\n+\t\t * path, wait once for concurrent reclaimers to put their\n+\t\t * isolated folios back; give up if that makes no progress.\n+\t\t */\n+\t\tif (stalled)\n+\t\t\treturn 0;\n+\n+\t\tstalled = true;\n+\t\treclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);\n+\n+\t\t/* We are about to die and free our memory. Return now. */\n+\t\tif (fatal_signal_pending(current)) {\n+\t\t\t*fatal = true;\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+}\n+\n static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \t\t\t struct scan_control *sc, int swappiness,\n-\t\t\t struct list_head *list, int *isolated,\n-\t\t\t int *isolate_type, int *isolate_scanned)\n+\t\t\t unsigned int allowed, struct list_head *list,\n+\t\t\t int *isolated, int *isolate_type, int *isolate_scanned)\n {\n \tint i;\n \tint total_scanned = 0;\n \tint type = get_type_to_scan(lruvec, swappiness);\n \n+\t/*\n+\t * The preferred type may have been excluded by\n+\t * throttle_evictable_types(); start from the other one.\n+\t */\n+\tif (!(allowed \u0026 BIT(type)))\n+\t\ttype = !type;\n+\n \tfor_each_evictable_type(i, swappiness) {\n \t\tint scanned;\n \t\tint tier = get_tier_idx(lruvec, type);\n@@ -4863,9 +4985,10 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \t\t/*\n \t\t * If scanned \u003e 0 and isolated == 0, avoid falling back to the\n \t\t * other type, as this type remains sufficient. Falling back\n-\t\t * too readily can disrupt the positive_ctrl_err() bias.\n+\t\t * too readily can disrupt the positive_ctrl_err() bias. Only\n+\t\t * fall back to a type that is not throttled.\n \t\t */\n-\t\tif (!scanned)\n+\t\tif (!scanned \u0026\u0026 (allowed \u0026 BIT(!type)))\n \t\t\ttype = !type;\n \t}\n \n@@ -4888,15 +5011,36 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \tbool skip_retry = false;\n \tstruct mem_cgroup *memcg = lruvec_memcg(lruvec);\n \tstruct pglist_data *pgdat = lruvec_pgdat(lruvec);\n+\tunsigned int allowed;\n+\tbool fatal;\n+\n+\tallowed = throttle_evictable_types(lruvec, swappiness, sc, \u0026fatal);\n+\tif (!allowed) {\n+\t\t/*\n+\t\t * We are about to die and free our memory. Like the legacy\n+\t\t * path, pretend some pages were reclaimed so reclaim\n+\t\t * unwinds quickly instead of looping back into the\n+\t\t * throttle.\n+\t\t */\n+\t\tif (fatal)\n+\t\t\tsc-\u003enr_reclaimed += SWAP_CLUSTER_MAX;\n+\n+\t\treturn 0;\n+\t}\n \n \tlruvec_lock_irq(lruvec);\n \n \t/* In case folio deletion left empty old gens, flush them */\n \ttry_to_inc_min_seq(lruvec, swappiness);\n \n-\tscanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,\n+\tscanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, allowed,\n \t\t\t\t \u0026list, \u0026isolated, \u0026type, \u0026type_scanned);\n \tnr_isolated = isolated;\n+\tif (nr_isolated) {\n+\t\t__mod_node_page_state(pgdat, NR_ISOLATED_ANON + type,\n+\t\t\t\t nr_isolated);\n+\t\tatomic_long_add(nr_isolated, \u0026lruvec-\u003elrugen.nr_isolated[type]);\n+\t}\n \n \t/* Scanning may have emptied the oldest gen, flush it */\n \tif (scanned)\n@@ -4959,6 +5103,9 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n \t\tgoto retry;\n \t}\n \n+\tatomic_long_sub(nr_isolated, \u0026lruvec-\u003elrugen.nr_isolated[type]);\n+\tmod_node_page_state(pgdat, NR_ISOLATED_ANON + type, -nr_isolated);\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"
} |
| 2/1 | 2026/09/03 03:16 | agent | patch-evaluator |
1mModel:core Args: null Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"throttle_evictable_types",
"lru_gen_too_many_isolated",
"isolate_folios",
"evict_folios",
"lru_gen_seq_write"
],
"Reasoning": "The patch introduces isolated folio accounting and reclaim throttling for Multi-Gen LRU (MGLRU) in mm/vmscan.c, implementing lru_gen_too_many_isolated() and throttle_evictable_types() to prevent excessive folio isolation during memory pressure and direct reclaim. This is a functional change in core memory management logic.",
"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 836d96433c0ed76d07112897bdf2664b1c287817
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Sep 3 03:16:46 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 94f9c3ff54160..819b6c059116e 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -580,6 +580,8 @@ struct lru_gen_folio {
/* can be modified without holding the LRU lock */
atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
+ /* the number of folios isolated from this lruvec and being reclaimed */
+ atomic_long_t nr_isolated[ANON_AND_FILE];
/* whether the multi-gen LRU is enabled */
bool enabled;
/* the memcg generation this lru_gen_folio belongs to */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index f11491ee9ed5c..211839d10909a 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4151,25 +4151,34 @@ static void set_initial_priority(struct pglist_data *pgdat, struct scan_control
sc->priority = clamp(priority, DEF_PRIORITY / 2, DEF_PRIORITY);
}
-static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)
+static unsigned long lruvec_type_evictable_size(struct lruvec *lruvec, int type)
{
- int gen, type, zone;
+ int gen, zone;
unsigned long seq, total = 0;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
DEFINE_MAX_SEQ(lruvec);
DEFINE_MIN_SEQ(lruvec);
- for_each_evictable_type(type, swappiness) {
- for (seq = min_seq[type]; seq <= max_seq; seq++) {
- gen = lru_gen_from_seq(seq);
- for (zone = 0; zone < MAX_NR_ZONES; zone++)
- total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
- }
+ for (seq = min_seq[type]; seq <= max_seq; seq++) {
+ gen = lru_gen_from_seq(seq);
+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
+ total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
}
return total;
}
+static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)
+{
+ unsigned long total = 0;
+ int type;
+
+ for_each_evictable_type(type, swappiness)
+ total += lruvec_type_evictable_size(lruvec, type);
+
+ return total;
+}
+
static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
{
unsigned long total;
@@ -4838,15 +4847,128 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)
return positive_ctrl_err(&sp, &pv);
}
+/*
+ * The MGLRU counterpart to too_many_isolated().
+ *
+ * too_many_isolated() compares node-wide isolated counts against
+ * node-wide inactive sizes. It predates per-memcg LRU lists, which
+ * moved isolation to per-lruvec granularity. MGLRU reclaimers contend
+ * per lruvec, so this check is per lruvec as well, and isolation in
+ * one memcg does not throttle reclaim in another.
+ *
+ * The legacy threshold, the size of the inactive list, is not usable
+ * here either: in MGLRU the inactive counters are only a compatibility
+ * shim - they jump around when swap runs out or a memcg hits its swap
+ * limit, and proactive aging can inflate them. Compare against the
+ * total number of evictable pages of the type divided by MIN_NR_GENS,
+ * approximating the size of one generation - the same unit the
+ * original MGLRU aging heuristics used.
+ */
+static bool lru_gen_too_many_isolated(struct lruvec *lruvec, int type,
+ struct scan_control *sc)
+{
+ unsigned long isolated, evictable;
+
+ if (current_is_kswapd())
+ return false;
+
+ if (!writeback_throttling_sane(sc))
+ return false;
+
+ isolated = atomic_long_read(&lruvec->lrugen.nr_isolated[type]);
+ evictable = lruvec_type_evictable_size(lruvec, type);
+
+ /*
+ * GFP_NOIO/GFP_NOFS callers are allowed to isolate more pages, so
+ * they won't be blocked by normal direct-reclaimers, forming a
+ * circular deadlock.
+ */
+ if (gfp_has_io_fs(sc->gfp_mask))
+ evictable >>= 3;
+
+ return isolated > evictable / MIN_NR_GENS;
+}
+
+/*
+ * Unlike the legacy path, where the LRU list to isolate from is known
+ * before isolation, isolate_folios() picks the type from the refault
+ * feedback and may fall back to the other one. Therefore, instead of
+ * throttling on a single type, collect the evictable types that do not
+ * have too many isolated folios, and only sleep when all of them do.
+ *
+ * Returns the mask of the types isolate_folios() may isolate from, or
+ * 0 if reclaim should stop. Also sets @fatal to tell the caller that
+ * the task received a fatal signal while waiting.
+ */
+static unsigned int throttle_evictable_types(struct lruvec *lruvec,
+ int swappiness,
+ struct scan_control *sc,
+ bool *fatal)
+{
+ unsigned int allowed;
+ bool stalled = false;
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+ int i;
+
+ *fatal = false;
+
+ for (;;) {
+ allowed = 0;
+ for_each_evictable_type(i, swappiness) {
+ /*
+ * A type with no evictable folios has nothing to
+ * isolate. Don't allow it, otherwise reclaim can be
+ * redirected to it and spin making no progress, e.g.
+ * when the only type with folios is over-isolated.
+ */
+ if (!lruvec_type_evictable_size(lruvec, i))
+ continue;
+
+ if (!lru_gen_too_many_isolated(lruvec, i, sc))
+ allowed |= BIT(i);
+ }
+
+ if (allowed) {
+ /* Wake up reclaimers waiting on the isolation to go down. */
+ wake_throttle_isolated(pgdat);
+ return allowed;
+ }
+
+ /*
+ * All evictable types are over-isolated. Like the legacy
+ * path, wait once for concurrent reclaimers to put their
+ * isolated folios back; give up if that makes no progress.
+ */
+ if (stalled)
+ return 0;
+
+ stalled = true;
+ reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);
+
+ /* We are about to die and free our memory. Return now. */
+ if (fatal_signal_pending(current)) {
+ *fatal = true;
+ return 0;
+ }
+ }
+}
+
static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
struct scan_control *sc, int swappiness,
- struct list_head *list, int *isolated,
- int *isolate_type, int *isolate_scanned)
+ unsigned int allowed, struct list_head *list,
+ int *isolated, int *isolate_type, int *isolate_scanned)
{
int i;
int total_scanned = 0;
int type = get_type_to_scan(lruvec, swappiness);
+ /*
+ * The preferred type may have been excluded by
+ * throttle_evictable_types(); start from the other one.
+ */
+ if (!(allowed & BIT(type)))
+ type = !type;
+
for_each_evictable_type(i, swappiness) {
int scanned;
int tier = get_tier_idx(lruvec, type);
@@ -4863,9 +4985,10 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
/*
* If scanned > 0 and isolated == 0, avoid falling back to the
* other type, as this type remains sufficient. Falling back
- * too readily can disrupt the positive_ctrl_err() bias.
+ * too readily can disrupt the positive_ctrl_err() bias. Only
+ * fall back to a type that is not throttled.
*/
- if (!scanned)
+ if (!scanned && (allowed & BIT(!type)))
type = !type;
}
@@ -4888,15 +5011,36 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
bool skip_retry = false;
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+ unsigned int allowed;
+ bool fatal;
+
+ allowed = throttle_evictable_types(lruvec, swappiness, sc, &fatal);
+ if (!allowed) {
+ /*
+ * We are about to die and free our memory. Like the legacy
+ * path, pretend some pages were reclaimed so reclaim
+ * unwinds quickly instead of looping back into the
+ * throttle.
+ */
+ if (fatal)
+ sc->nr_reclaimed += SWAP_CLUSTER_MAX;
+
+ return 0;
+ }
lruvec_lock_irq(lruvec);
/* In case folio deletion left empty old gens, flush them */
try_to_inc_min_seq(lruvec, swappiness);
- scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,
+ scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, allowed,
&list, &isolated, &type, &type_scanned);
nr_isolated = isolated;
+ if (nr_isolated) {
+ __mod_node_page_state(pgdat, NR_ISOLATED_ANON + type,
+ nr_isolated);
+ atomic_long_add(nr_isolated, &lruvec->lrugen.nr_isolated[type]);
+ }
/* Scanning may have emptied the oldest gen, flush it */
if (scanned)
@@ -4959,6 +5103,9 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
goto retry;
}
+ atomic_long_sub(nr_isolated, &lruvec->lrugen.nr_isolated[type]);
+ mod_node_page_state(pgdat, NR_ISOLATED_ANON + type, -nr_isolated);
+
if (nr_isolated > total_reclaimed)
mod_lruvec_state(lruvec, PGROTATE_ANON + type,
nr_isolated - total_reclaimed);
|
| 3/2 | 2026/09/03 03:16 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 5051 output: 26 thoughts: 218 Thoughts: **Analyzing the MGLRU Isolation Throttling Patch** Okay, so this patch hits `include/linux/mmzone.h` and `mm/vmscan.c`. Immediately, I'm thinking MGLRU, which is always interesting. Let's dig in and see the details. It looks like we're adding some isolation throttling for Multi-Gen LRU. Specifically, in `mm/vmscan.c`, the changes are centered around tracking isolated pages for throttling. A new `atomic_long_t nr_isolated[ANON_AND_FILE]` field is being added to the `lru_gen_folio` struct. We have a few new functions as well: `lru_gen_too_many_isolated`, `throttle_evictable_types`, and there are updates to `isolate_folios` and `evict_folios`. This all seems to be about implementing the actual throttling mechanism. The goal is likely to prevent a runaway situation where too many pages are isolated, which would block progress. Now, before I get too deep into the code, I need to check the dependencies. I should probably use `grepper` or just jump into `mm/Kconfig` to verify the Kconfig requirements for both Multi-Gen LRU (LRU_GEN, right?) and the vmscan code. Gotta make sure we're not breaking any pre-existing configurations or enabling this in an unexpected scenario. Let's see how this all fits into the bigger picture of memory management. |
| 4/2 | 2026/09/03 03:16 | tool | grepper |
0mArgs:{
"Expression": "LRU_GEN",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/Kconfig=1393=config PTE_MARKER_UFFD_WP\n--\nmm/Kconfig-1403-# multi-gen LRU {\nmm/Kconfig:1404:config LRU_GEN\nmm/Kconfig-1405-\tbool \"Multi-Gen LRU\"\n--\nmm/Kconfig-1412-\nmm/Kconfig:1413:config LRU_GEN_ENABLED\nmm/Kconfig-1414-\tbool \"Enable by default\"\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=378=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-395-\nmm/folio.c:396:#else /* !CONFIG_LRU_GEN */\nmm/folio.c-397-\n--\nmm/folio.c=402=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-406-\nmm/folio.c:407:#endif /* CONFIG_LRU_GEN */\nmm/folio.c-408-\n--\nmm/huge_memory.c=3685=static void __split_folio_to_order(struct folio *folio, int old_order,\n--\nmm/huge_memory.c-3745-\t\t\t\t (1L \u003c\u003c PG_dropbehind) |\nmm/huge_memory.c:3746:\t\t\t\t LRU_GEN_MASK | LRU_REFS_MASK));\nmm/huge_memory.c-3747-\n--\nmm/memcontrol.c=4627=static void mem_cgroup_exit(struct task_struct *task)\n--\nmm/memcontrol.c-4643-\nmm/memcontrol.c:4644:#ifdef CONFIG_LRU_GEN\nmm/memcontrol.c-4645-static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset)\n--\nmm/memcontrol.c=4663=static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset) {}\nmm/memcontrol.c:4664:#endif /* CONFIG_LRU_GEN */\nmm/memcontrol.c-4665-\n--\nmm/memory.c=6728=static inline void mm_account_fault(struct mm_struct *mm, struct pt_regs *regs,\n--\nmm/memory.c-6780-\nmm/memory.c:6781:#ifdef CONFIG_LRU_GEN\nmm/memory.c-6782-static void lru_gen_enter_fault(struct vm_area_struct *vma)\n--\nmm/memory.c=6797=static void lru_gen_exit_fault(void)\n--\nmm/memory.c-6799-}\nmm/memory.c:6800:#endif /* CONFIG_LRU_GEN */\nmm/memory.c-6801-\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=862=static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\n--\nmm/vmscan.c-865-}\nmm/vmscan.c:866:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-867-\n--\nmm/vmscan.c=2680=static bool can_age_anon_pages(struct lruvec *lruvec,\n--\nmm/vmscan.c-2691-\nmm/vmscan.c:2692:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-2693-\nmm/vmscan.c=2694=DEFINE_STATIC_KEY_FALSE(lru_switch);\nmm/vmscan.c:2695:#ifdef CONFIG_LRU_GEN_ENABLED\nmm/vmscan.c:2696:DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);\nmm/vmscan.c-2697-#define get_cap(cap)\tstatic_branch_likely(\u0026lru_gen_caps[cap])\nmm/vmscan.c-2698-#else\nmm/vmscan.c:2699:DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS);\nmm/vmscan.c-2700-#define get_cap(cap)\tstatic_branch_unlikely(\u0026lru_gen_caps[cap])\n--\nmm/vmscan.c=2703=static bool should_walk_mmu(void)\nmm/vmscan.c-2704-{\nmm/vmscan.c:2705:\treturn arch_has_hw_pte_young() \u0026\u0026 get_cap(LRU_GEN_MM_WALK);\nmm/vmscan.c-2706-}\n--\nmm/vmscan.c=2708=static bool should_clear_pmd_young(void)\nmm/vmscan.c-2709-{\nmm/vmscan.c:2710:\treturn arch_has_hw_nonleaf_pmd_young() \u0026\u0026 get_cap(LRU_GEN_NONLEAF_YOUNG);\nmm/vmscan.c-2711-}\n--\nmm/vmscan.c-2721-\tunsigned long min_seq[ANON_AND_FILE] = {\t\t\t\\\nmm/vmscan.c:2722:\t\tREAD_ONCE((lruvec)-\u003elrugen.min_seq[LRU_GEN_ANON]),\t\\\nmm/vmscan.c:2723:\t\tREAD_ONCE((lruvec)-\u003elrugen.min_seq[LRU_GEN_FILE]),\t\\\nmm/vmscan.c-2724-\t}\n--\nmm/vmscan.c=2880=static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\n--\nmm/vmscan.c-2899-\nmm/vmscan.c:2900:#ifdef CONFIG_LRU_GEN_WALKS_MMU\nmm/vmscan.c-2901-\n--\nmm/vmscan.c=3009=void lru_gen_migrate_mm(struct mm_struct *mm)\n--\nmm/vmscan.c-3037-\nmm/vmscan.c:3038:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\nmm/vmscan.c-3039-\n--\nmm/vmscan.c=3269=static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)\n--\nmm/vmscan.c-3287-\t\t/* lru_gen_del_folio() has isolated this page? */\nmm/vmscan.c:3288:\t\tif (!(old_flags \u0026 LRU_GEN_MASK))\nmm/vmscan.c-3289-\t\t\treturn -1;\nmm/vmscan.c-3290-\nmm/vmscan.c:3291:\t\tnew_flags = old_flags \u0026 ~(LRU_GEN_MASK | LRU_REFS_FLAGS);\nmm/vmscan.c:3292:\t\tnew_flags |= ((gen + 1UL) \u003c\u003c LRU_GEN_PGOFF) | BIT(PG_workingset);\nmm/vmscan.c-3293-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\nmm/vmscan.c-3294-\nmm/vmscan.c:3295:\treturn ((old_flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\nmm/vmscan.c-3296-}\n--\nmm/vmscan.c=3299=static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n--\nmm/vmscan.c-3305-\nmm/vmscan.c:3306:\tVM_WARN_ON_ONCE_FOLIO(!(old_flags \u0026 LRU_GEN_MASK), folio);\nmm/vmscan.c-3307-\nmm/vmscan.c-3308-\tdo {\nmm/vmscan.c:3309:\t\tnew_gen = ((old_flags \u0026 LRU_GEN_MASK) \u003e\u003e LRU_GEN_PGOFF) - 1;\nmm/vmscan.c-3310-\t\t/* folio_update_gen() has promoted this page? */\n--\nmm/vmscan.c-3315-\nmm/vmscan.c:3316:\t\tnew_flags = old_flags \u0026 ~(LRU_GEN_MASK | LRU_REFS_FLAGS);\nmm/vmscan.c:3317:\t\tnew_flags |= (new_gen + 1UL) \u003c\u003c LRU_GEN_PGOFF;\nmm/vmscan.c-3318-\t} while (!try_cmpxchg(\u0026folio-\u003eflags.f, \u0026old_flags, new_flags));\n--\nmm/vmscan.c=3953=static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-3989-\nmm/vmscan.c:3990:\t\tif (min_seq[LRU_GEN_ANON] \u003e seq \u0026\u0026 min_seq[LRU_GEN_FILE] \u003c seq)\nmm/vmscan.c:3991:\t\t\tmin_seq[LRU_GEN_ANON] = seq;\nmm/vmscan.c:3992:\t\telse if (min_seq[LRU_GEN_FILE] \u003e seq \u0026\u0026 min_seq[LRU_GEN_ANON] \u003c seq)\nmm/vmscan.c:3993:\t\t\tmin_seq[LRU_GEN_FILE] = seq;\nmm/vmscan.c-3994-\t}\n--\nmm/vmscan.c=4005=static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness)\n--\nmm/vmscan.c-4037-\t * the current max_seq need to be covered, since max_seq+1 can overlap\nmm/vmscan.c:4038:\t * with min_seq[LRU_GEN_ANON] if swapping is constrained. And if they do\nmm/vmscan.c-4039-\t * overlap, cold/hot inversion happens.\n--\nmm/vmscan.c=4831=static int get_type_to_scan(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-4835-\tif (swappiness \u003c= MIN_SWAPPINESS + 1)\nmm/vmscan.c:4836:\t\treturn LRU_GEN_FILE;\nmm/vmscan.c-4837-\nmm/vmscan.c-4838-\tif (swappiness \u003e= MAX_SWAPPINESS)\nmm/vmscan.c:4839:\t\treturn LRU_GEN_ANON;\nmm/vmscan.c-4840-\t/*\n--\nmm/vmscan.c-4843-\t */\nmm/vmscan.c:4844:\tread_ctrl_pos(lruvec, LRU_GEN_ANON, MAX_NR_TIERS, swappiness, \u0026sp);\nmm/vmscan.c:4845:\tread_ctrl_pos(lruvec, LRU_GEN_FILE, MAX_NR_TIERS, MAX_SWAPPINESS - swappiness, \u0026pv);\nmm/vmscan.c-4846-\n--\nmm/vmscan.c=5505=static void lru_gen_change_state(bool enabled)\n--\nmm/vmscan.c-5521-\tif (enabled)\nmm/vmscan.c:5522:\t\tstatic_branch_enable_cpuslocked(\u0026lru_gen_caps[LRU_GEN_CORE]);\nmm/vmscan.c-5523-\telse\nmm/vmscan.c:5524:\t\tstatic_branch_disable_cpuslocked(\u0026lru_gen_caps[LRU_GEN_CORE]);\nmm/vmscan.c-5525-\n--\nmm/vmscan.c=5586=static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n--\nmm/vmscan.c-5589-\nmm/vmscan.c:5590:\tif (get_cap(LRU_GEN_CORE))\nmm/vmscan.c:5591:\t\tcaps |= BIT(LRU_GEN_CORE);\nmm/vmscan.c-5592-\nmm/vmscan.c-5593-\tif (should_walk_mmu())\nmm/vmscan.c:5594:\t\tcaps |= BIT(LRU_GEN_MM_WALK);\nmm/vmscan.c-5595-\nmm/vmscan.c-5596-\tif (should_clear_pmd_young())\nmm/vmscan.c:5597:\t\tcaps |= BIT(LRU_GEN_NONLEAF_YOUNG);\nmm/vmscan.c-5598-\n--\nmm/vmscan.c=5603=static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,\n--\nmm/vmscan.c-5615-\nmm/vmscan.c:5616:\tfor (i = 0; i \u003c NR_LRU_GEN_CAPS; i++) {\nmm/vmscan.c-5617-\t\tbool enabled = caps \u0026 BIT(i);\nmm/vmscan.c-5618-\nmm/vmscan.c:5619:\t\tif (i == LRU_GEN_CORE)\nmm/vmscan.c-5620-\t\t\tlru_gen_change_state(enabled);\n--\nmm/vmscan.c=6080=static int __init init_lru_gen(void)\n--\nmm/vmscan.c-6082-\tBUILD_BUG_ON(MIN_NR_GENS + 1 \u003e= MAX_NR_GENS);\nmm/vmscan.c:6083:\tBUILD_BUG_ON(BIT(LRU_GEN_WIDTH) \u003c= MAX_NR_GENS);\nmm/vmscan.c-6084-\n--\nmm/vmscan.c=6095=late_initcall(init_lru_gen);\nmm/vmscan.c-6096-\nmm/vmscan.c:6097:#else /* !CONFIG_LRU_GEN */\nmm/vmscan.c-6098-\n--\nmm/vmscan.c=6109=static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-6113-\nmm/vmscan.c:6114:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-6115-\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"
} |
| 5/2 | 2026/09/03 03:16 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10229 output: 33 thoughts: 18 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into Kconfig: A Targeted Inspection** Okay, time to get to the heart of the matter. My initial focus is on `mm/Kconfig`. I'm going to start by zeroing in around line 1403. This area likely deals with a specific memory management configuration option or a set of related options, given the context of the Kconfig file. I'm expecting to find either the definition of the option itself, its dependencies, default value assignments, or the help text that guides users. It's a targeted approach; I'm not doing a general survey. I have a hunch about what I am looking for so I am going to see if my hypotheses hold up when I inspect the code. I am probably looking for some specific setting that controls the underlying behavior. I am ready to dig deeper as needed. Let's see what we find. |
| 6/2 | 2026/09/03 03:17 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 1400,
"LineCount": 40
}Results: {
"Contents": "1400:\t\t purposes. It is required to enable userfaultfd write protection on\n1401:\t\t file-backed memory types like shmem and hugetlbfs.\n1402:\t\n1403:\t# multi-gen LRU {\n1404:\tconfig LRU_GEN\n1405:\t\tbool \"Multi-Gen LRU\"\n1406:\t\tdepends on MMU\n1407:\t\t# make sure folio-\u003eflags has enough spare bits\n1408:\t\tdepends on 64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP\n1409:\t\thelp\n1410:\t\t A high performance LRU implementation to overcommit memory. See\n1411:\t\t Documentation/admin-guide/mm/multigen_lru.rst for details.\n1412:\t\n1413:\tconfig LRU_GEN_ENABLED\n1414:\t\tbool \"Enable by default\"\n1415:\t\tdepends on LRU_GEN\n1416:\t\thelp\n1417:\t\t This option enables the multi-gen LRU by default.\n1418:\t\n1419:\tconfig LRU_GEN_STATS\n1420:\t\tbool \"Full stats for debugging\"\n1421:\t\tdepends on LRU_GEN\n1422:\t\thelp\n1423:\t\t Do not enable this option unless you plan to look at historical stats\n1424:\t\t from evicted generations for debugging purpose.\n1425:\t\n1426:\t\t This option has a per-memcg and per-node memory overhead.\n1427:\t\n1428:\tconfig LRU_GEN_WALKS_MMU\n1429:\t\tdef_bool y\n1430:\t\tdepends on LRU_GEN \u0026\u0026 ARCH_HAS_HW_PTE_YOUNG\n1431:\t# }\n1432:\t\n1433:\tconfig ARCH_SUPPORTS_PER_VMA_LOCK\n1434:\t def_bool n\n1435:\t\n1436:\tconfig PER_VMA_LOCK\n1437:\t\tdef_bool y\n1438:\t\tdepends on ARCH_SUPPORTS_PER_VMA_LOCK \u0026\u0026 MMU \u0026\u0026 SMP\n1439:\t\thelp\n"
} |
| 7/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 11067 output: 35 thoughts: 67 Thoughts: Here's my take on the situation: **Investigating the LRU Generation Configuration** Okay, so I'm looking at this `CONFIG_LRU_GEN` option, which I believe is the key here. Now, `CONFIG_LRU_GEN_ENABLED` seems to be the one that turns it on by default, correct? Right, got it. Time to dive into the code. I need to understand how this is actually implemented. I'll focus on the changes made within `mm/vmscan.c`. I need to read carefully around the lines that were modified, I want to get the context right. I will study those sections for now. That should give me a good understanding of what's happening. |
| 8/2 | 2026/09/03 03:17 | tool | read-file |
0mArgs:{
"File": "mm/vmscan.c",
"FirstLine": 4830,
"LineCount": 80
}Results: {
"Contents": "4830:\t\n4831:\tstatic int get_type_to_scan(struct lruvec *lruvec, int swappiness)\n4832:\t{\n4833:\t\tstruct ctrl_pos sp, pv = {};\n4834:\t\n4835:\t\tif (swappiness \u003c= MIN_SWAPPINESS + 1)\n4836:\t\t\treturn LRU_GEN_FILE;\n4837:\t\n4838:\t\tif (swappiness \u003e= MAX_SWAPPINESS)\n4839:\t\t\treturn LRU_GEN_ANON;\n4840:\t\t/*\n4841:\t\t * Compare the sum of all tiers of anon with that of file to determine\n4842:\t\t * which type to scan.\n4843:\t\t */\n4844:\t\tread_ctrl_pos(lruvec, LRU_GEN_ANON, MAX_NR_TIERS, swappiness, \u0026sp);\n4845:\t\tread_ctrl_pos(lruvec, LRU_GEN_FILE, MAX_NR_TIERS, MAX_SWAPPINESS - swappiness, \u0026pv);\n4846:\t\n4847:\t\treturn positive_ctrl_err(\u0026sp, \u0026pv);\n4848:\t}\n4849:\t\n4850:\t/*\n4851:\t * The MGLRU counterpart to too_many_isolated().\n4852:\t *\n4853:\t * too_many_isolated() compares node-wide isolated counts against\n4854:\t * node-wide inactive sizes. It predates per-memcg LRU lists, which\n4855:\t * moved isolation to per-lruvec granularity. MGLRU reclaimers contend\n4856:\t * per lruvec, so this check is per lruvec as well, and isolation in\n4857:\t * one memcg does not throttle reclaim in another.\n4858:\t *\n4859:\t * The legacy threshold, the size of the inactive list, is not usable\n4860:\t * here either: in MGLRU the inactive counters are only a compatibility\n4861:\t * shim - they jump around when swap runs out or a memcg hits its swap\n4862:\t * limit, and proactive aging can inflate them. Compare against the\n4863:\t * total number of evictable pages of the type divided by MIN_NR_GENS,\n4864:\t * approximating the size of one generation - the same unit the\n4865:\t * original MGLRU aging heuristics used.\n4866:\t */\n4867:\tstatic bool lru_gen_too_many_isolated(struct lruvec *lruvec, int type,\n4868:\t\t\t\t\t struct scan_control *sc)\n4869:\t{\n4870:\t\tunsigned long isolated, evictable;\n4871:\t\n4872:\t\tif (current_is_kswapd())\n4873:\t\t\treturn false;\n4874:\t\n4875:\t\tif (!writeback_throttling_sane(sc))\n4876:\t\t\treturn false;\n4877:\t\n4878:\t\tisolated = atomic_long_read(\u0026lruvec-\u003elrugen.nr_isolated[type]);\n4879:\t\tevictable = lruvec_type_evictable_size(lruvec, type);\n4880:\t\n4881:\t\t/*\n4882:\t\t * GFP_NOIO/GFP_NOFS callers are allowed to isolate more pages, so\n4883:\t\t * they won't be blocked by normal direct-reclaimers, forming a\n4884:\t\t * circular deadlock.\n4885:\t\t */\n4886:\t\tif (gfp_has_io_fs(sc-\u003egfp_mask))\n4887:\t\t\tevictable \u003e\u003e= 3;\n4888:\t\n4889:\t\treturn isolated \u003e evictable / MIN_NR_GENS;\n4890:\t}\n4891:\t\n4892:\t/*\n4893:\t * Unlike the legacy path, where the LRU list to isolate from is known\n4894:\t * before isolation, isolate_folios() picks the type from the refault\n4895:\t * feedback and may fall back to the other one. Therefore, instead of\n4896:\t * throttling on a single type, collect the evictable types that do not\n4897:\t * have too many isolated folios, and only sleep when all of them do.\n4898:\t *\n4899:\t * Returns the mask of the types isolate_folios() may isolate from, or\n4900:\t * 0 if reclaim should stop. Also sets @fatal to tell the caller that\n4901:\t * the task received a fatal signal while waiting.\n4902:\t */\n4903:\tstatic unsigned int throttle_evictable_types(struct lruvec *lruvec,\n4904:\t\t\t\t\t\t int swappiness,\n4905:\t\t\t\t\t\t struct scan_control *sc,\n4906:\t\t\t\t\t\t bool *fatal)\n4907:\t{\n4908:\t\tunsigned int allowed;\n4909:\t\tbool stalled = false;\n"
} |
| 9/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 12651 output: 30 thoughts: 44 Thoughts: Okay, let's dive into this. I need to understand the control flow surrounding these eviction functions. **Deconstructing Folio Eviction and Isolation** My initial thought is to trace the callers and the call stack related to `evict_folios`, `isolate_folios`, and related routines. Understanding where these functions are invoked is key to grasping their overall role in the memory management lifecycle. This is where I start. Then, I'm going to pinpoint the definition of `evict_folios`, so I will search in `mm/vmscan.c`. It's likely involved in the page/folio scanning and reclaiming process, so that location makes sense as a starting point. From there, I'll build a mental model of how `evict_folios` interacts with the surrounding code, and identify other points of interest. |
| 10/2 | 2026/09/03 03:17 | tool | grepper |
0mArgs:{
"Expression": "evict_folios",
"PathPrefix": "mm/vmscan.c"
}Results: {
"Output": "mm/vmscan.c=4956=static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4997-\nmm/vmscan.c:4998:static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\nmm/vmscan.c-4999-\t\t\tstruct scan_control *sc, int swappiness)\n--\nmm/vmscan.c=5199=static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5222-\t\tnr_batch = min(nr_to_scan, MIN_LRU_BATCH);\nmm/vmscan.c:5223:\t\tdelta = evict_folios(nr_batch, lruvec, sc, swappiness);\nmm/vmscan.c-5224-\t\tif (!delta)\n--\nmm/vmscan.c=5826=static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,\n--\nmm/vmscan.c-5846-\t\tnr_batch = min(nr_to_reclaim - sc-\u003enr_reclaimed, MAX_LRU_BATCH);\nmm/vmscan.c:5847:\t\tif (!evict_folios(nr_batch, lruvec, sc, swappiness))\nmm/vmscan.c-5848-\t\t\treturn 0;\n"
} |
| 11/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 13295 output: 60 thoughts: 28 Thoughts: Here's my take on those functions: **Digging into the Eviction Logic** Okay, so I'm thinking about `try_to_shrink_lruvec` and `run_eviction`. My first question is always: where are these things actually *used*? I need to understand the call graph to get a handle on their purpose within the bigger picture. I need to trace them through the code base and see the context in which these eviction and shrinking operations are actually triggered. Are they part of a scheduled background task? Are they in response to memory pressure? Are they reacting to specific events within the LRU cache itself? Knowing the call sites is the essential first step to understanding what's really going on with these functions. This is where I start to see the story. |
| 12/2 | 2026/09/03 03:17 | tool | grepper |
0mArgs:{
"Expression": "try_to_shrink_lruvec|run_eviction|lru_gen_shrink_lruvec|lru_gen_shrink_node",
"PathPrefix": "mm/vmscan.c"
}Results: {
"Output": "mm/vmscan.c=5164=static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5198- */\nmm/vmscan.c:5199:static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\nmm/vmscan.c-5200-{\n--\nmm/vmscan.c=5244=static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5263-\nmm/vmscan.c:5264:\tneed_rotate = try_to_shrink_lruvec(lruvec, sc);\nmm/vmscan.c-5265-\n--\nmm/vmscan.c=5285=static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5354-\nmm/vmscan.c:5355:static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\nmm/vmscan.c-5356-{\n--\nmm/vmscan.c-5367-\nmm/vmscan.c:5368:\tif (try_to_shrink_lruvec(lruvec, sc))\nmm/vmscan.c-5369-\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);\n--\nmm/vmscan.c-5375-\nmm/vmscan.c:5376:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-5377-{\n--\nmm/vmscan.c=5815=static int run_aging(struct lruvec *lruvec, unsigned long seq,\n--\nmm/vmscan.c-5825-\nmm/vmscan.c:5826:static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,\nmm/vmscan.c-5827-\t\t\tint swappiness, unsigned long nr_to_reclaim)\n--\nmm/vmscan.c=5856=static int run_cmd(char cmd, u64 memcg_id, int nid, unsigned long seq,\n--\nmm/vmscan.c-5887-\tcase '-':\nmm/vmscan.c:5888:\t\terr = run_eviction(lruvec, seq, sc, swappiness, opt);\nmm/vmscan.c-5889-\t\tbreak;\n--\nmm/vmscan.c=6099=static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-6103-\nmm/vmscan.c:6104:static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\nmm/vmscan.c-6105-{\n--\nmm/vmscan.c-6108-\nmm/vmscan.c:6109:static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-6110-{\n--\nmm/vmscan.c=6116=static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-6127-\tif ((lru_gen_enabled() || lru_gen_switching()) \u0026\u0026 !root_reclaim(sc)) {\nmm/vmscan.c:6128:\t\tlru_gen_shrink_lruvec(lruvec, sc);\nmm/vmscan.c-6129-\n--\nmm/vmscan.c=6386=static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-6393-\t\tmemset(\u0026sc-\u003enr, 0, sizeof(sc-\u003enr));\nmm/vmscan.c:6394:\t\tlru_gen_shrink_node(pgdat, sc);\nmm/vmscan.c-6395-\n"
} |
| 13/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 14666 output: 29 thoughts: 102 Thoughts: **My Initial Thoughts on Memory Reclamation** Okay, so I'm thinking about memory reclamation within the kernel, specifically the main paths. I know that `shrink_lruvec` and `shrink_node` are the key functions, touching on direct reclaim, kswapd, and memory cgroup (memcg) reclaim mechanisms. I need to refresh my memory on these and maybe focus on the newer parts in these paths. Beyond that, I remember the debugfs and sysfs interfaces, like the ones under `/sys/kernel/mm/lru_gen/`. Those can provide valuable insights, like through interfaces like `lru_gen_seq_write` and `run_cmd`. I should probably start by taking a closer look at `lru_gen_seq_write`, or maybe some of the other sysfs interfaces, to get a handle on what's going on. That should give me a better perspective. |
| 14/2 | 2026/09/03 03:17 | tool | grepper |
0mArgs:{
"Expression": "run_cmd",
"PathPrefix": "mm/vmscan.c"
}Results: {
"Output": "mm/vmscan.c=5826=static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc,\n--\nmm/vmscan.c-5855-\nmm/vmscan.c:5856:static int run_cmd(char cmd, u64 memcg_id, int nid, unsigned long seq,\nmm/vmscan.c-5857-\t\t struct scan_control *sc, int swappiness, unsigned long opt)\n--\nmm/vmscan.c=5898=static ssize_t lru_gen_seq_write(struct file *file, const char __user *src,\n--\nmm/vmscan.c-5966-\nmm/vmscan.c:5967:\t\terr = run_cmd(cmd, memcg_id, nid, seq, \u0026sc, swappiness, opt);\nmm/vmscan.c-5968-\t\tif (err)\n"
} |
| 15/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 15240 output: 28 thoughts: 271 Thoughts: **My Initial Assessment of the Patch's Fuzzing Potential** Okay, so this patch is touching some pretty critical stuff: the core kernel logic for MGLRU page reclamation and isolation throttling. Instantly, my expert brain screams, "Fuzz it!" Given that it's altering fundamental memory management, specifically within the MGLRU framework (introducing things like `lru_gen_too_many_isolated`, `throttle_evictable_types`, atomic counter manipulations, and retry/abort logic for signal handling), it’s practically begging for it. This is a prime target for uncovering subtle memory corruption or unexpected behavior. Now, let's get down to brass tacks: 1. `WorthFuzzing`: Definitely a resounding "yes". We're dealing with memory management at a fundamental level, so the potential impact of bugs here is huge. 2. `EnableConfigs`: I need to double-check the configuration requirements. Looks like `LRU_GEN` is essential, a bool dependent on `MMU`, `64BIT`, `!SPARSEMEM` or `SPARSEMEM_VMEMMAP`. And of course, `LRU_GEN_ENABLED`, which depends on `LRU_GEN`. I'll need to confirm that these two are sufficient or if there are other relevant configurations, such as `LRU_GEN_STATS` that I need to be aware of. I should also do a quick grep search for anything else related to `lru_gen` or `vmscan.c` to be completely thorough. I'll search the repo for `CONFIG_LRU_GEN` to be sure. |
| 16/2 | 2026/09/03 03:17 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_LRU_GEN",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/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=378=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-395-\nmm/folio.c:396:#else /* !CONFIG_LRU_GEN */\nmm/folio.c-397-\n--\nmm/folio.c=402=static bool lru_gen_clear_refs(struct folio *folio)\n--\nmm/folio.c-406-\nmm/folio.c:407:#endif /* CONFIG_LRU_GEN */\nmm/folio.c-408-\n--\nmm/memcontrol.c=4627=static void mem_cgroup_exit(struct task_struct *task)\n--\nmm/memcontrol.c-4643-\nmm/memcontrol.c:4644:#ifdef CONFIG_LRU_GEN\nmm/memcontrol.c-4645-static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset)\n--\nmm/memcontrol.c=4663=static void mem_cgroup_lru_gen_attach(struct cgroup_taskset *tset) {}\nmm/memcontrol.c:4664:#endif /* CONFIG_LRU_GEN */\nmm/memcontrol.c-4665-\n--\nmm/memory.c=6728=static inline void mm_account_fault(struct mm_struct *mm, struct pt_regs *regs,\n--\nmm/memory.c-6780-\nmm/memory.c:6781:#ifdef CONFIG_LRU_GEN\nmm/memory.c-6782-static void lru_gen_enter_fault(struct vm_area_struct *vma)\n--\nmm/memory.c=6797=static void lru_gen_exit_fault(void)\n--\nmm/memory.c-6799-}\nmm/memory.c:6800:#endif /* CONFIG_LRU_GEN */\nmm/memory.c-6801-\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=862=static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\n--\nmm/vmscan.c-865-}\nmm/vmscan.c:866:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-867-\n--\nmm/vmscan.c=2680=static bool can_age_anon_pages(struct lruvec *lruvec,\n--\nmm/vmscan.c-2691-\nmm/vmscan.c:2692:#ifdef CONFIG_LRU_GEN\nmm/vmscan.c-2693-\nmm/vmscan.c=2694=DEFINE_STATIC_KEY_FALSE(lru_switch);\nmm/vmscan.c:2695:#ifdef CONFIG_LRU_GEN_ENABLED\nmm/vmscan.c-2696-DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);\n--\nmm/vmscan.c=2880=static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\n--\nmm/vmscan.c-2899-\nmm/vmscan.c:2900:#ifdef CONFIG_LRU_GEN_WALKS_MMU\nmm/vmscan.c-2901-\n--\nmm/vmscan.c=3009=void lru_gen_migrate_mm(struct mm_struct *mm)\n--\nmm/vmscan.c-3037-\nmm/vmscan.c:3038:#else /* !CONFIG_LRU_GEN_WALKS_MMU */\nmm/vmscan.c-3039-\n--\nmm/vmscan.c=6095=late_initcall(init_lru_gen);\nmm/vmscan.c-6096-\nmm/vmscan.c:6097:#else /* !CONFIG_LRU_GEN */\nmm/vmscan.c-6098-\n--\nmm/vmscan.c=6109=static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-6113-\nmm/vmscan.c:6114:#endif /* CONFIG_LRU_GEN */\nmm/vmscan.c-6115-\n--\nmm/workingset.c=213=static void unpack_shadow(void *shadow, int *memcgidp, pg_data_t **pgdat,\n--\nmm/workingset.c-232-\nmm/workingset.c:233:#ifdef CONFIG_LRU_GEN\nmm/workingset.c-234-\n--\nmm/workingset.c=292=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-336-\nmm/workingset.c:337:#else /* !CONFIG_LRU_GEN */\nmm/workingset.c-338-\n--\nmm/workingset.c=350=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-353-\nmm/workingset.c:354:#endif /* CONFIG_LRU_GEN */\nmm/workingset.c-355-\n"
} |
| 17/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 17409 output: 31 thoughts: 257 Thoughts: **My Analysis of LRU Generation Fuzzing Targets** Okay, let's dive into this LRU generation fuzzing setup. I need to figure out the right symbols to focus on, keeping in mind the guidance: "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)." So, first things first, let's make sure `CONFIG_LRU_GEN` and `CONFIG_LRU_GEN_ENABLED` are set appropriately. Now, let's examine `FocusSymbols` to see what's already there and if I need to add anything. My attention is drawn to the `mm/vmscan.c` file since it seems relevant to this area of memory management. I see a bunch of functions in there that deal with isolation and LRU generation but it is important to remember what kind of functions are preferred to be targeted. I see `evict_folios`, `isolate_folios`, `lru_gen_too_many_isolated`, `throttle_evictable_types`, and `lruvec_type_evictable_size`, but all of these are static, so they're likely helper functions. `lru_gen_seq_write` is a file operations handler, which might be interesting if it exposes something useful. Let's dig deeper. The strategy here is clear: pinpoint the non-static, non-inlined entry points or API entry points. That means I need to carefully analyze *all* functions in `mm/vmscan.c` that relate to LRU_GEN or isolation to identify any viable candidates. |
| 18/2 | 2026/09/03 03:17 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_",
"PathPrefix": "mm/vmscan.c"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 734 lines.\nUse more precise expression if possible.\n\nmm/vmscan.c=827=enum folio_references {\n--\nmm/vmscan.c-839- */\nmm/vmscan.c:840:static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\nmm/vmscan.c-841-{\n--\nmm/vmscan.c-861-#else\nmm/vmscan.c:862:static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)\nmm/vmscan.c-863-{\n--\nmm/vmscan.c=868=static enum folio_references folio_check_references(struct folio *folio,\n--\nmm/vmscan.c-892-\nmm/vmscan.c:893:\tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching()) {\nmm/vmscan.c-894-\t\tif (!referenced_ptes)\n--\nmm/vmscan.c-896-\nmm/vmscan.c:897:\t\treturn lru_gen_set_refs(folio, \u0026vma_flags) ? FOLIOREF_ACTIVATE : FOLIOREF_KEEP;\nmm/vmscan.c-898-\t}\n--\nmm/vmscan.c=2280=static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-2286-\nmm/vmscan.c:2287:\tif (lru_gen_enabled() \u0026\u0026 !lru_gen_switching())\nmm/vmscan.c-2288-\t\treturn;\n--\nmm/vmscan.c=2694=DEFINE_STATIC_KEY_FALSE(lru_switch);\nmm/vmscan.c-2695-#ifdef CONFIG_LRU_GEN_ENABLED\nmm/vmscan.c:2696:DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS);\nmm/vmscan.c:2697:#define get_cap(cap)\tstatic_branch_likely(\u0026lru_gen_caps[cap])\nmm/vmscan.c-2698-#else\nmm/vmscan.c:2699:DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS);\nmm/vmscan.c:2700:#define get_cap(cap)\tstatic_branch_unlikely(\u0026lru_gen_caps[cap])\nmm/vmscan.c-2701-#endif\n--\nmm/vmscan.c=2788=static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)\n--\nmm/vmscan.c-2817- * walk_pmd_range(); the eviction also report them when walking the rmap\nmm/vmscan.c:2818: * in lru_gen_look_around().\nmm/vmscan.c-2819- *\n--\nmm/vmscan.c=2835=static void get_item_key(void *item, int *key)\n--\nmm/vmscan.c-2844-\nmm/vmscan.c:2845:static bool test_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq,\nmm/vmscan.c-2846-\t\t\t void *item)\n--\nmm/vmscan.c-2860-\nmm/vmscan.c:2861:static void update_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq,\nmm/vmscan.c-2862-\t\t\t\tvoid *item)\n--\nmm/vmscan.c-2879-\nmm/vmscan.c:2880:static void reset_bloom_filter(struct lru_gen_mm_state *mm_state, unsigned long seq)\nmm/vmscan.c-2881-{\n--\nmm/vmscan.c-2901-\nmm/vmscan.c:2902:static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg)\nmm/vmscan.c-2903-{\nmm/vmscan.c:2904:\tstatic struct lru_gen_mm_list mm_list = {\nmm/vmscan.c-2905-\t\t.fifo = LIST_HEAD_INIT(mm_list.fifo),\n--\nmm/vmscan.c-2917-\nmm/vmscan.c:2918:static struct lru_gen_mm_state *get_mm_state(struct lruvec *lruvec)\nmm/vmscan.c-2919-{\n--\nmm/vmscan.c-2922-\nmm/vmscan.c:2923:static struct mm_struct *get_next_mm(struct lru_gen_mm_walk *walk)\nmm/vmscan.c-2924-{\n--\nmm/vmscan.c-2927-\tstruct pglist_data *pgdat = lruvec_pgdat(walk-\u003elruvec);\nmm/vmscan.c:2928:\tstruct lru_gen_mm_state *mm_state = get_mm_state(walk-\u003elruvec);\nmm/vmscan.c-2929-\n--\nmm/vmscan.c-2941-\nmm/vmscan.c:2942:void lru_gen_add_mm(struct mm_struct *mm)\nmm/vmscan.c-2943-{\n--\nmm/vmscan.c-2945-\tstruct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm);\nmm/vmscan.c:2946:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c-2947-\n--\nmm/vmscan.c-2956-\t\tstruct lruvec *lruvec = get_lruvec(memcg, nid);\nmm/vmscan.c:2957:\t\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-2958-\n--\nmm/vmscan.c-2968-\nmm/vmscan.c:2969:void lru_gen_del_mm(struct mm_struct *mm)\nmm/vmscan.c-2970-{\nmm/vmscan.c-2971-\tint nid;\nmm/vmscan.c:2972:\tstruct lru_gen_mm_list *mm_list;\nmm/vmscan.c-2973-\tstruct mem_cgroup *memcg = NULL;\n--\nmm/vmscan.c-2986-\t\tstruct lruvec *lruvec = get_lruvec(memcg, nid);\nmm/vmscan.c:2987:\t\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-2988-\n--\nmm/vmscan.c-3008-#ifdef CONFIG_MEMCG\nmm/vmscan.c:3009:void lru_gen_migrate_mm(struct mm_struct *mm)\nmm/vmscan.c-3010-{\n--\nmm/vmscan.c-3032-\nmm/vmscan.c:3033:\tlru_gen_del_mm(mm);\nmm/vmscan.c:3034:\tlru_gen_add_mm(mm);\nmm/vmscan.c-3035-}\n--\nmm/vmscan.c-3039-\nmm/vmscan.c:3040:static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg)\nmm/vmscan.c-3041-{\n--\nmm/vmscan.c-3044-\nmm/vmscan.c:3045:static struct lru_gen_mm_state *get_mm_state(struct lruvec *lruvec)\nmm/vmscan.c-3046-{\n--\nmm/vmscan.c-3049-\nmm/vmscan.c:3050:static struct mm_struct *get_next_mm(struct lru_gen_mm_walk *walk)\nmm/vmscan.c-3051-{\n--\nmm/vmscan.c-3056-\nmm/vmscan.c:3057:static void reset_mm_stats(struct lru_gen_mm_walk *walk, bool last)\nmm/vmscan.c-3058-{\n--\nmm/vmscan.c-3061-\tstruct lruvec *lruvec = walk-\u003elruvec;\nmm/vmscan.c:3062:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-3063-\n--\nmm/vmscan.c-3081-\nmm/vmscan.c:3082:static bool iterate_mm_list(struct lru_gen_mm_walk *walk, struct mm_struct **iter)\nmm/vmscan.c-3083-{\n--\nmm/vmscan.c-3088-\tstruct mem_cgroup *memcg = lruvec_memcg(lruvec);\nmm/vmscan.c:3089:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c:3090:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-3091-\n--\nmm/vmscan.c=3146=static bool iterate_mm_list_nowalk(struct lruvec *lruvec, unsigned long seq)\n--\nmm/vmscan.c-3149-\tstruct mem_cgroup *memcg = lruvec_memcg(lruvec);\nmm/vmscan.c:3150:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c:3151:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-3152-\n--\nmm/vmscan.c=3198=static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,\n--\nmm/vmscan.c-3201-\tint i;\nmm/vmscan.c:3202:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3203-\tint hist = lru_hist_from_seq(lrugen-\u003emin_seq[type]);\n--\nmm/vmscan.c=3217=static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)\n--\nmm/vmscan.c-3219-\tint hist, tier;\nmm/vmscan.c:3220:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3221-\tbool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS \u003e 1;\n--\nmm/vmscan.c=3269=static int folio_update_gen(struct folio *folio, int gen, const vma_flags_t *vma_flags)\n--\nmm/vmscan.c-3286-\tdo {\nmm/vmscan.c:3287:\t\t/* lru_gen_del_folio() has isolated this page? */\nmm/vmscan.c-3288-\t\tif (!(old_flags \u0026 LRU_GEN_MASK))\n--\nmm/vmscan.c=3299=static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n--\nmm/vmscan.c-3301-\tint type = folio_is_file_lru(folio);\nmm/vmscan.c:3302:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c:3303:\tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c-3304-\tunsigned long new_flags, old_flags = READ_ONCE(folio-\u003eflags.f);\n--\nmm/vmscan.c-3319-\nmm/vmscan.c:3320:\tlru_gen_update_size(lruvec, folio, old_gen, new_gen);\nmm/vmscan.c-3321-\n--\nmm/vmscan.c-3324-\nmm/vmscan.c:3325:static void update_batch_size(struct lru_gen_mm_walk *walk, struct folio *folio,\nmm/vmscan.c-3326-\t\t\t int old_gen, int new_gen)\n--\nmm/vmscan.c-3340-\nmm/vmscan.c:3341:static void reset_batch_size(struct lru_gen_mm_walk *walk)\nmm/vmscan.c-3342-{\n--\nmm/vmscan.c-3344-\tstruct lruvec *lruvec = lruvec_live_lock_irq(walk-\u003elruvec);\nmm/vmscan.c:3345:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3346-\n--\nmm/vmscan.c-3359-\nmm/vmscan.c:3360:\t\tif (lru_gen_is_active(lruvec, gen))\nmm/vmscan.c-3361-\t\t\tlru += LRU_ACTIVE;\n--\nmm/vmscan.c=3368=static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *args)\n--\nmm/vmscan.c-3371-\tstruct vm_area_struct *vma = args-\u003evma;\nmm/vmscan.c:3372:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3373-\n--\nmm/vmscan.c=3506=static bool suitable_to_scan(int total, int young)\n--\nmm/vmscan.c-3513-\nmm/vmscan.c:3514:static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struct *vma,\nmm/vmscan.c-3515-\t\tstruct folio *folio, int new_gen, bool dirty)\n--\nmm/vmscan.c-3530-\t\t\tupdate_batch_size(walk, folio, old_gen, new_gen);\nmm/vmscan.c:3531:\t} else if (lru_gen_set_refs(folio, \u0026vma-\u003eflags)) {\nmm/vmscan.c-3532-\t\told_gen = folio_lru_gen(folio);\n--\nmm/vmscan.c=3538=static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end,\n--\nmm/vmscan.c-3548-\tstruct folio *last = NULL;\nmm/vmscan.c:3549:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3550-\tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n--\nmm/vmscan.c-3552-\tDEFINE_MAX_SEQ(walk-\u003elruvec);\nmm/vmscan.c:3553:\tint gen = lru_gen_from_seq(max_seq);\nmm/vmscan.c-3554-\tunsigned int nr;\n--\nmm/vmscan.c=3629=static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,\n--\nmm/vmscan.c-3636-\tstruct folio *last = NULL;\nmm/vmscan.c:3637:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3638-\tstruct mem_cgroup *memcg = lruvec_memcg(walk-\u003elruvec);\n--\nmm/vmscan.c-3640-\tDEFINE_MAX_SEQ(walk-\u003elruvec);\nmm/vmscan.c:3641:\tint gen = lru_gen_from_seq(max_seq);\nmm/vmscan.c-3642-\n--\nmm/vmscan.c=3717=static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end,\n--\nmm/vmscan.c-3726-\tunsigned long first = -1;\nmm/vmscan.c:3727:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c:3728:\tstruct lru_gen_mm_state *mm_state = get_mm_state(walk-\u003elruvec);\nmm/vmscan.c-3729-\n--\nmm/vmscan.c=3790=static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end,\n--\nmm/vmscan.c-3796-\tunsigned long next;\nmm/vmscan.c:3797:\tstruct lru_gen_mm_walk *walk = args-\u003eprivate;\nmm/vmscan.c-3798-\n--\nmm/vmscan.c-3831-\nmm/vmscan.c:3832:static void walk_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk)\nmm/vmscan.c-3833-{\n--\nmm/vmscan.c-3866-\nmm/vmscan.c:3867:static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat, bool force_alloc)\nmm/vmscan.c-3868-{\nmm/vmscan.c:3869:\tstruct lru_gen_mm_walk *walk = current-\u003ereclaim_state-\u003emm_walk;\nmm/vmscan.c-3870-\n--\nmm/vmscan.c=3887=static void clear_mm_walk(void)\nmm/vmscan.c-3888-{\nmm/vmscan.c:3889:\tstruct lru_gen_mm_walk *walk = current-\u003ereclaim_state-\u003emm_walk;\nmm/vmscan.c-3890-\n--\nmm/vmscan.c=3900=static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)\n--\nmm/vmscan.c-3903-\tint remaining = MAX_LRU_BATCH;\nmm/vmscan.c:3904:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3905-\tint hist = lru_hist_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c:3906:\tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c-3907-\n--\nmm/vmscan.c=3953=static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-3956-\tbool seq_inc_flag = false;\nmm/vmscan.c:3957:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3958-\tDEFINE_MIN_SEQ(lruvec);\n--\nmm/vmscan.c-3964-\t\twhile (min_seq[type] + MIN_NR_GENS \u003c= lrugen-\u003emax_seq) {\nmm/vmscan.c:3965:\t\t\tgen = lru_gen_from_seq(min_seq[type]);\nmm/vmscan.c-3966-\n--\nmm/vmscan.c-3985-\nmm/vmscan.c:3986:\t/* see the comment on lru_gen_folio */\nmm/vmscan.c-3987-\tif (swappiness \u0026\u0026 swappiness \u003c= MAX_SWAPPINESS) {\n--\nmm/vmscan.c=4005=static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness)\n--\nmm/vmscan.c-4009-\tint type, zone;\nmm/vmscan.c:4010:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4011-restart:\n--\nmm/vmscan.c-4040-\t */\nmm/vmscan.c:4041:\tprev = lru_gen_from_seq(lrugen-\u003emax_seq - 1);\nmm/vmscan.c:4042:\tnext = lru_gen_from_seq(lrugen-\u003emax_seq + 1);\nmm/vmscan.c-4043-\n--\nmm/vmscan.c=4070=static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long seq,\n--\nmm/vmscan.c-4073-\tbool success;\nmm/vmscan.c:4074:\tstruct lru_gen_mm_walk *walk;\nmm/vmscan.c-4075-\tstruct mm_struct *mm = NULL;\nmm/vmscan.c:4076:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c:4077:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-4078-\n--\nmm/vmscan.c-4089-\t * If the hardware doesn't automatically set the accessed bit, fallback\nmm/vmscan.c:4090:\t * to lru_gen_look_around(), which only clears the accessed bit in a\nmm/vmscan.c-4091-\t * handful of PTEs. Spreading the work out over a period of time usually\n--\nmm/vmscan.c=4154=static unsigned long lruvec_type_evictable_size(struct lruvec *lruvec, int type)\n--\nmm/vmscan.c-4157-\tunsigned long seq, total = 0;\nmm/vmscan.c:4158:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4159-\tDEFINE_MAX_SEQ(lruvec);\n--\nmm/vmscan.c-4162-\tfor (seq = min_seq[type]; seq \u003c= max_seq; seq++) {\nmm/vmscan.c:4163:\t\tgen = lru_gen_from_seq(seq);\nmm/vmscan.c-4164-\t\tfor (zone = 0; zone \u003c MAX_NR_ZONES; zone++)\n--\nmm/vmscan.c=4194=static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,\n--\nmm/vmscan.c-4208-\nmm/vmscan.c:4209:\tgen = lru_gen_from_seq(evictable_min_seq(min_seq, swappiness));\nmm/vmscan.c-4210-\tbirth = READ_ONCE(lruvec-\u003elrugen.timestamps[gen]);\n--\nmm/vmscan.c-4215-/* to protect the working set of the last N jiffies */\nmm/vmscan.c:4216:static unsigned long lru_gen_min_ttl __read_mostly;\nmm/vmscan.c-4217-\nmm/vmscan.c:4218:static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)\nmm/vmscan.c-4219-{\nmm/vmscan.c-4220-\tstruct mem_cgroup *memcg;\nmm/vmscan.c:4221:\tunsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);\nmm/vmscan.c-4222-\tbool reclaimable = !min_ttl;\n--\nmm/vmscan.c-4264- */\nmm/vmscan.c:4265:bool lru_gen_look_around(struct page_vma_mapped_walk *pvmw, unsigned int nr)\nmm/vmscan.c-4266-{\n--\nmm/vmscan.c-4270-\tunsigned long end;\nmm/vmscan.c:4271:\tstruct lru_gen_mm_walk *walk;\nmm/vmscan.c-4272-\tstruct folio *last = NULL;\n--\nmm/vmscan.c-4280-\tstruct lruvec *lruvec;\nmm/vmscan.c:4281:\tstruct lru_gen_mm_state *mm_state;\nmm/vmscan.c-4282-\tunsigned long max_seq;\n--\nmm/vmscan.c-4320-\tmax_seq = READ_ONCE((lruvec)-\u003elrugen.max_seq);\nmm/vmscan.c:4321:\tgen = lru_gen_from_seq(max_seq);\nmm/vmscan.c-4322-\tmm_state = get_mm_state(lruvec);\n--\nmm/vmscan.c=4383=enum {\n--\nmm/vmscan.c-4390-\nmm/vmscan.c:4391:static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)\nmm/vmscan.c-4392-{\n--\nmm/vmscan.c-4438-\nmm/vmscan.c:4439:void lru_gen_online_memcg(struct mem_cgroup *memcg)\nmm/vmscan.c-4440-{\n--\nmm/vmscan.c-4463-\nmm/vmscan.c:4464:void lru_gen_offline_memcg(struct mem_cgroup *memcg)\nmm/vmscan.c-4465-{\n--\nmm/vmscan.c-4470-\nmm/vmscan.c:4471:\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);\nmm/vmscan.c-4472-\t}\n--\nmm/vmscan.c-4474-\nmm/vmscan.c:4475:void lru_gen_release_memcg(struct mem_cgroup *memcg)\nmm/vmscan.c-4476-{\n--\nmm/vmscan.c-4500-\nmm/vmscan.c:4501:void lru_gen_soft_reclaim(struct mem_cgroup *memcg, int nid)\nmm/vmscan.c-4502-{\n--\nmm/vmscan.c-4506-\tif (READ_ONCE(lruvec-\u003elrugen.seg) != MEMCG_LRU_HEAD)\nmm/vmscan.c:4507:\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);\nmm/vmscan.c-4508-}\nmm/vmscan.c-4509-\nmm/vmscan.c:4510:bool recheck_lru_gen_max_memcg(struct mem_cgroup *memcg, int nid)\nmm/vmscan.c-4511-{\n--\nmm/vmscan.c=4523=static void try_to_inc_max_seq_nowalk(struct mem_cgroup *memcg,\n--\nmm/vmscan.c-4525-{\nmm/vmscan.c:4526:\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\nmm/vmscan.c:4527:\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\nmm/vmscan.c-4528-\tint swappiness = mem_cgroup_swappiness(memcg);\n--\nmm/vmscan.c-4556- */\nmm/vmscan.c:4557:void max_lru_gen_memcg(struct mem_cgroup *memcg, int nid)\nmm/vmscan.c-4558-{\n--\nmm/vmscan.c-4583- * generations on an LRU list.\nmm/vmscan.c:4584: * 4. In lru_gen_del_folio(), the generation to which the folio belongs is\nmm/vmscan.c-4585- * found based on the generation information in folio-\u003eflags, and the\n--\nmm/vmscan.c-4587- * the lru size correctly during reparenting, otherwise the lru size may\nmm/vmscan.c:4588: * be updated incorrectly in lru_gen_del_folio().\nmm/vmscan.c-4589- *\n--\nmm/vmscan.c-4597- */\nmm/vmscan.c:4598:static void __lru_gen_reparent_memcg(struct lruvec *child_lruvec, struct lruvec *parent_lruvec,\nmm/vmscan.c-4599-\t\t\t\t int zone, int type)\nmm/vmscan.c-4600-{\nmm/vmscan.c:4601:\tstruct lru_gen_folio *child_lrugen, *parent_lrugen;\nmm/vmscan.c-4602-\tenum lru_list lru = type * LRU_INACTIVE_FILE;\n--\nmm/vmscan.c-4608-\tfor (i = 0; i \u003c get_nr_gens(child_lruvec, type); i++) {\nmm/vmscan.c:4609:\t\tint gen = lru_gen_from_seq(child_lrugen-\u003emax_seq - i);\nmm/vmscan.c-4610-\t\tlong nr_pages = child_lrugen-\u003enr_pages[gen][type][zone];\nmm/vmscan.c:4611:\t\tint child_lru_active = lru_gen_is_active(child_lruvec, gen) ? LRU_ACTIVE : 0;\nmm/vmscan.c:4612:\t\tint parent_lru_active = lru_gen_is_active(parent_lruvec, gen) ? LRU_ACTIVE : 0;\nmm/vmscan.c-4613-\n--\nmm/vmscan.c-4621-\nmm/vmscan.c:4622:\t\tif (lru_gen_is_active(child_lruvec, gen) != lru_gen_is_active(parent_lruvec, gen)) {\nmm/vmscan.c-4623-\t\t\t__update_lru_size(child_lruvec, lru + child_lru_active, zone, -nr_pages);\n--\nmm/vmscan.c-4628-\nmm/vmscan.c:4629:void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int nid)\nmm/vmscan.c-4630-{\n--\nmm/vmscan.c-4640-\t\tfor (type = 0; type \u003c ANON_AND_FILE; type++)\nmm/vmscan.c:4641:\t\t\t__lru_gen_reparent_memcg(child_lruvec, parent_lruvec, zid, type);\nmm/vmscan.c-4642-\n--\nmm/vmscan.c=4663=static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc,\n--\nmm/vmscan.c-4672-\tint tier = lru_tier_from_refs(refs, workingset);\nmm/vmscan.c:4673:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4674-\n--\nmm/vmscan.c-4681-\t/* promoted */\nmm/vmscan.c:4682:\tif (gen != lru_gen_from_seq(lrugen-\u003emin_seq[type])) {\nmm/vmscan.c-4683-\t\tlist_move(\u0026folio-\u003elru, \u0026lrugen-\u003efolios[gen][type][zone]);\n--\nmm/vmscan.c=4712=static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc)\n--\nmm/vmscan.c-4729-\nmm/vmscan.c:4730:\tsuccess = lru_gen_del_folio(lruvec, folio, true);\nmm/vmscan.c-4731-\tVM_WARN_ON_ONCE_FOLIO(!success, folio);\n--\nmm/vmscan.c=4736=static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4747-\tunsigned long remaining = nr_to_scan;\nmm/vmscan.c:4748:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4749-\n--\nmm/vmscan.c-4755-\nmm/vmscan.c:4756:\tgen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\nmm/vmscan.c-4757-\n--\nmm/vmscan.c=4831=static int get_type_to_scan(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-4866- */\nmm/vmscan.c:4867:static bool lru_gen_too_many_isolated(struct lruvec *lruvec, int type,\nmm/vmscan.c-4868-\t\t\t\t struct scan_control *sc)\n--\nmm/vmscan.c=4903=static unsigned int throttle_evictable_types(struct lruvec *lruvec,\n--\nmm/vmscan.c-4926-\nmm/vmscan.c:4927:\t\t\tif (!lru_gen_too_many_isolated(lruvec, i, sc))\nmm/vmscan.c-4928-\t\t\t\tallowed |= BIT(i);\n--\nmm/vmscan.c=4998=static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-5006-\tstruct reclaim_stat stat;\nmm/vmscan.c:5007:\tstruct lru_gen_mm_walk *walk;\nmm/vmscan.c-5008-\tint scanned, reclaimed;\n--\nmm/vmscan.c-5078-\t\t/* don't add rejected folios to the oldest generation */\nmm/vmscan.c:5079:\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])\nmm/vmscan.c-5080-\t\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS, BIT(PG_active));\n--\nmm/vmscan.c=5244=static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)\n--\nmm/vmscan.c-5251-\nmm/vmscan.c:5252:\t/* lru_gen_age_node() called mem_cgroup_calculate_protection() */\nmm/vmscan.c-5253-\tif (mem_cgroup_below_min(NULL, memcg))\n--\nmm/vmscan.c=5285=static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5291-\tstruct lruvec *lruvec;\nmm/vmscan.c:5292:\tstruct lru_gen_folio *lrugen;\nmm/vmscan.c-5293-\tstruct mem_cgroup *memcg;\n--\nmm/vmscan.c-5305-\t\tif (op) {\nmm/vmscan.c:5306:\t\t\tlru_gen_rotate_memcg(lruvec, op);\nmm/vmscan.c-5307-\t\t\top = 0;\n--\nmm/vmscan.c-5319-\t\tif (!mem_cgroup_tryget(memcg)) {\nmm/vmscan.c:5320:\t\t\tlru_gen_release_memcg(memcg);\nmm/vmscan.c-5321-\t\t\tmemcg = NULL;\n--\nmm/vmscan.c-5337-\tif (op)\nmm/vmscan.c:5338:\t\tlru_gen_rotate_memcg(lruvec, op);\nmm/vmscan.c-5339-\n--\nmm/vmscan.c-5344-\nmm/vmscan.c:5345:\t/* restart if raced with lru_gen_rotate_memcg() */\nmm/vmscan.c-5346-\tif (gen != get_nulls_value(pos))\n--\nmm/vmscan.c-5354-\nmm/vmscan.c:5355:static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)\nmm/vmscan.c-5356-{\n--\nmm/vmscan.c-5368-\tif (try_to_shrink_lruvec(lruvec, sc))\nmm/vmscan.c:5369:\t\tlru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);\nmm/vmscan.c-5370-\n--\nmm/vmscan.c-5375-\n\n"
} |
| 19/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28015 output: 28 thoughts: 407 Thoughts: **Analyzing MGLRU Eviction Points for Fuzzing Focus** Okay, let's break this down from a fuzzing perspective. The goal is to identify prime candidates within the MGLRU eviction process for focused fuzzing. We're looking for functions that are likely entry points, aren't inlined (so they have distinct addresses for the fuzzer), and aren't overly generic hot-path functions. We want to avoid biasing the test distribution, so broad, common functions are out. I'm starting by looking at `lru_gen_seq_write`, since it's associated with the `/sys/kernel/mm/lru_gen/` sysfs interface or debugfs. It makes sense to check both this and `enabled_store`. I'm also looking at the functions in the `lru_gen` context, which include: `lru_gen_too_many_isolated`, `throttle_evictable_types`, `isolate_folios`, `evict_folios`, `lru_gen_shrink_lruvec`, `lru_gen_shrink_node`, and `lru_gen_seq_write`. The instructions I'm following highlight the importance of non-static, non-inlined API entrypoints. This tells me I should prioritize functions that are *not* static, as the compiler is likely to inline those which don't have distinct symbol addresses, and are external to the MGLRU code as entry points. Given this, I'm now cross-referencing this information with the functions I just listed, specifically those in `mm/vmscan.c`. The ones that jump out as potential targets for fuzzing, given these constraints are: `lru_gen_seq_write`, `lru_gen_shrink_node`, `lru_gen_shrink_lruvec`, `evict_folios`, `isolate_folios`, `throttle_evictable_types`, and `lru_gen_too_many_isolated`. I need to now determine if any are non-static and fit this criterion, and also whether any functions elsewhere that serve as entry points into MGLRU eviction are worth exploring. Looking at sysfs and procfs functions like `lru_gen_seq_write` is probably a good starting point for locating those entry points. |
| 20/2 | 2026/09/03 03:17 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_seq",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/vmscan.c=5638=static const struct attribute_group lru_gen_attr_group = {\n--\nmm/vmscan.c-5646-\nmm/vmscan.c:5647:static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos)\nmm/vmscan.c-5648-{\n--\nmm/vmscan.c-5668-\nmm/vmscan.c:5669:static void lru_gen_seq_stop(struct seq_file *m, void *v)\nmm/vmscan.c-5670-{\n--\nmm/vmscan.c-5677-\nmm/vmscan.c:5678:static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos)\nmm/vmscan.c-5679-{\n--\nmm/vmscan.c-5696-\nmm/vmscan.c:5697:static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec,\nmm/vmscan.c-5698-\t\t\t\t unsigned long max_seq, unsigned long *min_seq,\n--\nmm/vmscan.c-5751-/* see Documentation/admin-guide/mm/multigen_lru.rst for details */\nmm/vmscan.c:5752:static int lru_gen_seq_show(struct seq_file *m, void *v)\nmm/vmscan.c-5753-{\n--\nmm/vmscan.c-5801-\t\tif (full)\nmm/vmscan.c:5802:\t\t\tlru_gen_seq_show_full(m, lruvec, max_seq, min_seq, seq);\nmm/vmscan.c-5803-\t}\n--\nmm/vmscan.c-5807-\nmm/vmscan.c:5808:static const struct seq_operations lru_gen_seq_ops = {\nmm/vmscan.c:5809:\t.start = lru_gen_seq_start,\nmm/vmscan.c:5810:\t.stop = lru_gen_seq_stop,\nmm/vmscan.c:5811:\t.next = lru_gen_seq_next,\nmm/vmscan.c:5812:\t.show = lru_gen_seq_show,\nmm/vmscan.c-5813-};\n--\nmm/vmscan.c=5856=static int run_cmd(char cmd, u64 memcg_id, int nid, unsigned long seq,\n--\nmm/vmscan.c-5897-/* see Documentation/admin-guide/mm/multigen_lru.rst for details */\nmm/vmscan.c:5898:static ssize_t lru_gen_seq_write(struct file *file, const char __user *src,\nmm/vmscan.c-5899-\t\t\t\t size_t len, loff_t *pos)\n--\nmm/vmscan.c-5981-\nmm/vmscan.c:5982:static int lru_gen_seq_open(struct inode *inode, struct file *file)\nmm/vmscan.c-5983-{\nmm/vmscan.c:5984:\treturn seq_open(file, \u0026lru_gen_seq_ops);\nmm/vmscan.c-5985-}\n--\nmm/vmscan.c=5987=static const struct file_operations lru_gen_rw_fops = {\nmm/vmscan.c:5988:\t.open = lru_gen_seq_open,\nmm/vmscan.c-5989-\t.read = seq_read,\nmm/vmscan.c:5990:\t.write = lru_gen_seq_write,\nmm/vmscan.c-5991-\t.llseek = seq_lseek,\n--\nmm/vmscan.c=5995=static const struct file_operations lru_gen_ro_fops = {\nmm/vmscan.c:5996:\t.open = lru_gen_seq_open,\nmm/vmscan.c-5997-\t.read = seq_read,\n"
} |
| 21/2 | 2026/09/03 03:17 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 30047 output: 164 thoughts: 359 Thoughts: **My Assessment of the MGLRU Patch** Alright, let's break this down. First, I need to understand what this patch is actually trying to achieve. I see it's focusing on the Multi-Gen LRU (MGLRU) subsystem, and specifically mentions new tracking and throttling mechanisms for isolated pages. I'm taking a look at a few "FocusSymbols" which are: `throttle_evictable_types`, `lru_gen_too_many_isolated`, `isolate_folios`, `evict_folios`, and `lru_gen_seq_write`. This hints that the goal here is to prevent lockups and excessive isolation by throttling the reclaim process when too many folios are in an isolated state. So it looks like a new throttle has been added to the process. Now, I'll quickly check the "EnableConfigs". It's using `LRU_GEN` and `LRU_GEN_ENABLED`. A quick look at the Kconfig file confirms the dependencies: `LRU_GEN` relies on `MMU` and a few architecture-specific conditions, and `LRU_GEN_ENABLED` depends on `LRU_GEN`. Okay, that's straightforward. Before I sign off, I just need to summarize everything. The patch is deemed "WorthFuzzing" because it's altering crucial memory reclamation logic within the MGLRU. It's adding this new isolation tracking and throttling, specifically in `mm/vmscan.c`, to manage excessive folio isolation. The important FocusSymbols and EnableConfigs are: `throttle_evictable_types`, `lru_gen_too_many_isolated`, `isolate_folios`, `evict_folios`, and `lru_gen_seq_write`, and `LRU_GEN`, `LRU_GEN_ENABLED` respectively. Okay, that's everything. I'm confident in the assessment. I will set the results. |
| 22/2 | 2026/09/03 03:17 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"throttle_evictable_types",
"lru_gen_too_many_isolated",
"isolate_folios",
"evict_folios",
"lru_gen_seq_write"
],
"Reasoning": "The patch introduces isolated folio accounting and reclaim throttling for Multi-Gen LRU (MGLRU) in mm/vmscan.c, implementing lru_gen_too_many_isolated() and throttle_evictable_types() to prevent excessive folio isolation during memory pressure and direct reclaim. This is a functional change in core memory management logic.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"LRU_GEN",
"LRU_GEN_ENABLED"
],
"FocusSymbols": [
"throttle_evictable_types",
"lru_gen_too_many_isolated",
"isolate_folios",
"evict_folios",
"lru_gen_seq_write"
],
"Reasoning": "The patch introduces isolated folio accounting and reclaim throttling for Multi-Gen LRU (MGLRU) in mm/vmscan.c, implementing lru_gen_too_many_isolated() and throttle_evictable_types() to prevent excessive folio isolation during memory pressure and direct reclaim. This is a functional change in core memory management logic.",
"WorthFuzzing": true
} |
| 23/1 | 2026/09/03 03:17 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 24/2 | 2026/09/03 03:17 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch adds per-lruvec isolation accounting (`nr_isolated`) and isolation throttling (`lru_gen_too_many_isolated`, `throttle_evictable_types`) for Multi-Gen LRU (MGLRU) page reclaim.\n\nAnalysis against KMSAN applicability:\n1. User-space leaks: No data is copied to user space or exposed via ioctls/sysfs/netlink/BPF buffers.\n2. Uninitialized variables: All added local variables (`allowed`, `fatal`, `stalled`) and struct fields (`nr_isolated`) are properly initialized and managed (e.g., `*fatal` is initialized to false upon entering `throttle_evictable_types()`, `struct lruvec` memory is zero-allocated during node/memcg initialization).\n3. Buffer bounds / memory handling: The patch only adjusts reclaimer scheduling, throttling, and isolation counters without touching uninitialized buffer access, variable-length buffers, or struct padding.\n\nAny potential bugs introduced by this patch would relate to reclaim throttling, concurrency, locking, or counter imbalances, which are handled by standard kernel debugging tools (LOCKDEP, KASAN, WARN_ON). There is no risk of uninitialized memory usage or info-leaks, so a dedicated KMSAN fuzzing session is not warranted.",
"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 836d96433c0ed76d07112897bdf2664b1c287817
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Sep 3 03:16:46 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 94f9c3ff54160..819b6c059116e 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -580,6 +580,8 @@ struct lru_gen_folio {
/* can be modified without holding the LRU lock */
atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
+ /* the number of folios isolated from this lruvec and being reclaimed */
+ atomic_long_t nr_isolated[ANON_AND_FILE];
/* whether the multi-gen LRU is enabled */
bool enabled;
/* the memcg generation this lru_gen_folio belongs to */
diff --git a/mm/vmscan.c b/mm/vmscan.c
index f11491ee9ed5c..211839d10909a 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4151,25 +4151,34 @@ static void set_initial_priority(struct pglist_data *pgdat, struct scan_control
sc->priority = clamp(priority, DEF_PRIORITY / 2, DEF_PRIORITY);
}
-static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)
+static unsigned long lruvec_type_evictable_size(struct lruvec *lruvec, int type)
{
- int gen, type, zone;
+ int gen, zone;
unsigned long seq, total = 0;
struct lru_gen_folio *lrugen = &lruvec->lrugen;
DEFINE_MAX_SEQ(lruvec);
DEFINE_MIN_SEQ(lruvec);
- for_each_evictable_type(type, swappiness) {
- for (seq = min_seq[type]; seq <= max_seq; seq++) {
- gen = lru_gen_from_seq(seq);
- for (zone = 0; zone < MAX_NR_ZONES; zone++)
- total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
- }
+ for (seq = min_seq[type]; seq <= max_seq; seq++) {
+ gen = lru_gen_from_seq(seq);
+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
+ total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
}
return total;
}
+static unsigned long lruvec_evictable_size(struct lruvec *lruvec, int swappiness)
+{
+ unsigned long total = 0;
+ int type;
+
+ for_each_evictable_type(type, swappiness)
+ total += lruvec_type_evictable_size(lruvec, type);
+
+ return total;
+}
+
static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
{
unsigned long total;
@@ -4838,15 +4847,128 @@ static int get_type_to_scan(struct lruvec *lruvec, int swappiness)
return positive_ctrl_err(&sp, &pv);
}
+/*
+ * The MGLRU counterpart to too_many_isolated().
+ *
+ * too_many_isolated() compares node-wide isolated counts against
+ * node-wide inactive sizes. It predates per-memcg LRU lists, which
+ * moved isolation to per-lruvec granularity. MGLRU reclaimers contend
+ * per lruvec, so this check is per lruvec as well, and isolation in
+ * one memcg does not throttle reclaim in another.
+ *
+ * The legacy threshold, the size of the inactive list, is not usable
+ * here either: in MGLRU the inactive counters are only a compatibility
+ * shim - they jump around when swap runs out or a memcg hits its swap
+ * limit, and proactive aging can inflate them. Compare against the
+ * total number of evictable pages of the type divided by MIN_NR_GENS,
+ * approximating the size of one generation - the same unit the
+ * original MGLRU aging heuristics used.
+ */
+static bool lru_gen_too_many_isolated(struct lruvec *lruvec, int type,
+ struct scan_control *sc)
+{
+ unsigned long isolated, evictable;
+
+ if (current_is_kswapd())
+ return false;
+
+ if (!writeback_throttling_sane(sc))
+ return false;
+
+ isolated = atomic_long_read(&lruvec->lrugen.nr_isolated[type]);
+ evictable = lruvec_type_evictable_size(lruvec, type);
+
+ /*
+ * GFP_NOIO/GFP_NOFS callers are allowed to isolate more pages, so
+ * they won't be blocked by normal direct-reclaimers, forming a
+ * circular deadlock.
+ */
+ if (gfp_has_io_fs(sc->gfp_mask))
+ evictable >>= 3;
+
+ return isolated > evictable / MIN_NR_GENS;
+}
+
+/*
+ * Unlike the legacy path, where the LRU list to isolate from is known
+ * before isolation, isolate_folios() picks the type from the refault
+ * feedback and may fall back to the other one. Therefore, instead of
+ * throttling on a single type, collect the evictable types that do not
+ * have too many isolated folios, and only sleep when all of them do.
+ *
+ * Returns the mask of the types isolate_folios() may isolate from, or
+ * 0 if reclaim should stop. Also sets @fatal to tell the caller that
+ * the task received a fatal signal while waiting.
+ */
+static unsigned int throttle_evictable_types(struct lruvec *lruvec,
+ int swappiness,
+ struct scan_control *sc,
+ bool *fatal)
+{
+ unsigned int allowed;
+ bool stalled = false;
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+ int i;
+
+ *fatal = false;
+
+ for (;;) {
+ allowed = 0;
+ for_each_evictable_type(i, swappiness) {
+ /*
+ * A type with no evictable folios has nothing to
+ * isolate. Don't allow it, otherwise reclaim can be
+ * redirected to it and spin making no progress, e.g.
+ * when the only type with folios is over-isolated.
+ */
+ if (!lruvec_type_evictable_size(lruvec, i))
+ continue;
+
+ if (!lru_gen_too_many_isolated(lruvec, i, sc))
+ allowed |= BIT(i);
+ }
+
+ if (allowed) {
+ /* Wake up reclaimers waiting on the isolation to go down. */
+ wake_throttle_isolated(pgdat);
+ return allowed;
+ }
+
+ /*
+ * All evictable types are over-isolated. Like the legacy
+ * path, wait once for concurrent reclaimers to put their
+ * isolated folios back; give up if that makes no progress.
+ */
+ if (stalled)
+ return 0;
+
+ stalled = true;
+ reclaim_throttle(pgdat, VMSCAN_THROTTLE_ISOLATED);
+
+ /* We are about to die and free our memory. Return now. */
+ if (fatal_signal_pending(current)) {
+ *fatal = true;
+ return 0;
+ }
+ }
+}
+
static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
struct scan_control *sc, int swappiness,
- struct list_head *list, int *isolated,
- int *isolate_type, int *isolate_scanned)
+ unsigned int allowed, struct list_head *list,
+ int *isolated, int *isolate_type, int *isolate_scanned)
{
int i;
int total_scanned = 0;
int type = get_type_to_scan(lruvec, swappiness);
+ /*
+ * The preferred type may have been excluded by
+ * throttle_evictable_types(); start from the other one.
+ */
+ if (!(allowed & BIT(type)))
+ type = !type;
+
for_each_evictable_type(i, swappiness) {
int scanned;
int tier = get_tier_idx(lruvec, type);
@@ -4863,9 +4985,10 @@ static int isolate_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
/*
* If scanned > 0 and isolated == 0, avoid falling back to the
* other type, as this type remains sufficient. Falling back
- * too readily can disrupt the positive_ctrl_err() bias.
+ * too readily can disrupt the positive_ctrl_err() bias. Only
+ * fall back to a type that is not throttled.
*/
- if (!scanned)
+ if (!scanned && (allowed & BIT(!type)))
type = !type;
}
@@ -4888,15 +5011,36 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
bool skip_retry = false;
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
+ unsigned int allowed;
+ bool fatal;
+
+ allowed = throttle_evictable_types(lruvec, swappiness, sc, &fatal);
+ if (!allowed) {
+ /*
+ * We are about to die and free our memory. Like the legacy
+ * path, pretend some pages were reclaimed so reclaim
+ * unwinds quickly instead of looping back into the
+ * throttle.
+ */
+ if (fatal)
+ sc->nr_reclaimed += SWAP_CLUSTER_MAX;
+
+ return 0;
+ }
lruvec_lock_irq(lruvec);
/* In case folio deletion left empty old gens, flush them */
try_to_inc_min_seq(lruvec, swappiness);
- scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,
+ scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness, allowed,
&list, &isolated, &type, &type_scanned);
nr_isolated = isolated;
+ if (nr_isolated) {
+ __mod_node_page_state(pgdat, NR_ISOLATED_ANON + type,
+ nr_isolated);
+ atomic_long_add(nr_isolated, &lruvec->lrugen.nr_isolated[type]);
+ }
/* Scanning may have emptied the oldest gen, flush it */
if (scanned)
@@ -4959,6 +5103,9 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
goto retry;
}
+ atomic_long_sub(nr_isolated, &lruvec->lrugen.nr_isolated[type]);
+ mod_node_page_state(pgdat, NR_ISOLATED_ANON + type, -nr_isolated);
+
if (nr_isolated > total_reclaimed)
mod_lruvec_state(lruvec, PGROTATE_ANON + type,
nr_isolated - total_reclaimed);
|
| 25/3 | 2026/09/03 03:17 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 5048 output: 20 thoughts: 219 Thoughts: **KMSAN Fuzzing Session Assessment** Okay, let's break down this patch and figure out if we need to throw a KMSAN fuzzing session at it. The changes seem to be in `include/linux/mmzone.h` and `mm/vmscan.c`, which immediately flags my attention. Anything touching memory management and particularly these files is always a potential hot spot. The core changes look to be: 1. Adding `atomic_long_t nr_isolated[ANON_AND_FILE];` to `struct lru_gen_folio`. This is interesting; tracking isolated folios at a `lru_gen_folio` level indicates fine-grained control over folio isolation within LRU lists. 2. Updates to `lruvec_type_evictable_size` and `lruvec_evictable_size`, which is expected to go along with managing these isolated folios. 3. New functions: `lru_gen_too_many_isolated()` and `throttle_evictable_types()` in `mm/vmscan.c`. These imply a mechanism for throttling or limiting folio isolation based on some criteria, and that’s a signal to pay close attention. 4. Modifications to `isolate_folios()` and `evict_folios()` to track and manage the folio counts within the lruvec. Before I go further, let's clarify that `struct lru_gen_folio` is properly initialized. Where is this struct initialized? I'm going to quickly search the codebase for where `lrugen` or `struct lru_gen_folio` is created to get a clear picture of how it's used. This will help determine if the new `nr_isolated` member is being correctly set up and managed. |
| 26/3 | 2026/09/03 03:18 | tool | grepper |
0mArgs:{
"Expression": "lru_gen_folio"
}Results: {
"Output": "include/linux/mm_inline.h=175=static inline void lru_gen_update_size(struct lruvec *lruvec, struct folio *folio,\n--\ninclude/linux/mm_inline.h-181-\tenum lru_list lru = type * LRU_INACTIVE_FILE;\ninclude/linux/mm_inline.h:182:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\ninclude/linux/mm_inline.h-183-\n--\ninclude/linux/mm_inline.h-220-\ninclude/linux/mm_inline.h:221:static inline unsigned long lru_gen_folio_seq(const struct lruvec *lruvec,\ninclude/linux/mm_inline.h-222-\t\t\t\t\t const struct folio *folio,\n--\ninclude/linux/mm_inline.h-226-\tint type = folio_is_file_lru(folio);\ninclude/linux/mm_inline.h:227:\tconst struct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\ninclude/linux/mm_inline.h-228-\n--\ninclude/linux/mm_inline.h=255=static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)\n--\ninclude/linux/mm_inline.h-261-\tint zone = folio_zonenum(folio);\ninclude/linux/mm_inline.h:262:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\ninclude/linux/mm_inline.h-263-\n--\ninclude/linux/mm_inline.h-268-\ninclude/linux/mm_inline.h:269:\tseq = lru_gen_folio_seq(lruvec, folio, reclaiming);\ninclude/linux/mm_inline.h-270-\tgen = lru_gen_from_seq(seq);\n--\ninclude/linux/mmzone.h=534=enum {\n--\ninclude/linux/mmzone.h-562- */\ninclude/linux/mmzone.h:563:struct lru_gen_folio {\ninclude/linux/mmzone.h-564-\t/* the aging increments the youngest generation number */\n--\ninclude/linux/mmzone.h-586-\tbool enabled;\ninclude/linux/mmzone.h:587:\t/* the memcg generation this lru_gen_folio belongs to */\ninclude/linux/mmzone.h-588-\tu8 gen;\ninclude/linux/mmzone.h:589:\t/* the list segment this lru_gen_folio belongs to */\ninclude/linux/mmzone.h-590-\tu8 seg;\ninclude/linux/mmzone.h:591:\t/* per-node lru_gen_folio list for global reclaim */\ninclude/linux/mmzone.h-592-\tstruct hlist_nulls_node list;\n--\ninclude/linux/mmzone.h=619=struct lru_gen_mm_walk {\n--\ninclude/linux/mmzone.h-621-\tstruct lruvec *lruvec;\ninclude/linux/mmzone.h:622:\t/* max_seq from lru_gen_folio: can be out of date */\ninclude/linux/mmzone.h-623-\tunsigned long seq;\n--\ninclude/linux/mmzone.h=681=struct lru_gen_memcg {\n--\ninclude/linux/mmzone.h-683-\tunsigned long seq;\ninclude/linux/mmzone.h:684:\t/* each memcg has one lru_gen_folio per node */\ninclude/linux/mmzone.h-685-\tunsigned long nr_memcgs[MEMCG_NR_GENS];\ninclude/linux/mmzone.h:686:\t/* per-node lru_gen_folio list for global reclaim */\ninclude/linux/mmzone.h-687-\tstruct hlist_nulls_head\tfifo[MEMCG_NR_GENS][MEMCG_NR_BINS];\n--\ninclude/linux/mmzone.h=768=struct lruvec {\n--\ninclude/linux/mmzone.h-789-\t/* evictable pages divided into generations */\ninclude/linux/mmzone.h:790:\tstruct lru_gen_folio\t\tlrugen;\ninclude/linux/mmzone.h-791-#ifdef CONFIG_LRU_GEN_WALKS_MMU\n--\ninclude/linux/mmzone.h=1480=typedef struct pglist_data {\n--\ninclude/linux/mmzone.h-1600-\tstruct lru_gen_mm_walk mm_walk;\ninclude/linux/mmzone.h:1601:\t/* lru_gen_folio list */\ninclude/linux/mmzone.h-1602-\tstruct lru_gen_memcg memcg_lru;\n--\nmm/folio.c=467=void folio_add_lru(struct folio *folio)\n--\nmm/folio.c-476-\t * For prefaulted file folios, folio_mark_accessed() sets\nmm/folio.c:477:\t * PG_referenced so lru_gen_folio_seq() places them into\nmm/folio.c-478-\t * the second oldest generation.\n--\nmm/vmscan.c=3198=static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,\n--\nmm/vmscan.c-3201-\tint i;\nmm/vmscan.c:3202:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3203-\tint hist = lru_hist_from_seq(lrugen-\u003emin_seq[type]);\n--\nmm/vmscan.c=3217=static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)\n--\nmm/vmscan.c-3219-\tint hist, tier;\nmm/vmscan.c:3220:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3221-\tbool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS \u003e 1;\n--\nmm/vmscan.c=3299=static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)\n--\nmm/vmscan.c-3301-\tint type = folio_is_file_lru(folio);\nmm/vmscan.c:3302:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3303-\tint new_gen, old_gen = lru_gen_from_seq(lrugen-\u003emin_seq[type]);\n--\nmm/vmscan.c=3341=static void reset_batch_size(struct lru_gen_mm_walk *walk)\n--\nmm/vmscan.c-3344-\tstruct lruvec *lruvec = lruvec_live_lock_irq(walk-\u003elruvec);\nmm/vmscan.c:3345:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3346-\n--\nmm/vmscan.c=3900=static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)\n--\nmm/vmscan.c-3903-\tint remaining = MAX_LRU_BATCH;\nmm/vmscan.c:3904:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3905-\tint hist = lru_hist_from_seq(lrugen-\u003emin_seq[type]);\n--\nmm/vmscan.c=3953=static void try_to_inc_min_seq(struct lruvec *lruvec, int swappiness)\n--\nmm/vmscan.c-3956-\tbool seq_inc_flag = false;\nmm/vmscan.c:3957:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-3958-\tDEFINE_MIN_SEQ(lruvec);\n--\nmm/vmscan.c-3985-\nmm/vmscan.c:3986:\t/* see the comment on lru_gen_folio */\nmm/vmscan.c-3987-\tif (swappiness \u0026\u0026 swappiness \u003c= MAX_SWAPPINESS) {\n--\nmm/vmscan.c=4005=static bool inc_max_seq(struct lruvec *lruvec, unsigned long seq, int swappiness)\n--\nmm/vmscan.c-4009-\tint type, zone;\nmm/vmscan.c:4010:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4011-restart:\n--\nmm/vmscan.c=4070=static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long seq,\n--\nmm/vmscan.c-4075-\tstruct mm_struct *mm = NULL;\nmm/vmscan.c:4076:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4077-\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\n--\nmm/vmscan.c=4154=static unsigned long lruvec_type_evictable_size(struct lruvec *lruvec, int type)\n--\nmm/vmscan.c-4157-\tunsigned long seq, total = 0;\nmm/vmscan.c:4158:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4159-\tDEFINE_MAX_SEQ(lruvec);\n--\nmm/vmscan.c=4598=static void __lru_gen_reparent_memcg(struct lruvec *child_lruvec, struct lruvec *parent_lruvec,\n--\nmm/vmscan.c-4600-{\nmm/vmscan.c:4601:\tstruct lru_gen_folio *child_lrugen, *parent_lrugen;\nmm/vmscan.c-4602-\tenum lru_list lru = type * LRU_INACTIVE_FILE;\n--\nmm/vmscan.c=4663=static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_control *sc,\n--\nmm/vmscan.c-4672-\tint tier = lru_tier_from_refs(refs, workingset);\nmm/vmscan.c:4673:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4674-\n--\nmm/vmscan.c=4736=static int scan_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-4747-\tunsigned long remaining = nr_to_scan;\nmm/vmscan.c:4748:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-4749-\n--\nmm/vmscan.c=4998=static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,\n--\nmm/vmscan.c-5078-\t\t/* don't add rejected folios to the oldest generation */\nmm/vmscan.c:5079:\t\tif (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])\nmm/vmscan.c-5080-\t\t\tset_mask_bits(\u0026folio-\u003eflags.f, LRU_REFS_FLAGS, BIT(PG_active));\n--\nmm/vmscan.c=5285=static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)\n--\nmm/vmscan.c-5291-\tstruct lruvec *lruvec;\nmm/vmscan.c:5292:\tstruct lru_gen_folio *lrugen;\nmm/vmscan.c-5293-\tstruct mem_cgroup *memcg;\n--\nmm/vmscan.c=5422=static bool __maybe_unused state_is_valid(struct lruvec *lruvec)\nmm/vmscan.c-5423-{\nmm/vmscan.c:5424:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-5425-\n--\nmm/vmscan.c=5697=static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec,\n--\nmm/vmscan.c-5703-\tint hist = lru_hist_from_seq(seq);\nmm/vmscan.c:5704:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-5705-\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\n--\nmm/vmscan.c=5752=static int lru_gen_seq_show(struct seq_file *m, void *v)\n--\nmm/vmscan.c-5756-\tstruct lruvec *lruvec = v;\nmm/vmscan.c:5757:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-5758-\tint nid = lruvec_pgdat(lruvec)-\u003enode_id;\n--\nmm/vmscan.c=6018=void lru_gen_init_lruvec(struct lruvec *lruvec)\n--\nmm/vmscan.c-6021-\tint gen, type, zone;\nmm/vmscan.c:6022:\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\nmm/vmscan.c-6023-\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\n--\nmm/workingset.c=235=static void *lru_gen_eviction(struct folio *folio)\n--\nmm/workingset.c-240-\tstruct lruvec *lruvec;\nmm/workingset.c:241:\tstruct lru_gen_folio *lrugen;\nmm/workingset.c-242-\tint type = folio_is_file_lru(folio);\n--\nmm/workingset.c=292=static void lru_gen_refault(struct folio *folio, void *shadow)\n--\nmm/workingset.c-298-\tstruct lruvec *lruvec;\nmm/workingset.c:299:\tstruct lru_gen_folio *lrugen;\nmm/workingset.c-300-\tint type = folio_is_file_lru(folio);\n"
} |
| 27/3 | 2026/09/03 03:18 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9638 output: 35 thoughts: 16 Thoughts: Okay, here's my attempt at summarizing the thought process, as if I were a seasoned professional in the field, starting with the requested bolded header: **Diving into `lru_gen_init_lruvec`** Alright, time to dig into this `lru_gen_init_lruvec` function. I need to refresh my understanding of how this specific LRU vector initialization is handled within the larger system. I'm assuming this is a core component, so a deep understanding is crucial. I want to make sure I grasp any subtle optimizations, potential pitfalls, or design choices that are at play here. Let's see how they're handling the initial allocation, what parameters it accepts, and how it interacts with the garbage collection and eviction mechanisms. My main concern is to understand its impact on overall performance and how robust this initialization process is. I'm looking for a clear description of all the steps, any assumptions made, and if there are any error conditions handled. Time to delve into the code! |
| 28/3 | 2026/09/03 03:18 | tool | read-file |
0mArgs:{
"File": "mm/vmscan.c",
"FirstLine": 6018,
"LineCount": 40
}Results: {
"Contents": "6018:\tvoid lru_gen_init_lruvec(struct lruvec *lruvec)\n6019:\t{\n6020:\t\tint i;\n6021:\t\tint gen, type, zone;\n6022:\t\tstruct lru_gen_folio *lrugen = \u0026lruvec-\u003elrugen;\n6023:\t\tstruct lru_gen_mm_state *mm_state = get_mm_state(lruvec);\n6024:\t\n6025:\t\tlrugen-\u003emax_seq = MIN_NR_GENS + 1;\n6026:\t\tlrugen-\u003eenabled = lru_gen_enabled();\n6027:\t\n6028:\t\tfor (i = 0; i \u003c= MIN_NR_GENS + 1; i++)\n6029:\t\t\tlrugen-\u003etimestamps[i] = jiffies;\n6030:\t\n6031:\t\tfor_each_gen_type_zone(gen, type, zone)\n6032:\t\t\tINIT_LIST_HEAD(\u0026lrugen-\u003efolios[gen][type][zone]);\n6033:\t\n6034:\t\tif (mm_state)\n6035:\t\t\tmm_state-\u003eseq = MIN_NR_GENS;\n6036:\t}\n6037:\t\n6038:\t#ifdef CONFIG_MEMCG\n6039:\t\n6040:\tvoid lru_gen_init_memcg(struct mem_cgroup *memcg)\n6041:\t{\n6042:\t\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\n6043:\t\n6044:\t\tif (!mm_list)\n6045:\t\t\treturn;\n6046:\t\n6047:\t\tINIT_LIST_HEAD(\u0026mm_list-\u003efifo);\n6048:\t\tspin_lock_init(\u0026mm_list-\u003elock);\n6049:\t}\n6050:\t\n6051:\tvoid lru_gen_exit_memcg(struct mem_cgroup *memcg)\n6052:\t{\n6053:\t\tint i;\n6054:\t\tint nid;\n6055:\t\tstruct lru_gen_mm_list *mm_list = get_mm_list(memcg);\n6056:\t\n6057:\t\tVM_WARN_ON_ONCE(mm_list \u0026\u0026 !list_empty(\u0026mm_list-\u003efifo));\n"
} |
| 29/3 | 2026/09/03 03:18 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10551 output: 303 thoughts: 602 Thoughts: **My Assessment of the KMSAN Need in This MGLRU Patch** Alright, let's break this down. My analysis focuses on whether this MGLRU patch introducing changes in page reclaim and throttling poses a risk related to uninitialized memory, thus necessitating KMSAN (Kernel Memory Sanitizer) checks. I've gone through the code and the relevant data structures with a fine-tooth comb. First, I confirm that the `struct lruvec` is initialized correctly, either within `struct pglist_data` (statically or during early boot) or within `struct mem_cgroup_per_node` via `kzalloc`. This ensures that fields are either zeroed or otherwise safely initialized. I also noted that atomic long types, like `evicted` and `refaulted` within `struct lru_gen_folio` are also zero-initialized by default. Then I examined the critical functions, specifically `throttle_evictable_types` and `evict_folios`, focusing on variable initialization. In `throttle_evictable_types`, the code passes a pointer to `bool *fatal` and initializes it immediately with `*fatal = false;`. A local `bool stalled = false;` is also initialized. In `evict_folios`, the `unsigned int allowed` variable is initialized by return from `throttle_evictable_types`. This is important, making it so that `fatal` and `allowed` are properly set. If `!allowed` then a condition executes where `sc->nr_reclaimed += SWAP_CLUSTER_MAX;` . The core concern here is uninitialized variables, potential memory leaks, and any chance of passing potentially uninitialized memory into user space. I can confirm that all variables, like `allowed` and `fatal`, are initialized before use, and there are no instances of user-space copies (`copy_to_user`, syscall return, etc.) that would expose uninitialized data. Furthermore, I see no risky stack/heap buffer manipulations that could leave memory uninitialized. Now, let's step through the KMSAN criteria. 1. **Kernel structures sent to userspace:** Nope. Nothing here that's leaking uninitialized data to userspace. 2. **Conditional logic based on potentially uninitialized variables:** No. All branching decisions rely on variables that are explicitly initialized before use, and all fields are safely zero-allocated or otherwise initialized. 3. **Complex data structure initialization with uninitialized fields used later:** Not here. The structures and their fields are properly set up. 4. **Bounds checks exposing uninitialized data:** Not applicable. There's no manipulation of existing buffers that could lead to uninitialized memory exposure through bounds issues. Given this thorough review against the NeedsKMSAN criteria, my conclusion is that **KMSAN is not needed here.** Standard tools like KASAN, LOCKDEP, and other bug detectors would be the appropriate tools to check for concurrency/locking/reference/memory safety problems. The risks of uninitialized memory are minimal in this patch. |
| 30/3 | 2026/09/03 03:18 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch adds per-lruvec isolation accounting (`nr_isolated`) and isolation throttling (`lru_gen_too_many_isolated`, `throttle_evictable_types`) for Multi-Gen LRU (MGLRU) page reclaim.\n\nAnalysis against KMSAN applicability:\n1. User-space leaks: No data is copied to user space or exposed via ioctls/sysfs/netlink/BPF buffers.\n2. Uninitialized variables: All added local variables (`allowed`, `fatal`, `stalled`) and struct fields (`nr_isolated`) are properly initialized and managed (e.g., `*fatal` is initialized to false upon entering `throttle_evictable_types()`, `struct lruvec` memory is zero-allocated during node/memcg initialization).\n3. Buffer bounds / memory handling: The patch only adjusts reclaimer scheduling, throttling, and isolation counters without touching uninitialized buffer access, variable-length buffers, or struct padding.\n\nAny potential bugs introduced by this patch would relate to reclaim throttling, concurrency, locking, or counter imbalances, which are handled by standard kernel debugging tools (LOCKDEP, KASAN, WARN_ON). There is no risk of uninitialized memory usage or info-leaks, so a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch adds per-lruvec isolation accounting (`nr_isolated`) and isolation throttling (`lru_gen_too_many_isolated`, `throttle_evictable_types`) for Multi-Gen LRU (MGLRU) page reclaim.\n\nAnalysis against KMSAN applicability:\n1. User-space leaks: No data is copied to user space or exposed via ioctls/sysfs/netlink/BPF buffers.\n2. Uninitialized variables: All added local variables (`allowed`, `fatal`, `stalled`) and struct fields (`nr_isolated`) are properly initialized and managed (e.g., `*fatal` is initialized to false upon entering `throttle_evictable_types()`, `struct lruvec` memory is zero-allocated during node/memcg initialization).\n3. Buffer bounds / memory handling: The patch only adjusts reclaimer scheduling, throttling, and isolation counters without touching uninitialized buffer access, variable-length buffers, or struct padding.\n\nAny potential bugs introduced by this patch would relate to reclaim throttling, concurrency, locking, or counter imbalances, which are handled by standard kernel debugging tools (LOCKDEP, KASAN, WARN_ON). There is no risk of uninitialized memory usage or info-leaks, so a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|