mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Chengfeng Lin <lin2530632123@gmail.com>
To: Joanne Koong <joannelkoong@gmail.com>, Yosry Ahmed <yosry@kernel.org>
Cc: Shakeel Butt <shakeel.butt@linux.dev>,
	Johannes Weiner <hannes@cmpxchg.org>, Song Hu <husong@kylinos.cn>,
	Andrew Morton <akpm@linux-foundation.org>,
	linux-mm@kvack.org, cgroups@vger.kernel.org,
	linux-kernel@vger.kernel.org, Nhat Pham <nphamcs@gmail.com>,
	Chengming Zhou <chengming.zhou@linux.dev>,
	yunzhao@cloudflare.com
Subject: Re: [PATCH] mm: memcg: use ratelimited stats flush in obj_cgroup_may_zswap()
Date: Wed, 11 Nov 2026 16:09:08 +0800	[thread overview]
Message-ID: <179438454803.6.16655549133383619919.zswap-rstat-counters@gmail.com> (raw)
In-Reply-To: <CAO9r8zPqJncFKQbF3tGe1d4sg-4dLQ3dQRGze_vab26wWrXTqA@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 4335 bytes --]

Hi Joanne, Yosry, and all,

With existing rstat updates retained, hierarchical per-CPU counters reduced
single-worker PAGEOUT CPU by 44.69% and whole-program CPU by 21.78% in this
bare-metal test.

One worker uses MADV_PAGEOUT on 32 MiB of compressible anonymous memory in
a private cgroup, with THP disabled and memory.zswap.max=64 MiB. The limit
is not reached. The host is an i7-12700KF with 20 online logical CPUs,
full preemption, LZO and zsmalloc. Unlimited controls change only the
zswap limit to max.

A matched parent/commit/parent test of 0efc297a3c49
("cgroup/rstat: avoid disabling irqs for O(num_cpu)") showed:

  CPU time, ms / 32 MiB         Parent A   Commit    Parent B
  PAGEOUT                       17.180     24.464    17.316
  Whole test program            46.215     53.419    46.235

PAGEOUT increased by 7.216 ms (41.84%) and whole-program CPU by 7.193 ms
(15.56%). Both versions stored 8192 pages and performed 8192 limit-check
flushes: a finite zswap limit forces a stats flush before each store.

To limit interrupt-disabled time, the commit
releases and reacquires the rstat lock after processing each possible CPU,
with cond_resched() in between and cpu_relax() if it does not reschedule.
Previously this was conditional on rescheduling or lock contention.
Per-page flushing repeats these extra operations.

On MM baseline 2967f3ccdb5f, I adapted Joanne's hierarchical per-CPU option
[1]. The limit pre-check uses dedicated counters, retaining MEMCG_ZSWAP_B
rstat updates and the IRQ-latency protection. Extra updates are included:

  CPU time, ms / 32 MiB         Base A     Counter   Base B
  PAGEOUT                       23.903     13.113    23.514
  Whole test program            49.247     38.569    49.363

PAGEOUT CPU fell by 10.595 ms (44.69%) and whole-program CPU by 10.737 ms
(21.78%). Stores were unchanged; forced flushes fell from 8192 to zero.
These two comparisons use different baselines.

Both comparisons used fresh A/B/A boots, two warmups and nine clean
samples per condition per boot. PAGEOUT return drift was 0.80%/-1.63%,
with maximum within-boot CV of 2.82%/3.55%. PAGEOUT measures worker CPU;
whole-program CPU includes preparation, re-access, verification
and cleanup, but not external cgroup setup or host-wide background work.

A single-worker unlimited control changed PAGEOUT/whole-program CPU by
+0.41%/+0.86%, with PAGEOUT CV up to 4.74%; there was no clear difference.

A supplementary A/B/A test compared the MM baseline with the counter
derivative, with eight workers pinned to different P cores, each processing
32 MiB (256 MiB total). Their shared cgroup had memory.max=2 GiB and
memory.swap.max=1 GiB.
The zswap limit remained 64 MiB; compressed usage was about 18.75 MiB,
below the limit. Counter-kernel means follow, with baseline-relative changes:

  Eight-worker metric, per 256 MiB   Finite limit          Unlimited
  Summed PAGEOUT CPU                199.238 ms (-84.04%)  196.272 ms (+1.17%)
  Summed re-access/verify CPU        215.365 ms ( -1.36%)  214.950 ms (+0.09%)
  Whole-program CPU                 663.404 ms (-61.39%)  660.535 ms (+0.24%)
  Whole-program elapsed time         94.618 ms (-58.26%)   95.183 ms (+0.99%)

Both kernels stored 65,536 pages. Finite-limit forced flushes fell from
65,536 to zero; unlimited controls performed none. Counter-kernel PAGEOUT
CVs were 4.51%/4.91%, with baseline return drift of +5.10%/+0.97%.
All nine finite-limit counter samples were below both baseline groups,
but baseline drift limits the precision of the gain estimate.
The small unlimited increase does not
establish a regression. Re-access and elapsed-time CVs were at most 2.81%.

Elapsed time includes preparation and cleanup; it is not summed CPU time.
The re-access metric includes reading and verifying the data; zswap
decompression was not timed separately.

Both kernels passed bounded leaf/ancestor-limit, dynamic-limit,
cross-CPU-freeing and cgroup-offlining checks. Deep cgroup hierarchies, CPU
hotplug, and 32-bit systems were not tested.

I hope these results help assess the benefit and update cost of dedicated
counters while retaining the existing statistics. The reproducer sources and
the measured counter patch are attached.

[1] https://github.com/joannekoong/linux/commit/9092e0057f038ffca43ae3f35faf58c33b1d7c9b

Thanks,
Chengfeng

[-- Attachment #2: zswap-reproducer.tar.gz --]
[-- Type: application/gzip, Size: 7170 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: hierarchical-with-rstat.patch --]
[-- Type: text/x-patch, Size: 2579 bytes --]

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 4d00748..13e705b 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -674,6 +674,11 @@ struct memcg_vmstats_percpu {
 	struct memcg_vmstats_percpu __percpu	*parent_pcpu;
 	struct memcg_vmstats			*vmstats;
 
+#ifdef CONFIG_ZSWAP
+	/* Hierarchical compressed bytes, updated eagerly and summed on read. */
+	long			zswap_hier;
+#endif
+
 	/* The above should fit a single cacheline for memcg_rstat_updated() */
 
 	/* Local (CPU and cgroup) page state & events */
@@ -6212,6 +6217,35 @@ static struct cftype swap_files[] = {
 };
 
 #ifdef CONFIG_ZSWAP
+/*
+ * Evaluation of Joanne Koong's hierarchical per-CPU experiment (9092e0057f03).
+ * Keep the existing rstat updates: public usage and shrinker accounting still
+ * consume them. Only the limit pre-check uses this additional counter.
+ * Callers hold rcu_read_lock() and use the same memcg as their rstat update.
+ */
+static void memcg_zswap_hier_add(struct mem_cgroup *memcg, long val)
+{
+	struct memcg_vmstats_percpu __percpu *statc_pcpu;
+	struct memcg_vmstats_percpu *statc;
+
+	statc_pcpu = memcg->vmstats_percpu;
+	for (; statc_pcpu; statc_pcpu = statc->parent_pcpu) {
+		statc = this_cpu_ptr(statc_pcpu);
+		this_cpu_add(statc_pcpu->zswap_hier, val);
+	}
+}
+
+static unsigned long memcg_zswap_hier_sum(struct mem_cgroup *memcg)
+{
+	long sum = 0;
+	int cpu;
+
+	for_each_possible_cpu(cpu)
+		sum += READ_ONCE(per_cpu_ptr(memcg->vmstats_percpu,
+					     cpu)->zswap_hier);
+	return max(sum, 0L);
+}
+
 /**
  * obj_cgroup_may_zswap - check if this cgroup can zswap
  * @objcg: the object cgroup
@@ -6245,9 +6279,7 @@ bool obj_cgroup_may_zswap(struct obj_cgroup *objcg)
 			break;
 		}
 
-		/* Force flush to get accurate stats for charging */
-		__mem_cgroup_flush_stats(memcg, true);
-		pages = memcg_page_state(memcg, MEMCG_ZSWAP_B) / PAGE_SIZE;
+		pages = memcg_zswap_hier_sum(memcg) / PAGE_SIZE;
 		if (pages < max)
 			continue;
 		ret = false;
@@ -6283,6 +6315,7 @@ void obj_cgroup_charge_zswap(struct obj_cgroup *objcg, size_t size)
 
 	rcu_read_lock();
 	memcg = obj_cgroup_memcg(objcg);
+	memcg_zswap_hier_add(memcg, size);
 	mod_memcg_state(memcg, MEMCG_ZSWAP_B, size);
 	mod_memcg_state(memcg, MEMCG_ZSWAPPED, 1);
 	if (size == PAGE_SIZE)
@@ -6311,6 +6344,7 @@ void obj_cgroup_uncharge_zswap(struct obj_cgroup *objcg, size_t size)
 
 	rcu_read_lock();
 	memcg = obj_cgroup_memcg(objcg);
+	memcg_zswap_hier_add(memcg, -(long)size);
 	mod_memcg_state(memcg, MEMCG_ZSWAP_B, -size);
 	mod_memcg_state(memcg, MEMCG_ZSWAPPED, -1);
 	if (size == PAGE_SIZE)

  reply	other threads:[~2026-11-11  8:12 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-17 13:18 Song Hu
2026-08-17 16:04 ` Shakeel Butt
     [not found] ` <1787017182353556.21.seg@mailgw.kylinos.cn>
2026-08-18  1:53   ` Song Hu
2026-08-18 18:35     ` Yosry Ahmed
2026-08-20 22:18       ` Joanne Koong
2026-08-21 17:51         ` Johannes Weiner
2026-08-21 19:28           ` Yosry Ahmed
2026-08-24 21:01             ` Joanne Koong
2026-08-24 21:16               ` Yosry Ahmed
2026-08-25  0:23                 ` Joanne Koong
2026-08-26 21:38                 ` Shakeel Butt
2026-08-26 21:55                   ` Yosry Ahmed
2026-08-26 23:08                     ` Shakeel Butt
2026-08-26 23:14                       ` Yosry Ahmed
2026-11-11  8:09                         ` Chengfeng Lin [this message]
2026-11-13 18:50                           ` Joanne Koong
2026-11-15 13:38                             ` Chengfeng Lin
2026-08-24 21:31         ` Joshua Hahn
2026-08-25  0:23           ` Joanne Koong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=179438454803.6.16655549133383619919.zswap-rstat-counters@gmail.com \
    --to=lin2530632123@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=cgroups@vger.kernel.org \
    --cc=chengming.zhou@linux.dev \
    --cc=hannes@cmpxchg.org \
    --cc=husong@kylinos.cn \
    --cc=joannelkoong@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=nphamcs@gmail.com \
    --cc=shakeel.butt@linux.dev \
    --cc=yosry@kernel.org \
    --cc=yunzhao@cloudflare.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®