| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/30 00:27 | flow | patch-triage |
3mArgs:null Results: {
"EnableConfigs": [
"MEMCG",
"MEMCG_V1"
],
"FocusSymbols": [
"page_counter_try_charge",
"page_counter_refill_stock",
"page_counter_drain_stock_fully",
"page_counter_stock_flush_required",
"drain_all_stock"
],
"KMSANReasoning": "The patch refactors the per-CPU charge stock caching mechanism previously exclusive to memcontrol (memcg_stock) into a generic page counter stock (struct page_counter_stock_pcp) in mm/page_counter.c, allowing both memory and swap counters to benefit from stock caching.\n\nApplicability analysis:\n1. KASAN / Standard Detectors: The primary risks associated with this refactoring involve concurrent access and synchronization across CPUs (local_trylock, drain work items), cgroup css reference count tracking (css_get/css_put on stock caching), and potential use-after-free or out-of-bounds accesses on stock cache arrays. These are fully covered by KASAN, LOCKDEP, and standard kernel debugging facilities.\n2. KMSAN:\n - The new stack variable `nr_charged` passed to `page_counter_try_charge()` in `try_charge_memcg()` is unconditionally written on all success return paths (`*nr_charged = 0` on stock hit, and `*nr_charged = charge` on successful direct charge) before being evaluated at `check_high:`.\n - The per-CPU stock structures (`memory_stock`, `memsw_stock`) are statically allocated per-CPU variables zero-initialized in BSS, and the embedded `work_struct` is properly initialized via `INIT_WORK`.\n - New `struct page_counter` fields (`stock`, `stock_css`) are explicitly initialized to NULL in `page_counter_init()`, and memcgs are allocated with `kmem_cache_zalloc`.\n - No data is copied to user space, and no uninitialized padding, heap, or stack memory can be leaked or used in control flow.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch series refactors the per-CPU memcg charging stock mechanism into struct page_counter, moving stock batch charging, refilling, and draining logic into mm/page_counter.c, while also adding dedicated per-CPU stock handling for memsw under CONFIG_MEMCG_V1. These modifications touch core memory management and cgroup accounting paths involving concurrency, per-CPU locks, and reference counting, making them highly relevant and reachable for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/30 00:27 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e62cf3c2c4db5de6242a166612bf218ed99c8817\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Sep 30 00:27:42 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/page_counter.h b/include/linux/page_counter.h\nindex 2baf7a2b29b2e..58532259e7f59 100644\n--- a/include/linux/page_counter.h\n+++ b/include/linux/page_counter.h\n@@ -5,8 +5,30 @@\n #include \u003clinux/atomic.h\u003e\n #include \u003clinux/cache.h\u003e\n #include \u003clinux/limits.h\u003e\n+#include \u003clinux/percpu.h\u003e\n+#include \u003clinux/local_lock.h\u003e\n+#include \u003clinux/workqueue_types.h\u003e\n #include \u003casm/page.h\u003e\n \n+/*\n+ * The value of NR_PAGE_COUNTER_STOCK is selected to keep the cached counters\n+ * and their nr_pages in a single cacheline. This may change in the future.\n+ */\n+#define NR_PAGE_COUNTER_STOCK 7\n+#define PAGE_COUNTER_STOCK_BATCH 64UL\n+struct cgroup_subsys_state;\n+struct page_counter;\n+struct page_counter_stock_pcp {\n+\tlocal_trylock_t lock;\n+\tu8 nr_pages[NR_PAGE_COUNTER_STOCK];\n+\tstruct page_counter *cached[NR_PAGE_COUNTER_STOCK];\n+\n+\tstruct page_counter_stock_pcp __percpu *base;\n+\tstruct work_struct work;\n+\tunsigned long flags;\n+\tu8 drain_idx;\n+};\n+\n struct page_counter {\n \t/*\n \t * Make sure 'usage' does not share cacheline with any other field in\n@@ -41,6 +63,8 @@ struct page_counter {\n \tunsigned long high;\n \tunsigned long max;\n \tstruct page_counter *parent;\n+\tstruct page_counter_stock_pcp __percpu *stock;\n+\tstruct cgroup_subsys_state *stock_css;\n } ____cacheline_internodealigned_in_smp;\n \n #if BITS_PER_LONG == 32\n@@ -61,6 +85,8 @@ static inline void page_counter_init(struct page_counter *counter,\n \tcounter-\u003eparent = parent;\n \tcounter-\u003eprotection_support = protection_support;\n \tcounter-\u003etrack_failcnt = false;\n+\tcounter-\u003estock = NULL;\n+\tcounter-\u003estock_css = NULL;\n }\n \n static inline unsigned long page_counter_read(const struct page_counter *counter)\n@@ -72,8 +98,13 @@ long page_counter_margin(const struct page_counter *counter);\n void page_counter_cancel(struct page_counter *counter, unsigned long nr_pages);\n void page_counter_charge(struct page_counter *counter, unsigned long nr_pages);\n bool page_counter_try_charge(struct page_counter *counter,\n-\t\t\t unsigned long nr_pages,\n-\t\t\t struct page_counter **fail);\n+\t\t\t unsigned long nr_pages, struct page_counter **fail,\n+\t\t\t bool may_batch, unsigned long *nr_charged);\n+void page_counter_refill_stock(struct page_counter *counter,\n+\t\t\t unsigned long nr_pages);\n+void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock);\n+bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock,\n+\t\t\t\t struct cgroup_subsys_state *root_css);\n void page_counter_uncharge(struct page_counter *counter, unsigned long nr_pages);\n void page_counter_set_min(struct page_counter *counter, unsigned long nr_pages);\n void page_counter_set_low(struct page_counter *counter, unsigned long nr_pages);\ndiff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c\nindex 4683f3d680226..569307aa4bd62 100644\n--- a/kernel/cgroup/dmem.c\n+++ b/kernel/cgroup/dmem.c\n@@ -736,7 +736,7 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,\n \t\tgoto err;\n \t}\n \n-\tif (!page_counter_try_charge(\u0026pool-\u003ecnt, size, \u0026fail)) {\n+\tif (!page_counter_try_charge(\u0026pool-\u003ecnt, size, \u0026fail, false, NULL)) {\n \t\tif (ret_limit_pool) {\n \t\t\t*ret_limit_pool = container_of(fail, struct dmem_cgroup_pool_state, cnt);\n \t\t\tcss_get(\u0026(*ret_limit_pool)-\u003ecs-\u003ecss);\ndiff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c\nindex 3fb41311e4c7d..bbc2dd3fa3c97 100644\n--- a/mm/hugetlb_cgroup.c\n+++ b/mm/hugetlb_cgroup.c\n@@ -304,7 +304,7 @@ static int __hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,\n \n \tif (!page_counter_try_charge(\n \t\t __hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),\n-\t\t nr_pages, \u0026counter)) {\n+\t\t nr_pages, \u0026counter, false, NULL)) {\n \t\tret = -ENOMEM;\n \t\thugetlb_event(h_cg, idx, HUGETLB_MAX);\n \t\tcss_put(\u0026h_cg-\u003ecss);\ndiff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c\nindex b7f2868885071..5660ba4597db2 100644\n--- a/mm/memcontrol-v1.c\n+++ b/mm/memcontrol-v1.c\n@@ -122,10 +122,13 @@ static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)\n \t\tif (swap)\n \t\t\tval += total_swap_pages - get_nr_swap_pages();\n \t} else {\n-\t\tif (!swap)\n+\t\tif (!swap) {\n \t\t\tval = page_counter_read(\u0026memcg-\u003ememory);\n-\t\telse\n-\t\t\tval = page_counter_read(\u0026memcg-\u003ememsw);\n+\t\t} else {\n+\t\t\t/* Preserve the user-visible memory \u003c= memsw invariant. */\n+\t\t\tval = max(page_counter_read(\u0026memcg-\u003ememory),\n+\t\t\t\t page_counter_read(\u0026memcg-\u003ememsw));\n+\t\t}\n \t}\n \treturn val;\n }\n@@ -2193,7 +2196,8 @@ bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,\n {\n \tstruct page_counter *fail;\n \n-\tif (page_counter_try_charge(\u0026memcg-\u003etcpmem, nr_pages, \u0026fail)) {\n+\tif (page_counter_try_charge(\u0026memcg-\u003etcpmem, nr_pages, \u0026fail,\n+\t\t\t\t false, NULL)) {\n \t\tmemcg-\u003etcpmem_pressure = 0;\n \t\treturn true;\n \t}\ndiff --git a/mm/memcontrol.c b/mm/memcontrol.c\nindex aad0498a7bd65..6cae2d84f37c0 100644\n--- a/mm/memcontrol.c\n+++ b/mm/memcontrol.c\n@@ -2063,34 +2063,19 @@ void mem_cgroup_print_oom_group(struct mem_cgroup *memcg)\n \tpr_cont(\" are going to be killed due to memory.oom.group set\\n\");\n }\n \n-/*\n- * The value of NR_MEMCG_STOCK is selected to keep the cached memcgs and their\n- * nr_pages in a single cacheline. This may change in future.\n- */\n-#define NR_MEMCG_STOCK 7\n-\n-/*\n- * Watermarks for a charge stock slot, in the spirit of pcp-\u003ehigh and\n- * pcp-\u003ebatch: MEMCG_STOCK_HIGH is the high watermark at which a slot is\n- * trimmed, and it is trimmed down to MEMCG_STOCK_LOW rather than emptied.\n- */\n-#define MEMCG_STOCK_LOW\t\t(MEMCG_CHARGE_BATCH / 2)\n-#define MEMCG_STOCK_HIGH\t(MEMCG_CHARGE_BATCH)\n-\n #define FLUSHING_CACHED_CHARGE\t0\n-struct memcg_stock_pcp {\n-\tlocal_trylock_t lock;\n-\tuint8_t nr_pages[NR_MEMCG_STOCK];\n-\tstruct mem_cgroup *cached[NR_MEMCG_STOCK];\n \n-\tstruct work_struct work;\n-\tunsigned long flags;\n-\tuint8_t drain_idx;\n+static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memory_stock) = {\n+\t.lock = INIT_LOCAL_TRYLOCK(lock),\n+\t.base = \u0026memory_stock,\n };\n \n-static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {\n+#ifdef CONFIG_MEMCG_V1\n+static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memsw_stock) = {\n \t.lock = INIT_LOCAL_TRYLOCK(lock),\n+\t.base = \u0026memsw_stock,\n };\n+#endif\n \n /*\n * NR_OBJ_STOCK is sized so the entire hot path of obj_stock_pcp\n@@ -2139,52 +2124,6 @@ static void drain_obj_stock(struct obj_stock_pcp *stock);\n static bool obj_stock_flush_required(struct obj_stock_pcp *stock,\n \t\t\t\t struct mem_cgroup *root_memcg);\n \n-/**\n- * consume_stock: Try to consume stocked charge on this cpu.\n- * @memcg: memcg to consume from.\n- * @nr_pages: how many pages to charge.\n- *\n- * Consume the cached charge if enough nr_pages are present otherwise return\n- * failure. Also return failure for charge request larger than\n- * MEMCG_CHARGE_BATCH or if the local lock is already taken.\n- *\n- * returns true if successful, false otherwise.\n- */\n-static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)\n-{\n-\tstruct memcg_stock_pcp *stock;\n-\tuint8_t stock_pages;\n-\tbool ret = false;\n-\tint i;\n-\n-\tif (nr_pages \u003e MEMCG_CHARGE_BATCH ||\n-\t !local_trylock(\u0026memcg_stock.lock))\n-\t\treturn ret;\n-\n-\tstock = this_cpu_ptr(\u0026memcg_stock);\n-\n-\tfor (i = 0; i \u003c NR_MEMCG_STOCK; ++i) {\n-\t\tif (memcg != READ_ONCE(stock-\u003ecached[i]))\n-\t\t\tcontinue;\n-\n-\t\tstock_pages = READ_ONCE(stock-\u003enr_pages[i]);\n-\t\tif (stock_pages \u003e= nr_pages) {\n-\t\t\tstock_pages -= nr_pages;\n-\t\t\tWRITE_ONCE(stock-\u003enr_pages[i], stock_pages);\n-\t\t\tif (!stock_pages) {\n-\t\t\t\tcss_put(\u0026memcg-\u003ecss);\n-\t\t\t\tWRITE_ONCE(stock-\u003ecached[i], NULL);\n-\t\t\t}\n-\t\t\tret = true;\n-\t\t}\n-\t\tbreak;\n-\t}\n-\n-\tlocal_unlock(\u0026memcg_stock.lock);\n-\n-\treturn ret;\n-}\n-\n static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\n {\n \tpage_counter_uncharge(\u0026memcg-\u003ememory, nr_pages);\n@@ -2192,49 +2131,22 @@ static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\n \t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\n }\n \n-/*\n- * Returns stocks cached in percpu and reset cached information.\n- */\n-static void drain_stock(struct memcg_stock_pcp *stock, int i)\n+static void drain_local_stock(struct work_struct *work)\n {\n-\tstruct mem_cgroup *old = READ_ONCE(stock-\u003ecached[i]);\n-\tuint8_t stock_pages;\n-\n-\tif (!old)\n-\t\treturn;\n-\n-\tstock_pages = READ_ONCE(stock-\u003enr_pages[i]);\n-\tif (stock_pages) {\n-\t\tmemcg_uncharge(old, stock_pages);\n-\t\tWRITE_ONCE(stock-\u003enr_pages[i], 0);\n-\t}\n-\n-\tcss_put(\u0026old-\u003ecss);\n-\tWRITE_ONCE(stock-\u003ecached[i], NULL);\n-}\n-\n-static void drain_stock_fully(struct memcg_stock_pcp *stock)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i \u003c NR_MEMCG_STOCK; ++i)\n-\t\tdrain_stock(stock, i);\n-}\n-\n-static void drain_local_memcg_stock(struct work_struct *dummy)\n-{\n-\tstruct memcg_stock_pcp *stock;\n+\tstruct page_counter_stock_pcp *pcp_stock;\n+\tstruct page_counter_stock_pcp __percpu *stock;\n \n \tif (WARN_ONCE(!in_task(), \"drain in non-task context\"))\n \t\treturn;\n \n-\tlocal_lock(\u0026memcg_stock.lock);\n+\tstock = container_of(work, struct page_counter_stock_pcp, work)-\u003ebase;\n+\tlocal_lock(\u0026stock-\u003elock);\n \n-\tstock = this_cpu_ptr(\u0026memcg_stock);\n-\tdrain_stock_fully(stock);\n-\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026stock-\u003eflags);\n+\tpcp_stock = this_cpu_ptr(stock);\n+\tpage_counter_drain_stock_fully(pcp_stock);\n+\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags);\n \n-\tlocal_unlock(\u0026memcg_stock.lock);\n+\tlocal_unlock(\u0026stock-\u003elock);\n }\n \n static void drain_local_obj_stock(struct work_struct *dummy)\n@@ -2253,92 +2165,6 @@ static void drain_local_obj_stock(struct work_struct *dummy)\n \tlocal_unlock(\u0026obj_stock.lock);\n }\n \n-static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)\n-{\n-\tstruct memcg_stock_pcp *stock;\n-\tstruct mem_cgroup *cached;\n-\tunsigned int stock_pages;\n-\tbool success = false;\n-\tint empty_slot = -1;\n-\tint i;\n-\n-\t/*\n-\t * nr_pages[] is a uint8_t and a slot's count is capped at\n-\t * MEMCG_STOCK_HIGH. Raising MEMCG_CHARGE_BATCH beyond 127 would need\n-\t * more careful handling of nr_pages[] in struct memcg_stock_pcp.\n-\t */\n-\tBUILD_BUG_ON(MEMCG_CHARGE_BATCH \u003e S8_MAX);\n-\tBUILD_BUG_ON(MEMCG_STOCK_HIGH \u003e U8_MAX);\n-\n-\tVM_WARN_ON_ONCE(mem_cgroup_is_root(memcg));\n-\n-\tif (nr_pages \u003e MEMCG_CHARGE_BATCH ||\n-\t !local_trylock(\u0026memcg_stock.lock)) {\n-\t\t/*\n-\t\t * In case of larger than batch refill or unlikely failure to\n-\t\t * lock the percpu memcg_stock.lock, uncharge memcg directly.\n-\t\t */\n-\t\tmemcg_uncharge(memcg, nr_pages);\n-\t\treturn;\n-\t}\n-\n-\tstock = this_cpu_ptr(\u0026memcg_stock);\n-\tfor (i = 0; i \u003c NR_MEMCG_STOCK; ++i) {\n-\t\tcached = READ_ONCE(stock-\u003ecached[i]);\n-\t\tif (!cached \u0026\u0026 empty_slot == -1)\n-\t\t\tempty_slot = i;\n-\t\tif (memcg == READ_ONCE(stock-\u003ecached[i])) {\n-\t\t\tstock_pages = READ_ONCE(stock-\u003enr_pages[i]) + nr_pages;\n-\t\t\tif (stock_pages \u003e MEMCG_STOCK_HIGH) {\n-\t\t\t\tmemcg_uncharge(memcg,\n-\t\t\t\t\t stock_pages - MEMCG_STOCK_LOW);\n-\t\t\t\tstock_pages = MEMCG_STOCK_LOW;\n-\t\t\t}\n-\t\t\tWRITE_ONCE(stock-\u003enr_pages[i], stock_pages);\n-\t\t\tsuccess = true;\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\n-\tif (!success) {\n-\t\ti = empty_slot;\n-\t\tif (i == -1) {\n-\t\t\ti = stock-\u003edrain_idx++;\n-\t\t\tif (stock-\u003edrain_idx == NR_MEMCG_STOCK)\n-\t\t\t\tstock-\u003edrain_idx = 0;\n-\t\t\tdrain_stock(stock, i);\n-\t\t}\n-\t\tcss_get(\u0026memcg-\u003ecss);\n-\t\tWRITE_ONCE(stock-\u003ecached[i], memcg);\n-\t\tWRITE_ONCE(stock-\u003enr_pages[i], nr_pages);\n-\t}\n-\n-\tlocal_unlock(\u0026memcg_stock.lock);\n-}\n-\n-static bool is_memcg_drain_needed(struct memcg_stock_pcp *stock,\n-\t\t\t\t struct mem_cgroup *root_memcg)\n-{\n-\tstruct mem_cgroup *memcg;\n-\tbool flush = false;\n-\tint i;\n-\n-\trcu_read_lock();\n-\tfor (i = 0; i \u003c NR_MEMCG_STOCK; ++i) {\n-\t\tmemcg = READ_ONCE(stock-\u003ecached[i]);\n-\t\tif (!memcg)\n-\t\t\tcontinue;\n-\n-\t\tif (READ_ONCE(stock-\u003enr_pages[i]) \u0026\u0026\n-\t\t mem_cgroup_is_descendant(memcg, root_memcg)) {\n-\t\t\tflush = true;\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\trcu_read_unlock();\n-\treturn flush;\n-}\n-\n static bool schedule_drain_work(int cpu, struct work_struct *work)\n {\n \t/*\n@@ -2355,12 +2181,30 @@ static bool schedule_drain_work(int cpu, struct work_struct *work)\n \treturn true;\n }\n \n+static void schedule_stock_drain(struct page_counter_stock_pcp __percpu *stock,\n+\t\t\t\t struct cgroup_subsys_state *root_css,\n+\t\t\t\t int cpu, int curcpu)\n+{\n+\tstruct page_counter_stock_pcp *pcp_stock = per_cpu_ptr(stock, cpu);\n+\n+\tif (test_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags) ||\n+\t !page_counter_stock_flush_required(pcp_stock, root_css) ||\n+\t test_and_set_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags))\n+\t\treturn;\n+\n+\tif (cpu == curcpu)\n+\t\tdrain_local_stock(\u0026pcp_stock-\u003ework);\n+\telse if (!schedule_drain_work(cpu, \u0026pcp_stock-\u003ework))\n+\t\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags);\n+}\n+\n /*\n * Drains all per-CPU charge caches for given root_memcg resp. subtree\n * of the hierarchy under it.\n */\n void drain_all_stock(struct mem_cgroup *root_memcg)\n {\n+\tstruct cgroup_subsys_state *root_css = \u0026root_memcg-\u003ecss;\n \tint cpu, curcpu;\n \n \t/* If someone's already draining, avoid adding running more workers. */\n@@ -2370,24 +2214,18 @@ void drain_all_stock(struct mem_cgroup *root_memcg)\n \t * Notify other cpus that system-wide \"drain\" is running\n \t * We do not care about races with the cpu hotplug because cpu down\n \t * as well as workers from this path always operate on the local\n-\t * per-cpu data. CPU up doesn't touch memcg_stock at all.\n+\t * per-cpu data. CPU up doesn't touch the stocks at all.\n \t */\n \tmigrate_disable();\n \tcurcpu = smp_processor_id();\n \tfor_each_online_cpu(cpu) {\n-\t\tstruct memcg_stock_pcp *memcg_st = \u0026per_cpu(memcg_stock, cpu);\n \t\tstruct obj_stock_pcp *obj_st = \u0026per_cpu(obj_stock, cpu);\n \n-\t\tif (!test_bit(FLUSHING_CACHED_CHARGE, \u0026memcg_st-\u003eflags) \u0026\u0026\n-\t\t is_memcg_drain_needed(memcg_st, root_memcg) \u0026\u0026\n-\t\t !test_and_set_bit(FLUSHING_CACHED_CHARGE,\n-\t\t\t\t \u0026memcg_st-\u003eflags)) {\n-\t\t\tif (cpu == curcpu)\n-\t\t\t\tdrain_local_memcg_stock(\u0026memcg_st-\u003ework);\n-\t\t\telse if (!schedule_drain_work(cpu, \u0026memcg_st-\u003ework))\n-\t\t\t\tclear_bit(FLUSHING_CACHED_CHARGE,\n-\t\t\t\t\t \u0026memcg_st-\u003eflags);\n-\t\t}\n+\t\tschedule_stock_drain(\u0026memory_stock, root_css, cpu, curcpu);\n+#ifdef CONFIG_MEMCG_V1\n+\t\tif (do_memsw_account())\n+\t\t\tschedule_stock_drain(\u0026memsw_stock, root_css, cpu, curcpu);\n+#endif\n \n \t\tif (!test_bit(FLUSHING_CACHED_CHARGE, \u0026obj_st-\u003eflags) \u0026\u0026\n \t\t obj_stock_flush_required(obj_st, root_memcg) \u0026\u0026\n@@ -2406,12 +2244,19 @@ void drain_all_stock(struct mem_cgroup *root_memcg)\n \n static int memcg_hotplug_cpu_dead(unsigned int cpu)\n {\n-\tstruct memcg_stock_pcp *memcg_st = \u0026per_cpu(memcg_stock, cpu);\n+\tstruct page_counter_stock_pcp *stock;\n \tstruct obj_stock_pcp *obj_st = \u0026per_cpu(obj_stock, cpu);\n \n \t/* no need for the local lock */\n \tdrain_obj_stock(obj_st);\n-\tdrain_stock_fully(memcg_st);\n+\tstock = per_cpu_ptr(\u0026memory_stock, cpu);\n+\tpage_counter_drain_stock_fully(stock);\n+\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026stock-\u003eflags);\n+#ifdef CONFIG_MEMCG_V1\n+\tstock = per_cpu_ptr(\u0026memsw_stock, cpu);\n+\tpage_counter_drain_stock_fully(stock);\n+\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026stock-\u003eflags);\n+#endif\n \n \t/*\n \t * A drain work queued before the CPU went away is executed by an\n@@ -2419,7 +2264,6 @@ static int memcg_hotplug_cpu_dead(unsigned int cpu)\n \t * clear the flags here to make these stocks drainable again once\n \t * the CPU comes back online.\n \t */\n-\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026memcg_st-\u003eflags);\n \tclear_bit(FLUSHING_CACHED_CHARGE, \u0026obj_st-\u003eflags);\n \n \treturn 0;\n@@ -2699,10 +2543,10 @@ void __mem_cgroup_handle_over_high(gfp_t gfp_mask)\n static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n \t\t\t unsigned int nr_pages)\n {\n-\tunsigned int batch = max(MEMCG_CHARGE_BATCH, nr_pages);\n \tint nr_retries = MAX_RECLAIM_RETRIES;\n \tstruct mem_cgroup *mem_over_limit;\n \tstruct page_counter *counter;\n+\tunsigned long nr_charged;\n \tunsigned long nr_reclaimed;\n \tbool passed_oom = false;\n \tunsigned int reclaim_options;\n@@ -2710,33 +2554,30 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n \tbool raised_max_event = false;\n \tunsigned long pflags;\n \tbool allow_spinning = gfpflags_allow_spinning(gfp_mask);\n+\tbool may_batch = allow_spinning;\n \tint ret = 0;\n \n retry:\n-\tif (consume_stock(memcg, nr_pages))\n-\t\treturn ret;\n-\n-\tif (!allow_spinning)\n-\t\t/* Avoid the refill and flush of the older stock */\n-\t\tbatch = nr_pages;\n-\n \treclaim_options = MEMCG_RECLAIM_MAY_SWAP;\n-\tif (!do_memsw_account() ||\n-\t page_counter_try_charge(\u0026memcg-\u003ememsw, batch, \u0026counter)) {\n-\t\tif (page_counter_try_charge(\u0026memcg-\u003ememory, batch, \u0026counter))\n-\t\t\tgoto done_restock;\n-\t\tif (do_memsw_account())\n-\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, batch);\n-\t\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n-\t} else {\n+\tif (do_memsw_account() \u0026\u0026\n+\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\n+\t\t\t\t may_batch, NULL)) {\n \t\tmem_over_limit = mem_cgroup_from_counter(counter, memsw);\n \t\treclaim_options \u0026= ~MEMCG_RECLAIM_MAY_SWAP;\n+\t\tgoto reclaim;\n \t}\n \n-\tif (batch \u003e nr_pages) {\n-\t\tbatch = nr_pages;\n-\t\tgoto retry;\n-\t}\n+\tif (page_counter_try_charge(\u0026memcg-\u003ememory, nr_pages, \u0026counter,\n+\t\t\t\t may_batch, \u0026nr_charged))\n+\t\tgoto check_high;\n+\n+\tif (do_memsw_account())\n+\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\n+\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n+\n+reclaim:\n+\t/* Do not retry speculative batch charges after the first miss. */\n+\tmay_batch = false;\n \n \t/*\n \t * Prevent unbounded recursion when reclaim operations need to\n@@ -2849,10 +2690,9 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n \n \treturn ret;\n \n-done_restock:\n-\tif (batch \u003e nr_pages)\n-\t\trefill_stock(memcg, batch - nr_pages);\n-\n+check_high:\n+\tif (!nr_charged)\n+\t\treturn ret;\n \t/*\n \t * If the hierarchy is above the normal consumption range, schedule\n \t * reclaim on returning to userland. We can perform reclaim here\n@@ -2892,7 +2732,7 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n \t\t\t * and distribute reclaim work and delay penalties\n \t\t\t * based on how much each task is actually allocating.\n \t\t\t */\n-\t\t\tcurrent-\u003ememcg_nr_pages_over_high += batch;\n+\t\t\tcurrent-\u003ememcg_nr_pages_over_high += nr_charged;\n \t\t\tset_notify_resume(current);\n \t\t\tbreak;\n \t\t}\n@@ -3197,8 +3037,11 @@ static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,\n \n \taccount_kmem_nmi_safe(memcg, -nr_pages);\n \tmemcg1_account_kmem(memcg, -nr_pages);\n-\tif (!mem_cgroup_is_root(memcg))\n-\t\trefill_stock(memcg, nr_pages);\n+\tif (!mem_cgroup_is_root(memcg)) {\n+\t\tpage_counter_refill_stock(\u0026memcg-\u003ememory, nr_pages);\n+\t\tif (do_memsw_account())\n+\t\t\tpage_counter_refill_stock(\u0026memcg-\u003ememsw, nr_pages);\n+\t}\n \n \tcss_put(\u0026memcg-\u003ecss);\n }\n@@ -4345,8 +4188,14 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\n \tpage_counter_set_high(\u0026memcg-\u003eswap, PAGE_COUNTER_MAX);\n \tif (parent) {\n \t\tpage_counter_init(\u0026memcg-\u003ememory, \u0026parent-\u003ememory, memcg_on_dfl);\n+\t\tmemcg-\u003ememory.stock = \u0026memory_stock;\n+\t\tmemcg-\u003ememory.stock_css = \u0026memcg-\u003ecss;\n \t\tpage_counter_init(\u0026memcg-\u003eswap, \u0026parent-\u003eswap, false);\n #ifdef CONFIG_MEMCG_V1\n+\t\tif (!memcg_on_dfl) {\n+\t\t\tmemcg-\u003ememsw.stock = \u0026memsw_stock;\n+\t\t\tmemcg-\u003ememsw.stock_css = \u0026memcg-\u003ecss;\n+\t\t}\n \t\tWRITE_ONCE(memcg-\u003eswappiness, mem_cgroup_swappiness(parent));\n \t\tmemcg-\u003ememory.track_failcnt = !memcg_on_dfl;\n \t\tmemcg-\u003ememsw.track_failcnt = !memcg_on_dfl;\n@@ -5813,7 +5662,7 @@ void mem_cgroup_sk_uncharge(const struct sock *sk, unsigned int nr_pages)\n \n \tmod_memcg_state(memcg, MEMCG_SOCK, -nr_pages);\n \n-\trefill_stock(memcg, nr_pages);\n+\tpage_counter_refill_stock(\u0026memcg-\u003ememory, nr_pages);\n }\n \n void mem_cgroup_flush_workqueue(void)\n@@ -5961,6 +5810,8 @@ int __init mem_cgroup_init(void)\n \t * exceed S32_MAX / PAGE_SIZE.\n \t */\n \tBUILD_BUG_ON(MEMCG_CHARGE_BATCH \u003e S32_MAX / PAGE_SIZE);\n+\t/* Batched page-counter charges feed memcg's memory.high accounting. */\n+\tBUILD_BUG_ON(MEMCG_CHARGE_BATCH != PAGE_COUNTER_STOCK_BATCH);\n \n \tmemcg_struct_check();\n \n@@ -5971,8 +5822,12 @@ int __init mem_cgroup_init(void)\n \tWARN_ON(!memcg_wq);\n \n \tfor_each_possible_cpu(cpu) {\n-\t\tINIT_WORK(\u0026per_cpu_ptr(\u0026memcg_stock, cpu)-\u003ework,\n-\t\t\t drain_local_memcg_stock);\n+\t\tINIT_WORK(\u0026per_cpu_ptr(\u0026memory_stock, cpu)-\u003ework,\n+\t\t\t drain_local_stock);\n+#ifdef CONFIG_MEMCG_V1\n+\t\tINIT_WORK(\u0026per_cpu_ptr(\u0026memsw_stock, cpu)-\u003ework,\n+\t\t\t drain_local_stock);\n+#endif\n \t\tINIT_WORK(\u0026per_cpu_ptr(\u0026obj_stock, cpu)-\u003ework,\n \t\t\t drain_local_obj_stock);\n \t}\n@@ -6023,7 +5878,8 @@ int __mem_cgroup_try_charge_swap(struct folio *folio)\n \t\tprivate_id = mem_cgroup_private_id_get(memcg, nr_pages);\n \n \t\tif (!mem_cgroup_private_id_is_root(private_id) \u0026\u0026\n-\t\t !page_counter_try_charge(\u0026memcg-\u003eswap, nr_pages, \u0026counter)) {\n+\t\t !page_counter_try_charge(\u0026memcg-\u003eswap, nr_pages, \u0026counter,\n+\t\t\t\t\t false, NULL)) {\n \t\t\tmemcg_memory_event(memcg, MEMCG_SWAP_MAX);\n \t\t\tmemcg_memory_event(memcg, MEMCG_SWAP_FAIL);\n \t\t\tmem_cgroup_private_id_put(private_id, nr_pages);\ndiff --git a/mm/page_counter.c b/mm/page_counter.c\nindex 9167ffd1380c8..e48ef37016c50 100644\n--- a/mm/page_counter.c\n+++ b/mm/page_counter.c\n@@ -7,6 +7,7 @@\n \n #include \u003clinux/page_counter.h\u003e\n #include \u003clinux/atomic.h\u003e\n+#include \u003clinux/cgroup.h\u003e\n #include \u003clinux/kernel.h\u003e\n #include \u003clinux/math64.h\u003e\n #include \u003clinux/string.h\u003e\n@@ -14,6 +15,14 @@\n #include \u003clinux/bug.h\u003e\n #include \u003casm/page.h\u003e\n \n+/*\n+ * Watermarks for a charge stock slot, in the spirit of pcp-\u003ehigh and\n+ * pcp-\u003ebatch: PAGE_COUNTER_STOCK_HIGH is the high watermark at which a slot is\n+ * trimmed down to PAGE_COUNTER_STOCK_LOW rather than emptied.\n+ */\n+#define PAGE_COUNTER_STOCK_LOW (PAGE_COUNTER_STOCK_BATCH / 2)\n+#define PAGE_COUNTER_STOCK_HIGH PAGE_COUNTER_STOCK_BATCH\n+\n static bool track_protection(struct page_counter *c)\n {\n \treturn c-\u003eprotection_support;\n@@ -127,23 +136,76 @@ void page_counter_charge(struct page_counter *counter, unsigned long nr_pages)\n \t}\n }\n \n+/*\n+ * Consume the cached charge if enough nr_pages are present, otherwise return\n+ * failure. Also return failure for charge requests larger than\n+ * PAGE_COUNTER_STOCK_BATCH or if the local lock is already taken.\n+ */\n+static bool page_counter_consume_stock(struct page_counter *counter,\n+\t\t\t\t unsigned long nr_pages)\n+{\n+\tstruct page_counter_stock_pcp __percpu *stock = counter-\u003estock;\n+\tstruct page_counter_stock_pcp *pcp_stock;\n+\tu8 stock_pages;\n+\tbool ret = false;\n+\tint i;\n+\n+\tif (nr_pages \u003e PAGE_COUNTER_STOCK_BATCH ||\n+\t !local_trylock(\u0026stock-\u003elock))\n+\t\treturn false;\n+\n+\tpcp_stock = this_cpu_ptr(stock);\n+\tfor (i = 0; i \u003c NR_PAGE_COUNTER_STOCK; i++) {\n+\t\tif (counter != READ_ONCE(pcp_stock-\u003ecached[i]))\n+\t\t\tcontinue;\n+\n+\t\tstock_pages = READ_ONCE(pcp_stock-\u003enr_pages[i]);\n+\t\tif (stock_pages \u003e= nr_pages) {\n+\t\t\tstock_pages -= nr_pages;\n+\t\t\tWRITE_ONCE(pcp_stock-\u003enr_pages[i], stock_pages);\n+\t\t\tif (!stock_pages) {\n+\t\t\t\tcss_put(counter-\u003estock_css);\n+\t\t\t\tWRITE_ONCE(pcp_stock-\u003ecached[i], NULL);\n+\t\t\t}\n+\t\t\tret = true;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tlocal_unlock(\u0026stock-\u003elock);\n+\n+\treturn ret;\n+}\n+\n /**\n- * page_counter_try_charge - try to hierarchically charge pages\n+ * page_counter_try_charge - try to hierarchically charge pages using stock\n * @counter: counter\n- * @nr_pages: number of pages to charge\n- * @fail: points first counter to hit its limit, if any\n+ * @nr_pages: number of pages requested\n+ * @fail: points to the first counter to hit its limit, if any\n+ * @may_batch: whether a stock miss may trigger a batch charge\n+ * @nr_charged: optional; set to the hierarchy charge size on success\n *\n- * Returns %true on success, or %false and @fail if the counter or one\n- * of its ancestors has hit its configured limit.\n+ * Return: %true if the request was satisfied. A failed batch charge may update\n+ * @fail before an exact retry succeeds.\n */\n bool page_counter_try_charge(struct page_counter *counter,\n-\t\t\t unsigned long nr_pages,\n-\t\t\t struct page_counter **fail)\n+\t\t\t unsigned long nr_pages, struct page_counter **fail,\n+\t\t\t bool may_batch, unsigned long *nr_charged)\n {\n+\tunsigned long charge = nr_pages;\n \tstruct page_counter *c;\n \tbool protection = track_protection(counter);\n \tbool track_failcnt = counter-\u003etrack_failcnt;\n \n+\tif (counter-\u003estock \u0026\u0026 may_batch)\n+\t\tcharge = max(nr_pages, PAGE_COUNTER_STOCK_BATCH);\n+\n+retry:\n+\tif (counter-\u003estock \u0026\u0026 page_counter_consume_stock(counter, nr_pages)) {\n+\t\tif (nr_charged)\n+\t\t\t*nr_charged = 0;\n+\t\treturn true;\n+\t}\n+\n \tfor (c = counter; c; c = c-\u003eparent) {\n \t\tlong new;\n \t\t/*\n@@ -160,9 +222,9 @@ bool page_counter_try_charge(struct page_counter *counter,\n \t\t * we either see the new limit or the setter sees the\n \t\t * counter has changed and retries.\n \t\t */\n-\t\tnew = atomic_long_add_return(nr_pages, \u0026c-\u003eusage);\n+\t\tnew = atomic_long_add_return(charge, \u0026c-\u003eusage);\n \t\tif (new \u003e c-\u003emax) {\n-\t\t\tatomic_long_sub(nr_pages, \u0026c-\u003eusage);\n+\t\t\tatomic_long_sub(charge, \u0026c-\u003eusage);\n \t\t\t/*\n \t\t\t * This is racy, but we can live with some\n \t\t\t * inaccuracy in the failcnt which is only used\n@@ -183,15 +245,142 @@ bool page_counter_try_charge(struct page_counter *counter,\n \t\t\t\tWRITE_ONCE(c-\u003ewatermark, new);\n \t\t}\n \t}\n+\tif (charge \u003e nr_pages)\n+\t\tpage_counter_refill_stock(counter, charge - nr_pages);\n+\tif (nr_charged)\n+\t\t*nr_charged = charge;\n \treturn true;\n \n failed:\n \tfor (c = counter; c != *fail; c = c-\u003eparent)\n-\t\tpage_counter_cancel(c, nr_pages);\n+\t\tpage_counter_cancel(c, charge);\n+\n+\tif (charge \u003e nr_pages) {\n+\t\tcharge = nr_pages;\n+\t\tgoto retry;\n+\t}\n \n \treturn false;\n }\n \n+static void page_counter_drain_stock(struct page_counter_stock_pcp *stock,\n+\t\t\t\t int i)\n+{\n+\tstruct page_counter *counter = READ_ONCE(stock-\u003ecached[i]);\n+\tu8 nr_pages;\n+\n+\tif (!counter)\n+\t\treturn;\n+\n+\tnr_pages = READ_ONCE(stock-\u003enr_pages[i]);\n+\tif (nr_pages) {\n+\t\tpage_counter_uncharge(counter, nr_pages);\n+\t\tWRITE_ONCE(stock-\u003enr_pages[i], 0);\n+\t}\n+\tcss_put(counter-\u003estock_css);\n+\tWRITE_ONCE(stock-\u003ecached[i], NULL);\n+}\n+\n+void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c NR_PAGE_COUNTER_STOCK; i++)\n+\t\tpage_counter_drain_stock(stock, i);\n+}\n+\n+bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock,\n+\t\t\t\t struct cgroup_subsys_state *root_css)\n+{\n+\tstruct cgroup_subsys_state *css;\n+\tstruct page_counter *counter;\n+\tbool flush = false;\n+\tint i;\n+\n+\trcu_read_lock();\n+\tfor (i = 0; i \u003c NR_PAGE_COUNTER_STOCK; i++) {\n+\t\tcounter = READ_ONCE(stock-\u003ecached[i]);\n+\t\tif (!counter)\n+\t\t\tcontinue;\n+\t\tcss = READ_ONCE(counter-\u003estock_css);\n+\n+\t\tif (READ_ONCE(stock-\u003enr_pages[i]) \u0026\u0026\n+\t\t cgroup_is_descendant(css-\u003ecgroup, root_css-\u003ecgroup)) {\n+\t\t\tflush = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\trcu_read_unlock();\n+\treturn flush;\n+}\n+\n+/**\n+ * page_counter_refill_stock - return pages to a counter's stock\n+ * @counter: counter to return pages to\n+ * @nr_pages: number of pages to return\n+ *\n+ * If the stock cannot accept the pages, uncharge them from the hierarchy.\n+ */\n+void page_counter_refill_stock(struct page_counter *counter,\n+\t\t\t unsigned long nr_pages)\n+{\n+\tstruct page_counter_stock_pcp __percpu *stock = counter-\u003estock;\n+\tstruct page_counter_stock_pcp *pcp_stock;\n+\tunsigned int stock_pages;\n+\tint empty_slot = -1;\n+\tint i;\n+\n+\t/*\n+\t * nr_pages[] is a u8 and a slot is capped at PAGE_COUNTER_STOCK_HIGH.\n+\t * Raising PAGE_COUNTER_STOCK_BATCH beyond 127 would need careful\n+\t * handling of nr_pages[] in struct page_counter_stock_pcp.\n+\t */\n+\tBUILD_BUG_ON(PAGE_COUNTER_STOCK_BATCH \u003e S8_MAX);\n+\tBUILD_BUG_ON(PAGE_COUNTER_STOCK_HIGH \u003e U8_MAX);\n+\n+\tif (!stock || nr_pages \u003e PAGE_COUNTER_STOCK_BATCH ||\n+\t !local_trylock(\u0026stock-\u003elock)) {\n+\t\t/*\n+\t\t * For a larger-than-batch refill or an unlikely failure to lock\n+\t\t * the per-CPU stock, uncharge the hierarchy directly.\n+\t\t */\n+\t\tpage_counter_uncharge(counter, nr_pages);\n+\t\treturn;\n+\t}\n+\n+\tpcp_stock = this_cpu_ptr(stock);\n+\tfor (i = 0; i \u003c NR_PAGE_COUNTER_STOCK; i++) {\n+\t\tstruct page_counter *cached = READ_ONCE(pcp_stock-\u003ecached[i]);\n+\n+\t\tif (!cached \u0026\u0026 empty_slot == -1)\n+\t\t\tempty_slot = i;\n+\t\tif (counter != cached)\n+\t\t\tcontinue;\n+\n+\t\tstock_pages = READ_ONCE(pcp_stock-\u003enr_pages[i]) + nr_pages;\n+\t\tif (stock_pages \u003e PAGE_COUNTER_STOCK_HIGH) {\n+\t\t\tpage_counter_uncharge(counter,\n+\t\t\t\t\t stock_pages - PAGE_COUNTER_STOCK_LOW);\n+\t\t\tstock_pages = PAGE_COUNTER_STOCK_LOW;\n+\t\t}\n+\t\tWRITE_ONCE(pcp_stock-\u003enr_pages[i], stock_pages);\n+\t\tlocal_unlock(\u0026stock-\u003elock);\n+\t\treturn;\n+\t}\n+\n+\ti = empty_slot;\n+\tif (i == -1) {\n+\t\ti = pcp_stock-\u003edrain_idx++;\n+\t\tif (pcp_stock-\u003edrain_idx == NR_PAGE_COUNTER_STOCK)\n+\t\t\tpcp_stock-\u003edrain_idx = 0;\n+\t\tpage_counter_drain_stock(pcp_stock, i);\n+\t}\n+\tcss_get(counter-\u003estock_css);\n+\tWRITE_ONCE(pcp_stock-\u003ecached[i], counter);\n+\tWRITE_ONCE(pcp_stock-\u003enr_pages[i], nr_pages);\n+\tlocal_unlock(\u0026stock-\u003elock);\n+}\n+\n /**\n * page_counter_uncharge - hierarchically uncharge pages\n * @counter: counter\n"
} |
| 2/1 | 2026/09/30 00:27 | agent | patch-evaluator |
2mModel:core Args: null Results: {
"EnableConfigs": [
"MEMCG",
"MEMCG_V1"
],
"FocusSymbols": [
"page_counter_try_charge",
"page_counter_refill_stock",
"page_counter_drain_stock_fully",
"page_counter_stock_flush_required",
"drain_all_stock"
],
"Reasoning": "The patch series refactors the per-CPU memcg charging stock mechanism into struct page_counter, moving stock batch charging, refilling, and draining logic into mm/page_counter.c, while also adding dedicated per-CPU stock handling for memsw under CONFIG_MEMCG_V1. These modifications touch core memory management and cgroup accounting paths involving concurrency, per-CPU locks, and reference counting, making them highly relevant and reachable for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit e62cf3c2c4db5de6242a166612bf218ed99c8817
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Sep 30 00:27:42 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/page_counter.h b/include/linux/page_counter.h
index 2baf7a2b29b2e..58532259e7f59 100644
--- a/include/linux/page_counter.h
+++ b/include/linux/page_counter.h
@@ -5,8 +5,30 @@
#include <linux/atomic.h>
#include <linux/cache.h>
#include <linux/limits.h>
+#include <linux/percpu.h>
+#include <linux/local_lock.h>
+#include <linux/workqueue_types.h>
#include <asm/page.h>
+/*
+ * The value of NR_PAGE_COUNTER_STOCK is selected to keep the cached counters
+ * and their nr_pages in a single cacheline. This may change in the future.
+ */
+#define NR_PAGE_COUNTER_STOCK 7
+#define PAGE_COUNTER_STOCK_BATCH 64UL
+struct cgroup_subsys_state;
+struct page_counter;
+struct page_counter_stock_pcp {
+ local_trylock_t lock;
+ u8 nr_pages[NR_PAGE_COUNTER_STOCK];
+ struct page_counter *cached[NR_PAGE_COUNTER_STOCK];
+
+ struct page_counter_stock_pcp __percpu *base;
+ struct work_struct work;
+ unsigned long flags;
+ u8 drain_idx;
+};
+
struct page_counter {
/*
* Make sure 'usage' does not share cacheline with any other field in
@@ -41,6 +63,8 @@ struct page_counter {
unsigned long high;
unsigned long max;
struct page_counter *parent;
+ struct page_counter_stock_pcp __percpu *stock;
+ struct cgroup_subsys_state *stock_css;
} ____cacheline_internodealigned_in_smp;
#if BITS_PER_LONG == 32
@@ -61,6 +85,8 @@ static inline void page_counter_init(struct page_counter *counter,
counter->parent = parent;
counter->protection_support = protection_support;
counter->track_failcnt = false;
+ counter->stock = NULL;
+ counter->stock_css = NULL;
}
static inline unsigned long page_counter_read(const struct page_counter *counter)
@@ -72,8 +98,13 @@ long page_counter_margin(const struct page_counter *counter);
void page_counter_cancel(struct page_counter *counter, unsigned long nr_pages);
void page_counter_charge(struct page_counter *counter, unsigned long nr_pages);
bool page_counter_try_charge(struct page_counter *counter,
- unsigned long nr_pages,
- struct page_counter **fail);
+ unsigned long nr_pages, struct page_counter **fail,
+ bool may_batch, unsigned long *nr_charged);
+void page_counter_refill_stock(struct page_counter *counter,
+ unsigned long nr_pages);
+void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock);
+bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock,
+ struct cgroup_subsys_state *root_css);
void page_counter_uncharge(struct page_counter *counter, unsigned long nr_pages);
void page_counter_set_min(struct page_counter *counter, unsigned long nr_pages);
void page_counter_set_low(struct page_counter *counter, unsigned long nr_pages);
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 4683f3d680226..569307aa4bd62 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -736,7 +736,7 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,
goto err;
}
- if (!page_counter_try_charge(&pool->cnt, size, &fail)) {
+ if (!page_counter_try_charge(&pool->cnt, size, &fail, false, NULL)) {
if (ret_limit_pool) {
*ret_limit_pool = container_of(fail, struct dmem_cgroup_pool_state, cnt);
css_get(&(*ret_limit_pool)->cs->css);
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
index 3fb41311e4c7d..bbc2dd3fa3c97 100644
--- a/mm/hugetlb_cgroup.c
+++ b/mm/hugetlb_cgroup.c
@@ -304,7 +304,7 @@ static int __hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
if (!page_counter_try_charge(
__hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),
- nr_pages, &counter)) {
+ nr_pages, &counter, false, NULL)) {
ret = -ENOMEM;
hugetlb_event(h_cg, idx, HUGETLB_MAX);
css_put(&h_cg->css);
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index b7f2868885071..5660ba4597db2 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -122,10 +122,13 @@ static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
if (swap)
val += total_swap_pages - get_nr_swap_pages();
} else {
- if (!swap)
+ if (!swap) {
val = page_counter_read(&memcg->memory);
- else
- val = page_counter_read(&memcg->memsw);
+ } else {
+ /* Preserve the user-visible memory <= memsw invariant. */
+ val = max(page_counter_read(&memcg->memory),
+ page_counter_read(&memcg->memsw));
+ }
}
return val;
}
@@ -2193,7 +2196,8 @@ bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,
{
struct page_counter *fail;
- if (page_counter_try_charge(&memcg->tcpmem, nr_pages, &fail)) {
+ if (page_counter_try_charge(&memcg->tcpmem, nr_pages, &fail,
+ false, NULL)) {
memcg->tcpmem_pressure = 0;
return true;
}
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index aad0498a7bd65..6cae2d84f37c0 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2063,34 +2063,19 @@ void mem_cgroup_print_oom_group(struct mem_cgroup *memcg)
pr_cont(" are going to be killed due to memory.oom.group set\n");
}
-/*
- * The value of NR_MEMCG_STOCK is selected to keep the cached memcgs and their
- * nr_pages in a single cacheline. This may change in future.
- */
-#define NR_MEMCG_STOCK 7
-
-/*
- * Watermarks for a charge stock slot, in the spirit of pcp->high and
- * pcp->batch: MEMCG_STOCK_HIGH is the high watermark at which a slot is
- * trimmed, and it is trimmed down to MEMCG_STOCK_LOW rather than emptied.
- */
-#define MEMCG_STOCK_LOW (MEMCG_CHARGE_BATCH / 2)
-#define MEMCG_STOCK_HIGH (MEMCG_CHARGE_BATCH)
-
#define FLUSHING_CACHED_CHARGE 0
-struct memcg_stock_pcp {
- local_trylock_t lock;
- uint8_t nr_pages[NR_MEMCG_STOCK];
- struct mem_cgroup *cached[NR_MEMCG_STOCK];
- struct work_struct work;
- unsigned long flags;
- uint8_t drain_idx;
+static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memory_stock) = {
+ .lock = INIT_LOCAL_TRYLOCK(lock),
+ .base = &memory_stock,
};
-static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
+#ifdef CONFIG_MEMCG_V1
+static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memsw_stock) = {
.lock = INIT_LOCAL_TRYLOCK(lock),
+ .base = &memsw_stock,
};
+#endif
/*
* NR_OBJ_STOCK is sized so the entire hot path of obj_stock_pcp
@@ -2139,52 +2124,6 @@ static void drain_obj_stock(struct obj_stock_pcp *stock);
static bool obj_stock_flush_required(struct obj_stock_pcp *stock,
struct mem_cgroup *root_memcg);
-/**
- * consume_stock: Try to consume stocked charge on this cpu.
- * @memcg: memcg to consume from.
- * @nr_pages: how many pages to charge.
- *
- * Consume the cached charge if enough nr_pages are present otherwise return
- * failure. Also return failure for charge request larger than
- * MEMCG_CHARGE_BATCH or if the local lock is already taken.
- *
- * returns true if successful, false otherwise.
- */
-static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
-{
- struct memcg_stock_pcp *stock;
- uint8_t stock_pages;
- bool ret = false;
- int i;
-
- if (nr_pages > MEMCG_CHARGE_BATCH ||
- !local_trylock(&memcg_stock.lock))
- return ret;
-
- stock = this_cpu_ptr(&memcg_stock);
-
- for (i = 0; i < NR_MEMCG_STOCK; ++i) {
- if (memcg != READ_ONCE(stock->cached[i]))
- continue;
-
- stock_pages = READ_ONCE(stock->nr_pages[i]);
- if (stock_pages >= nr_pages) {
- stock_pages -= nr_pages;
- WRITE_ONCE(stock->nr_pages[i], stock_pages);
- if (!stock_pages) {
- css_put(&memcg->css);
- WRITE_ONCE(stock->cached[i], NULL);
- }
- ret = true;
- }
- break;
- }
-
- local_unlock(&memcg_stock.lock);
-
- return ret;
-}
-
static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)
{
page_counter_uncharge(&memcg->memory, nr_pages);
@@ -2192,49 +2131,22 @@ static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)
page_counter_uncharge(&memcg->memsw, nr_pages);
}
-/*
- * Returns stocks cached in percpu and reset cached information.
- */
-static void drain_stock(struct memcg_stock_pcp *stock, int i)
+static void drain_local_stock(struct work_struct *work)
{
- struct mem_cgroup *old = READ_ONCE(stock->cached[i]);
- uint8_t stock_pages;
-
- if (!old)
- return;
-
- stock_pages = READ_ONCE(stock->nr_pages[i]);
- if (stock_pages) {
- memcg_uncharge(old, stock_pages);
- WRITE_ONCE(stock->nr_pages[i], 0);
- }
-
- css_put(&old->css);
- WRITE_ONCE(stock->cached[i], NULL);
-}
-
-static void drain_stock_fully(struct memcg_stock_pcp *stock)
-{
- int i;
-
- for (i = 0; i < NR_MEMCG_STOCK; ++i)
- drain_stock(stock, i);
-}
-
-static void drain_local_memcg_stock(struct work_struct *dummy)
-{
- struct memcg_stock_pcp *stock;
+ struct page_counter_stock_pcp *pcp_stock;
+ struct page_counter_stock_pcp __percpu *stock;
if (WARN_ONCE(!in_task(), "drain in non-task context"))
return;
- local_lock(&memcg_stock.lock);
+ stock = container_of(work, struct page_counter_stock_pcp, work)->base;
+ local_lock(&stock->lock);
- stock = this_cpu_ptr(&memcg_stock);
- drain_stock_fully(stock);
- clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
+ pcp_stock = this_cpu_ptr(stock);
+ page_counter_drain_stock_fully(pcp_stock);
+ clear_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags);
- local_unlock(&memcg_stock.lock);
+ local_unlock(&stock->lock);
}
static void drain_local_obj_stock(struct work_struct *dummy)
@@ -2253,92 +2165,6 @@ static void drain_local_obj_stock(struct work_struct *dummy)
local_unlock(&obj_stock.lock);
}
-static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
-{
- struct memcg_stock_pcp *stock;
- struct mem_cgroup *cached;
- unsigned int stock_pages;
- bool success = false;
- int empty_slot = -1;
- int i;
-
- /*
- * nr_pages[] is a uint8_t and a slot's count is capped at
- * MEMCG_STOCK_HIGH. Raising MEMCG_CHARGE_BATCH beyond 127 would need
- * more careful handling of nr_pages[] in struct memcg_stock_pcp.
- */
- BUILD_BUG_ON(MEMCG_CHARGE_BATCH > S8_MAX);
- BUILD_BUG_ON(MEMCG_STOCK_HIGH > U8_MAX);
-
- VM_WARN_ON_ONCE(mem_cgroup_is_root(memcg));
-
- if (nr_pages > MEMCG_CHARGE_BATCH ||
- !local_trylock(&memcg_stock.lock)) {
- /*
- * In case of larger than batch refill or unlikely failure to
- * lock the percpu memcg_stock.lock, uncharge memcg directly.
- */
- memcg_uncharge(memcg, nr_pages);
- return;
- }
-
- stock = this_cpu_ptr(&memcg_stock);
- for (i = 0; i < NR_MEMCG_STOCK; ++i) {
- cached = READ_ONCE(stock->cached[i]);
- if (!cached && empty_slot == -1)
- empty_slot = i;
- if (memcg == READ_ONCE(stock->cached[i])) {
- stock_pages = READ_ONCE(stock->nr_pages[i]) + nr_pages;
- if (stock_pages > MEMCG_STOCK_HIGH) {
- memcg_uncharge(memcg,
- stock_pages - MEMCG_STOCK_LOW);
- stock_pages = MEMCG_STOCK_LOW;
- }
- WRITE_ONCE(stock->nr_pages[i], stock_pages);
- success = true;
- break;
- }
- }
-
- if (!success) {
- i = empty_slot;
- if (i == -1) {
- i = stock->drain_idx++;
- if (stock->drain_idx == NR_MEMCG_STOCK)
- stock->drain_idx = 0;
- drain_stock(stock, i);
- }
- css_get(&memcg->css);
- WRITE_ONCE(stock->cached[i], memcg);
- WRITE_ONCE(stock->nr_pages[i], nr_pages);
- }
-
- local_unlock(&memcg_stock.lock);
-}
-
-static bool is_memcg_drain_needed(struct memcg_stock_pcp *stock,
- struct mem_cgroup *root_memcg)
-{
- struct mem_cgroup *memcg;
- bool flush = false;
- int i;
-
- rcu_read_lock();
- for (i = 0; i < NR_MEMCG_STOCK; ++i) {
- memcg = READ_ONCE(stock->cached[i]);
- if (!memcg)
- continue;
-
- if (READ_ONCE(stock->nr_pages[i]) &&
- mem_cgroup_is_descendant(memcg, root_memcg)) {
- flush = true;
- break;
- }
- }
- rcu_read_unlock();
- return flush;
-}
-
static bool schedule_drain_work(int cpu, struct work_struct *work)
{
/*
@@ -2355,12 +2181,30 @@ static bool schedule_drain_work(int cpu, struct work_struct *work)
return true;
}
+static void schedule_stock_drain(struct page_counter_stock_pcp __percpu *stock,
+ struct cgroup_subsys_state *root_css,
+ int cpu, int curcpu)
+{
+ struct page_counter_stock_pcp *pcp_stock = per_cpu_ptr(stock, cpu);
+
+ if (test_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags) ||
+ !page_counter_stock_flush_required(pcp_stock, root_css) ||
+ test_and_set_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags))
+ return;
+
+ if (cpu == curcpu)
+ drain_local_stock(&pcp_stock->work);
+ else if (!schedule_drain_work(cpu, &pcp_stock->work))
+ clear_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags);
+}
+
/*
* Drains all per-CPU charge caches for given root_memcg resp. subtree
* of the hierarchy under it.
*/
void drain_all_stock(struct mem_cgroup *root_memcg)
{
+ struct cgroup_subsys_state *root_css = &root_memcg->css;
int cpu, curcpu;
/* If someone's already draining, avoid adding running more workers. */
@@ -2370,24 +2214,18 @@ void drain_all_stock(struct mem_cgroup *root_memcg)
* Notify other cpus that system-wide "drain" is running
* We do not care about races with the cpu hotplug because cpu down
* as well as workers from this path always operate on the local
- * per-cpu data. CPU up doesn't touch memcg_stock at all.
+ * per-cpu data. CPU up doesn't touch the stocks at all.
*/
migrate_disable();
curcpu = smp_processor_id();
for_each_online_cpu(cpu) {
- struct memcg_stock_pcp *memcg_st = &per_cpu(memcg_stock, cpu);
struct obj_stock_pcp *obj_st = &per_cpu(obj_stock, cpu);
- if (!test_bit(FLUSHING_CACHED_CHARGE, &memcg_st->flags) &&
- is_memcg_drain_needed(memcg_st, root_memcg) &&
- !test_and_set_bit(FLUSHING_CACHED_CHARGE,
- &memcg_st->flags)) {
- if (cpu == curcpu)
- drain_local_memcg_stock(&memcg_st->work);
- else if (!schedule_drain_work(cpu, &memcg_st->work))
- clear_bit(FLUSHING_CACHED_CHARGE,
- &memcg_st->flags);
- }
+ schedule_stock_drain(&memory_stock, root_css, cpu, curcpu);
+#ifdef CONFIG_MEMCG_V1
+ if (do_memsw_account())
+ schedule_stock_drain(&memsw_stock, root_css, cpu, curcpu);
+#endif
if (!test_bit(FLUSHING_CACHED_CHARGE, &obj_st->flags) &&
obj_stock_flush_required(obj_st, root_memcg) &&
@@ -2406,12 +2244,19 @@ void drain_all_stock(struct mem_cgroup *root_memcg)
static int memcg_hotplug_cpu_dead(unsigned int cpu)
{
- struct memcg_stock_pcp *memcg_st = &per_cpu(memcg_stock, cpu);
+ struct page_counter_stock_pcp *stock;
struct obj_stock_pcp *obj_st = &per_cpu(obj_stock, cpu);
/* no need for the local lock */
drain_obj_stock(obj_st);
- drain_stock_fully(memcg_st);
+ stock = per_cpu_ptr(&memory_stock, cpu);
+ page_counter_drain_stock_fully(stock);
+ clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
+#ifdef CONFIG_MEMCG_V1
+ stock = per_cpu_ptr(&memsw_stock, cpu);
+ page_counter_drain_stock_fully(stock);
+ clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
+#endif
/*
* A drain work queued before the CPU went away is executed by an
@@ -2419,7 +2264,6 @@ static int memcg_hotplug_cpu_dead(unsigned int cpu)
* clear the flags here to make these stocks drainable again once
* the CPU comes back online.
*/
- clear_bit(FLUSHING_CACHED_CHARGE, &memcg_st->flags);
clear_bit(FLUSHING_CACHED_CHARGE, &obj_st->flags);
return 0;
@@ -2699,10 +2543,10 @@ void __mem_cgroup_handle_over_high(gfp_t gfp_mask)
static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned int nr_pages)
{
- unsigned int batch = max(MEMCG_CHARGE_BATCH, nr_pages);
int nr_retries = MAX_RECLAIM_RETRIES;
struct mem_cgroup *mem_over_limit;
struct page_counter *counter;
+ unsigned long nr_charged;
unsigned long nr_reclaimed;
bool passed_oom = false;
unsigned int reclaim_options;
@@ -2710,33 +2554,30 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
bool raised_max_event = false;
unsigned long pflags;
bool allow_spinning = gfpflags_allow_spinning(gfp_mask);
+ bool may_batch = allow_spinning;
int ret = 0;
retry:
- if (consume_stock(memcg, nr_pages))
- return ret;
-
- if (!allow_spinning)
- /* Avoid the refill and flush of the older stock */
- batch = nr_pages;
-
reclaim_options = MEMCG_RECLAIM_MAY_SWAP;
- if (!do_memsw_account() ||
- page_counter_try_charge(&memcg->memsw, batch, &counter)) {
- if (page_counter_try_charge(&memcg->memory, batch, &counter))
- goto done_restock;
- if (do_memsw_account())
- page_counter_uncharge(&memcg->memsw, batch);
- mem_over_limit = mem_cgroup_from_counter(counter, memory);
- } else {
+ if (do_memsw_account() &&
+ !page_counter_try_charge(&memcg->memsw, nr_pages, &counter,
+ may_batch, NULL)) {
mem_over_limit = mem_cgroup_from_counter(counter, memsw);
reclaim_options &= ~MEMCG_RECLAIM_MAY_SWAP;
+ goto reclaim;
}
- if (batch > nr_pages) {
- batch = nr_pages;
- goto retry;
- }
+ if (page_counter_try_charge(&memcg->memory, nr_pages, &counter,
+ may_batch, &nr_charged))
+ goto check_high;
+
+ if (do_memsw_account())
+ page_counter_uncharge(&memcg->memsw, nr_pages);
+ mem_over_limit = mem_cgroup_from_counter(counter, memory);
+
+reclaim:
+ /* Do not retry speculative batch charges after the first miss. */
+ may_batch = false;
/*
* Prevent unbounded recursion when reclaim operations need to
@@ -2849,10 +2690,9 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
return ret;
-done_restock:
- if (batch > nr_pages)
- refill_stock(memcg, batch - nr_pages);
-
+check_high:
+ if (!nr_charged)
+ return ret;
/*
* If the hierarchy is above the normal consumption range, schedule
* reclaim on returning to userland. We can perform reclaim here
@@ -2892,7 +2732,7 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
* and distribute reclaim work and delay penalties
* based on how much each task is actually allocating.
*/
- current->memcg_nr_pages_over_high += batch;
+ current->memcg_nr_pages_over_high += nr_charged;
set_notify_resume(current);
break;
}
@@ -3197,8 +3037,11 @@ static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,
account_kmem_nmi_safe(memcg, -nr_pages);
memcg1_account_kmem(memcg, -nr_pages);
- if (!mem_cgroup_is_root(memcg))
- refill_stock(memcg, nr_pages);
+ if (!mem_cgroup_is_root(memcg)) {
+ page_counter_refill_stock(&memcg->memory, nr_pages);
+ if (do_memsw_account())
+ page_counter_refill_stock(&memcg->memsw, nr_pages);
+ }
css_put(&memcg->css);
}
@@ -4345,8 +4188,14 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
if (parent) {
page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
+ memcg->memory.stock = &memory_stock;
+ memcg->memory.stock_css = &memcg->css;
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
+ if (!memcg_on_dfl) {
+ memcg->memsw.stock = &memsw_stock;
+ memcg->memsw.stock_css = &memcg->css;
+ }
WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
memcg->memory.track_failcnt = !memcg_on_dfl;
memcg->memsw.track_failcnt = !memcg_on_dfl;
@@ -5813,7 +5662,7 @@ void mem_cgroup_sk_uncharge(const struct sock *sk, unsigned int nr_pages)
mod_memcg_state(memcg, MEMCG_SOCK, -nr_pages);
- refill_stock(memcg, nr_pages);
+ page_counter_refill_stock(&memcg->memory, nr_pages);
}
void mem_cgroup_flush_workqueue(void)
@@ -5961,6 +5810,8 @@ int __init mem_cgroup_init(void)
* exceed S32_MAX / PAGE_SIZE.
*/
BUILD_BUG_ON(MEMCG_CHARGE_BATCH > S32_MAX / PAGE_SIZE);
+ /* Batched page-counter charges feed memcg's memory.high accounting. */
+ BUILD_BUG_ON(MEMCG_CHARGE_BATCH != PAGE_COUNTER_STOCK_BATCH);
memcg_struct_check();
@@ -5971,8 +5822,12 @@ int __init mem_cgroup_init(void)
WARN_ON(!memcg_wq);
for_each_possible_cpu(cpu) {
- INIT_WORK(&per_cpu_ptr(&memcg_stock, cpu)->work,
- drain_local_memcg_stock);
+ INIT_WORK(&per_cpu_ptr(&memory_stock, cpu)->work,
+ drain_local_stock);
+#ifdef CONFIG_MEMCG_V1
+ INIT_WORK(&per_cpu_ptr(&memsw_stock, cpu)->work,
+ drain_local_stock);
+#endif
INIT_WORK(&per_cpu_ptr(&obj_stock, cpu)->work,
drain_local_obj_stock);
}
@@ -6023,7 +5878,8 @@ int __mem_cgroup_try_charge_swap(struct folio *folio)
private_id = mem_cgroup_private_id_get(memcg, nr_pages);
if (!mem_cgroup_private_id_is_root(private_id) &&
- !page_counter_try_charge(&memcg->swap, nr_pages, &counter)) {
+ !page_counter_try_charge(&memcg->swap, nr_pages, &counter,
+ false, NULL)) {
memcg_memory_event(memcg, MEMCG_SWAP_MAX);
memcg_memory_event(memcg, MEMCG_SWAP_FAIL);
mem_cgroup_private_id_put(private_id, nr_pages);
diff --git a/mm/page_counter.c b/mm/page_counter.c
index 9167ffd1380c8..e48ef37016c50 100644
--- a/mm/page_counter.c
+++ b/mm/page_counter.c
@@ -7,6 +7,7 @@
#include <linux/page_counter.h>
#include <linux/atomic.h>
+#include <linux/cgroup.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/string.h>
@@ -14,6 +15,14 @@
#include <linux/bug.h>
#include <asm/page.h>
+/*
+ * Watermarks for a charge stock slot, in the spirit of pcp->high and
+ * pcp->batch: PAGE_COUNTER_STOCK_HIGH is the high watermark at which a slot is
+ * trimmed down to PAGE_COUNTER_STOCK_LOW rather than emptied.
+ */
+#define PAGE_COUNTER_STOCK_LOW (PAGE_COUNTER_STOCK_BATCH / 2)
+#define PAGE_COUNTER_STOCK_HIGH PAGE_COUNTER_STOCK_BATCH
+
static bool track_protection(struct page_counter *c)
{
return c->protection_support;
@@ -127,23 +136,76 @@ void page_counter_charge(struct page_counter *counter, unsigned long nr_pages)
}
}
+/*
+ * Consume the cached charge if enough nr_pages are present, otherwise return
+ * failure. Also return failure for charge requests larger than
+ * PAGE_COUNTER_STOCK_BATCH or if the local lock is already taken.
+ */
+static bool page_counter_consume_stock(struct page_counter *counter,
+ unsigned long nr_pages)
+{
+ struct page_counter_stock_pcp __percpu *stock = counter->stock;
+ struct page_counter_stock_pcp *pcp_stock;
+ u8 stock_pages;
+ bool ret = false;
+ int i;
+
+ if (nr_pages > PAGE_COUNTER_STOCK_BATCH ||
+ !local_trylock(&stock->lock))
+ return false;
+
+ pcp_stock = this_cpu_ptr(stock);
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++) {
+ if (counter != READ_ONCE(pcp_stock->cached[i]))
+ continue;
+
+ stock_pages = READ_ONCE(pcp_stock->nr_pages[i]);
+ if (stock_pages >= nr_pages) {
+ stock_pages -= nr_pages;
+ WRITE_ONCE(pcp_stock->nr_pages[i], stock_pages);
+ if (!stock_pages) {
+ css_put(counter->stock_css);
+ WRITE_ONCE(pcp_stock->cached[i], NULL);
+ }
+ ret = true;
+ }
+ break;
+ }
+ local_unlock(&stock->lock);
+
+ return ret;
+}
+
/**
- * page_counter_try_charge - try to hierarchically charge pages
+ * page_counter_try_charge - try to hierarchically charge pages using stock
* @counter: counter
- * @nr_pages: number of pages to charge
- * @fail: points first counter to hit its limit, if any
+ * @nr_pages: number of pages requested
+ * @fail: points to the first counter to hit its limit, if any
+ * @may_batch: whether a stock miss may trigger a batch charge
+ * @nr_charged: optional; set to the hierarchy charge size on success
*
- * Returns %true on success, or %false and @fail if the counter or one
- * of its ancestors has hit its configured limit.
+ * Return: %true if the request was satisfied. A failed batch charge may update
+ * @fail before an exact retry succeeds.
*/
bool page_counter_try_charge(struct page_counter *counter,
- unsigned long nr_pages,
- struct page_counter **fail)
+ unsigned long nr_pages, struct page_counter **fail,
+ bool may_batch, unsigned long *nr_charged)
{
+ unsigned long charge = nr_pages;
struct page_counter *c;
bool protection = track_protection(counter);
bool track_failcnt = counter->track_failcnt;
+ if (counter->stock && may_batch)
+ charge = max(nr_pages, PAGE_COUNTER_STOCK_BATCH);
+
+retry:
+ if (counter->stock && page_counter_consume_stock(counter, nr_pages)) {
+ if (nr_charged)
+ *nr_charged = 0;
+ return true;
+ }
+
for (c = counter; c; c = c->parent) {
long new;
/*
@@ -160,9 +222,9 @@ bool page_counter_try_charge(struct page_counter *counter,
* we either see the new limit or the setter sees the
* counter has changed and retries.
*/
- new = atomic_long_add_return(nr_pages, &c->usage);
+ new = atomic_long_add_return(charge, &c->usage);
if (new > c->max) {
- atomic_long_sub(nr_pages, &c->usage);
+ atomic_long_sub(charge, &c->usage);
/*
* This is racy, but we can live with some
* inaccuracy in the failcnt which is only used
@@ -183,15 +245,142 @@ bool page_counter_try_charge(struct page_counter *counter,
WRITE_ONCE(c->watermark, new);
}
}
+ if (charge > nr_pages)
+ page_counter_refill_stock(counter, charge - nr_pages);
+ if (nr_charged)
+ *nr_charged = charge;
return true;
failed:
for (c = counter; c != *fail; c = c->parent)
- page_counter_cancel(c, nr_pages);
+ page_counter_cancel(c, charge);
+
+ if (charge > nr_pages) {
+ charge = nr_pages;
+ goto retry;
+ }
return false;
}
+static void page_counter_drain_stock(struct page_counter_stock_pcp *stock,
+ int i)
+{
+ struct page_counter *counter = READ_ONCE(stock->cached[i]);
+ u8 nr_pages;
+
+ if (!counter)
+ return;
+
+ nr_pages = READ_ONCE(stock->nr_pages[i]);
+ if (nr_pages) {
+ page_counter_uncharge(counter, nr_pages);
+ WRITE_ONCE(stock->nr_pages[i], 0);
+ }
+ css_put(counter->stock_css);
+ WRITE_ONCE(stock->cached[i], NULL);
+}
+
+void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock)
+{
+ int i;
+
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++)
+ page_counter_drain_stock(stock, i);
+}
+
+bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock,
+ struct cgroup_subsys_state *root_css)
+{
+ struct cgroup_subsys_state *css;
+ struct page_counter *counter;
+ bool flush = false;
+ int i;
+
+ rcu_read_lock();
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++) {
+ counter = READ_ONCE(stock->cached[i]);
+ if (!counter)
+ continue;
+ css = READ_ONCE(counter->stock_css);
+
+ if (READ_ONCE(stock->nr_pages[i]) &&
+ cgroup_is_descendant(css->cgroup, root_css->cgroup)) {
+ flush = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return flush;
+}
+
+/**
+ * page_counter_refill_stock - return pages to a counter's stock
+ * @counter: counter to return pages to
+ * @nr_pages: number of pages to return
+ *
+ * If the stock cannot accept the pages, uncharge them from the hierarchy.
+ */
+void page_counter_refill_stock(struct page_counter *counter,
+ unsigned long nr_pages)
+{
+ struct page_counter_stock_pcp __percpu *stock = counter->stock;
+ struct page_counter_stock_pcp *pcp_stock;
+ unsigned int stock_pages;
+ int empty_slot = -1;
+ int i;
+
+ /*
+ * nr_pages[] is a u8 and a slot is capped at PAGE_COUNTER_STOCK_HIGH.
+ * Raising PAGE_COUNTER_STOCK_BATCH beyond 127 would need careful
+ * handling of nr_pages[] in struct page_counter_stock_pcp.
+ */
+ BUILD_BUG_ON(PAGE_COUNTER_STOCK_BATCH > S8_MAX);
+ BUILD_BUG_ON(PAGE_COUNTER_STOCK_HIGH > U8_MAX);
+
+ if (!stock || nr_pages > PAGE_COUNTER_STOCK_BATCH ||
+ !local_trylock(&stock->lock)) {
+ /*
+ * For a larger-than-batch refill or an unlikely failure to lock
+ * the per-CPU stock, uncharge the hierarchy directly.
+ */
+ page_counter_uncharge(counter, nr_pages);
+ return;
+ }
+
+ pcp_stock = this_cpu_ptr(stock);
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++) {
+ struct page_counter *cached = READ_ONCE(pcp_stock->cached[i]);
+
+ if (!cached && empty_slot == -1)
+ empty_slot = i;
+ if (counter != cached)
+ continue;
+
+ stock_pages = READ_ONCE(pcp_stock->nr_pages[i]) + nr_pages;
+ if (stock_pages > PAGE_COUNTER_STOCK_HIGH) {
+ page_counter_uncharge(counter,
+ stock_pages - PAGE_COUNTER_STOCK_LOW);
+ stock_pages = PAGE_COUNTER_STOCK_LOW;
+ }
+ WRITE_ONCE(pcp_stock->nr_pages[i], stock_pages);
+ local_unlock(&stock->lock);
+ return;
+ }
+
+ i = empty_slot;
+ if (i == -1) {
+ i = pcp_stock->drain_idx++;
+ if (pcp_stock->drain_idx == NR_PAGE_COUNTER_STOCK)
+ pcp_stock->drain_idx = 0;
+ page_counter_drain_stock(pcp_stock, i);
+ }
+ css_get(counter->stock_css);
+ WRITE_ONCE(pcp_stock->cached[i], counter);
+ WRITE_ONCE(pcp_stock->nr_pages[i], nr_pages);
+ local_unlock(&stock->lock);
+}
+
/**
* page_counter_uncharge - hierarchically uncharge pages
* @counter: counter
|
| 3/2 | 2026/09/30 00:27 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13878 output: 25 thoughts: 900 Thoughts: **WorthFuzzing Evaluation: Memcg and Page Counter Refactor** Okay, let's break this down. My initial assessment is that this patch series *likely* warrants syzkaller fuzzing, but I need to be thorough. The user is asking for my expert opinion, so I'll approach this systematically. First, I need to understand what's actually being changed. This commit touches `page_counter.h`, `dmem.c`, `hugetlb_cgroup.c`, `memcontrol-v1.c`, `memcontrol.c`, and `page_counter.c`. It looks like we're moving the per-CPU stock logic for memcg charging from `mm/memcontrol.c` into a more generic `page_counter` structure, specifically within `mm/page_counter.c`. This fundamentally shifts how memory charges are handled. Previously, `memcg_stock_pcp` in `mm/memcontrol.c` managed the cached pages per CPU. Now, that logic is in `page_counter_stock_pcp`, which is now defined in `include/linux/page_counter.h`. Functions like `page_counter_try_charge`, `page_counter_refill_stock`, `page_counter_drain_stock_fully`, `page_counter_stock_flush_required`, and `page_counter_consume_stock` are implemented in `mm/page_counter.c`. I see that `try_charge_memcg` is simplified, delegating to this new `page_counter_try_charge` and that the updated parameters are passed to this new function. We also see updates to memsw stock for `CONFIG_MEMCG_V1`. Key functions that have been updated are `drain_all_stock`, `memcg_hotplug_cpu_dead`, `memcg_css_alloc`, `obj_cgroup_uncharge_pages`, and `mem_cgroup_sk_uncharge`. The next critical step is to determine if this code is reachable. The answer is a resounding "yes." These files are all part of core memory management (mm/), specifically related to memcg and page accounting. Memcg is a fundamental kernel feature, meaning that it is heavily used by general workloads. This is precisely the kind of area syzkaller excels at targeting. Now, let's apply the fuzzing triage philosophy. This definitely qualifies. It touches reachable, executable code in a core subsystem (memory management). We're seeing *significant* refactoring of data structures, especially regarding how per-CPU memory stocks are handled. This directly alters memory charging, concurrency with per-CPU local locks, and atomic operations. This touches on aspects like reference counting using `css_get`/`css_put` which is key when accounting for memory in the kernel. Therefore, WorthFuzzing = true. However, before finalizing, I need to double-check that this is a definitive call. I'll need to confirm that this memory cgroup stuff is active in a reasonable configuration. I will check `Kconfig` for the relevant symbols (`page_counter`, `memcontrol`, etc.) and scan `mm/Makefile` and `Kconfig` with `grepper` to see if `MEMCG` is enabled in common configurations and is not somehow behind a highly specific, rarely enabled option. If all checks pass, I will recommend it for fuzzing. |
| 4/2 | 2026/09/30 00:28 | tool | grepper |
0mArgs:{
"Expression": "page_counter",
"PathPrefix": "mm/"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 841 lines.\nUse more precise expression if possible.\n\nmm/Makefile=102=obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o khugepaged.o\nmm/Makefile:103:obj-$(CONFIG_PAGE_COUNTER) += page_counter.o\nmm/Makefile-104-obj-$(CONFIG_LIVEUPDATE_MEMFD) += memfd_luo.o\n--\nmm/bpf_memcontrol.c=113=__bpf_kfunc unsigned long bpf_mem_cgroup_usage(struct mem_cgroup *memcg)\nmm/bpf_memcontrol.c-114-{\nmm/bpf_memcontrol.c:115:\treturn page_counter_read(\u0026memcg-\u003ememory) * PAGE_SIZE;\nmm/bpf_memcontrol.c-116-}\n--\nmm/hugetlb_cgroup.c-13-#include \u003clinux/cgroup.h\u003e\nmm/hugetlb_cgroup.c:14:#include \u003clinux/page_counter.h\u003e\nmm/hugetlb_cgroup.c-15-#include \u003clinux/slab.h\u003e\n--\nmm/hugetlb_cgroup.c=33=static struct cftype *legacy_files;\nmm/hugetlb_cgroup.c-34-\nmm/hugetlb_cgroup.c:35:static inline struct page_counter *\nmm/hugetlb_cgroup.c-36-__hugetlb_cgroup_counter_from_cgroup(struct hugetlb_cgroup *h_cg, int idx,\n--\nmm/hugetlb_cgroup.c-43-\nmm/hugetlb_cgroup.c:44:static inline struct page_counter *\nmm/hugetlb_cgroup.c-45-hugetlb_cgroup_counter_from_cgroup(struct hugetlb_cgroup *h_cg, int idx)\n--\nmm/hugetlb_cgroup.c-49-\nmm/hugetlb_cgroup.c:50:static inline struct page_counter *\nmm/hugetlb_cgroup.c-51-hugetlb_cgroup_counter_from_cgroup_rsvd(struct hugetlb_cgroup *h_cg, int idx)\n--\nmm/hugetlb_cgroup.c=79=static inline bool hugetlb_cgroup_have_usage(struct hugetlb_cgroup *h_cg)\n--\nmm/hugetlb_cgroup.c-83-\tfor_each_hstate(h) {\nmm/hugetlb_cgroup.c:84:\t\tif (page_counter_read(\nmm/hugetlb_cgroup.c-85-\t\t hugetlb_cgroup_counter_from_cgroup(h_cg, hstate_index(h))))\n--\nmm/hugetlb_cgroup.c=91=static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup,\n--\nmm/hugetlb_cgroup.c-96-\tfor (idx = 0; idx \u003c HUGE_MAX_HSTATE; idx++) {\nmm/hugetlb_cgroup.c:97:\t\tstruct page_counter *fault, *fault_parent = NULL;\nmm/hugetlb_cgroup.c:98:\t\tstruct page_counter *rsvd, *rsvd_parent = NULL;\nmm/hugetlb_cgroup.c-99-\t\tunsigned long limit;\n--\nmm/hugetlb_cgroup.c-110-\nmm/hugetlb_cgroup.c:111:\t\tpage_counter_init(fault, fault_parent, false);\nmm/hugetlb_cgroup.c:112:\t\tpage_counter_init(rsvd, rsvd_parent, false);\nmm/hugetlb_cgroup.c-113-\n--\nmm/hugetlb_cgroup.c-121-\nmm/hugetlb_cgroup.c:122:\t\tret = page_counter_set_max(fault, limit);\nmm/hugetlb_cgroup.c-123-\t\tVM_WARN_ON_ONCE(ret);\nmm/hugetlb_cgroup.c:124:\t\tret = page_counter_set_max(rsvd, limit);\nmm/hugetlb_cgroup.c-125-\t\tVM_WARN_ON_ONCE(ret);\n--\nmm/hugetlb_cgroup.c=217=static void hugetlb_cgroup_move_parent(int idx, struct hugetlb_cgroup *h_cg,\n--\nmm/hugetlb_cgroup.c-220-\tunsigned int nr_pages;\nmm/hugetlb_cgroup.c:221:\tstruct page_counter *counter;\nmm/hugetlb_cgroup.c-222-\tstruct hugetlb_cgroup *hcg;\n--\nmm/hugetlb_cgroup.c-237-\t\t/* root has no limit */\nmm/hugetlb_cgroup.c:238:\t\tpage_counter_charge(\u0026parent-\u003ehugepage[idx], nr_pages);\nmm/hugetlb_cgroup.c-239-\t}\n--\nmm/hugetlb_cgroup.c-241-\t/* Take the pages off the local counter */\nmm/hugetlb_cgroup.c:242:\tpage_counter_cancel(counter, nr_pages);\nmm/hugetlb_cgroup.c-243-\n--\nmm/hugetlb_cgroup.c=286=static int __hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,\n--\nmm/hugetlb_cgroup.c-290-\tint ret = 0;\nmm/hugetlb_cgroup.c:291:\tstruct page_counter *counter;\nmm/hugetlb_cgroup.c-292-\tstruct hugetlb_cgroup *h_cg = NULL;\n--\nmm/hugetlb_cgroup.c-304-\nmm/hugetlb_cgroup.c:305:\tif (!page_counter_try_charge(\nmm/hugetlb_cgroup.c-306-\t\t __hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),\n--\nmm/hugetlb_cgroup.c=374=static void __hugetlb_cgroup_uncharge_folio(int idx, unsigned long nr_pages,\n--\nmm/hugetlb_cgroup.c-386-\nmm/hugetlb_cgroup.c:387:\tpage_counter_uncharge(__hugetlb_cgroup_counter_from_cgroup(h_cg, idx,\nmm/hugetlb_cgroup.c-388-\t\t\t\t\t\t\t\t rsvd),\n--\nmm/hugetlb_cgroup.c=418=static void __hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,\n--\nmm/hugetlb_cgroup.c-424-\nmm/hugetlb_cgroup.c:425:\tpage_counter_uncharge(__hugetlb_cgroup_counter_from_cgroup(h_cg, idx,\nmm/hugetlb_cgroup.c-426-\t\t\t\t\t\t\t\t rsvd),\n--\nmm/hugetlb_cgroup.c=445=void hugetlb_cgroup_uncharge_counter(struct resv_map *resv, unsigned long start,\n--\nmm/hugetlb_cgroup.c-451-\nmm/hugetlb_cgroup.c:452:\tpage_counter_uncharge(resv-\u003ereservation_counter,\nmm/hugetlb_cgroup.c-453-\t\t\t (end - start) * resv-\u003epages_per_hpage);\n--\nmm/hugetlb_cgroup.c=457=void hugetlb_cgroup_uncharge_file_region(struct resv_map *resv,\n--\nmm/hugetlb_cgroup.c-466-\t !resv-\u003ereservation_counter) {\nmm/hugetlb_cgroup.c:467:\t\tpage_counter_uncharge(rg-\u003ereservation_counter,\nmm/hugetlb_cgroup.c-468-\t\t\t\t nr_pages * resv-\u003epages_per_hpage);\n--\nmm/hugetlb_cgroup.c=489=static int hugetlb_cgroup_read_numa_stat(struct seq_file *seq, void *dummy)\n--\nmm/hugetlb_cgroup.c-518-\tseq_printf(seq, \"%stotal=%lu\", legacy ? \"hierarchical_\" : \"\",\nmm/hugetlb_cgroup.c:519:\t\t page_counter_read(\u0026h_cg-\u003ehugepage[idx]) * PAGE_SIZE);\nmm/hugetlb_cgroup.c-520-\n--\nmm/hugetlb_cgroup.c=542=static u64 hugetlb_cgroup_read_u64(struct cgroup_subsys_state *css,\n--\nmm/hugetlb_cgroup.c-544-{\nmm/hugetlb_cgroup.c:545:\tstruct page_counter *counter;\nmm/hugetlb_cgroup.c:546:\tstruct page_counter *rsvd_counter;\nmm/hugetlb_cgroup.c-547-\tstruct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css);\n--\nmm/hugetlb_cgroup.c-553-\tcase RES_USAGE:\nmm/hugetlb_cgroup.c:554:\t\treturn (u64)page_counter_read(counter) * PAGE_SIZE;\nmm/hugetlb_cgroup.c-555-\tcase RES_RSVD_USAGE:\nmm/hugetlb_cgroup.c:556:\t\treturn (u64)page_counter_read(rsvd_counter) * PAGE_SIZE;\nmm/hugetlb_cgroup.c-557-\tcase RES_LIMIT:\n--\nmm/hugetlb_cgroup.c=574=static int hugetlb_cgroup_read_u64_max(struct seq_file *seq, void *v)\n--\nmm/hugetlb_cgroup.c-579-\tunsigned long limit;\nmm/hugetlb_cgroup.c:580:\tstruct page_counter *counter;\nmm/hugetlb_cgroup.c-581-\tstruct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(seq_css(seq));\n--\nmm/hugetlb_cgroup.c-593-\tcase RES_USAGE:\nmm/hugetlb_cgroup.c:594:\t\tval = (u64)page_counter_read(counter);\nmm/hugetlb_cgroup.c-595-\t\tseq_printf(seq, \"%llu\\n\", val * PAGE_SIZE);\n--\nmm/hugetlb_cgroup.c=616=static ssize_t hugetlb_cgroup_write(struct kernfs_open_file *of,\n--\nmm/hugetlb_cgroup.c-628-\tbuf = strstrip(buf);\nmm/hugetlb_cgroup.c:629:\tret = page_counter_memparse(buf, max, \u0026nr_pages);\nmm/hugetlb_cgroup.c-630-\tif (ret)\n--\nmm/hugetlb_cgroup.c-641-\t\tmutex_lock(\u0026hugetlb_limit_mutex);\nmm/hugetlb_cgroup.c:642:\t\tret = page_counter_set_max(\nmm/hugetlb_cgroup.c-643-\t\t\t__hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),\n--\nmm/hugetlb_cgroup.c=666=static ssize_t hugetlb_cgroup_reset(struct kernfs_open_file *of,\n--\nmm/hugetlb_cgroup.c-669-\tint ret = 0;\nmm/hugetlb_cgroup.c:670:\tstruct page_counter *counter, *rsvd_counter;\nmm/hugetlb_cgroup.c-671-\tstruct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));\n--\nmm/hugetlb_cgroup.c-677-\tcase RES_MAX_USAGE:\nmm/hugetlb_cgroup.c:678:\t\tpage_counter_reset_watermark(counter);\nmm/hugetlb_cgroup.c-679-\t\tbreak;\nmm/hugetlb_cgroup.c-680-\tcase RES_RSVD_MAX_USAGE:\nmm/hugetlb_cgroup.c:681:\t\tpage_counter_reset_watermark(rsvd_counter);\nmm/hugetlb_cgroup.c-682-\t\tbreak;\n--\nmm/memcontrol-v1.c=111=static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)\n--\nmm/memcontrol-v1.c-125-\t\tif (!swap) {\nmm/memcontrol-v1.c:126:\t\t\tval = page_counter_read(\u0026memcg-\u003ememory);\nmm/memcontrol-v1.c-127-\t\t} else {\nmm/memcontrol-v1.c-128-\t\t\t/* Preserve the user-visible memory \u003c= memsw invariant. */\nmm/memcontrol-v1.c:129:\t\t\tval = max(page_counter_read(\u0026memcg-\u003ememory),\nmm/memcontrol-v1.c:130:\t\t\t\t page_counter_read(\u0026memcg-\u003ememsw));\nmm/memcontrol-v1.c-131-\t\t}\n--\nmm/memcontrol-v1.c=272=void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci)\n--\nmm/memcontrol-v1.c-312-\tif (!obj_cgroup_is_root(objcg)) {\nmm/memcontrol-v1.c:313:\t\tpage_counter_uncharge(\u0026memcg-\u003ememory, nr_entries);\nmm/memcontrol-v1.c-314-\nmm/memcontrol-v1.c-315-\t\tif (mem_cgroup_private_id_is_root(private_id))\nmm/memcontrol-v1.c:316:\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_entries);\nmm/memcontrol-v1.c-317-\t}\n--\nmm/memcontrol-v1.c=427=static int __mem_cgroup_usage_register_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-435-\nmm/memcontrol-v1.c:436:\tret = page_counter_memparse(args, \"-1\", \u0026threshold);\nmm/memcontrol-v1.c-437-\tif (ret)\n--\nmm/memcontrol-v1.c=1424=static int mem_cgroup_resize_max(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-1430-\tbool limits_invariant;\nmm/memcontrol-v1.c:1431:\tstruct page_counter *counter = memsw ? \u0026memcg-\u003ememsw : \u0026memcg-\u003ememory;\nmm/memcontrol-v1.c-1432-\n--\nmm/memcontrol-v1.c-1452-\t\t\tenlarge = true;\nmm/memcontrol-v1.c:1453:\t\tret = page_counter_set_max(counter, max);\nmm/memcontrol-v1.c-1454-\t\tmutex_unlock(\u0026memcg_max_mutex);\n--\nmm/memcontrol-v1.c=1487=static int mem_cgroup_force_empty(struct mem_cgroup *memcg)\n--\nmm/memcontrol-v1.c-1496-\t/* try to free all pages in this cgroup */\nmm/memcontrol-v1.c:1497:\twhile (nr_retries \u0026\u0026 page_counter_read(\u0026memcg-\u003ememory)) {\nmm/memcontrol-v1.c-1498-\t\tif (signal_pending(current))\n--\nmm/memcontrol-v1.c=1549=static ssize_t mem_cgroup_soft_limit_write(struct kernfs_open_file *of,\n--\nmm/memcontrol-v1.c-1554-\nmm/memcontrol-v1.c:1555:\tret = page_counter_memparse(strstrip(buf), \"-1\", \u0026nr_pages);\nmm/memcontrol-v1.c-1556-\tif (ret)\n--\nmm/memcontrol-v1.c=1567=static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,\n--\nmm/memcontrol-v1.c-1570-\tstruct mem_cgroup *memcg = mem_cgroup_from_css(css);\nmm/memcontrol-v1.c:1571:\tstruct page_counter *counter;\nmm/memcontrol-v1.c-1572-\n--\nmm/memcontrol-v1.c-1595-\t\t\treturn (u64)mem_cgroup_usage(memcg, true) * PAGE_SIZE;\nmm/memcontrol-v1.c:1596:\t\treturn (u64)page_counter_read(counter) * PAGE_SIZE;\nmm/memcontrol-v1.c-1597-\tcase RES_LIMIT:\n--\nmm/memcontrol-v1.c=1618=static int memcg_update_tcp_max(struct mem_cgroup *memcg, unsigned long max)\n--\nmm/memcontrol-v1.c-1623-\nmm/memcontrol-v1.c:1624:\tret = page_counter_set_max(\u0026memcg-\u003etcpmem, max);\nmm/memcontrol-v1.c-1625-\tif (ret)\n--\nmm/memcontrol-v1.c=1657=static ssize_t mem_cgroup_write(struct kernfs_open_file *of,\n--\nmm/memcontrol-v1.c-1664-\tbuf = strstrip(buf);\nmm/memcontrol-v1.c:1665:\tret = page_counter_memparse(buf, \"-1\", \u0026nr_pages);\nmm/memcontrol-v1.c-1666-\tif (ret)\n--\nmm/memcontrol-v1.c=1701=static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf,\n--\nmm/memcontrol-v1.c-1704-\tstruct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));\nmm/memcontrol-v1.c:1705:\tstruct page_counter *counter;\nmm/memcontrol-v1.c-1706-\n--\nmm/memcontrol-v1.c-1725-\tcase RES_MAX_USAGE:\nmm/memcontrol-v1.c:1726:\t\tpage_counter_reset_watermark(counter);\nmm/memcontrol-v1.c-1727-\t\tbreak;\n--\nmm/memcontrol-v1.c=2184=void memcg1_account_kmem(struct mem_cgroup *memcg, int nr_pages)\n--\nmm/memcontrol-v1.c-2187-\t\tif (nr_pages \u003e 0)\nmm/memcontrol-v1.c:2188:\t\t\tpage_counter_charge(\u0026memcg-\u003ekmem, nr_pages);\nmm/memcontrol-v1.c-2189-\t\telse\nmm/memcontrol-v1.c:2190:\t\t\tpage_counter_uncharge(\u0026memcg-\u003ekmem, -nr_pages);\nmm/memcontrol-v1.c-2191-\t}\n--\nmm/memcontrol-v1.c=2194=bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,\n--\nmm/memcontrol-v1.c-2196-{\nmm/memcontrol-v1.c:2197:\tstruct page_counter *fail;\nmm/memcontrol-v1.c-2198-\nmm/memcontrol-v1.c:2199:\tif (page_counter_try_charge(\u0026memcg-\u003etcpmem, nr_pages, \u0026fail,\nmm/memcontrol-v1.c-2200-\t\t\t\t false, NULL)) {\n--\nmm/memcontrol-v1.c-2205-\tif (gfp_mask \u0026 __GFP_NOFAIL) {\nmm/memcontrol-v1.c:2206:\t\tpage_counter_charge(\u0026memcg-\u003etcpmem, nr_pages);\nmm/memcontrol-v1.c-2207-\t\treturn true;\n--\nmm/memcontrol-v1.h=85=static inline void memcg1_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)\nmm/memcontrol-v1.h-86-{\nmm/memcontrol-v1.h:87:\tpage_counter_uncharge(\u0026memcg-\u003etcpmem, nr_pages);\nmm/memcontrol-v1.h-88-}\n--\nmm/memcontrol.c-28-#include \u003clinux/cgroup-defs.h\u003e\nmm/memcontrol.c:29:#include \u003clinux/page_counter.h\u003e\nmm/memcontrol.c-30-#include \u003clinux/memcontrol.h\u003e\n--\nmm/memcontrol.c=1582=static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1587-\nmm/memcontrol.c:1588:\tcount = page_counter_read(\u0026memcg-\u003ememory);\nmm/memcontrol.c-1589-\tlimit = READ_ONCE(memcg-\u003ememory.max);\n--\nmm/memcontrol.c-1593-\tif (do_memsw_account()) {\nmm/memcontrol.c:1594:\t\tcount = page_counter_read(\u0026memcg-\u003ememsw);\nmm/memcontrol.c-1595-\t\tlimit = READ_ONCE(memcg-\u003ememsw.max);\n--\nmm/memcontrol.c=1856=void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1870-\tpr_info(\"memory: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1871:\t\tK((u64)page_counter_read(\u0026memcg-\u003ememory)),\nmm/memcontrol.c-1872-\t\tK((u64)READ_ONCE(memcg-\u003ememory.max)), memory_failcnt);\n--\nmm/memcontrol.c-1874-\t\tpr_info(\"swap: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1875:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003eswap)),\nmm/memcontrol.c-1876-\t\t\tK((u64)READ_ONCE(memcg-\u003eswap.max)),\n--\nmm/memcontrol.c-1880-\t\tpr_info(\"memory+swap: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1881:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003ememsw)),\nmm/memcontrol.c-1882-\t\t\tK((u64)memcg-\u003ememsw.max), memcg-\u003ememsw.failcnt);\nmm/memcontrol.c-1883-\t\tpr_info(\"kmem: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1884:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003ekmem)),\nmm/memcontrol.c-1885-\t\t\tK((u64)memcg-\u003ekmem.max), memcg-\u003ekmem.failcnt);\n--\nmm/memcontrol.c=2059=void mem_cgroup_print_oom_group(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-2067-\nmm/memcontrol.c:2068:static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memory_stock) = {\nmm/memcontrol.c-2069-\t.lock = INIT_LOCAL_TRYLOCK(lock),\n--\nmm/memcontrol.c-2073-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:2074:static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memsw_stock) = {\nmm/memcontrol.c-2075-\t.lock = INIT_LOCAL_TRYLOCK(lock),\n--\nmm/memcontrol.c=2127=static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\nmm/memcontrol.c-2128-{\nmm/memcontrol.c:2129:\tpage_counter_uncharge(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c-2130-\tif (do_memsw_account())\nmm/memcontrol.c:2131:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2132-}\n--\nmm/memcontrol.c=2134=static void drain_local_stock(struct work_struct *work)\nmm/memcontrol.c-2135-{\nmm/memcontrol.c:2136:\tstruct page_counter_stock_pcp *pcp_stock;\nmm/memcontrol.c:2137:\tstruct page_counter_stock_pcp __percpu *stock;\nmm/memcontrol.c-2138-\n--\nmm/memcontrol.c-2141-\nmm/memcontrol.c:2142:\tstock = container_of(work, struct page_counter_stock_pcp, work)-\u003ebase;\nmm/memcontrol.c-2143-\tlocal_lock(\u0026stock-\u003elock);\n--\nmm/memcontrol.c-2145-\tpcp_stock = this_cpu_ptr(stock);\nmm/memcontrol.c:2146:\tpage_counter_drain_stock_fully(pcp_stock);\nmm/memcontrol.c-2147-\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags);\n--\nmm/memcontrol.c=2168=static bool schedule_drain_work(int cpu, struct work_struct *work)\n--\nmm/memcontrol.c-2183-\nmm/memcontrol.c:2184:static void schedule_stock_drain(struct page_counter_stock_pcp __percpu *stock,\nmm/memcontrol.c-2185-\t\t\t\t struct cgroup_subsys_state *root_css,\n--\nmm/memcontrol.c-2187-{\nmm/memcontrol.c:2188:\tstruct page_counter_stock_pcp *pcp_stock = per_cpu_ptr(stock, cpu);\nmm/memcontrol.c-2189-\nmm/memcontrol.c-2190-\tif (test_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags) ||\nmm/memcontrol.c:2191:\t !page_counter_stock_flush_required(pcp_stock, root_css) ||\nmm/memcontrol.c-2192-\t test_and_set_bit(FLUSHING_CACHED_CHARGE, \u0026pcp_stock-\u003eflags))\n--\nmm/memcontrol.c=2245=static int memcg_hotplug_cpu_dead(unsigned int cpu)\nmm/memcontrol.c-2246-{\nmm/memcontrol.c:2247:\tstruct page_counter_stock_pcp *stock;\nmm/memcontrol.c-2248-\tstruct obj_stock_pcp *obj_st = \u0026per_cpu(obj_stock, cpu);\n--\nmm/memcontrol.c-2252-\tstock = per_cpu_ptr(\u0026memory_stock, cpu);\nmm/memcontrol.c:2253:\tpage_counter_drain_stock_fully(stock);\nmm/memcontrol.c-2254-\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026stock-\u003eflags);\n--\nmm/memcontrol.c-2256-\tstock = per_cpu_ptr(\u0026memsw_stock, cpu);\nmm/memcontrol.c:2257:\tpage_counter_drain_stock_fully(stock);\nmm/memcontrol.c-2258-\tclear_bit(FLUSHING_CACHED_CHARGE, \u0026stock-\u003eflags);\n--\nmm/memcontrol.c=2272=static unsigned long reclaim_high(struct mem_cgroup *memcg,\n--\nmm/memcontrol.c-2280-\nmm/memcontrol.c:2281:\t\tif (page_counter_read(\u0026memcg-\u003ememory) \u003c=\nmm/memcontrol.c-2282-\t\t READ_ONCE(memcg-\u003ememory.high))\n--\nmm/memcontrol.c=2383=static u64 mem_find_max_overage(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-2387-\tdo {\nmm/memcontrol.c:2388:\t\toverage = calculate_overage(page_counter_read(\u0026memcg-\u003ememory),\nmm/memcontrol.c-2389-\t\t\t\t\t READ_ONCE(memcg-\u003ememory.high));\n--\nmm/memcontrol.c=2397=static u64 swap_find_max_overage(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-2401-\tdo {\nmm/memcontrol.c:2402:\t\toverage = calculate_overage(page_counter_read(\u0026memcg-\u003eswap),\nmm/memcontrol.c-2403-\t\t\t\t\t READ_ONCE(memcg-\u003eswap.high));\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2547-\tstruct mem_cgroup *mem_over_limit;\nmm/memcontrol.c:2548:\tstruct page_counter *counter;\nmm/memcontrol.c-2549-\tunsigned long nr_charged;\n--\nmm/memcontrol.c-2562-\tif (do_memsw_account() \u0026\u0026\nmm/memcontrol.c:2563:\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\nmm/memcontrol.c-2564-\t\t\t\t may_batch, NULL)) {\n--\nmm/memcontrol.c-2569-\nmm/memcontrol.c:2570:\tif (page_counter_try_charge(\u0026memcg-\u003ememory, nr_pages, \u0026counter,\nmm/memcontrol.c-2571-\t\t\t\t may_batch, \u0026nr_charged))\n--\nmm/memcontrol.c-2574-\tif (do_memsw_account())\nmm/memcontrol.c:2575:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2576-\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n--\nmm/memcontrol.c-2678-\t */\nmm/memcontrol.c:2679:\tpage_counter_charge(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c-2680-\tif (do_memsw_account())\nmm/memcontrol.c:2681:\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2682-\n--\nmm/memcontrol.c-2707-\nmm/memcontrol.c:2708:\t\tmem_high = page_counter_read(\u0026memcg-\u003ememory) \u003e\nmm/memcontrol.c-2709-\t\t\tREAD_ONCE(memcg-\u003ememory.high);\nmm/memcontrol.c:2710:\t\tswap_high = page_counter_read(\u0026memcg-\u003eswap) \u003e\nmm/memcontrol.c-2711-\t\t\tREAD_ONCE(memcg-\u003eswap.high);\n--\nmm/memcontrol.c=3031=static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,\n--\nmm/memcontrol.c-3040-\tif (!mem_cgroup_is_root(memcg)) {\nmm/memcontrol.c:3041:\t\tpage_counter_refill_stock(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c-3042-\t\tif (do_memsw_account())\nmm/memcontrol.c:3043:\t\t\tpage_counter_refill_stock(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-3044-\t}\n--\nmm/memcontrol.c=3727=void mem_cgroup_wb_stats(struct bdi_writeback *wb, unsigned long *pfilepages,\n--\nmm/memcontrol.c-3744-\t\t\t\t\t READ_ONCE(memcg-\u003ememory.high));\nmm/memcontrol.c:3745:\t\tunsigned long used = page_counter_read(\u0026memcg-\u003ememory);\nmm/memcontrol.c-3746-\n--\nmm/memcontrol.c=4171=mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\n--\nmm/memcontrol.c-4182-\nmm/memcontrol.c:4183:\tpage_counter_set_high(\u0026memcg-\u003ememory, PAGE_COUNTER_MAX);\nmm/memcontrol.c-4184-#ifdef CONFIG_ZSWAP\n--\nmm/memcontrol.c-4187-#endif\nmm/memcontrol.c:4188:\tpage_counter_set_high(\u0026memcg-\u003eswap, PAGE_COUNTER_MAX);\nmm/memcontrol.c-4189-\tif (parent) {\nmm/memcontrol.c:4190:\t\tpage_counter_init(\u0026memcg-\u003ememory, \u0026parent-\u003ememory, memcg_on_dfl);\nmm/memcontrol.c-4191-\t\tmemcg-\u003ememory.stock = \u0026memory_stock;\nmm/memcontrol.c-4192-\t\tmemcg-\u003ememory.stock_css = \u0026memcg-\u003ecss;\nmm/memcontrol.c:4193:\t\tpage_counter_init(\u0026memcg-\u003eswap, \u0026parent-\u003eswap, false);\nmm/memcontrol.c-4194-#ifdef CONFIG_MEMCG_V1\n--\nmm/memcontrol.c-4202-\t\tWRITE_ONCE(memcg-\u003eoom_kill_disable, READ_ONCE(parent-\u003eoom_kill_disable));\nmm/memcontrol.c:4203:\t\tpage_counter_init(\u0026memcg-\u003ekmem, \u0026parent-\u003ekmem, false);\nmm/memcontrol.c:4204:\t\tpage_counter_init(\u0026memcg-\u003etcpmem, \u0026parent-\u003etcpmem, false);\nmm/memcontrol.c-4205-\t\tmemcg-\u003etcpmem.track_failcnt = !memcg_on_dfl;\n--\nmm/memcontrol.c-4209-\t\tinit_memcg_events();\nmm/memcontrol.c:4210:\t\tpage_counter_init(\u0026memcg-\u003ememory, NULL, true);\nmm/memcontrol.c:4211:\t\tpage_counter_init(\u0026memcg-\u003eswap, NULL, false);\nmm/memcontrol.c-4212-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:4213:\t\tpage_counter_init(\u0026memcg-\u003ekmem, NULL, false);\nmm/memcontrol.c:4214:\t\tpage_counter_init(\u0026memcg-\u003etcpmem, NULL, false);\nmm/memcontrol.c-4215-#endif\n--\nmm/memcontrol.c=4307=static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)\n--\nmm/memcontrol.c-4312-\nmm/memcontrol.c:4313:\tpage_counter_set_min(\u0026memcg-\u003ememory, 0);\nmm/memcontrol.c:4314:\tpage_counter_set_low(\u0026memcg-\u003ememory, 0);\nmm/memcontrol.c-4315-\n--\nmm/memcontrol.c=4380=static void mem_cgroup_css_reset(struct cgroup_subsys_state *css)\n--\nmm/memcontrol.c-4383-\nmm/memcontrol.c:4384:\tpage_counter_set_max(\u0026memcg-\u003ememory, PAGE_COUNTER_MAX);\nmm/memcontrol.c:4385:\tpage_counter_set_max(\u0026memcg-\u003eswap, PAGE_COUNTER_MAX);\nmm/memcontrol.c-4386-\tWRITE_ONCE(memcg-\u003eoom_group, false);\n--\nmm/memcontrol.c-4391-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:4392:\tpage_counter_set_max(\u0026memcg-\u003ekmem, PAGE_COUNTER_MAX);\nmm/memcontrol.c:4393:\tpage_counter_set_max(\u0026memcg-\u003etcpmem, PAGE_COUNTER_MAX);\nmm/memcontrol.c-4394-#endif\nmm/memcontrol.c:4395:\tpage_counter_set_min(\u0026memcg-\u003ememory, 0);\nmm/memcontrol.c:4396:\tpage_counter_set_low(\u0026memcg-\u003ememory, 0);\nmm/memcontrol.c:4397:\tpage_counter_set_high(\u0026memcg-\u003ememory, PAGE_COUNTER_MAX);\nmm/memcontrol.c:4398:\tpage_counter_set_high(\u0026memcg-\u003eswap, PAGE_COUNTER_MAX);\nmm/memcontrol.c-4399-\tmemcg_wb_domain_size_changed(memcg);\n--\nmm/memcontrol.c=4658=static u64 memory_current_read(struct cgroup_subsys_state *css,\n--\nmm/memcontrol.c-4662-\nmm/memcontrol.c:4663:\treturn (u64)page_counter_read(\u0026memcg-\u003ememory) * PAGE_SIZE;\nmm/memcontrol.c-4664-}\n--\nmm/memcontrol.c-4667-\nmm/memcontrol.c:4668:static int peak_show(struct seq_file *sf, void *v, struct page_counter *pc)\nmm/memcontrol.c-4669-{\n--\nmm/memcontrol.c=4713=static ssize_t peak_write(struct kernfs_open_file *of, char *buf, size_t nbytes,\nmm/memcontrol.c:4714:\t\t\t loff_t off, struct page_counter *pc,\nmm/memcontrol.c-4715-\t\t\t struct list_head *watchers)\n--\nmm/memcontrol.c-4723-\nmm/memcontrol.c:4724:\tusage = page_counter_read(pc);\nmm/memcontrol.c-4725-\told_watermark = READ_ONCE(pc-\u003elocal_watermark);\n--\nmm/memcontrol.c=4759=static ssize_t memory_min_write(struct kernfs_open_file *of,\n--\nmm/memcontrol.c-4766-\tbuf = strstrip(buf);\nmm/memcontrol.c:4767:\terr = page_counter_memparse(buf, \"max\", \u0026min);\nmm/memcontrol.c-4768-\tif (err)\n--\nmm/memcontrol.c-4770-\nmm/memcontrol.c:4771:\tpage_counter_set_min(\u0026memcg-\u003ememory, min);\nmm/memcontrol.c-4772-\n--\nmm/memcontrol.c=4782=static ssize_t memory_low_write(struct kernfs_open_file *of,\n--\nmm/memcontrol.c-4789-\tbuf = strstrip(buf);\nmm/memcontrol.c:4790:\terr = page_counter_memparse(buf, \"max\", \u0026low);\nmm/memcontrol.c-4791-\tif (err)\n--\nmm/memcontrol.c-4793-\nmm/memcontrol.c:4794:\tpage_counter_set_low(\u0026memcg-\u003ememory, low);\nmm/memcontrol.c-4795-\n--\nmm/memcontrol.c=4805=static ssize_t memory_high_write(struct kernfs_open_file *of,\n--\nmm/memcontrol.c-4814-\tbuf = strstrip(buf);\nmm/memcontrol.c:4815:\terr = page_counter_memparse(buf, \"max\", \u0026high);\nmm/memcontrol.c-4816-\tif (err)\n--\n\n"
} |
| 5/2 | 2026/09/30 00:28 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26105 output: 26 thoughts: 15 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Kconfig Dive for Memory Accounting** Okay, so I need to understand how `PAGE_COUNTER` and `MEMCG` interact. It's time to dig into the Kconfig files. This likely involves tracing dependencies and configurations to see how these features are enabled, disabled, and configured in relation to each other. I'm looking for the precise relationships, conditional dependencies, and default settings. Specifically, I'm trying to determine if `PAGE_COUNTER` is a hard requirement for `MEMCG`, if they're mutually exclusive under certain architectures, or if there's any clever interplay I need to be aware of. The goal is a clear mental map of their configuration options and potential conflicts. Time to get my grep on and follow the breadcrumbs. |
| 6/2 | 2026/09/30 00:29 | tool | grepper |
0mError:no matches Args: {
"Expression": "config PAGE_COUNTER",
"PathPrefix": "mm/"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26336 output: 18 thoughts: 16 Thoughts: Here's my thought process, summarized as you requested: **Kconfig Exploration for PAGE_COUNTER** Alright, the objective is clear: I need to locate where `PAGE_COUNTER` is defined and used within the Kconfig system. Given my understanding of Kconfig, the most efficient approach is to initiate a comprehensive search across the entire Kconfig tree. I'm not going to limit myself to any specific module or area initially; I need to cast a wide net to ensure I don't miss any instances. This global search will be the foundation for understanding how `PAGE_COUNTER` fits into the broader configuration landscape. I'll need to use the appropriate tools or commands for this global search within the Kconfig files, whether that's `grep`, a dedicated Kconfig search utility, or whatever method is best supported by my current setup. The goal is to get a complete picture before diving into individual Kconfig fragments. |
| 8/2 | 2026/09/30 00:29 | tool | grepper |
0mArgs:{
"Expression": "config PAGE_COUNTER"
}Results: {
"Output": "init/Kconfig=1063=if CGROUPS\ninit/Kconfig-1064-\ninit/Kconfig:1065:config PAGE_COUNTER\ninit/Kconfig-1066-\tbool\n"
} |
| 9/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26628 output: 33 thoughts: 33 Thoughts: Okay, let's dive into this. Here's how I'd approach understanding the `PAGE_COUNTER` configuration option, as if these were my internal thought processes: **Investigating the PAGE_COUNTER Configuration** Alright, I need to get a clear picture of how `PAGE_COUNTER` works within this kernel. My immediate thought is to zero in on its definition and dependencies. That means heading straight for the `init/Kconfig` file, specifically around lines 1060 to 1100. That's usually the area where these core configuration options are declared. I know that's where I can get the canonical definition of `PAGE_COUNTER` and its associated dependencies. Understanding the dependencies is key; I need to know *what* needs to be enabled *before* `PAGE_COUNTER` can even be considered, and perhaps, *what* is enabled *by* `PAGE_COUNTER`. This information will provide the basis for troubleshooting any issues or making any enhancements. So, I need to find the configuration syntax in the Kconfig file, and use that to work my way through the code. |
| 10/2 | 2026/09/30 00:29 | tool | read-file |
0mArgs:{
"File": "init/Kconfig",
"FirstLine": 1060,
"LineCount": 50
}Results: {
"Contents": "1060:\t\n1061:\t\t Say N if unsure.\n1062:\t\n1063:\tif CGROUPS\n1064:\t\n1065:\tconfig PAGE_COUNTER\n1066:\t\tbool\n1067:\t\n1068:\tconfig CGROUP_FAVOR_DYNMODS\n1069:\t\tbool \"Favor dynamic modification latency reduction by default\"\n1070:\t\thelp\n1071:\t\t This option enables the \"favordynmods\" mount option by default\n1072:\t\t which reduces the latencies of dynamic cgroup modifications such\n1073:\t\t as task migrations and controller on/offs at the cost of making\n1074:\t\t hot path operations such as forks and exits more expensive.\n1075:\t\n1076:\t\t Say N if unsure.\n1077:\t\n1078:\tconfig MEMCG\n1079:\t\tbool \"Memory controller\"\n1080:\t\tselect PAGE_COUNTER\n1081:\t\tselect EVENTFD\n1082:\t\tselect SLAB_OBJ_EXT\n1083:\t\tselect VM_EVENT_COUNTERS\n1084:\t\thelp\n1085:\t\t Provides control over the memory footprint of tasks in a cgroup.\n1086:\t\n1087:\tconfig MEMCG_NMI_UNSAFE\n1088:\t\tbool\n1089:\t\tdepends on MEMCG\n1090:\t\tdepends on HAVE_NMI\n1091:\t\tdepends on !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS \u0026\u0026 !ARCH_HAVE_NMI_SAFE_CMPXCHG\n1092:\t\tdefault y\n1093:\t\n1094:\tconfig MEMCG_NMI_SAFETY_REQUIRES_ATOMIC\n1095:\t\tbool\n1096:\t\tdepends on MEMCG\n1097:\t\tdepends on HAVE_NMI\n1098:\t\tdepends on !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS \u0026\u0026 ARCH_HAVE_NMI_SAFE_CMPXCHG\n1099:\t\tdefault y\n1100:\t\n1101:\tconfig MEMCG_V1\n1102:\t\tbool \"Legacy cgroup v1 memory controller\"\n1103:\t\tdepends on MEMCG\n1104:\t\tdefault n\n1105:\t\thelp\n1106:\t\t Legacy cgroup v1 memory controller which has been deprecated by\n1107:\t\t cgroup v2 implementation. The v1 is there for legacy applications\n1108:\t\t which haven't migrated to the new cgroup v2 interface yet. If you\n1109:\t\t do not have any such application then you are completely fine leaving\n"
} |
| 11/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27629 output: 18 thoughts: 20 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own: **Investigating the PAGE_COUNTER Dependency** Alright, so the MEMCG subsystem is using `PAGE_COUNTER`. That immediately triggers my curiosity. I need to understand the scope of this. What other parts of the kernel are relying on `PAGE_COUNTER`? Is this a localized dependency or something more widespread? My goal is to determine the complete set of modules and subsystems that touch `PAGE_COUNTER`. This will help determine how changes to `PAGE_COUNTER` will affect the broader system. What's using it? What's *depending* on it, and why? I need to get a clear picture of its overall reach. Let’s start digging and map the calls. |
| 12/2 | 2026/09/30 00:29 | tool | grepper |
0mArgs:{
"Expression": "select PAGE_COUNTER"
}Results: {
"Output": "init/Kconfig=1078=config MEMCG\ninit/Kconfig-1079-\tbool \"Memory controller\"\ninit/Kconfig:1080:\tselect PAGE_COUNTER\ninit/Kconfig-1081-\tselect EVENTFD\n--\ninit/Kconfig=1265=config CGROUP_DMEM\ninit/Kconfig-1266-\tbool \"Device memory controller (DMEM)\"\ninit/Kconfig:1267:\tselect PAGE_COUNTER\ninit/Kconfig-1268-\thelp\n--\ninit/Kconfig=1286=config CGROUP_HUGETLB\n--\ninit/Kconfig-1288-\tdepends on HUGETLB_PAGE\ninit/Kconfig:1289:\tselect PAGE_COUNTER\ninit/Kconfig-1290-\tdefault n\n"
} |
| 13/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28052 output: 21 thoughts: 1416 Thoughts: **Analyzing the Memcg and Page Counter Changes** Okay, so I'm looking at this commit and what stands out immediately are the modifications across several files related to memory control groups (memcg) and page counters. The changes seem substantial, touching both core memcg code and the page counter infrastructure. First, I noticed the affected files: `page_counter.h`, `dmem.c`, `hugetlb_cgroup.c`, `memcontrol-v1.c`, `memcontrol.c`, and `page_counter.c`. I see that `CONFIG_MEMCG` is required for `memcontrol.c` and `page_counter.c`, while `CONFIG_MEMCG_V1` specifically activates the memsw stock-related code in `memcontrol.c` and is also linked to changes in `memcontrol-v1.c`. The changes to `hugetlb_cgroup.c` and `dmem.c` appear to be minor argument updates to `page_counter_try_charge`. This suggests a focus on integrating a new page counter stock mechanism into the existing memcg framework. Specifically in `mm/memcontrol.c` I see code related to the `memsw_stock`, with calls to `schedule_stock_drain`, `page_counter_drain_stock_fully`, and checks for `FLUSHING_CACHED_CHARGE`. And in `mm/memcontrol-v1.c` I see the `mem_cgroup_usage` function being modified to use `max(memory, memsw)`, and `memcg1_charge_skmem` being modified. This code is clearly managing the stock of memory for a memcg. Now, let's dive into the function changes. I see a bunch of interesting updates in `mm/page_counter.c`: * `page_counter_try_charge` has been significantly modified with stock checking, batch charging, refill on surplus, and cancel on failure, which is the core change here. * `page_counter_consume_stock` and `page_counter_drain_stock` are static helpers. * `page_counter_drain_stock_fully`, `page_counter_stock_flush_required`, and `page_counter_refill_stock` are non-static and exposed for other modules. In `mm/memcontrol.c`, the changes seem to be primarily about integrating the new page counter stock mechanism: * `try_charge_memcg` is updated to utilize the new `page_counter_try_charge`. * Several functions like `drain_local_stock`, `schedule_stock_drain`, `drain_all_stock`, `memcg_hotplug_cpu_dead`, `obj_cgroup_uncharge_pages`, `memcg_css_alloc`, and `mem_cgroup_sk_uncharge` are modified to use the new stock draining/refilling functionality. `mm/memcontrol-v1.c` has `mem_cgroup_usage` updated and a change in `memcg1_charge_skmem` using the new `page_counter_try_charge` with the new arguments. Now, considering the fuzzer focus symbols guidelines: The key is to avoid "ubiquitous lifecycle hot-paths" and target specific feature logic. The question is whether `page_counter_try_charge` is ubiquitous. It seems to be a key element for memory charging, but is that a ubiquitous function, or can it be considered feature-specific in the context of this memcg change? I'll need to check the call sites of `page_counter_try_charge` to determine this. If it's only called within the memcg code, it's probably safe to include as a focus symbol. The static inlines are trickier and would probably require that I target the non-static functions exercising them. |
| 14/2 | 2026/09/30 00:29 | tool | grepper |
0mArgs:{
"Expression": "page_counter_try_charge"
}Results: {
"Output": "include/linux/page_counter.h=99=void page_counter_charge(struct page_counter *counter, unsigned long nr_pages);\ninclude/linux/page_counter.h:100:bool page_counter_try_charge(struct page_counter *counter,\ninclude/linux/page_counter.h-101-\t\t\t unsigned long nr_pages, struct page_counter **fail,\n--\nkernel/cgroup/dmem.c=714=int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,\n--\nkernel/cgroup/dmem.c-738-\nkernel/cgroup/dmem.c:739:\tif (!page_counter_try_charge(\u0026pool-\u003ecnt, size, \u0026fail, false, NULL)) {\nkernel/cgroup/dmem.c-740-\t\tif (ret_limit_pool) {\n--\nmm/hugetlb_cgroup.c=286=static int __hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,\n--\nmm/hugetlb_cgroup.c-304-\nmm/hugetlb_cgroup.c:305:\tif (!page_counter_try_charge(\nmm/hugetlb_cgroup.c-306-\t\t __hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),\n--\nmm/memcontrol-v1.c=2194=bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,\n--\nmm/memcontrol-v1.c-2198-\nmm/memcontrol-v1.c:2199:\tif (page_counter_try_charge(\u0026memcg-\u003etcpmem, nr_pages, \u0026fail,\nmm/memcontrol-v1.c-2200-\t\t\t\t false, NULL)) {\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2562-\tif (do_memsw_account() \u0026\u0026\nmm/memcontrol.c:2563:\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\nmm/memcontrol.c-2564-\t\t\t\t may_batch, NULL)) {\n--\nmm/memcontrol.c-2569-\nmm/memcontrol.c:2570:\tif (page_counter_try_charge(\u0026memcg-\u003ememory, nr_pages, \u0026counter,\nmm/memcontrol.c-2571-\t\t\t\t may_batch, \u0026nr_charged))\n--\nmm/memcontrol.c=5854=int __mem_cgroup_try_charge_swap(struct folio *folio)\n--\nmm/memcontrol.c-5880-\t\tif (!mem_cgroup_private_id_is_root(private_id) \u0026\u0026\nmm/memcontrol.c:5881:\t\t !page_counter_try_charge(\u0026memcg-\u003eswap, nr_pages, \u0026counter,\nmm/memcontrol.c-5882-\t\t\t\t\t false, NULL)) {\n--\nmm/page_counter.c=144=static bool page_counter_consume_stock(struct page_counter *counter,\n--\nmm/page_counter.c-179-/**\nmm/page_counter.c:180: * page_counter_try_charge - try to hierarchically charge pages using stock\nmm/page_counter.c-181- * @counter: counter\n--\nmm/page_counter.c-189- */\nmm/page_counter.c:190:bool page_counter_try_charge(struct page_counter *counter,\nmm/page_counter.c-191-\t\t\t unsigned long nr_pages, struct page_counter **fail,\n--\nmm/page_counter.c=407=int page_counter_set_max(struct page_counter *counter, unsigned long nr_pages)\n--\nmm/page_counter.c-418-\t\t * and after, so the read-swap-read is ordered and\nmm/page_counter.c:419:\t\t * ensures coherency with page_counter_try_charge():\nmm/page_counter.c-420-\t\t * that function modifies the count before checking\n--\ntools/perf/Documentation/perf.data-directory-format.txt=32=e.g.\n--\ntools/perf/Documentation/perf.data-directory-format.txt-60- perf 15316 2060795.480914: 37606 cycles: ffffffffa2f121fe perf_event_addr_filters_exec+0x2e (vmlinux)\ntools/perf/Documentation/perf.data-directory-format.txt:61: uname 15316 2060795.480924: 588287 cycles: ffffffffa303a56d page_counter_try_charge+0x6d (vmlinux)\ntools/perf/Documentation/perf.data-directory-format.txt-62- uname 15316 2060795.481067: 2261945 cycles: ffffffffa301438f kmem_cache_free+0x4f (vmlinux)\n"
} |
| 15/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31704 output: 28 thoughts: 65 Thoughts: Here's my thought process, as if I were considering this code: **Investigating Memory Charge Timing** Okay, I'm trying to understand the memory accounting. Specifically, I'm wondering if `page_counter_try_charge` is invoked *during* memory allocation, or if it happens elsewhere. That's a crucial point for optimizing our memory tracking, and timing is key. Right, so, where does `try_charge_memcg` get called? That's the real question. Let me check the call sites for that function; it's the core of how the memory charges are attempted. Ah, looks like `try_charge_memcg` is called within `charge_memcg`, and potentially also by `mem_cgroup_charge`. I need to confirm that relationship. The calling flow must be understood, or this whole investigation will be for naught! |
| 16/2 | 2026/09/30 00:29 | tool | grepper |
0mArgs:{
"Expression": "try_charge_memcg",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol.c=2453=void __mem_cgroup_handle_over_high(gfp_t gfp_mask)\n--\nmm/memcontrol.c-2542-\nmm/memcontrol.c:2543:static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\nmm/memcontrol.c-2544-\t\t\t unsigned int nr_pages)\n--\nmm/memcontrol.c=2755=static inline int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2760-\nmm/memcontrol.c:2761:\treturn try_charge_memcg(memcg, gfp_mask, nr_pages);\nmm/memcontrol.c-2762-}\n--\nmm/memcontrol.c=5213=static int charge_memcg(struct folio *folio, struct mem_cgroup *memcg,\n--\nmm/memcontrol.c-5221-\tif (!obj_cgroup_is_root(objcg))\nmm/memcontrol.c:5222:\t\tret = try_charge_memcg(memcg, gfp, folio_nr_pages(folio));\nmm/memcontrol.c-5223-\tif (ret) {\n--\nmm/memcontrol.c=5633=bool mem_cgroup_sk_charge(const struct sock *sk, unsigned int nr_pages,\n--\nmm/memcontrol.c-5640-\nmm/memcontrol.c:5641:\tif (try_charge_memcg(memcg, gfp_mask, nr_pages) == 0) {\nmm/memcontrol.c-5642-\t\tmod_memcg_state(memcg, MEMCG_SOCK, nr_pages);\n"
} |
| 17/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 32457 output: 27 thoughts: 507 Thoughts: **Analyzing Memory Accounting in Non-Root Cgroups** Okay, so `try_charge_memcg` is the key function when non-root cgroups are involved in charging folio or sock memory. The thing to remember is that syzkaller often runs in the root cgroup or, if the sandbox is active or if syzkaller is creating cgroups, in child cgroups. Now, I need to get a handle on what modifications have been made to `page_counter.c` and `memcontrol.c`. Let's break it down function by function: In `page_counter.c`, I'm seeing: * `page_counter_refill_stock` * `page_counter_drain_stock_fully` * `page_counter_stock_flush_required` * `page_counter_try_charge` And in `memcontrol.c`: * `drain_all_stock` * `memcg_hotplug_cpu_dead` * `obj_cgroup_uncharge_pages` * `mem_cgroup_sk_uncharge` * `drain_local_stock` * `schedule_stock_drain` (a static function, so internal to that file) Now, I want to confirm which of those functions in `page_counter.c` are actually exported (i.e., not static). I'll peek into `include/linux/page_counter.h`: ```c bool page_counter_try_charge(struct page_counter *counter, unsigned long nr_pages, struct page_counter **fail, bool may_batch, unsigned long *nr_charged); void page_counter_refill_stock(struct page_counter *counter, unsigned long nr_pages); void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock); bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock, struct cgroup_subsys_state *root_css); ``` Yep, they're all there in the header file, confirming that these four functions in `page_counter.c` are indeed exported and non-static. Those being: 1. `page_counter_refill_stock` 2. `page_counter_drain_stock_fully` 3. `page_counter_stock_flush_required` 4. `page_counter_try_charge` Next, I need to understand `drain_all_stock` in `mm/memcontrol.c`. It's a `void drain_all_stock(struct mem_cgroup *root_memcg)` function. I'll need to figure out where this function is called. |
| 18/2 | 2026/09/30 00:29 | tool | grepper |
0mArgs:{
"Expression": "drain_all_stock",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol-v1.c=1424=static int mem_cgroup_resize_max(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-1463-\t\tif (!drained) {\nmm/memcontrol-v1.c:1464:\t\t\tdrain_all_stock(memcg);\nmm/memcontrol-v1.c-1465-\t\t\tdrained = true;\n--\nmm/memcontrol-v1.c=1487=static int mem_cgroup_force_empty(struct mem_cgroup *memcg)\n--\nmm/memcontrol-v1.c-1493-\nmm/memcontrol-v1.c:1494:\tdrain_all_stock(memcg);\nmm/memcontrol-v1.c-1495-\n--\nmm/memcontrol-v1.h-20-\nmm/memcontrol-v1.h:21:void drain_all_stock(struct mem_cgroup *root_memcg);\nmm/memcontrol-v1.h-22-\n--\nmm/memcontrol.c=2184=static void schedule_stock_drain(struct page_counter_stock_pcp __percpu *stock,\n--\nmm/memcontrol.c-2204- */\nmm/memcontrol.c:2205:void drain_all_stock(struct mem_cgroup *root_memcg)\nmm/memcontrol.c-2206-{\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2621-\tif (!drained) {\nmm/memcontrol.c:2622:\t\tdrain_all_stock(mem_over_limit);\nmm/memcontrol.c-2623-\t\tdrained = true;\n--\nmm/memcontrol.c=4307=static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)\n--\nmm/memcontrol.c-4328-\nmm/memcontrol.c:4329:\tdrain_all_stock(memcg);\nmm/memcontrol.c-4330-\n--\nmm/memcontrol.c=4805=static ssize_t memory_high_write(struct kernfs_open_file *of,\n--\nmm/memcontrol.c-4841-\t\tif (!drained) {\nmm/memcontrol.c:4842:\t\t\tdrain_all_stock(memcg);\nmm/memcontrol.c-4843-\t\t\tdrained = true;\n--\nmm/memcontrol.c=4864=static ssize_t memory_max_write(struct kernfs_open_file *of,\n--\nmm/memcontrol.c-4899-\t\tif (!drained) {\nmm/memcontrol.c:4900:\t\t\tdrain_all_stock(memcg);\nmm/memcontrol.c-4901-\t\t\tdrained = true;\n"
} |
| 19/2 | 2026/09/30 00:29 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 34390 output: 189 thoughts: 3215 Thoughts: **Summary of Thoughts on Memory Control Changes** Okay, here's the breakdown. My thinking starts with `drain_all_stock` and its role in memory control, specifically when limits are updated. This function seems central to draining per-cpu stock for a memcg hierarchy. I also need to consider other functions within the memcontrol code, like `memcg1_charge_skmem` in `mm/memcontrol-v1.c` and `dmem_cgroup_try_charge` in `kernel/cgroup/dmem.c`. These appear to be related, but perhaps more specific to socket memory or device memory cgroups. The key is to avoid focusing on *ubiquitous* functions. The goal is to identify functions that specifically implement new logic or represent critical entrypoints. The guidelines are clear: steer clear of common functions. I need to focus on the functions that directly touch the *changes*. Functions like `page_counter_refill_stock`, `page_counter_drain_stock_fully`, and `page_counter_stock_flush_required` are all relevant. And, of course, the core is `page_counter_try_charge` and how it's modified; in particular, the batch charging logic. This function is no longer just a "general memory allocator", but the heart of the page counter charging function itself, where the important changes happened. Importantly, I am also considering that root cgroup allocations bypass `try_charge_memcg`. The functions I'm focusing on are the ones that have been added or modified: `page_counter_try_charge`, `page_counter_refill_stock`, `page_counter_drain_stock_fully`, `page_counter_stock_flush_required`, and `drain_all_stock`. The others seem less directly involved. Regarding configuration, I'm confident that `MEMCG` and `MEMCG_V1` are required. The changes touch the core of memory management with the move from memcg-specific structs to the generic page_counter structs. These changes include speculative batch charging in `page_counter_try_charge`, the new per-cpu `memsw_stock` for memcg v1, and the refactoring of drain/refill logic. These changes involve locking, atomic operations, workqueues, and cgroup hierarchy traversal, making them very relevant for fuzzing, which will need the "MEMCG" and "MEMCG_V1" configs. These features also mean that the overall code is much more likely to be reachable by fuzzing tasks running in cgroups or exercising memory charging/reclaim/limit changes. The `WorthFuzzing` flag must be set to `true`. I'll go back and verify all the config flags, but I believe the "MEMCG" and "MEMCG_V1" flags are the only required ones. My final list of functions for focus is: `page_counter_try_charge`, `page_counter_refill_stock`, `page_counter_drain_stock_fully`, `page_counter_stock_flush_required`, and `drain_all_stock`. These are the key functions directly involved in the added or modified stock logic. |
| 20/2 | 2026/09/30 00:29 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"MEMCG",
"MEMCG_V1"
],
"FocusSymbols": [
"page_counter_try_charge",
"page_counter_refill_stock",
"page_counter_drain_stock_fully",
"page_counter_stock_flush_required",
"drain_all_stock"
],
"Reasoning": "The patch series refactors the per-CPU memcg charging stock mechanism into struct page_counter, moving stock batch charging, refilling, and draining logic into mm/page_counter.c, while also adding dedicated per-CPU stock handling for memsw under CONFIG_MEMCG_V1. These modifications touch core memory management and cgroup accounting paths involving concurrency, per-CPU locks, and reference counting, making them highly relevant and reachable for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"MEMCG",
"MEMCG_V1"
],
"FocusSymbols": [
"page_counter_try_charge",
"page_counter_refill_stock",
"page_counter_drain_stock_fully",
"page_counter_stock_flush_required",
"drain_all_stock"
],
"Reasoning": "The patch series refactors the per-CPU memcg charging stock mechanism into struct page_counter, moving stock batch charging, refilling, and draining logic into mm/page_counter.c, while also adding dedicated per-CPU stock handling for memsw under CONFIG_MEMCG_V1. These modifications touch core memory management and cgroup accounting paths involving concurrency, per-CPU locks, and reference counting, making them highly relevant and reachable for fuzzing.",
"WorthFuzzing": true
} |
| 21/1 | 2026/09/30 00:29 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 22/2 | 2026/09/30 00:29 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch refactors the per-CPU charge stock caching mechanism previously exclusive to memcontrol (memcg_stock) into a generic page counter stock (struct page_counter_stock_pcp) in mm/page_counter.c, allowing both memory and swap counters to benefit from stock caching.\n\nApplicability analysis:\n1. KASAN / Standard Detectors: The primary risks associated with this refactoring involve concurrent access and synchronization across CPUs (local_trylock, drain work items), cgroup css reference count tracking (css_get/css_put on stock caching), and potential use-after-free or out-of-bounds accesses on stock cache arrays. These are fully covered by KASAN, LOCKDEP, and standard kernel debugging facilities.\n2. KMSAN:\n - The new stack variable `nr_charged` passed to `page_counter_try_charge()` in `try_charge_memcg()` is unconditionally written on all success return paths (`*nr_charged = 0` on stock hit, and `*nr_charged = charge` on successful direct charge) before being evaluated at `check_high:`.\n - The per-CPU stock structures (`memory_stock`, `memsw_stock`) are statically allocated per-CPU variables zero-initialized in BSS, and the embedded `work_struct` is properly initialized via `INIT_WORK`.\n - New `struct page_counter` fields (`stock`, `stock_css`) are explicitly initialized to NULL in `page_counter_init()`, and memcgs are allocated with `kmem_cache_zalloc`.\n - No data is copied to user space, and no uninitialized padding, heap, or stack memory can be leaked or used in control flow.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit e62cf3c2c4db5de6242a166612bf218ed99c8817
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Sep 30 00:27:42 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/page_counter.h b/include/linux/page_counter.h
index 2baf7a2b29b2e..58532259e7f59 100644
--- a/include/linux/page_counter.h
+++ b/include/linux/page_counter.h
@@ -5,8 +5,30 @@
#include <linux/atomic.h>
#include <linux/cache.h>
#include <linux/limits.h>
+#include <linux/percpu.h>
+#include <linux/local_lock.h>
+#include <linux/workqueue_types.h>
#include <asm/page.h>
+/*
+ * The value of NR_PAGE_COUNTER_STOCK is selected to keep the cached counters
+ * and their nr_pages in a single cacheline. This may change in the future.
+ */
+#define NR_PAGE_COUNTER_STOCK 7
+#define PAGE_COUNTER_STOCK_BATCH 64UL
+struct cgroup_subsys_state;
+struct page_counter;
+struct page_counter_stock_pcp {
+ local_trylock_t lock;
+ u8 nr_pages[NR_PAGE_COUNTER_STOCK];
+ struct page_counter *cached[NR_PAGE_COUNTER_STOCK];
+
+ struct page_counter_stock_pcp __percpu *base;
+ struct work_struct work;
+ unsigned long flags;
+ u8 drain_idx;
+};
+
struct page_counter {
/*
* Make sure 'usage' does not share cacheline with any other field in
@@ -41,6 +63,8 @@ struct page_counter {
unsigned long high;
unsigned long max;
struct page_counter *parent;
+ struct page_counter_stock_pcp __percpu *stock;
+ struct cgroup_subsys_state *stock_css;
} ____cacheline_internodealigned_in_smp;
#if BITS_PER_LONG == 32
@@ -61,6 +85,8 @@ static inline void page_counter_init(struct page_counter *counter,
counter->parent = parent;
counter->protection_support = protection_support;
counter->track_failcnt = false;
+ counter->stock = NULL;
+ counter->stock_css = NULL;
}
static inline unsigned long page_counter_read(const struct page_counter *counter)
@@ -72,8 +98,13 @@ long page_counter_margin(const struct page_counter *counter);
void page_counter_cancel(struct page_counter *counter, unsigned long nr_pages);
void page_counter_charge(struct page_counter *counter, unsigned long nr_pages);
bool page_counter_try_charge(struct page_counter *counter,
- unsigned long nr_pages,
- struct page_counter **fail);
+ unsigned long nr_pages, struct page_counter **fail,
+ bool may_batch, unsigned long *nr_charged);
+void page_counter_refill_stock(struct page_counter *counter,
+ unsigned long nr_pages);
+void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock);
+bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock,
+ struct cgroup_subsys_state *root_css);
void page_counter_uncharge(struct page_counter *counter, unsigned long nr_pages);
void page_counter_set_min(struct page_counter *counter, unsigned long nr_pages);
void page_counter_set_low(struct page_counter *counter, unsigned long nr_pages);
diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c
index 4683f3d680226..569307aa4bd62 100644
--- a/kernel/cgroup/dmem.c
+++ b/kernel/cgroup/dmem.c
@@ -736,7 +736,7 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size,
goto err;
}
- if (!page_counter_try_charge(&pool->cnt, size, &fail)) {
+ if (!page_counter_try_charge(&pool->cnt, size, &fail, false, NULL)) {
if (ret_limit_pool) {
*ret_limit_pool = container_of(fail, struct dmem_cgroup_pool_state, cnt);
css_get(&(*ret_limit_pool)->cs->css);
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
index 3fb41311e4c7d..bbc2dd3fa3c97 100644
--- a/mm/hugetlb_cgroup.c
+++ b/mm/hugetlb_cgroup.c
@@ -304,7 +304,7 @@ static int __hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
if (!page_counter_try_charge(
__hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),
- nr_pages, &counter)) {
+ nr_pages, &counter, false, NULL)) {
ret = -ENOMEM;
hugetlb_event(h_cg, idx, HUGETLB_MAX);
css_put(&h_cg->css);
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index b7f2868885071..5660ba4597db2 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -122,10 +122,13 @@ static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
if (swap)
val += total_swap_pages - get_nr_swap_pages();
} else {
- if (!swap)
+ if (!swap) {
val = page_counter_read(&memcg->memory);
- else
- val = page_counter_read(&memcg->memsw);
+ } else {
+ /* Preserve the user-visible memory <= memsw invariant. */
+ val = max(page_counter_read(&memcg->memory),
+ page_counter_read(&memcg->memsw));
+ }
}
return val;
}
@@ -2193,7 +2196,8 @@ bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,
{
struct page_counter *fail;
- if (page_counter_try_charge(&memcg->tcpmem, nr_pages, &fail)) {
+ if (page_counter_try_charge(&memcg->tcpmem, nr_pages, &fail,
+ false, NULL)) {
memcg->tcpmem_pressure = 0;
return true;
}
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index aad0498a7bd65..6cae2d84f37c0 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2063,34 +2063,19 @@ void mem_cgroup_print_oom_group(struct mem_cgroup *memcg)
pr_cont(" are going to be killed due to memory.oom.group set\n");
}
-/*
- * The value of NR_MEMCG_STOCK is selected to keep the cached memcgs and their
- * nr_pages in a single cacheline. This may change in future.
- */
-#define NR_MEMCG_STOCK 7
-
-/*
- * Watermarks for a charge stock slot, in the spirit of pcp->high and
- * pcp->batch: MEMCG_STOCK_HIGH is the high watermark at which a slot is
- * trimmed, and it is trimmed down to MEMCG_STOCK_LOW rather than emptied.
- */
-#define MEMCG_STOCK_LOW (MEMCG_CHARGE_BATCH / 2)
-#define MEMCG_STOCK_HIGH (MEMCG_CHARGE_BATCH)
-
#define FLUSHING_CACHED_CHARGE 0
-struct memcg_stock_pcp {
- local_trylock_t lock;
- uint8_t nr_pages[NR_MEMCG_STOCK];
- struct mem_cgroup *cached[NR_MEMCG_STOCK];
- struct work_struct work;
- unsigned long flags;
- uint8_t drain_idx;
+static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memory_stock) = {
+ .lock = INIT_LOCAL_TRYLOCK(lock),
+ .base = &memory_stock,
};
-static DEFINE_PER_CPU_ALIGNED(struct memcg_stock_pcp, memcg_stock) = {
+#ifdef CONFIG_MEMCG_V1
+static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memsw_stock) = {
.lock = INIT_LOCAL_TRYLOCK(lock),
+ .base = &memsw_stock,
};
+#endif
/*
* NR_OBJ_STOCK is sized so the entire hot path of obj_stock_pcp
@@ -2139,52 +2124,6 @@ static void drain_obj_stock(struct obj_stock_pcp *stock);
static bool obj_stock_flush_required(struct obj_stock_pcp *stock,
struct mem_cgroup *root_memcg);
-/**
- * consume_stock: Try to consume stocked charge on this cpu.
- * @memcg: memcg to consume from.
- * @nr_pages: how many pages to charge.
- *
- * Consume the cached charge if enough nr_pages are present otherwise return
- * failure. Also return failure for charge request larger than
- * MEMCG_CHARGE_BATCH or if the local lock is already taken.
- *
- * returns true if successful, false otherwise.
- */
-static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
-{
- struct memcg_stock_pcp *stock;
- uint8_t stock_pages;
- bool ret = false;
- int i;
-
- if (nr_pages > MEMCG_CHARGE_BATCH ||
- !local_trylock(&memcg_stock.lock))
- return ret;
-
- stock = this_cpu_ptr(&memcg_stock);
-
- for (i = 0; i < NR_MEMCG_STOCK; ++i) {
- if (memcg != READ_ONCE(stock->cached[i]))
- continue;
-
- stock_pages = READ_ONCE(stock->nr_pages[i]);
- if (stock_pages >= nr_pages) {
- stock_pages -= nr_pages;
- WRITE_ONCE(stock->nr_pages[i], stock_pages);
- if (!stock_pages) {
- css_put(&memcg->css);
- WRITE_ONCE(stock->cached[i], NULL);
- }
- ret = true;
- }
- break;
- }
-
- local_unlock(&memcg_stock.lock);
-
- return ret;
-}
-
static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)
{
page_counter_uncharge(&memcg->memory, nr_pages);
@@ -2192,49 +2131,22 @@ static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)
page_counter_uncharge(&memcg->memsw, nr_pages);
}
-/*
- * Returns stocks cached in percpu and reset cached information.
- */
-static void drain_stock(struct memcg_stock_pcp *stock, int i)
+static void drain_local_stock(struct work_struct *work)
{
- struct mem_cgroup *old = READ_ONCE(stock->cached[i]);
- uint8_t stock_pages;
-
- if (!old)
- return;
-
- stock_pages = READ_ONCE(stock->nr_pages[i]);
- if (stock_pages) {
- memcg_uncharge(old, stock_pages);
- WRITE_ONCE(stock->nr_pages[i], 0);
- }
-
- css_put(&old->css);
- WRITE_ONCE(stock->cached[i], NULL);
-}
-
-static void drain_stock_fully(struct memcg_stock_pcp *stock)
-{
- int i;
-
- for (i = 0; i < NR_MEMCG_STOCK; ++i)
- drain_stock(stock, i);
-}
-
-static void drain_local_memcg_stock(struct work_struct *dummy)
-{
- struct memcg_stock_pcp *stock;
+ struct page_counter_stock_pcp *pcp_stock;
+ struct page_counter_stock_pcp __percpu *stock;
if (WARN_ONCE(!in_task(), "drain in non-task context"))
return;
- local_lock(&memcg_stock.lock);
+ stock = container_of(work, struct page_counter_stock_pcp, work)->base;
+ local_lock(&stock->lock);
- stock = this_cpu_ptr(&memcg_stock);
- drain_stock_fully(stock);
- clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
+ pcp_stock = this_cpu_ptr(stock);
+ page_counter_drain_stock_fully(pcp_stock);
+ clear_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags);
- local_unlock(&memcg_stock.lock);
+ local_unlock(&stock->lock);
}
static void drain_local_obj_stock(struct work_struct *dummy)
@@ -2253,92 +2165,6 @@ static void drain_local_obj_stock(struct work_struct *dummy)
local_unlock(&obj_stock.lock);
}
-static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
-{
- struct memcg_stock_pcp *stock;
- struct mem_cgroup *cached;
- unsigned int stock_pages;
- bool success = false;
- int empty_slot = -1;
- int i;
-
- /*
- * nr_pages[] is a uint8_t and a slot's count is capped at
- * MEMCG_STOCK_HIGH. Raising MEMCG_CHARGE_BATCH beyond 127 would need
- * more careful handling of nr_pages[] in struct memcg_stock_pcp.
- */
- BUILD_BUG_ON(MEMCG_CHARGE_BATCH > S8_MAX);
- BUILD_BUG_ON(MEMCG_STOCK_HIGH > U8_MAX);
-
- VM_WARN_ON_ONCE(mem_cgroup_is_root(memcg));
-
- if (nr_pages > MEMCG_CHARGE_BATCH ||
- !local_trylock(&memcg_stock.lock)) {
- /*
- * In case of larger than batch refill or unlikely failure to
- * lock the percpu memcg_stock.lock, uncharge memcg directly.
- */
- memcg_uncharge(memcg, nr_pages);
- return;
- }
-
- stock = this_cpu_ptr(&memcg_stock);
- for (i = 0; i < NR_MEMCG_STOCK; ++i) {
- cached = READ_ONCE(stock->cached[i]);
- if (!cached && empty_slot == -1)
- empty_slot = i;
- if (memcg == READ_ONCE(stock->cached[i])) {
- stock_pages = READ_ONCE(stock->nr_pages[i]) + nr_pages;
- if (stock_pages > MEMCG_STOCK_HIGH) {
- memcg_uncharge(memcg,
- stock_pages - MEMCG_STOCK_LOW);
- stock_pages = MEMCG_STOCK_LOW;
- }
- WRITE_ONCE(stock->nr_pages[i], stock_pages);
- success = true;
- break;
- }
- }
-
- if (!success) {
- i = empty_slot;
- if (i == -1) {
- i = stock->drain_idx++;
- if (stock->drain_idx == NR_MEMCG_STOCK)
- stock->drain_idx = 0;
- drain_stock(stock, i);
- }
- css_get(&memcg->css);
- WRITE_ONCE(stock->cached[i], memcg);
- WRITE_ONCE(stock->nr_pages[i], nr_pages);
- }
-
- local_unlock(&memcg_stock.lock);
-}
-
-static bool is_memcg_drain_needed(struct memcg_stock_pcp *stock,
- struct mem_cgroup *root_memcg)
-{
- struct mem_cgroup *memcg;
- bool flush = false;
- int i;
-
- rcu_read_lock();
- for (i = 0; i < NR_MEMCG_STOCK; ++i) {
- memcg = READ_ONCE(stock->cached[i]);
- if (!memcg)
- continue;
-
- if (READ_ONCE(stock->nr_pages[i]) &&
- mem_cgroup_is_descendant(memcg, root_memcg)) {
- flush = true;
- break;
- }
- }
- rcu_read_unlock();
- return flush;
-}
-
static bool schedule_drain_work(int cpu, struct work_struct *work)
{
/*
@@ -2355,12 +2181,30 @@ static bool schedule_drain_work(int cpu, struct work_struct *work)
return true;
}
+static void schedule_stock_drain(struct page_counter_stock_pcp __percpu *stock,
+ struct cgroup_subsys_state *root_css,
+ int cpu, int curcpu)
+{
+ struct page_counter_stock_pcp *pcp_stock = per_cpu_ptr(stock, cpu);
+
+ if (test_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags) ||
+ !page_counter_stock_flush_required(pcp_stock, root_css) ||
+ test_and_set_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags))
+ return;
+
+ if (cpu == curcpu)
+ drain_local_stock(&pcp_stock->work);
+ else if (!schedule_drain_work(cpu, &pcp_stock->work))
+ clear_bit(FLUSHING_CACHED_CHARGE, &pcp_stock->flags);
+}
+
/*
* Drains all per-CPU charge caches for given root_memcg resp. subtree
* of the hierarchy under it.
*/
void drain_all_stock(struct mem_cgroup *root_memcg)
{
+ struct cgroup_subsys_state *root_css = &root_memcg->css;
int cpu, curcpu;
/* If someone's already draining, avoid adding running more workers. */
@@ -2370,24 +2214,18 @@ void drain_all_stock(struct mem_cgroup *root_memcg)
* Notify other cpus that system-wide "drain" is running
* We do not care about races with the cpu hotplug because cpu down
* as well as workers from this path always operate on the local
- * per-cpu data. CPU up doesn't touch memcg_stock at all.
+ * per-cpu data. CPU up doesn't touch the stocks at all.
*/
migrate_disable();
curcpu = smp_processor_id();
for_each_online_cpu(cpu) {
- struct memcg_stock_pcp *memcg_st = &per_cpu(memcg_stock, cpu);
struct obj_stock_pcp *obj_st = &per_cpu(obj_stock, cpu);
- if (!test_bit(FLUSHING_CACHED_CHARGE, &memcg_st->flags) &&
- is_memcg_drain_needed(memcg_st, root_memcg) &&
- !test_and_set_bit(FLUSHING_CACHED_CHARGE,
- &memcg_st->flags)) {
- if (cpu == curcpu)
- drain_local_memcg_stock(&memcg_st->work);
- else if (!schedule_drain_work(cpu, &memcg_st->work))
- clear_bit(FLUSHING_CACHED_CHARGE,
- &memcg_st->flags);
- }
+ schedule_stock_drain(&memory_stock, root_css, cpu, curcpu);
+#ifdef CONFIG_MEMCG_V1
+ if (do_memsw_account())
+ schedule_stock_drain(&memsw_stock, root_css, cpu, curcpu);
+#endif
if (!test_bit(FLUSHING_CACHED_CHARGE, &obj_st->flags) &&
obj_stock_flush_required(obj_st, root_memcg) &&
@@ -2406,12 +2244,19 @@ void drain_all_stock(struct mem_cgroup *root_memcg)
static int memcg_hotplug_cpu_dead(unsigned int cpu)
{
- struct memcg_stock_pcp *memcg_st = &per_cpu(memcg_stock, cpu);
+ struct page_counter_stock_pcp *stock;
struct obj_stock_pcp *obj_st = &per_cpu(obj_stock, cpu);
/* no need for the local lock */
drain_obj_stock(obj_st);
- drain_stock_fully(memcg_st);
+ stock = per_cpu_ptr(&memory_stock, cpu);
+ page_counter_drain_stock_fully(stock);
+ clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
+#ifdef CONFIG_MEMCG_V1
+ stock = per_cpu_ptr(&memsw_stock, cpu);
+ page_counter_drain_stock_fully(stock);
+ clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
+#endif
/*
* A drain work queued before the CPU went away is executed by an
@@ -2419,7 +2264,6 @@ static int memcg_hotplug_cpu_dead(unsigned int cpu)
* clear the flags here to make these stocks drainable again once
* the CPU comes back online.
*/
- clear_bit(FLUSHING_CACHED_CHARGE, &memcg_st->flags);
clear_bit(FLUSHING_CACHED_CHARGE, &obj_st->flags);
return 0;
@@ -2699,10 +2543,10 @@ void __mem_cgroup_handle_over_high(gfp_t gfp_mask)
static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned int nr_pages)
{
- unsigned int batch = max(MEMCG_CHARGE_BATCH, nr_pages);
int nr_retries = MAX_RECLAIM_RETRIES;
struct mem_cgroup *mem_over_limit;
struct page_counter *counter;
+ unsigned long nr_charged;
unsigned long nr_reclaimed;
bool passed_oom = false;
unsigned int reclaim_options;
@@ -2710,33 +2554,30 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
bool raised_max_event = false;
unsigned long pflags;
bool allow_spinning = gfpflags_allow_spinning(gfp_mask);
+ bool may_batch = allow_spinning;
int ret = 0;
retry:
- if (consume_stock(memcg, nr_pages))
- return ret;
-
- if (!allow_spinning)
- /* Avoid the refill and flush of the older stock */
- batch = nr_pages;
-
reclaim_options = MEMCG_RECLAIM_MAY_SWAP;
- if (!do_memsw_account() ||
- page_counter_try_charge(&memcg->memsw, batch, &counter)) {
- if (page_counter_try_charge(&memcg->memory, batch, &counter))
- goto done_restock;
- if (do_memsw_account())
- page_counter_uncharge(&memcg->memsw, batch);
- mem_over_limit = mem_cgroup_from_counter(counter, memory);
- } else {
+ if (do_memsw_account() &&
+ !page_counter_try_charge(&memcg->memsw, nr_pages, &counter,
+ may_batch, NULL)) {
mem_over_limit = mem_cgroup_from_counter(counter, memsw);
reclaim_options &= ~MEMCG_RECLAIM_MAY_SWAP;
+ goto reclaim;
}
- if (batch > nr_pages) {
- batch = nr_pages;
- goto retry;
- }
+ if (page_counter_try_charge(&memcg->memory, nr_pages, &counter,
+ may_batch, &nr_charged))
+ goto check_high;
+
+ if (do_memsw_account())
+ page_counter_uncharge(&memcg->memsw, nr_pages);
+ mem_over_limit = mem_cgroup_from_counter(counter, memory);
+
+reclaim:
+ /* Do not retry speculative batch charges after the first miss. */
+ may_batch = false;
/*
* Prevent unbounded recursion when reclaim operations need to
@@ -2849,10 +2690,9 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
return ret;
-done_restock:
- if (batch > nr_pages)
- refill_stock(memcg, batch - nr_pages);
-
+check_high:
+ if (!nr_charged)
+ return ret;
/*
* If the hierarchy is above the normal consumption range, schedule
* reclaim on returning to userland. We can perform reclaim here
@@ -2892,7 +2732,7 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
* and distribute reclaim work and delay penalties
* based on how much each task is actually allocating.
*/
- current->memcg_nr_pages_over_high += batch;
+ current->memcg_nr_pages_over_high += nr_charged;
set_notify_resume(current);
break;
}
@@ -3197,8 +3037,11 @@ static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,
account_kmem_nmi_safe(memcg, -nr_pages);
memcg1_account_kmem(memcg, -nr_pages);
- if (!mem_cgroup_is_root(memcg))
- refill_stock(memcg, nr_pages);
+ if (!mem_cgroup_is_root(memcg)) {
+ page_counter_refill_stock(&memcg->memory, nr_pages);
+ if (do_memsw_account())
+ page_counter_refill_stock(&memcg->memsw, nr_pages);
+ }
css_put(&memcg->css);
}
@@ -4345,8 +4188,14 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
if (parent) {
page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
+ memcg->memory.stock = &memory_stock;
+ memcg->memory.stock_css = &memcg->css;
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
+ if (!memcg_on_dfl) {
+ memcg->memsw.stock = &memsw_stock;
+ memcg->memsw.stock_css = &memcg->css;
+ }
WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
memcg->memory.track_failcnt = !memcg_on_dfl;
memcg->memsw.track_failcnt = !memcg_on_dfl;
@@ -5813,7 +5662,7 @@ void mem_cgroup_sk_uncharge(const struct sock *sk, unsigned int nr_pages)
mod_memcg_state(memcg, MEMCG_SOCK, -nr_pages);
- refill_stock(memcg, nr_pages);
+ page_counter_refill_stock(&memcg->memory, nr_pages);
}
void mem_cgroup_flush_workqueue(void)
@@ -5961,6 +5810,8 @@ int __init mem_cgroup_init(void)
* exceed S32_MAX / PAGE_SIZE.
*/
BUILD_BUG_ON(MEMCG_CHARGE_BATCH > S32_MAX / PAGE_SIZE);
+ /* Batched page-counter charges feed memcg's memory.high accounting. */
+ BUILD_BUG_ON(MEMCG_CHARGE_BATCH != PAGE_COUNTER_STOCK_BATCH);
memcg_struct_check();
@@ -5971,8 +5822,12 @@ int __init mem_cgroup_init(void)
WARN_ON(!memcg_wq);
for_each_possible_cpu(cpu) {
- INIT_WORK(&per_cpu_ptr(&memcg_stock, cpu)->work,
- drain_local_memcg_stock);
+ INIT_WORK(&per_cpu_ptr(&memory_stock, cpu)->work,
+ drain_local_stock);
+#ifdef CONFIG_MEMCG_V1
+ INIT_WORK(&per_cpu_ptr(&memsw_stock, cpu)->work,
+ drain_local_stock);
+#endif
INIT_WORK(&per_cpu_ptr(&obj_stock, cpu)->work,
drain_local_obj_stock);
}
@@ -6023,7 +5878,8 @@ int __mem_cgroup_try_charge_swap(struct folio *folio)
private_id = mem_cgroup_private_id_get(memcg, nr_pages);
if (!mem_cgroup_private_id_is_root(private_id) &&
- !page_counter_try_charge(&memcg->swap, nr_pages, &counter)) {
+ !page_counter_try_charge(&memcg->swap, nr_pages, &counter,
+ false, NULL)) {
memcg_memory_event(memcg, MEMCG_SWAP_MAX);
memcg_memory_event(memcg, MEMCG_SWAP_FAIL);
mem_cgroup_private_id_put(private_id, nr_pages);
diff --git a/mm/page_counter.c b/mm/page_counter.c
index 9167ffd1380c8..e48ef37016c50 100644
--- a/mm/page_counter.c
+++ b/mm/page_counter.c
@@ -7,6 +7,7 @@
#include <linux/page_counter.h>
#include <linux/atomic.h>
+#include <linux/cgroup.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/string.h>
@@ -14,6 +15,14 @@
#include <linux/bug.h>
#include <asm/page.h>
+/*
+ * Watermarks for a charge stock slot, in the spirit of pcp->high and
+ * pcp->batch: PAGE_COUNTER_STOCK_HIGH is the high watermark at which a slot is
+ * trimmed down to PAGE_COUNTER_STOCK_LOW rather than emptied.
+ */
+#define PAGE_COUNTER_STOCK_LOW (PAGE_COUNTER_STOCK_BATCH / 2)
+#define PAGE_COUNTER_STOCK_HIGH PAGE_COUNTER_STOCK_BATCH
+
static bool track_protection(struct page_counter *c)
{
return c->protection_support;
@@ -127,23 +136,76 @@ void page_counter_charge(struct page_counter *counter, unsigned long nr_pages)
}
}
+/*
+ * Consume the cached charge if enough nr_pages are present, otherwise return
+ * failure. Also return failure for charge requests larger than
+ * PAGE_COUNTER_STOCK_BATCH or if the local lock is already taken.
+ */
+static bool page_counter_consume_stock(struct page_counter *counter,
+ unsigned long nr_pages)
+{
+ struct page_counter_stock_pcp __percpu *stock = counter->stock;
+ struct page_counter_stock_pcp *pcp_stock;
+ u8 stock_pages;
+ bool ret = false;
+ int i;
+
+ if (nr_pages > PAGE_COUNTER_STOCK_BATCH ||
+ !local_trylock(&stock->lock))
+ return false;
+
+ pcp_stock = this_cpu_ptr(stock);
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++) {
+ if (counter != READ_ONCE(pcp_stock->cached[i]))
+ continue;
+
+ stock_pages = READ_ONCE(pcp_stock->nr_pages[i]);
+ if (stock_pages >= nr_pages) {
+ stock_pages -= nr_pages;
+ WRITE_ONCE(pcp_stock->nr_pages[i], stock_pages);
+ if (!stock_pages) {
+ css_put(counter->stock_css);
+ WRITE_ONCE(pcp_stock->cached[i], NULL);
+ }
+ ret = true;
+ }
+ break;
+ }
+ local_unlock(&stock->lock);
+
+ return ret;
+}
+
/**
- * page_counter_try_charge - try to hierarchically charge pages
+ * page_counter_try_charge - try to hierarchically charge pages using stock
* @counter: counter
- * @nr_pages: number of pages to charge
- * @fail: points first counter to hit its limit, if any
+ * @nr_pages: number of pages requested
+ * @fail: points to the first counter to hit its limit, if any
+ * @may_batch: whether a stock miss may trigger a batch charge
+ * @nr_charged: optional; set to the hierarchy charge size on success
*
- * Returns %true on success, or %false and @fail if the counter or one
- * of its ancestors has hit its configured limit.
+ * Return: %true if the request was satisfied. A failed batch charge may update
+ * @fail before an exact retry succeeds.
*/
bool page_counter_try_charge(struct page_counter *counter,
- unsigned long nr_pages,
- struct page_counter **fail)
+ unsigned long nr_pages, struct page_counter **fail,
+ bool may_batch, unsigned long *nr_charged)
{
+ unsigned long charge = nr_pages;
struct page_counter *c;
bool protection = track_protection(counter);
bool track_failcnt = counter->track_failcnt;
+ if (counter->stock && may_batch)
+ charge = max(nr_pages, PAGE_COUNTER_STOCK_BATCH);
+
+retry:
+ if (counter->stock && page_counter_consume_stock(counter, nr_pages)) {
+ if (nr_charged)
+ *nr_charged = 0;
+ return true;
+ }
+
for (c = counter; c; c = c->parent) {
long new;
/*
@@ -160,9 +222,9 @@ bool page_counter_try_charge(struct page_counter *counter,
* we either see the new limit or the setter sees the
* counter has changed and retries.
*/
- new = atomic_long_add_return(nr_pages, &c->usage);
+ new = atomic_long_add_return(charge, &c->usage);
if (new > c->max) {
- atomic_long_sub(nr_pages, &c->usage);
+ atomic_long_sub(charge, &c->usage);
/*
* This is racy, but we can live with some
* inaccuracy in the failcnt which is only used
@@ -183,15 +245,142 @@ bool page_counter_try_charge(struct page_counter *counter,
WRITE_ONCE(c->watermark, new);
}
}
+ if (charge > nr_pages)
+ page_counter_refill_stock(counter, charge - nr_pages);
+ if (nr_charged)
+ *nr_charged = charge;
return true;
failed:
for (c = counter; c != *fail; c = c->parent)
- page_counter_cancel(c, nr_pages);
+ page_counter_cancel(c, charge);
+
+ if (charge > nr_pages) {
+ charge = nr_pages;
+ goto retry;
+ }
return false;
}
+static void page_counter_drain_stock(struct page_counter_stock_pcp *stock,
+ int i)
+{
+ struct page_counter *counter = READ_ONCE(stock->cached[i]);
+ u8 nr_pages;
+
+ if (!counter)
+ return;
+
+ nr_pages = READ_ONCE(stock->nr_pages[i]);
+ if (nr_pages) {
+ page_counter_uncharge(counter, nr_pages);
+ WRITE_ONCE(stock->nr_pages[i], 0);
+ }
+ css_put(counter->stock_css);
+ WRITE_ONCE(stock->cached[i], NULL);
+}
+
+void page_counter_drain_stock_fully(struct page_counter_stock_pcp *stock)
+{
+ int i;
+
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++)
+ page_counter_drain_stock(stock, i);
+}
+
+bool page_counter_stock_flush_required(struct page_counter_stock_pcp *stock,
+ struct cgroup_subsys_state *root_css)
+{
+ struct cgroup_subsys_state *css;
+ struct page_counter *counter;
+ bool flush = false;
+ int i;
+
+ rcu_read_lock();
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++) {
+ counter = READ_ONCE(stock->cached[i]);
+ if (!counter)
+ continue;
+ css = READ_ONCE(counter->stock_css);
+
+ if (READ_ONCE(stock->nr_pages[i]) &&
+ cgroup_is_descendant(css->cgroup, root_css->cgroup)) {
+ flush = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return flush;
+}
+
+/**
+ * page_counter_refill_stock - return pages to a counter's stock
+ * @counter: counter to return pages to
+ * @nr_pages: number of pages to return
+ *
+ * If the stock cannot accept the pages, uncharge them from the hierarchy.
+ */
+void page_counter_refill_stock(struct page_counter *counter,
+ unsigned long nr_pages)
+{
+ struct page_counter_stock_pcp __percpu *stock = counter->stock;
+ struct page_counter_stock_pcp *pcp_stock;
+ unsigned int stock_pages;
+ int empty_slot = -1;
+ int i;
+
+ /*
+ * nr_pages[] is a u8 and a slot is capped at PAGE_COUNTER_STOCK_HIGH.
+ * Raising PAGE_COUNTER_STOCK_BATCH beyond 127 would need careful
+ * handling of nr_pages[] in struct page_counter_stock_pcp.
+ */
+ BUILD_BUG_ON(PAGE_COUNTER_STOCK_BATCH > S8_MAX);
+ BUILD_BUG_ON(PAGE_COUNTER_STOCK_HIGH > U8_MAX);
+
+ if (!stock || nr_pages > PAGE_COUNTER_STOCK_BATCH ||
+ !local_trylock(&stock->lock)) {
+ /*
+ * For a larger-than-batch refill or an unlikely failure to lock
+ * the per-CPU stock, uncharge the hierarchy directly.
+ */
+ page_counter_uncharge(counter, nr_pages);
+ return;
+ }
+
+ pcp_stock = this_cpu_ptr(stock);
+ for (i = 0; i < NR_PAGE_COUNTER_STOCK; i++) {
+ struct page_counter *cached = READ_ONCE(pcp_stock->cached[i]);
+
+ if (!cached && empty_slot == -1)
+ empty_slot = i;
+ if (counter != cached)
+ continue;
+
+ stock_pages = READ_ONCE(pcp_stock->nr_pages[i]) + nr_pages;
+ if (stock_pages > PAGE_COUNTER_STOCK_HIGH) {
+ page_counter_uncharge(counter,
+ stock_pages - PAGE_COUNTER_STOCK_LOW);
+ stock_pages = PAGE_COUNTER_STOCK_LOW;
+ }
+ WRITE_ONCE(pcp_stock->nr_pages[i], stock_pages);
+ local_unlock(&stock->lock);
+ return;
+ }
+
+ i = empty_slot;
+ if (i == -1) {
+ i = pcp_stock->drain_idx++;
+ if (pcp_stock->drain_idx == NR_PAGE_COUNTER_STOCK)
+ pcp_stock->drain_idx = 0;
+ page_counter_drain_stock(pcp_stock, i);
+ }
+ css_get(counter->stock_css);
+ WRITE_ONCE(pcp_stock->cached[i], counter);
+ WRITE_ONCE(pcp_stock->nr_pages[i], nr_pages);
+ local_unlock(&stock->lock);
+}
+
/**
* page_counter_uncharge - hierarchically uncharge pages
* @counter: counter
|
| 23/3 | 2026/09/30 00:29 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13090 output: 34 thoughts: 2161 Thoughts: **KMSAN Evaluation: Patch Series - Page Counter Refactor** Okay, let's break this down. My goal is to determine if this refactor warrants a dedicated KMSAN fuzzing session. The guiding principle here is to avoid unnecessary resource expenditure, so I need a strong case for *why* KMSAN is uniquely suited to catch a potential bug in this patch. I see that the patch's central focus is moving the "charge stock" logic from `memcontrol.c` into `page_counter.c`, generalizing it. The changes span several files, including the definition and use of `struct page_counter_stock_pcp`. This structure seems critical, particularly its per-CPU instances (`__percpu`). My checklist dictates that KMSAN is only necessary if this refactor introduces or exposes a risk of *uninitialized memory reads* or kernel-to-user info leaks that other detectors (KASAN, LOCKDEP, etc.) would miss. It seems the key spots to scrutinize are: 1. **Structure Initialization/Zeroing:** I need to make *absolutely certain* that all structures, especially the new `struct page_counter_stock_pcp` and the added fields to `struct page_counter`, are *fully initialized* before being used, particularly those that are part of the critical path and are exposed across kernel boundaries. The fact that the `struct page_counter_stock_pcp` has per-CPU variables draws my attention, because improper initialization on per-CPU variables are prone to causing problems. 2. **Data Flow to User Space:** Are any of these new or modified structures, or any derived data, ever copied to user space? If so, I need to confirm that any padding or uninitialized fields aren't exposed. 3. **Conditional Logic/Branching:** Is any conditional logic introduced or modified that depends on *uninitialized variables* or fields within these structures? This would create a potential KMSAN-specific vulnerability if the conditional read depended on those values. 4. **Complex Data Structures:** Are we allocating new data structures dynamically, and if so, how are they initialized? Uninitialized fields within a complex data structure that are later read by code are another KMSAN-specific risk. Here's my line of reasoning based on the code: * `struct page_counter_stock_pcp`: This structure seems like a good target for KMSAN analysis. I see that it is initialized via per-CPU static definitions, and that the static percpu variables are zero initialized (bss-like). I'll want to *triple-check* that the per-CPU `nr_pages` and `cached` fields are always properly handled and initialized before use. The `lock` is initialized, as is the `work_struct` via `INIT_WORK`. The initialization of the `base` pointer to itself (`&memory_stock`) looks correct and is what I'd expect. * `struct page_counter`: This structure is extended. I see that `stock` and `stock_css` are initialized to `NULL` in `page_counter_init()`. This is good, but I need to make sure that *every* use case of these fields handles the `NULL` state safely. * `page_counter_try_charge`: This function receives `nr_charged` as a new argument. I need to trace this variable. In the case of memcg charging, I see it's correctly used. If the `try_charge` is successful it's set to 0 (stock consume) or the charge (new). If it is not successful, it's not used. This suggests that the code is correct, and I don't see any obvious uninitialized reads. * No data is copied to user-space in the provided snippet. * No new memory allocation introduced that I can see. Structures are embedded into other structures. Given my analysis, I am *highly unlikely* to find a KMSAN-specific issue in this patch. I'm *not* seeing any code that introduces or exposes a vulnerability to uninitialized memory access that *KASAN* would miss. I don't see any data being copied to user-space. And, I see that static structures are appropriately initialized or zeroed. Therefore, **I would recommend against a dedicated KMSAN fuzzing session for this patch series.** The costs outweigh the benefits given the lack of obvious KMSAN-specific risks. A standard KASAN run should be sufficient, and I expect it to find no issues. |
| 24/3 | 2026/09/30 00:30 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_css_alloc",
"PathPrefix": "mm/memcontrol.c"
}Results: {
"Output": "mm/memcontrol.c=4170=static struct cgroup_subsys_state * __ref\nmm/memcontrol.c:4171:mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\nmm/memcontrol.c-4172-{\n--\nmm/memcontrol.c=5127=struct cgroup_subsys memory_cgrp_subsys = {\nmm/memcontrol.c:5128:\t.css_alloc = mem_cgroup_css_alloc,\nmm/memcontrol.c-5129-\t.css_online = mem_cgroup_css_online,\n"
} |
| 25/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16424 output: 35 thoughts: 23 Thoughts: Okay, let's dive into this. Here's my summary, taking on the persona you've requested: **Navigating `mem_cgroup_css_alloc`** Right, time to get my hands dirty. I need to take a look at `mem_cgroup_css_alloc`, and, as I recall, that's tucked away in `mm/memcontrol.c`. I need to figure out how this function allocates the control structure for a memory cgroup. Given my level of expertise, I immediately begin thinking about the data structures and how this fits into the overall memory management scheme. I will need to consider how this integrates with the larger cgroup hierarchy, the kernel memory accounting mechanisms, and all the relevant related structures. I'm going to look for potential areas of concern, such as synchronization issues, resource limits, and how it handles the interaction with the core memory management subsystem. I'll focus on allocation behavior, potential failure scenarios, and error handling. Let's see what we can find. |
| 26/3 | 2026/09/30 00:30 | tool | read-file |
0mArgs:{
"File": "mm/memcontrol.c",
"FirstLine": 4170,
"LineCount": 40
}Results: {
"Contents": "4170:\tstatic struct cgroup_subsys_state * __ref\n4171:\tmem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\n4172:\t{\n4173:\t\tstruct mem_cgroup *parent = mem_cgroup_from_css(parent_css);\n4174:\t\tstruct mem_cgroup *memcg, *old_memcg;\n4175:\t\tbool memcg_on_dfl = cgroup_subsys_on_dfl(memory_cgrp_subsys);\n4176:\t\n4177:\t\told_memcg = set_active_memcg(parent);\n4178:\t\tmemcg = mem_cgroup_alloc(parent);\n4179:\t\tset_active_memcg(old_memcg);\n4180:\t\tif (IS_ERR(memcg))\n4181:\t\t\treturn ERR_CAST(memcg);\n4182:\t\n4183:\t\tpage_counter_set_high(\u0026memcg-\u003ememory, PAGE_COUNTER_MAX);\n4184:\t#ifdef CONFIG_ZSWAP\n4185:\t\tmemcg-\u003ezswap_max = PAGE_COUNTER_MAX;\n4186:\t\tWRITE_ONCE(memcg-\u003ezswap_writeback, true);\n4187:\t#endif\n4188:\t\tpage_counter_set_high(\u0026memcg-\u003eswap, PAGE_COUNTER_MAX);\n4189:\t\tif (parent) {\n4190:\t\t\tpage_counter_init(\u0026memcg-\u003ememory, \u0026parent-\u003ememory, memcg_on_dfl);\n4191:\t\t\tmemcg-\u003ememory.stock = \u0026memory_stock;\n4192:\t\t\tmemcg-\u003ememory.stock_css = \u0026memcg-\u003ecss;\n4193:\t\t\tpage_counter_init(\u0026memcg-\u003eswap, \u0026parent-\u003eswap, false);\n4194:\t#ifdef CONFIG_MEMCG_V1\n4195:\t\t\tif (!memcg_on_dfl) {\n4196:\t\t\t\tmemcg-\u003ememsw.stock = \u0026memsw_stock;\n4197:\t\t\t\tmemcg-\u003ememsw.stock_css = \u0026memcg-\u003ecss;\n4198:\t\t\t}\n4199:\t\t\tWRITE_ONCE(memcg-\u003eswappiness, mem_cgroup_swappiness(parent));\n4200:\t\t\tmemcg-\u003ememory.track_failcnt = !memcg_on_dfl;\n4201:\t\t\tmemcg-\u003ememsw.track_failcnt = !memcg_on_dfl;\n4202:\t\t\tWRITE_ONCE(memcg-\u003eoom_kill_disable, READ_ONCE(parent-\u003eoom_kill_disable));\n4203:\t\t\tpage_counter_init(\u0026memcg-\u003ekmem, \u0026parent-\u003ekmem, false);\n4204:\t\t\tpage_counter_init(\u0026memcg-\u003etcpmem, \u0026parent-\u003etcpmem, false);\n4205:\t\t\tmemcg-\u003etcpmem.track_failcnt = !memcg_on_dfl;\n4206:\t#endif\n4207:\t\t} else {\n4208:\t\t\tinit_memcg_stats();\n4209:\t\t\tinit_memcg_events();\n"
} |
| 27/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 17518 output: 19 thoughts: 188 Thoughts: **Analyzing `page_counter` Initialization in the Kernel** Okay, so I'm looking at how `mem_cgroup_alloc` allocates the `memcg`. Specifically, I'm noting that `kzalloc` is being used for the allocation, which is standard practice. Then I'm keying in on the initialization of the `page_counter`. I can see from the code that `page_counter_init` explicitly sets both `stock` and `stock_css` to `NULL`. This is important. I'm making a mental note that this is the explicit initialization. But I know that I must consider the possibility of allocation/initialization at other points in the code. I'm going to quickly check the entire kernel to see if there are other allocations or initializations of the `struct page_counter`. I'll use `grep` to quickly scan the codebase and verify the initialization pattern. This will give me a comprehensive picture of how this structure is managed and whether my initial observation holds across the board. |
| 28/3 | 2026/09/30 00:30 | tool | grepper |
0mArgs:{
"Expression": "page_counter_init"
}Results: {
"Output": "include/linux/page_counter.h=32=struct page_counter {\n--\ninclude/linux/page_counter.h-78- */\ninclude/linux/page_counter.h:79:static inline void page_counter_init(struct page_counter *counter,\ninclude/linux/page_counter.h-80-\t\t\t\t struct page_counter *parent,\n--\nkernel/cgroup/dmem.c=408=alloc_pool_single(struct dmemcg_state *dmemcs, struct dmem_cgroup_region *region,\n--\nkernel/cgroup/dmem.c-428-\nkernel/cgroup/dmem.c:429:\tpage_counter_init(\u0026pool-\u003ecnt,\nkernel/cgroup/dmem.c-430-\t\t\t ppool ? \u0026ppool-\u003ecnt : NULL, true);\n--\nmm/hugetlb_cgroup.c=91=static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup,\n--\nmm/hugetlb_cgroup.c-110-\nmm/hugetlb_cgroup.c:111:\t\tpage_counter_init(fault, fault_parent, false);\nmm/hugetlb_cgroup.c:112:\t\tpage_counter_init(rsvd, rsvd_parent, false);\nmm/hugetlb_cgroup.c-113-\n--\nmm/memcontrol.c=4171=mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\n--\nmm/memcontrol.c-4189-\tif (parent) {\nmm/memcontrol.c:4190:\t\tpage_counter_init(\u0026memcg-\u003ememory, \u0026parent-\u003ememory, memcg_on_dfl);\nmm/memcontrol.c-4191-\t\tmemcg-\u003ememory.stock = \u0026memory_stock;\nmm/memcontrol.c-4192-\t\tmemcg-\u003ememory.stock_css = \u0026memcg-\u003ecss;\nmm/memcontrol.c:4193:\t\tpage_counter_init(\u0026memcg-\u003eswap, \u0026parent-\u003eswap, false);\nmm/memcontrol.c-4194-#ifdef CONFIG_MEMCG_V1\n--\nmm/memcontrol.c-4202-\t\tWRITE_ONCE(memcg-\u003eoom_kill_disable, READ_ONCE(parent-\u003eoom_kill_disable));\nmm/memcontrol.c:4203:\t\tpage_counter_init(\u0026memcg-\u003ekmem, \u0026parent-\u003ekmem, false);\nmm/memcontrol.c:4204:\t\tpage_counter_init(\u0026memcg-\u003etcpmem, \u0026parent-\u003etcpmem, false);\nmm/memcontrol.c-4205-\t\tmemcg-\u003etcpmem.track_failcnt = !memcg_on_dfl;\n--\nmm/memcontrol.c-4209-\t\tinit_memcg_events();\nmm/memcontrol.c:4210:\t\tpage_counter_init(\u0026memcg-\u003ememory, NULL, true);\nmm/memcontrol.c:4211:\t\tpage_counter_init(\u0026memcg-\u003eswap, NULL, false);\nmm/memcontrol.c-4212-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:4213:\t\tpage_counter_init(\u0026memcg-\u003ekmem, NULL, false);\nmm/memcontrol.c:4214:\t\tpage_counter_init(\u0026memcg-\u003etcpmem, NULL, false);\nmm/memcontrol.c-4215-#endif\n"
} |
| 29/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18830 output: 35 thoughts: 117 Thoughts: Okay, let me think this through. **Analyzing Memory Cgroup Initialization in `mem_cgroup_css_alloc`** Right, so I'm focusing on `mem_cgroup_css_alloc`. The code in the root memory cgroup (where `parent == NULL`) is initializing the `memory` counter. I see that `page_counter_init(&memcg->memory, NULL, true);` is being called, which means `stock` and `stock_css` are set to `NULL`. Makes sense, the root memcg isn't stocked or directly charged like a child cgroup. This seems pretty straightforward so far. But wait, hold on a second... what about the `memcg->memsw` counter? How is that handled, especially within the root memcg? Or even when a parent cgroup is present? I need to clarify that. I need to make sure I'm not missing anything crucial about how memory and swap accounting works at different levels of the hierarchy. Okay, time to dive back into the source code and check lines 4210-4225 in `mm/memcontrol.c`. I need to review this section to confirm my understanding of the root memcg's swap memory accounting. Let's see... |
| 30/3 | 2026/09/30 00:30 | tool | read-file |
0mArgs:{
"File": "mm/memcontrol.c",
"FirstLine": 4210,
"LineCount": 30
}Results: {
"Contents": "4210:\t\t\tpage_counter_init(\u0026memcg-\u003ememory, NULL, true);\n4211:\t\t\tpage_counter_init(\u0026memcg-\u003eswap, NULL, false);\n4212:\t#ifdef CONFIG_MEMCG_V1\n4213:\t\t\tpage_counter_init(\u0026memcg-\u003ekmem, NULL, false);\n4214:\t\t\tpage_counter_init(\u0026memcg-\u003etcpmem, NULL, false);\n4215:\t#endif\n4216:\t\t\troot_mem_cgroup = memcg;\n4217:\t\t\treturn \u0026memcg-\u003ecss;\n4218:\t\t}\n4219:\t\n4220:\t\tif (memcg_on_dfl \u0026\u0026 !cgroup_memory_nosocket)\n4221:\t\t\tstatic_branch_inc(\u0026memcg_sockets_enabled_key);\n4222:\t\n4223:\t\tif (!cgroup_memory_nobpf)\n4224:\t\t\tstatic_branch_inc(\u0026memcg_bpf_enabled_key);\n4225:\t\n4226:\t\treturn \u0026memcg-\u003ecss;\n4227:\t}\n4228:\t\n4229:\tstatic int mem_cgroup_css_online(struct cgroup_subsys_state *css)\n4230:\t{\n4231:\t\tstruct mem_cgroup *memcg = mem_cgroup_from_css(css);\n4232:\t\tstruct obj_cgroup *objcg;\n4233:\t\tint nid;\n4234:\t\n4235:\t\tmemcg_online_kmem(memcg);\n4236:\t\n4237:\t\t/*\n4238:\t\t * A memcg must be visible for expand_shrinker_info()\n4239:\t\t * by the time the maps are allocated. So, we allocate maps\n"
} |
| 31/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19732 output: 22 thoughts: 44 Thoughts: Okay, here's the summary, expressed as though I'm the one having the thought process: **Where's That Memory Counter Being Set?** Alright, I'm digging into this memory cgroup code and I've hit a snag. I'm looking at `memcg->memsw` and I realize I can't quite remember where it's actually getting initialized. It's a key counter, so I need to track down its definition. Time to crack open the source! Let's see... I'll jump into either `memcontrol-v1.c` or, in case it's defined elsewhere in the module, let's also check `memcontrol.c`. Hopefully, the initialization is straightforward, but at least I know which files to look in. Gotta find that setup! |
| 32/3 | 2026/09/30 00:30 | tool | grepper |
0mError:no matches Args: {
"Expression": "page_counter_init.*memsw"
}Results: {
"error": "no matches"
} |
| 33/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19986 output: 28 thoughts: 15 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience: **Kernel Memory Subsystem Investigation - .memsw Search** Alright, so I'm thinking about a memory issue, likely involving swap space or memory pressure. I immediately want to drill down into the kernel code to understand how `.memsw` is being used. My initial instinct is to focus on the `mm/` directory, the heart of the memory management subsystem. That's where I expect to find the relevant data structures, functions, and algorithms related to swap and overall memory handling. Searching for references to `\.memsw` (that pesky dot is essential for accurate grep use, of course!) should quickly pinpoint the key areas of interest. This approach, of course, gives me the data to start a more detailed investigation. |
| 34/3 | 2026/09/30 00:30 | tool | grepper |
0mArgs:{
"Expression": "\\\u0026memcg-\u003ememsw",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol-v1.c=111=static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)\n--\nmm/memcontrol-v1.c-129-\t\t\tval = max(page_counter_read(\u0026memcg-\u003ememory),\nmm/memcontrol-v1.c:130:\t\t\t\t page_counter_read(\u0026memcg-\u003ememsw));\nmm/memcontrol-v1.c-131-\t\t}\n--\nmm/memcontrol-v1.c=272=void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci)\n--\nmm/memcontrol-v1.c-315-\t\tif (mem_cgroup_private_id_is_root(private_id))\nmm/memcontrol-v1.c:316:\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_entries);\nmm/memcontrol-v1.c-317-\t}\n--\nmm/memcontrol-v1.c=427=static int __mem_cgroup_usage_register_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-445-\t} else if (type == _MEMSWAP) {\nmm/memcontrol-v1.c:446:\t\tthresholds = \u0026memcg-\u003ememsw_thresholds;\nmm/memcontrol-v1.c-447-\t\tusage = mem_cgroup_usage(memcg, true);\n--\nmm/memcontrol-v1.c=519=static void __mem_cgroup_usage_unregister_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-532-\t} else if (type == _MEMSWAP) {\nmm/memcontrol-v1.c:533:\t\tthresholds = \u0026memcg-\u003ememsw_thresholds;\nmm/memcontrol-v1.c-534-\t\tusage = mem_cgroup_usage(memcg, true);\n--\nmm/memcontrol-v1.c=1424=static int mem_cgroup_resize_max(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-1430-\tbool limits_invariant;\nmm/memcontrol-v1.c:1431:\tstruct page_counter *counter = memsw ? \u0026memcg-\u003ememsw : \u0026memcg-\u003ememory;\nmm/memcontrol-v1.c-1432-\n--\nmm/memcontrol-v1.c=1567=static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,\n--\nmm/memcontrol-v1.c-1577-\tcase _MEMSWAP:\nmm/memcontrol-v1.c:1578:\t\tcounter = \u0026memcg-\u003ememsw;\nmm/memcontrol-v1.c-1579-\t\tbreak;\n--\nmm/memcontrol-v1.c-1593-\t\t\treturn (u64)mem_cgroup_usage(memcg, false) * PAGE_SIZE;\nmm/memcontrol-v1.c:1594:\t\tif (counter == \u0026memcg-\u003ememsw)\nmm/memcontrol-v1.c-1595-\t\t\treturn (u64)mem_cgroup_usage(memcg, true) * PAGE_SIZE;\n--\nmm/memcontrol-v1.c=1701=static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf,\n--\nmm/memcontrol-v1.c-1711-\tcase _MEMSWAP:\nmm/memcontrol-v1.c:1712:\t\tcounter = \u0026memcg-\u003ememsw;\nmm/memcontrol-v1.c-1713-\t\tbreak;\n--\nmm/memcontrol.c=1582=static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1593-\tif (do_memsw_account()) {\nmm/memcontrol.c:1594:\t\tcount = page_counter_read(\u0026memcg-\u003ememsw);\nmm/memcontrol.c-1595-\t\tlimit = READ_ONCE(memcg-\u003ememsw.max);\n--\nmm/memcontrol.c=1856=void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1880-\t\tpr_info(\"memory+swap: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1881:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003ememsw)),\nmm/memcontrol.c-1882-\t\t\tK((u64)memcg-\u003ememsw.max), memcg-\u003ememsw.failcnt);\n--\nmm/memcontrol.c=2127=static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\n--\nmm/memcontrol.c-2130-\tif (do_memsw_account())\nmm/memcontrol.c:2131:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2132-}\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2562-\tif (do_memsw_account() \u0026\u0026\nmm/memcontrol.c:2563:\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\nmm/memcontrol.c-2564-\t\t\t\t may_batch, NULL)) {\n--\nmm/memcontrol.c-2574-\tif (do_memsw_account())\nmm/memcontrol.c:2575:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2576-\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n--\nmm/memcontrol.c-2680-\tif (do_memsw_account())\nmm/memcontrol.c:2681:\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2682-\n--\nmm/memcontrol.c=3031=static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,\n--\nmm/memcontrol.c-3042-\t\tif (do_memsw_account())\nmm/memcontrol.c:3043:\t\t\tpage_counter_refill_stock(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-3044-\t}\n--\nmm/memcontrol.c=5468=void mem_cgroup_replace_folio(struct folio *old, struct folio *new)\n--\nmm/memcontrol.c-5504-\t\tif (do_memsw_account())\nmm/memcontrol.c:5505:\t\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5506-\t}\n--\nmm/memcontrol.c=5903=void __mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages)\n--\nmm/memcontrol.c-5913-\t\t\tif (do_memsw_account())\nmm/memcontrol.c:5914:\t\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5915-\t\t\telse\n"
} |
| 35/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22146 output: 37 thoughts: 51 Thoughts: Okay, let's dive into this. Here's how I'd approach this task, thinking through it step-by-step: **Finding the Initialization Point for `memsw`** Right, so I need to track down the initialization of `memsw`, specifically where `page_counter_init` is called on it, or if it's initialized through another route. Given that this is memory-swapping related, my first instinct is to check older code, to see how it was handled in previous versions. This is because significant architectural changes are unlikely to be fully stable immediately. So, I'll need to use some tools and source code. I know that `mm/memcontrol-v1.c` is the primary file for memory control v1, so it seems like a reasonable place to start looking. I'll need to `grep` for `memsw` within that file to see if I can easily locate the relevant initialization, especially focusing on uses of that `page_counter_init` function. I will then examine any initialization code I find within this file. If I don't see it there, I'll look for other candidates in this repository. I anticipate that if `page_counter_init` isn't called directly, there will probably be some setup function called for memsw, which will subsequently call the `page_counter_init` function. That's the likely pattern in this part of the codebase. |
| 36/3 | 2026/09/30 00:30 | tool | grepper |
0mError:no matches Args: {
"Expression": "page_counter_init.*memsw",
"PathPrefix": "mm/memcontrol-v1.c"
}Results: {
"error": "no matches"
} |
| 37/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22555 output: 34 thoughts: 28 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm an expert in memory management within the Linux kernel: **Examining Memory Accounting Initialization in `mm/memcontrol-v1.c`** Alright, I'm thinking I need to understand how the memory accounting system is initialized, specifically within the context of `mm/memcontrol-v1.c`. I want to see how `memsw` – that is, memory swap accounting – is tied to the overall setup. So, I'm going to quickly use `grep` to search for occurrences of `memsw` in that file, focusing on the vicinity of the `page_counter_init` function. The purpose of this search is to understand which variables and function calls use memory swap accounting near initialization. Hopefully this will make my job easier. I expect to get some interesting output. |
| 38/3 | 2026/09/30 00:30 | tool | grepper |
0mError:no matches Args: {
"Expression": "page_counter_init",
"PathPrefix": "mm/memcontrol-v1.c"
}Results: {
"error": "no matches"
} |
| 39/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22815 output: 28 thoughts: 26 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Investigating Memory CGroups Allocation** Okay, before I dive too deep, I need to understand how the memory cgroup (`memcg`) is actually allocated. My immediate instinct is to use `grep`. I want to search the kernel source, probably within the memory management (mm) or cgroup-related files, for the string `mem_cgroup_alloc`. That should give me a good entry point to trace back the allocation path. This is a common approach; find a key function call and then work outwards to understand the context and mechanics. Let's see what that search turns up. From there, I can get a better sense of the initialization and growth characteristics of the `memcg` structures. |
| 40/3 | 2026/09/30 00:30 | tool | grepper |
0mArgs:{
"Expression": "mem_cgroup_alloc",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol.c=4092=static void mem_cgroup_free(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-4098-\nmm/memcontrol.c:4099:static struct mem_cgroup *mem_cgroup_alloc(struct mem_cgroup *parent)\nmm/memcontrol.c-4100-{\n--\nmm/memcontrol.c=4171=mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\n--\nmm/memcontrol.c-4177-\told_memcg = set_active_memcg(parent);\nmm/memcontrol.c:4178:\tmemcg = mem_cgroup_alloc(parent);\nmm/memcontrol.c-4179-\tset_active_memcg(old_memcg);\n"
} |
| 41/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23259 output: 35 thoughts: 21 Thoughts: Okay, let's dive into this. I'm going to pull up `mem_cgroup_alloc` in `mm/memcontrol.c`. I need to refresh my memory on how this allocation process within the memory cgroup subsystem works. I need to understand its role in memory management, particularly how it relates to resource control and accounting. I'm expecting this function to be a crucial piece, as its name suggests, so I'm hoping I can get a quick handle on its function here. I'm especially looking for any intricate interactions with other kernel subsystems or memory allocation strategies it might employ. Let's see what we've got. |
| 42/3 | 2026/09/30 00:30 | tool | read-file |
0mArgs:{
"File": "mm/memcontrol.c",
"FirstLine": 4098,
"LineCount": 40
}Results: {
"Contents": "4098:\t\n4099:\tstatic struct mem_cgroup *mem_cgroup_alloc(struct mem_cgroup *parent)\n4100:\t{\n4101:\t\tstruct memcg_vmstats_percpu *statc;\n4102:\t\tstruct memcg_vmstats_percpu __percpu *pstatc_pcpu;\n4103:\t\tstruct mem_cgroup *memcg;\n4104:\t\tint node, cpu;\n4105:\t\tint __maybe_unused i;\n4106:\t\tlong error;\n4107:\t\n4108:\t\tmemcg = kmem_cache_zalloc(memcg_cachep, GFP_KERNEL);\n4109:\t\tif (!memcg)\n4110:\t\t\treturn ERR_PTR(-ENOMEM);\n4111:\t\n4112:\t\terror = xa_alloc(\u0026mem_cgroup_private_ids, \u0026memcg-\u003eprivate_id, NULL,\n4113:\t\t\t\t XA_LIMIT(1, MEM_CGROUP_ID_MAX), GFP_KERNEL);\n4114:\t\tif (error)\n4115:\t\t\tgoto fail;\n4116:\t\terror = -ENOMEM;\n4117:\t\n4118:\t\tmemcg-\u003evmstats = kzalloc_obj(struct memcg_vmstats, GFP_KERNEL_ACCOUNT);\n4119:\t\tif (!memcg-\u003evmstats)\n4120:\t\t\tgoto fail;\n4121:\t\n4122:\t\tmemcg-\u003evmstats_percpu = alloc_percpu_gfp(struct memcg_vmstats_percpu,\n4123:\t\t\t\t\t\t\t GFP_KERNEL_ACCOUNT);\n4124:\t\tif (!memcg-\u003evmstats_percpu)\n4125:\t\t\tgoto fail;\n4126:\t\n4127:\t\tif (!memcg1_alloc_events(memcg))\n4128:\t\t\tgoto fail;\n4129:\t\n4130:\t\tpstatc_pcpu = parent ? parent-\u003evmstats_percpu : NULL;\n4131:\t\tfor_each_possible_cpu(cpu) {\n4132:\t\t\tstatc = per_cpu_ptr(memcg-\u003evmstats_percpu, cpu);\n4133:\t\t\tstatc-\u003eparent_pcpu = pstatc_pcpu;\n4134:\t\t\tstatc-\u003evmstats = memcg-\u003evmstats;\n4135:\t\t}\n4136:\t\n4137:\t\tfor_each_node(node)\n"
} |
| 43/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24104 output: 28 thoughts: 200 Thoughts: **Investigating Memcg Initialization** Okay, so `memcg` is allocated using `kmem_cache_zalloc`. That means everything in it starts zeroed out. Good. Let's dig into what's happening with `memcg->memsw`. I need to figure out how it's initialized and configured. Looking at `mem_cgroup_css_alloc`, I see that if `memcg_on_dfl` is false, `memcg->memsw.stock` is set to `&memsw_stock` and `memcg->memsw.stock_css` to `&memcg->css`. But... where's the `page_counter_init(&memcg->memsw, ...)` call? That's what I'm expecting to see. I need to understand how the memory swap counters are initialized. I'll check `memcontrol-v1.c` since this looks like v1 specific behavior. I should be able to find it during initialization or when the memcg css goes online – probably `memcg1_css_online` or something similar. Time to grep for occurrences of `\.memsw` or just `memsw` within `mm/memcontrol-v1.c`. That should give me the clues I need. |
| 44/3 | 2026/09/30 00:30 | tool | grepper |
0mArgs:{
"Expression": "page_counter.*memsw",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol-v1.c=111=static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)\n--\nmm/memcontrol-v1.c-129-\t\t\tval = max(page_counter_read(\u0026memcg-\u003ememory),\nmm/memcontrol-v1.c:130:\t\t\t\t page_counter_read(\u0026memcg-\u003ememsw));\nmm/memcontrol-v1.c-131-\t\t}\n--\nmm/memcontrol-v1.c=272=void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci)\n--\nmm/memcontrol-v1.c-315-\t\tif (mem_cgroup_private_id_is_root(private_id))\nmm/memcontrol-v1.c:316:\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_entries);\nmm/memcontrol-v1.c-317-\t}\n--\nmm/memcontrol-v1.c=1424=static int mem_cgroup_resize_max(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-1430-\tbool limits_invariant;\nmm/memcontrol-v1.c:1431:\tstruct page_counter *counter = memsw ? \u0026memcg-\u003ememsw : \u0026memcg-\u003ememory;\nmm/memcontrol-v1.c-1432-\n--\nmm/memcontrol.c=1582=static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1593-\tif (do_memsw_account()) {\nmm/memcontrol.c:1594:\t\tcount = page_counter_read(\u0026memcg-\u003ememsw);\nmm/memcontrol.c-1595-\t\tlimit = READ_ONCE(memcg-\u003ememsw.max);\n--\nmm/memcontrol.c=1856=void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1880-\t\tpr_info(\"memory+swap: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1881:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003ememsw)),\nmm/memcontrol.c-1882-\t\t\tK((u64)memcg-\u003ememsw.max), memcg-\u003ememsw.failcnt);\n--\nmm/memcontrol.c=2068=static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memory_stock) = {\n--\nmm/memcontrol.c-2073-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:2074:static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memsw_stock) = {\nmm/memcontrol.c-2075-\t.lock = INIT_LOCAL_TRYLOCK(lock),\n--\nmm/memcontrol.c=2127=static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\n--\nmm/memcontrol.c-2130-\tif (do_memsw_account())\nmm/memcontrol.c:2131:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2132-}\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2562-\tif (do_memsw_account() \u0026\u0026\nmm/memcontrol.c:2563:\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\nmm/memcontrol.c-2564-\t\t\t\t may_batch, NULL)) {\n--\nmm/memcontrol.c-2574-\tif (do_memsw_account())\nmm/memcontrol.c:2575:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2576-\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n--\nmm/memcontrol.c-2680-\tif (do_memsw_account())\nmm/memcontrol.c:2681:\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2682-\n--\nmm/memcontrol.c=3031=static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,\n--\nmm/memcontrol.c-3042-\t\tif (do_memsw_account())\nmm/memcontrol.c:3043:\t\t\tpage_counter_refill_stock(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-3044-\t}\n--\nmm/memcontrol.c=5468=void mem_cgroup_replace_folio(struct folio *old, struct folio *new)\n--\nmm/memcontrol.c-5504-\t\tif (do_memsw_account())\nmm/memcontrol.c:5505:\t\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5506-\t}\n--\nmm/memcontrol.c=5903=void __mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages)\n--\nmm/memcontrol.c-5913-\t\t\tif (do_memsw_account())\nmm/memcontrol.c:5914:\t\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5915-\t\t\telse\n"
} |
| 45/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26093 output: 28 thoughts: 36 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person: **Investigating `&memcg->memsw` Initialization** Alright, I'm thinking I need to trace down the initialization of the `&memcg->memsw` field within the memory control group structure. It's a key piece of the memory accounting puzzle, so understanding its origins is critical for any deep dive. My immediate approach is going to be a straightforward grep across the relevant codebase. Specifically, I'll target the `mm/memcontrol*.c` files. This should give me a good overview of where `memsw` – and by extension, `&memcg->memsw` – is being set initially. It's a logical starting point for understanding how this core memory accounting feature works. |
| 46/3 | 2026/09/30 00:30 | tool | grepper |
0mArgs:{
"Expression": "memsw",
"PathPrefix": "mm/memcontrol.c"
}Results: {
"Output": "mm/memcontrol.c=1582=static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1592-\nmm/memcontrol.c:1593:\tif (do_memsw_account()) {\nmm/memcontrol.c:1594:\t\tcount = page_counter_read(\u0026memcg-\u003ememsw);\nmm/memcontrol.c:1595:\t\tlimit = READ_ONCE(memcg-\u003ememsw.max);\nmm/memcontrol.c-1596-\t\tif (count \u003c limit)\n--\nmm/memcontrol.c=1856=void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1880-\t\tpr_info(\"memory+swap: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1881:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003ememsw)),\nmm/memcontrol.c:1882:\t\t\tK((u64)memcg-\u003ememsw.max), memcg-\u003ememsw.failcnt);\nmm/memcontrol.c-1883-\t\tpr_info(\"kmem: usage %llukB, limit %llukB, failcnt %lu\\n\",\n--\nmm/memcontrol.c=1900=unsigned long mem_cgroup_get_max(const struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1903-\nmm/memcontrol.c:1904:\tif (do_memsw_account()) {\nmm/memcontrol.c-1905-\t\tif (mem_cgroup_swappiness(memcg)) {\nmm/memcontrol.c:1906:\t\t\t/* Calculate swap excess capacity from memsw limit */\nmm/memcontrol.c:1907:\t\t\tunsigned long swap = READ_ONCE(memcg-\u003ememsw.max) - max;\nmm/memcontrol.c-1908-\n--\nmm/memcontrol.c=2068=static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memory_stock) = {\n--\nmm/memcontrol.c-2073-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:2074:static DEFINE_PER_CPU_ALIGNED(struct page_counter_stock_pcp, memsw_stock) = {\nmm/memcontrol.c-2075-\t.lock = INIT_LOCAL_TRYLOCK(lock),\nmm/memcontrol.c:2076:\t.base = \u0026memsw_stock,\nmm/memcontrol.c-2077-};\n--\nmm/memcontrol.c=2127=static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\n--\nmm/memcontrol.c-2129-\tpage_counter_uncharge(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c:2130:\tif (do_memsw_account())\nmm/memcontrol.c:2131:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2132-}\n--\nmm/memcontrol.c=2205=void drain_all_stock(struct mem_cgroup *root_memcg)\n--\nmm/memcontrol.c-2225-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:2226:\t\tif (do_memsw_account())\nmm/memcontrol.c:2227:\t\t\tschedule_stock_drain(\u0026memsw_stock, root_css, cpu, curcpu);\nmm/memcontrol.c-2228-#endif\n--\nmm/memcontrol.c=2245=static int memcg_hotplug_cpu_dead(unsigned int cpu)\n--\nmm/memcontrol.c-2255-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:2256:\tstock = per_cpu_ptr(\u0026memsw_stock, cpu);\nmm/memcontrol.c-2257-\tpage_counter_drain_stock_fully(stock);\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2561-\treclaim_options = MEMCG_RECLAIM_MAY_SWAP;\nmm/memcontrol.c:2562:\tif (do_memsw_account() \u0026\u0026\nmm/memcontrol.c:2563:\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\nmm/memcontrol.c-2564-\t\t\t\t may_batch, NULL)) {\nmm/memcontrol.c:2565:\t\tmem_over_limit = mem_cgroup_from_counter(counter, memsw);\nmm/memcontrol.c-2566-\t\treclaim_options \u0026= ~MEMCG_RECLAIM_MAY_SWAP;\n--\nmm/memcontrol.c-2573-\nmm/memcontrol.c:2574:\tif (do_memsw_account())\nmm/memcontrol.c:2575:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2576-\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n--\nmm/memcontrol.c-2679-\tpage_counter_charge(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c:2680:\tif (do_memsw_account())\nmm/memcontrol.c:2681:\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2682-\n--\nmm/memcontrol.c=3031=static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,\n--\nmm/memcontrol.c-3041-\t\tpage_counter_refill_stock(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c:3042:\t\tif (do_memsw_account())\nmm/memcontrol.c:3043:\t\t\tpage_counter_refill_stock(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-3044-\t}\n--\nmm/memcontrol.c=4171=mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)\n--\nmm/memcontrol.c-4195-\t\tif (!memcg_on_dfl) {\nmm/memcontrol.c:4196:\t\t\tmemcg-\u003ememsw.stock = \u0026memsw_stock;\nmm/memcontrol.c:4197:\t\t\tmemcg-\u003ememsw.stock_css = \u0026memcg-\u003ecss;\nmm/memcontrol.c-4198-\t\t}\n--\nmm/memcontrol.c-4200-\t\tmemcg-\u003ememory.track_failcnt = !memcg_on_dfl;\nmm/memcontrol.c:4201:\t\tmemcg-\u003ememsw.track_failcnt = !memcg_on_dfl;\nmm/memcontrol.c-4202-\t\tWRITE_ONCE(memcg-\u003eoom_kill_disable, READ_ONCE(parent-\u003eoom_kill_disable));\n--\nmm/memcontrol.c=5468=void mem_cgroup_replace_folio(struct folio *old, struct folio *new)\n--\nmm/memcontrol.c-5503-\t\tpage_counter_charge(\u0026memcg-\u003ememory, nr_pages);\nmm/memcontrol.c:5504:\t\tif (do_memsw_account())\nmm/memcontrol.c:5505:\t\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5506-\t}\n--\nmm/memcontrol.c=5801=int __init mem_cgroup_init(void)\n--\nmm/memcontrol.c-5827-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:5828:\t\tINIT_WORK(\u0026per_cpu_ptr(\u0026memsw_stock, cpu)-\u003ework,\nmm/memcontrol.c-5829-\t\t\t drain_local_stock);\n--\nmm/memcontrol.c=5854=int __mem_cgroup_try_charge_swap(struct folio *folio)\n--\nmm/memcontrol.c-5862-\nmm/memcontrol.c:5863:\tif (do_memsw_account())\nmm/memcontrol.c-5864-\t\treturn 0;\n--\nmm/memcontrol.c=5903=void __mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages)\n--\nmm/memcontrol.c-5912-\t\tif (!mem_cgroup_private_id_is_root(id)) {\nmm/memcontrol.c:5913:\t\t\tif (do_memsw_account())\nmm/memcontrol.c:5914:\t\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5915-\t\t\telse\n--\nmm/memcontrol.c=5924=long mem_cgroup_get_nr_swap_pages(const struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-5927-\nmm/memcontrol.c:5928:\tif (!mem_cgroup_disabled() \u0026\u0026 !do_memsw_account())\nmm/memcontrol.c-5929-\t\tnr_swap_pages = min(nr_swap_pages, page_counter_margin(\u0026memcg-\u003eswap));\n--\nmm/memcontrol.c=5940=long mem_cgroup_get_folio_swap_margin(const struct folio *folio)\n--\nmm/memcontrol.c-5944-\nmm/memcontrol.c:5945:\tif (mem_cgroup_disabled() || do_memsw_account() ||\nmm/memcontrol.c-5946-\t !folio_memcg_charged(folio))\n--\nmm/memcontrol.c=5957=bool mem_cgroup_swap_full(const struct folio *folio)\n--\nmm/memcontrol.c-5965-\t\treturn true;\nmm/memcontrol.c:5966:\tif (do_memsw_account() || !folio_memcg_charged(folio))\nmm/memcontrol.c-5967-\t\treturn ret;\n--\nmm/memcontrol.c=6316=static int __init mem_cgroup_swap_init(void)\n--\nmm/memcontrol.c-6322-#ifdef CONFIG_MEMCG_V1\nmm/memcontrol.c:6323:\tWARN_ON(cgroup_add_legacy_cftypes(\u0026memory_cgrp_subsys, memsw_files));\nmm/memcontrol.c-6324-#endif\n"
} |
| 47/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29116 output: 32 thoughts: 48 Thoughts: Here's my thought process, distilled: **Digging into Memory Accounting in Control Groups** Okay, so I need to understand this memory accounting issue better. My first instinct is to zero in on the core control group (cgroup) infrastructure. Let's see...I'm going to start by checking out functions like `memcg1_css_online` and `memcg1_mem_cgroup_css_alloc`, or anything else that looks like it's intimately involved with the initialization and lifecycle of memory cgroup CSS objects. Those should give me a good entry point. Then, I'm going to follow the trail from the object's allocation. Specifically, let's use `grep` to hunt for instances of `page_counter_init` within the `mm/` directory. That'll expose the key initializations of the page counter. I need to understand what's being initialized and *how*. The goal is to trace the path of these memory counters in the cgroup's life cycle. |
| 48/3 | 2026/09/30 00:30 | tool | grepper |
0mError:no matches Args: {
"Expression": "page_counter_init.*parent.*memsw",
"PathPrefix": "mm/"
}Results: {
"error": "no matches"
} |
| 49/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29427 output: 26 thoughts: 458 Thoughts: **Investigating memsw Initialization**
Okay, so I'm trying to figure out where the `page_counter_init` function is called for `memsw`. It feels like I'm missing something, so my first instinct is to check the `git log` using `-S memsw` or search for `memcg->memsw`. Let's get a handle on where this is happening.
Alright, now I'm in `mm/memcontrol.c`, specifically around lines 4183-4218, looking at `mem_cgroup_css_alloc`. Let's focus on what's happening around line 4190.
Looking at the code, I see:
```c
page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
memcg->memory.stock = &memory_stock;
memcg->memory.stock_css = &memcg->css;
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
if (!memcg_on_dfl) {
memcg->memsw.stock = &memsw_stock;
memcg->memsw.stock_css = &memcg->css;
}
WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
memcg->memory.track_failcnt = !memcg_on_dfl;
memcg->memsw.track_failcnt = !memcg_on_dfl;
WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
page_counter_init(&memcg->kmem, &parent->kmem, false);
page_counter_init(&memcg->tcpmem, &parent->tcpmem, false);
memcg->tcpmem.track_failcnt = !memcg_on_dfl;
#endif
```
Wait a second... even *before* the patch, it seems like `page_counter_init` isn't called for `memcg->memsw` here! That's strange. I need to understand why this is. I'll need to go back and check the `git log` again, but this time, I'll focus on lines 4190-4210 of `mm/memcontrol.c`, or grep for `memsw`. I need to figure out the history here. This is crucial for understanding the initialization process.
|
| 50/3 | 2026/09/30 00:30 | tool | grepper |
0mError:no matches Args: {
"Expression": "page_counter_init\\(\u0026memcg-\u003ememsw"
}Results: {
"error": "no matches"
} |
| 51/3 | 2026/09/30 00:30 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30495 output: 31 thoughts: 58 Thoughts: Okay, let's see... **Tracking Down the memsw Initialization** Hmm, I need to figure out where `memsw` is actually initialized within the kernel's memory control mechanisms. Where would that happen? Let's start with a look at `memcg1_css_alloc` and the related `memcontrol-v1.c` file. Seems like the logical place to start. Alright, time to do a targeted search. I'll search within `mm/memcontrol-v1.c` for instances of `memsw`. Let's see what turns up. |
| 52/3 | 2026/09/30 00:31 | tool | grepper |
0mArgs:{
"Expression": "memsw",
"PathPrefix": "mm/memcontrol-v1.c"
}Results: {
"Output": "mm/memcontrol-v1.c=111=static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)\n--\nmm/memcontrol-v1.c-127-\t\t} else {\nmm/memcontrol-v1.c:128:\t\t\t/* Preserve the user-visible memory \u003c= memsw invariant. */\nmm/memcontrol-v1.c-129-\t\t\tval = max(page_counter_read(\u0026memcg-\u003ememory),\nmm/memcontrol-v1.c:130:\t\t\t\t page_counter_read(\u0026memcg-\u003ememsw));\nmm/memcontrol-v1.c-131-\t\t}\n--\nmm/memcontrol-v1.c=136=static void __mem_cgroup_threshold(struct mem_cgroup *memcg, bool swap)\n--\nmm/memcontrol-v1.c-145-\telse\nmm/memcontrol-v1.c:146:\t\tt = rcu_dereference(memcg-\u003ememsw_thresholds.primary);\nmm/memcontrol-v1.c-147-\n--\nmm/memcontrol-v1.c=187=static void mem_cgroup_threshold(struct mem_cgroup *memcg)\n--\nmm/memcontrol-v1.c-190-\t\t__mem_cgroup_threshold(memcg, false);\nmm/memcontrol-v1.c:191:\t\tif (do_memsw_account())\nmm/memcontrol-v1.c-192-\t\t\t__mem_cgroup_threshold(memcg, true);\n--\nmm/memcontrol-v1.c=250=void memcg1_commit_charge(struct folio *folio, struct mem_cgroup *memcg)\n--\nmm/memcontrol-v1.c-261-/**\nmm/memcontrol-v1.c:262: * __memcg1_swapout - transfer a memsw charge to swap\nmm/memcontrol-v1.c:263: * @folio: folio whose memsw charge to transfer\nmm/memcontrol-v1.c-264- * @ci: the locked swap cluster holding the swap entries\nmm/memcontrol-v1.c-265- *\nmm/memcontrol-v1.c:266: * Transfer the memsw charge of @folio to the swap entry stored in\nmm/memcontrol-v1.c-267- * folio-\u003eswap.\n--\nmm/memcontrol-v1.c=272=void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci)\n--\nmm/memcontrol-v1.c-286-\nmm/memcontrol-v1.c:287:\tif (!do_memsw_account())\nmm/memcontrol-v1.c-288-\t\treturn;\n--\nmm/memcontrol-v1.c-315-\t\tif (mem_cgroup_private_id_is_root(private_id))\nmm/memcontrol-v1.c:316:\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_entries);\nmm/memcontrol-v1.c-317-\t}\n--\nmm/memcontrol-v1.c=343=void memcg1_swapin(struct folio *folio)\n--\nmm/memcontrol-v1.c-363-\t */\nmm/memcontrol-v1.c:364:\tif (!do_memsw_account())\nmm/memcontrol-v1.c-365-\t\treturn;\n--\nmm/memcontrol-v1.c=427=static int __mem_cgroup_usage_register_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-445-\t} else if (type == _MEMSWAP) {\nmm/memcontrol-v1.c:446:\t\tthresholds = \u0026memcg-\u003ememsw_thresholds;\nmm/memcontrol-v1.c-447-\t\tusage = mem_cgroup_usage(memcg, true);\n--\nmm/memcontrol-v1.c=507=static int mem_cgroup_usage_register_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-512-\nmm/memcontrol-v1.c:513:static int memsw_cgroup_usage_register_event(struct mem_cgroup *memcg,\nmm/memcontrol-v1.c-514-\tstruct eventfd_ctx *eventfd, const char *args)\n--\nmm/memcontrol-v1.c=519=static void __mem_cgroup_usage_unregister_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-532-\t} else if (type == _MEMSWAP) {\nmm/memcontrol-v1.c:533:\t\tthresholds = \u0026memcg-\u003ememsw_thresholds;\nmm/memcontrol-v1.c-534-\t\tusage = mem_cgroup_usage(memcg, true);\n--\nmm/memcontrol-v1.c=604=static void mem_cgroup_usage_unregister_event(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-609-\nmm/memcontrol-v1.c:610:static void memsw_cgroup_usage_unregister_event(struct mem_cgroup *memcg,\nmm/memcontrol-v1.c-611-\tstruct eventfd_ctx *eventfd)\n--\nmm/memcontrol-v1.c=747=static ssize_t memcg_write_event_control(struct kernfs_open_file *of,\n--\nmm/memcontrol-v1.c-845-\t\tevent-\u003eunregister_event = vmpressure_unregister_event;\nmm/memcontrol-v1.c:846:\t} else if (!strcmp(name, \"memory.memsw.usage_in_bytes\")) {\nmm/memcontrol-v1.c:847:\t\tevent-\u003eregister_event = memsw_cgroup_usage_register_event;\nmm/memcontrol-v1.c:848:\t\tevent-\u003eunregister_event = memsw_cgroup_usage_unregister_event;\nmm/memcontrol-v1.c-849-\t} else {\n--\nmm/memcontrol-v1.c=1424=static int mem_cgroup_resize_max(struct mem_cgroup *memcg,\nmm/memcontrol-v1.c:1425:\t\t\t\t unsigned long max, bool memsw)\nmm/memcontrol-v1.c-1426-{\n--\nmm/memcontrol-v1.c-1430-\tbool limits_invariant;\nmm/memcontrol-v1.c:1431:\tstruct page_counter *counter = memsw ? \u0026memcg-\u003ememsw : \u0026memcg-\u003ememory;\nmm/memcontrol-v1.c-1432-\n--\nmm/memcontrol-v1.c-1440-\t\t/*\nmm/memcontrol-v1.c:1441:\t\t * Make sure that the new limit (memsw or memory limit) doesn't\nmm/memcontrol-v1.c:1442:\t\t * break our basic invariant rule memory.max \u003c= memsw.max.\nmm/memcontrol-v1.c-1443-\t\t */\nmm/memcontrol-v1.c:1444:\t\tlimits_invariant = memsw ? max \u003e= READ_ONCE(memcg-\u003ememory.max) :\nmm/memcontrol-v1.c:1445:\t\t\t\t\t max \u003c= memcg-\u003ememsw.max;\nmm/memcontrol-v1.c-1446-\t\tif (!limits_invariant) {\n--\nmm/memcontrol-v1.c-1469-\t\tif (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,\nmm/memcontrol-v1.c:1470:\t\t\t\tmemsw ? 0 : MEMCG_RECLAIM_MAY_SWAP, NULL)) {\nmm/memcontrol-v1.c-1471-\t\t\tret = -EBUSY;\n--\nmm/memcontrol-v1.c=1567=static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,\n--\nmm/memcontrol-v1.c-1577-\tcase _MEMSWAP:\nmm/memcontrol-v1.c:1578:\t\tcounter = \u0026memcg-\u003ememsw;\nmm/memcontrol-v1.c-1579-\t\tbreak;\n--\nmm/memcontrol-v1.c-1593-\t\t\treturn (u64)mem_cgroup_usage(memcg, false) * PAGE_SIZE;\nmm/memcontrol-v1.c:1594:\t\tif (counter == \u0026memcg-\u003ememsw)\nmm/memcontrol-v1.c-1595-\t\t\treturn (u64)mem_cgroup_usage(memcg, true) * PAGE_SIZE;\n--\nmm/memcontrol-v1.c=1701=static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf,\n--\nmm/memcontrol-v1.c-1711-\tcase _MEMSWAP:\nmm/memcontrol-v1.c:1712:\t\tcounter = \u0026memcg-\u003ememsw;\nmm/memcontrol-v1.c-1713-\t\tbreak;\n--\nmm/memcontrol-v1.c=1888=void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)\nmm/memcontrol-v1.c-1889-{\nmm/memcontrol-v1.c:1890:\tunsigned long memory, memsw;\nmm/memcontrol-v1.c-1891-\tstruct mem_cgroup *mi;\n--\nmm/memcontrol-v1.c-1914-\t/* Hierarchical information */\nmm/memcontrol-v1.c:1915:\tmemory = memsw = PAGE_COUNTER_MAX;\nmm/memcontrol-v1.c-1916-\tfor (mi = memcg; mi; mi = parent_mem_cgroup(mi)) {\nmm/memcontrol-v1.c-1917-\t\tmemory = min(memory, READ_ONCE(mi-\u003ememory.max));\nmm/memcontrol-v1.c:1918:\t\tmemsw = min(memsw, READ_ONCE(mi-\u003ememsw.max));\nmm/memcontrol-v1.c-1919-\t}\n--\nmm/memcontrol-v1.c-1921-\t\t (u64)memory * PAGE_SIZE);\nmm/memcontrol-v1.c:1922:\tseq_buf_printf(s, \"hierarchical_memsw_limit %llu\\n\",\nmm/memcontrol-v1.c:1923:\t\t (u64)memsw * PAGE_SIZE);\nmm/memcontrol-v1.c-1924-\n--\nmm/memcontrol-v1.c=2030=struct cftype mem_cgroup_legacy_files[] = {\n--\nmm/memcontrol-v1.c-2156-\nmm/memcontrol-v1.c:2157:struct cftype memsw_files[] = {\nmm/memcontrol-v1.c-2158-\t{\nmm/memcontrol-v1.c:2159:\t\t.name = \"memsw.usage_in_bytes\",\nmm/memcontrol-v1.c-2160-\t\t.private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),\n--\nmm/memcontrol-v1.c-2163-\t{\nmm/memcontrol-v1.c:2164:\t\t.name = \"memsw.max_usage_in_bytes\",\nmm/memcontrol-v1.c-2165-\t\t.private = MEMFILE_PRIVATE(_MEMSWAP, RES_MAX_USAGE),\n--\nmm/memcontrol-v1.c-2169-\t{\nmm/memcontrol-v1.c:2170:\t\t.name = \"memsw.limit_in_bytes\",\nmm/memcontrol-v1.c-2171-\t\t.private = MEMFILE_PRIVATE(_MEMSWAP, RES_LIMIT),\n--\nmm/memcontrol-v1.c-2175-\t{\nmm/memcontrol-v1.c:2176:\t\t.name = \"memsw.failcnt\",\nmm/memcontrol-v1.c-2177-\t\t.private = MEMFILE_PRIVATE(_MEMSWAP, RES_FAILCNT),\n"
} |
| 53/3 | 2026/09/30 00:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33908 output: 33 thoughts: 82 Thoughts: Okay, here's my thought process as I'm looking at this code, summarized in the first person: **Navigating `mem_cgroup_css_alloc` and Initialization** Alright, I'm digging into `mem_cgroup_css_alloc` in `mm/memcontrol.c`, around line 4200. My immediate question is, "Where the heck is `memcg->memsw` initialized?" I know this memory cgroup stuff pretty well, and that's a key structure member. I need to trace back where the `memsw` field is getting its initial value. My first instinct is to check `memcg1_css_alloc` – is *that* where it happens? It's the most logical place to look for initialization. Let me see if that function exists in this file... nope! Let's just grep for any `memcg1_` functions in `mm/memcontrol-v1.h`. That's where I'll find its definition if it's not in the current file. |
| 54/3 | 2026/09/30 00:31 | tool | grepper |
0mArgs:{
"Expression": "memcg1_",
"PathPrefix": "mm/memcontrol-v1.h"
}Results: {
"Output": "mm/memcontrol-v1.h=46=unsigned long memcg_page_state_local_output(const struct mem_cgroup *memcg, int item);\nmm/memcontrol-v1.h:47:bool memcg1_alloc_events(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h:48:void memcg1_free_events(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h-49-\nmm/memcontrol-v1.h:50:void memcg1_memcg_init(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h-51-\nmm/memcontrol-v1.h=52=struct cgroup_taskset;\nmm/memcontrol-v1.h:53:void memcg1_css_offline(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h-54-\n--\nmm/memcontrol-v1.h=56=enum res_type {\n--\nmm/memcontrol-v1.h-62-\nmm/memcontrol-v1.h:63:bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked);\nmm/memcontrol-v1.h:64:void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked);\nmm/memcontrol-v1.h:65:void memcg1_oom_recover(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h-66-\nmm/memcontrol-v1.h:67:void memcg1_commit_charge(struct folio *folio, struct mem_cgroup *memcg);\nmm/memcontrol-v1.h:68:void memcg1_uncharge_batch(struct mem_cgroup *memcg, unsigned long pgpgout,\nmm/memcontrol-v1.h-69-\t\t\t unsigned long nr_memory);\nmm/memcontrol-v1.h-70-\nmm/memcontrol-v1.h:71:void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s);\nmm/memcontrol-v1.h:72:void reparent_memcg1_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent);\nmm/memcontrol-v1.h:73:void reparent_memcg1_lruvec_state_local(struct mem_cgroup *memcg, struct mem_cgroup *parent);\nmm/memcontrol-v1.h-74-\nmm/memcontrol-v1.h=75=void reparent_memcg_state_local(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.h-77-\nmm/memcontrol-v1.h:78:void memcg1_account_kmem(struct mem_cgroup *memcg, int nr_pages);\nmm/memcontrol-v1.h:79:static inline bool memcg1_tcpmem_active(struct mem_cgroup *memcg)\nmm/memcontrol-v1.h-80-{\n--\nmm/memcontrol-v1.h-82-}\nmm/memcontrol-v1.h:83:bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,\nmm/memcontrol-v1.h-84-\t\t\t gfp_t gfp_mask);\nmm/memcontrol-v1.h:85:static inline void memcg1_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)\nmm/memcontrol-v1.h-86-{\n--\nmm/memcontrol-v1.h=95=static inline bool do_memsw_account(void) { return false; }\nmm/memcontrol-v1.h:96:static inline bool memcg1_alloc_events(struct mem_cgroup *memcg) { return true; }\nmm/memcontrol-v1.h:97:static inline void memcg1_free_events(struct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h-98-\nmm/memcontrol-v1.h:99:static inline void memcg1_memcg_init(struct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h:100:static inline void memcg1_css_offline(struct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h-101-\nmm/memcontrol-v1.h:102:static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked)\nmm/memcontrol-v1.h-103-{\n--\nmm/memcontrol-v1.h-106-}\nmm/memcontrol-v1.h:107:static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {}\nmm/memcontrol-v1.h:108:static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h-109-\nmm/memcontrol-v1.h:110:static inline void memcg1_commit_charge(struct folio *folio,\nmm/memcontrol-v1.h-111-\t\t\t\t\tstruct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h-112-\nmm/memcontrol-v1.h:113:static inline void memcg1_uncharge_batch(struct mem_cgroup *memcg,\nmm/memcontrol-v1.h-114-\t\t\t\t\t unsigned long pgpgout,\n--\nmm/memcontrol-v1.h-116-\nmm/memcontrol-v1.h:117:static inline void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s) {}\nmm/memcontrol-v1.h-118-\nmm/memcontrol-v1.h:119:static inline void memcg1_account_kmem(struct mem_cgroup *memcg, int nr_pages) {}\nmm/memcontrol-v1.h:120:static inline bool memcg1_tcpmem_active(struct mem_cgroup *memcg) { return false; }\nmm/memcontrol-v1.h:121:static inline bool memcg1_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages,\nmm/memcontrol-v1.h-122-\t\t\t\t gfp_t gfp_mask) { return true; }\nmm/memcontrol-v1.h:123:static inline void memcg1_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages) {}\nmm/memcontrol-v1.h-124-\n"
} |
| 55/3 | 2026/09/30 00:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35966 output: 30 thoughts: 15 Thoughts: Okay, let's dive into this. Here's my thought process on checking `memcg1_memcg_init`: **Diving into `memcg1_memcg_init`** Alright, I'm thinking, "Let's check `memcg1_memcg_init`." I need to understand how this function fits into the grand scheme of memory control groups. It's probably a crucial initialization point, maybe the very first step for a specific memory control group hierarchy, or perhaps even the root of the v1 hierarchy if we're dealing with a system that uses both v1 and v2. I need to figure out what data structures it's setting up, what other functions it calls, and what preconditions it expects. Are we talking about allocating control group structures, hooking into the kernel's memory management subsystems, or possibly registering resource controllers? Knowing this init function is key to tracing how memory is managed in a containerized environment, especially when digging into performance issues or debugging memory leaks. Gotta see if it’s similar to how `memcg_init` works or does something completely new. Let's get to it! |
| 56/3 | 2026/09/30 00:31 | tool | grepper |
0mArgs:{
"Expression": "memcg1_memcg_init",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol-v1.c=747=static ssize_t memcg_write_event_control(struct kernfs_open_file *of,\n--\nmm/memcontrol-v1.c-886-\nmm/memcontrol-v1.c:887:void memcg1_memcg_init(struct mem_cgroup *memcg)\nmm/memcontrol-v1.c-888-{\n--\nmm/memcontrol-v1.h=48=void memcg1_free_events(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h-49-\nmm/memcontrol-v1.h:50:void memcg1_memcg_init(struct mem_cgroup *memcg);\nmm/memcontrol-v1.h-51-\n--\nmm/memcontrol-v1.h=97=static inline void memcg1_free_events(struct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h-98-\nmm/memcontrol-v1.h:99:static inline void memcg1_memcg_init(struct mem_cgroup *memcg) {}\nmm/memcontrol-v1.h-100-static inline void memcg1_css_offline(struct mem_cgroup *memcg) {}\n--\nmm/memcontrol.c=4099=static struct mem_cgroup *mem_cgroup_alloc(struct mem_cgroup *parent)\n--\nmm/memcontrol.c-4153-#endif\nmm/memcontrol.c:4154:\tmemcg1_memcg_init(memcg);\nmm/memcontrol.c-4155-\tmemcg-\u003ekmemcg_id = -1;\n"
} |
| 57/3 | 2026/09/30 00:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36693 output: 37 thoughts: 26 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already quite familiar with memory management and Linux kernel internals: **Diving into `memcg1_memcg_init`** Alright, let's get down to business. Time to refresh my memory and dig into the initialization of the first-generation memory control group code, specifically the `memcg1_memcg_init` function. I want to see how this fundamental part of the memcg subsystem is set up. I know it's in `mm/memcontrol-v1.c`, so I'm heading there for a closer look. I'm expecting to find the foundational structures being allocated and initialized, probably some basic accounting setup, and perhaps some early registration steps. I want to see the details of how the initial memory cgroup is structured in this v1 implementation. Understanding this function will give me a better grasp of the old implementation. |
| 58/3 | 2026/09/30 00:31 | tool | read-file |
0mArgs:{
"File": "mm/memcontrol-v1.c",
"FirstLine": 885,
"LineCount": 25
}Results: {
"Contents": " 885:\t}\n 886:\t\n 887:\tvoid memcg1_memcg_init(struct mem_cgroup *memcg)\n 888:\t{\n 889:\t\tINIT_LIST_HEAD(\u0026memcg-\u003eoom_notify);\n 890:\t\tmutex_init(\u0026memcg-\u003ethresholds_lock);\n 891:\t\tINIT_LIST_HEAD(\u0026memcg-\u003eevent_list);\n 892:\t\tspin_lock_init(\u0026memcg-\u003eevent_list_lock);\n 893:\t}\n 894:\t\n 895:\tvoid memcg1_css_offline(struct mem_cgroup *memcg)\n 896:\t{\n 897:\t\tstruct mem_cgroup_event *event, *tmp;\n 898:\t\n 899:\t\t/*\n 900:\t\t * Unregister events and notify userspace.\n 901:\t\t * Notify userspace about cgroup removing only after rmdir of cgroup\n 902:\t\t * directory to avoid race between userspace and kernelspace.\n 903:\t\t */\n 904:\t\tspin_lock_irq(\u0026memcg-\u003eevent_list_lock);\n 905:\t\tlist_for_each_entry_safe(event, tmp, \u0026memcg-\u003eevent_list, list) {\n 906:\t\t\tlist_del_init(\u0026event-\u003elist);\n 907:\t\t\tschedule_work(\u0026event-\u003eremove);\n 908:\t\t}\n 909:\t\tspin_unlock_irq(\u0026memcg-\u003eevent_list_lock);\n"
} |
| 59/3 | 2026/09/30 00:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37362 output: 31 thoughts: 76 Thoughts: Here's my thought process, as if I were analyzing this code: **Finding the Initialization of `memcg->memsw`** Okay, first things first, I need to understand where `memcg->memsw` is initialized. I *know* it's a `page_counter`, so my initial guess is to look for a call to `page_counter_init(&memcg->memsw)`. But... hold on a second. Did I miss something? I have to be absolutely sure. Is that *the* place to start, or are we missing something obvious here? I need to confirm the initialization isn't happening in some subtly different location. I’m going to run a quick test. Let's make sure that `page_counter_init` is *actually* called somewhere. The fastest way to confirm that is a grep across the entire kernel source, specifically looking for any instance of `\.memsw`. That should immediately show me if I'm on the right track, or if I need to widen my search. That'll let me know where to investigate next. |
| 60/3 | 2026/09/30 00:31 | tool | grepper |
0mArgs:{
"Expression": "\u0026[a-zA-Z0-9_]+-\u003ememsw\\b"
}Results: {
"Output": "mm/memcontrol-v1.c=111=static unsigned long mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)\n--\nmm/memcontrol-v1.c-129-\t\t\tval = max(page_counter_read(\u0026memcg-\u003ememory),\nmm/memcontrol-v1.c:130:\t\t\t\t page_counter_read(\u0026memcg-\u003ememsw));\nmm/memcontrol-v1.c-131-\t\t}\n--\nmm/memcontrol-v1.c=272=void __memcg1_swapout(struct folio *folio, struct swap_cluster_info *ci)\n--\nmm/memcontrol-v1.c-315-\t\tif (mem_cgroup_private_id_is_root(private_id))\nmm/memcontrol-v1.c:316:\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_entries);\nmm/memcontrol-v1.c-317-\t}\n--\nmm/memcontrol-v1.c=1424=static int mem_cgroup_resize_max(struct mem_cgroup *memcg,\n--\nmm/memcontrol-v1.c-1430-\tbool limits_invariant;\nmm/memcontrol-v1.c:1431:\tstruct page_counter *counter = memsw ? \u0026memcg-\u003ememsw : \u0026memcg-\u003ememory;\nmm/memcontrol-v1.c-1432-\n--\nmm/memcontrol-v1.c=1567=static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,\n--\nmm/memcontrol-v1.c-1577-\tcase _MEMSWAP:\nmm/memcontrol-v1.c:1578:\t\tcounter = \u0026memcg-\u003ememsw;\nmm/memcontrol-v1.c-1579-\t\tbreak;\n--\nmm/memcontrol-v1.c-1593-\t\t\treturn (u64)mem_cgroup_usage(memcg, false) * PAGE_SIZE;\nmm/memcontrol-v1.c:1594:\t\tif (counter == \u0026memcg-\u003ememsw)\nmm/memcontrol-v1.c-1595-\t\t\treturn (u64)mem_cgroup_usage(memcg, true) * PAGE_SIZE;\n--\nmm/memcontrol-v1.c=1701=static ssize_t mem_cgroup_reset(struct kernfs_open_file *of, char *buf,\n--\nmm/memcontrol-v1.c-1711-\tcase _MEMSWAP:\nmm/memcontrol-v1.c:1712:\t\tcounter = \u0026memcg-\u003ememsw;\nmm/memcontrol-v1.c-1713-\t\tbreak;\n--\nmm/memcontrol.c=1582=static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1593-\tif (do_memsw_account()) {\nmm/memcontrol.c:1594:\t\tcount = page_counter_read(\u0026memcg-\u003ememsw);\nmm/memcontrol.c-1595-\t\tlimit = READ_ONCE(memcg-\u003ememsw.max);\n--\nmm/memcontrol.c=1856=void mem_cgroup_print_oom_meminfo(struct mem_cgroup *memcg)\n--\nmm/memcontrol.c-1880-\t\tpr_info(\"memory+swap: usage %llukB, limit %llukB, failcnt %lu\\n\",\nmm/memcontrol.c:1881:\t\t\tK((u64)page_counter_read(\u0026memcg-\u003ememsw)),\nmm/memcontrol.c-1882-\t\t\tK((u64)memcg-\u003ememsw.max), memcg-\u003ememsw.failcnt);\n--\nmm/memcontrol.c=2127=static void memcg_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)\n--\nmm/memcontrol.c-2130-\tif (do_memsw_account())\nmm/memcontrol.c:2131:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2132-}\n--\nmm/memcontrol.c=2543=static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,\n--\nmm/memcontrol.c-2562-\tif (do_memsw_account() \u0026\u0026\nmm/memcontrol.c:2563:\t !page_counter_try_charge(\u0026memcg-\u003ememsw, nr_pages, \u0026counter,\nmm/memcontrol.c-2564-\t\t\t\t may_batch, NULL)) {\n--\nmm/memcontrol.c-2574-\tif (do_memsw_account())\nmm/memcontrol.c:2575:\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2576-\tmem_over_limit = mem_cgroup_from_counter(counter, memory);\n--\nmm/memcontrol.c-2680-\tif (do_memsw_account())\nmm/memcontrol.c:2681:\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-2682-\n--\nmm/memcontrol.c=3031=static void obj_cgroup_uncharge_pages(struct obj_cgroup *objcg,\n--\nmm/memcontrol.c-3042-\t\tif (do_memsw_account())\nmm/memcontrol.c:3043:\t\t\tpage_counter_refill_stock(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-3044-\t}\n--\nmm/memcontrol.c=5468=void mem_cgroup_replace_folio(struct folio *old, struct folio *new)\n--\nmm/memcontrol.c-5504-\t\tif (do_memsw_account())\nmm/memcontrol.c:5505:\t\t\tpage_counter_charge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5506-\t}\n--\nmm/memcontrol.c=5903=void __mem_cgroup_uncharge_swap(unsigned short id, unsigned int nr_pages)\n--\nmm/memcontrol.c-5913-\t\t\tif (do_memsw_account())\nmm/memcontrol.c:5914:\t\t\t\tpage_counter_uncharge(\u0026memcg-\u003ememsw, nr_pages);\nmm/memcontrol.c-5915-\t\t\telse\n"
} |
| 61/3 | 2026/09/30 00:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39391 output: 34 thoughts: 33 Thoughts: Here's my thought process, as if I were analyzing this code: **Diving into the Memory Controller Structure** Okay, so I need to understand how `memsw` is defined within the memory cgroup framework. My first step is to locate the `struct mem_cgroup` definition; I know that's going to be in `include/linux/memcontrol.h`. Time to dig in and see how the memory accounting is actually structured. Then I'll focus on tracking down the specific definition of `memsw` within that structure. That should give me the foundation I need to understand how it's being used. |
| 62/3 | 2026/09/30 00:31 | tool | grepper |
0mArgs:{
"Expression": "struct page_counter memsw",
"PathPrefix": "include/linux/memcontrol.h"
}Results: {
"Output": "include/linux/memcontrol.h=197=struct mem_cgroup {\n--\ninclude/linux/memcontrol.h-204-\t\tstruct page_counter swap;\t/* v2 only */\ninclude/linux/memcontrol.h:205:\t\tstruct page_counter memsw;\t/* v1 only */\ninclude/linux/memcontrol.h-206-\t};\n"
} |
| 63/3 | 2026/09/30 00:31 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39697 output: 411 thoughts: 2551 Thoughts: **KMSAN Assessment: A Thorough Review** Okay, so I've taken a good look at this patch. It's refactoring the memory cgroup (memcg) stock mechanism, bringing it into the `page_counter` structure. I see the consolidation and extension of stock handling for both `memcg->memory` and, in v1 configurations, `memcg->memsw`. The patch also changes how `page_counter_try_charge` works, incorporating `may_batch` and `nr_charged`. First thing I did was make sure I understood the memory layout: a `union` is shared between `swap` and `memsw`. I then walked through the initialization in `mem_cgroup_css_alloc`, and confirmed everything is being set up correctly, including the initializations of `stock` and `stock_css` to `NULL` where appropriate. My primary concern, as always, is whether this introduces any uninitialized memory bugs or information leaks. I've meticulously checked every variable declaration and out-parameter usage. In the `try_charge_memcg` function, `nr_charged` is initialized *every* time before it's used. The same holds true in `page_counter_try_charge`; even when the return value is false, there is initialization. The other out-parameter, `fail`, is only ever inspected when `page_counter_try_charge` returns false, and this is unchanged from the pre-patch behavior. The per-CPU stock data itself is initialized correctly and there are no instances of kernel data being copied to user-space in the affected code. I didn't see any changes to control flow or length calculations that could lead to uninitialized memory being exposed. Now, I understand this *could* introduce other bugs, like concurrency or locking issues (which `LOCKDEP` is good at catching), memory accounting errors, use-after-free, or NULL pointer dereferences (KASAN is good at catching use-after-free). However, based on my understanding, these are not related to uninitialized memory. KMSAN won't provide any extra value here. I've carefully compared the patch's changes to the criteria for `NeedsKMSAN`. No kernel structures are being sent to user space, no conditional logic relies on uninitialized variables, all data structures are properly initialized, and there are no new bounds checks or length calculations that could lead to reading uninitialized bytes. So, for these reasons, and after a very detailed evaluation, I'm confident that `NeedsKMSAN` should be set to `false`. The patch, as far as I can see, introduces no risks of uninitialized memory usage or information leaks. KMSAN is not necessary here. |
| 64/3 | 2026/09/30 00:31 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch refactors the per-CPU charge stock caching mechanism previously exclusive to memcontrol (memcg_stock) into a generic page counter stock (struct page_counter_stock_pcp) in mm/page_counter.c, allowing both memory and swap counters to benefit from stock caching.\n\nApplicability analysis:\n1. KASAN / Standard Detectors: The primary risks associated with this refactoring involve concurrent access and synchronization across CPUs (local_trylock, drain work items), cgroup css reference count tracking (css_get/css_put on stock caching), and potential use-after-free or out-of-bounds accesses on stock cache arrays. These are fully covered by KASAN, LOCKDEP, and standard kernel debugging facilities.\n2. KMSAN:\n - The new stack variable `nr_charged` passed to `page_counter_try_charge()` in `try_charge_memcg()` is unconditionally written on all success return paths (`*nr_charged = 0` on stock hit, and `*nr_charged = charge` on successful direct charge) before being evaluated at `check_high:`.\n - The per-CPU stock structures (`memory_stock`, `memsw_stock`) are statically allocated per-CPU variables zero-initialized in BSS, and the embedded `work_struct` is properly initialized via `INIT_WORK`.\n - New `struct page_counter` fields (`stock`, `stock_css`) are explicitly initialized to NULL in `page_counter_init()`, and memcgs are allocated with `kmem_cache_zalloc`.\n - No data is copied to user space, and no uninitialized padding, heap, or stack memory can be leaked or used in control flow.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch refactors the per-CPU charge stock caching mechanism previously exclusive to memcontrol (memcg_stock) into a generic page counter stock (struct page_counter_stock_pcp) in mm/page_counter.c, allowing both memory and swap counters to benefit from stock caching.\n\nApplicability analysis:\n1. KASAN / Standard Detectors: The primary risks associated with this refactoring involve concurrent access and synchronization across CPUs (local_trylock, drain work items), cgroup css reference count tracking (css_get/css_put on stock caching), and potential use-after-free or out-of-bounds accesses on stock cache arrays. These are fully covered by KASAN, LOCKDEP, and standard kernel debugging facilities.\n2. KMSAN:\n - The new stack variable `nr_charged` passed to `page_counter_try_charge()` in `try_charge_memcg()` is unconditionally written on all success return paths (`*nr_charged = 0` on stock hit, and `*nr_charged = charge` on successful direct charge) before being evaluated at `check_high:`.\n - The per-CPU stock structures (`memory_stock`, `memsw_stock`) are statically allocated per-CPU variables zero-initialized in BSS, and the embedded `work_struct` is properly initialized via `INIT_WORK`.\n - New `struct page_counter` fields (`stock`, `stock_css`) are explicitly initialized to NULL in `page_counter_init()`, and memcgs are allocated with `kmem_cache_zalloc`.\n - No data is copied to user space, and no uninitialized padding, heap, or stack memory can be leaked or used in control flow.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|