* [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
@ 2026-03-12 4:06 Li Wang
2026-03-12 4:06 ` [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap Li Wang
` (6 more replies)
0 siblings, 7 replies; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Johannes Weiner,
Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo, Roman Gushchin,
Shakeel Butt
Currently, test_zswap only checks for the presence of zswap via
/sys/module/zswap, but it does not account for the global runtime
state in /sys/module/zswap/parameters/enabled.
If zswap is configured in the kernel but globally disabled at runtime,
the zswap cgroup tests will run in an invalid environment and fail
spuriously.
Fix this by reading the 'enabled' parameter and gracefully skipping
the test when zswap is disabled. A helpful hint is also added to the
skip message to show users how to enable it.
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
---
Notes:
v1 --> v2:
* remove enable/disable_zswap functions
* skip the test if zswap is not enabled
* reporting fail when zswap_enabled return -1
tools/testing/selftests/cgroup/test_zswap.c | 28 +++++++++++++++++----
1 file changed, 23 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 64ebc3f3f203..ec64daaa2f5a 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -589,23 +589,41 @@ struct zswap_test {
};
#undef T
-static bool zswap_configured(void)
+static int zswap_enabled(void)
{
- return access("/sys/module/zswap", F_OK) == 0;
+ char buf[16];
+ ssize_t n;
+
+ if (access("/sys/module/zswap", F_OK))
+ ksft_exit_skip("zswap isn't configured\n");
+
+ n = read_text("/sys/module/zswap/parameters/enabled", buf, sizeof(buf));
+ if (n <= 0)
+ return -1;
+
+ if (buf[0] == 'Y')
+ return 1;
+ else if (buf[0] == 'N')
+ return 0;
+
+ return -1;
}
int main(int argc, char **argv)
{
char root[PATH_MAX];
- int i;
+ int i, state;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
- if (!zswap_configured())
- ksft_exit_skip("zswap isn't configured\n");
+ state = zswap_enabled();
+ if (state == 0)
+ ksft_exit_skip("zswap is disabled (hint: echo 1 > /sys/module/zswap/parameters/enabled)\n");
+ else if (state < 0)
+ ksft_exit_fail_msg("Failed to read zswap state\n");
/*
* Check that memory controller is available:
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
@ 2026-03-12 4:06 ` Li Wang
2026-03-12 22:41 ` Yosry Ahmed
2026-03-12 4:06 ` [PATCH v2 3/7] selftests/cgroup: use runtime page size for zswpin check Li Wang
` (5 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Johannes Weiner,
Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo, Roman Gushchin,
Shakeel Butt
test_swapin_nozswap can hit OOM before reaching its assertions on some
setups. The test currently sets memory.max=8M and then allocates/reads
32M with memory.zswap.max=0, which may over-constrain reclaim and kill
the workload process.
Raise memory.max to 24M so the workload can make forward progress, and
lower the swap_peak expectation from 24M to 8M to keep the check robust
across environments.
The test intent is unchanged: verify that swapping happens while zswap
remains unused when memory.zswap.max=0.
=== Error Logs ===
# ./test_zswap
TAP version 13
1..7
ok 1 test_zswap_usage
not ok 2 test_swapin_nozswap
...
# dmesg
[271641.879153] test_zswap invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=0
[271641.879168] CPU: 1 UID: 0 PID: 177372 Comm: test_zswap Kdump: loaded Not tainted 6.12.0-211.el10.ppc64le #1 VOLUNTARY
[271641.879171] Hardware name: IBM,9009-41A POWER9 (architected) 0x4e0202 0xf000005 of:IBM,FW940.02 (UL940_041) hv:phyp pSeries
[271641.879173] Call Trace:
[271641.879174] [c00000037540f730] [c00000000127ec44] dump_stack_lvl+0x88/0xc4 (unreliable)
[271641.879184] [c00000037540f760] [c0000000005cc594] dump_header+0x5c/0x1e4
[271641.879188] [c00000037540f7e0] [c0000000005cb464] oom_kill_process+0x324/0x3b0
[271641.879192] [c00000037540f860] [c0000000005cbe48] out_of_memory+0x118/0x420
[271641.879196] [c00000037540f8f0] [c00000000070d8ec] mem_cgroup_out_of_memory+0x18c/0x1b0
[271641.879200] [c00000037540f990] [c000000000713888] try_charge_memcg+0x598/0x890
[271641.879204] [c00000037540fa70] [c000000000713dbc] charge_memcg+0x5c/0x110
[271641.879207] [c00000037540faa0] [c0000000007159f8] __mem_cgroup_charge+0x48/0x120
[271641.879211] [c00000037540fae0] [c000000000641914] alloc_anon_folio+0x2b4/0x5a0
[271641.879215] [c00000037540fb60] [c000000000641d58] do_anonymous_page+0x158/0x6b0
[271641.879218] [c00000037540fbd0] [c000000000642f8c] __handle_mm_fault+0x4bc/0x910
[271641.879221] [c00000037540fcf0] [c000000000643500] handle_mm_fault+0x120/0x3c0
[271641.879224] [c00000037540fd40] [c00000000014bba0] ___do_page_fault+0x1c0/0x980
[271641.879228] [c00000037540fdf0] [c00000000014c44c] hash__do_page_fault+0x2c/0xc0
[271641.879232] [c00000037540fe20] [c0000000001565d8] do_hash_fault+0x128/0x1d0
[271641.879236] [c00000037540fe50] [c000000000008be0] data_access_common_virt+0x210/0x220
[271641.879548] Tasks state (memory values in pages):
...
[271641.879550] [ pid ] uid tgid total_vm rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name
[271641.879555] [ 177372] 0 177372 571 0 0 0 0 51200 96 0 test_zswap
[271641.879562] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=/,mems_allowed=0,oom_memcg=/no_zswap_test,task_memcg=/no_zswap_test,task=test_zswap,pid=177372,uid=0
[271641.879578] Memory cgroup out of memory: Killed process 177372 (test_zswap) total-vm:36544kB, anon-rss:0kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:50kB oom_score_adj:0
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
---
tools/testing/selftests/cgroup/test_zswap.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index ec64daaa2f5a..8bf5fc34bb23 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -168,7 +168,7 @@ static int test_swapin_nozswap(const char *root)
goto out;
if (cg_create(test_group))
goto out;
- if (cg_write(test_group, "memory.max", "8M"))
+ if (cg_write(test_group, "memory.max", "24M"))
goto out;
if (cg_write(test_group, "memory.zswap.max", "0"))
goto out;
@@ -184,8 +184,8 @@ static int test_swapin_nozswap(const char *root)
goto out;
}
- if (swap_peak < MB(24)) {
- ksft_print_msg("at least 24MB of memory should be swapped out\n");
+ if (swap_peak < MB(8)) {
+ ksft_print_msg("at least 8MB of memory should be swapped out\n");
goto out;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 3/7] selftests/cgroup: use runtime page size for zswpin check
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
2026-03-12 4:06 ` [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap Li Wang
@ 2026-03-12 4:06 ` Li Wang
2026-03-12 4:06 ` [PATCH v2 4/7] selftests/cgroup: rename PAGE_SIZE to BUF_SIZE in cgroup_util Li Wang
` (4 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Yosry Ahmed,
Johannes Weiner, Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo,
Roman Gushchin, Shakeel Butt
test_zswapin compares memory.stat:zswpin (counted in pages) against a
byte threshold converted with PAGE_SIZE. In cgroup selftests, PAGE_SIZE
is hardcoded to 4096, which makes the conversion wrong on systems with
non-4K base pages (e.g. 64K).
As a result, the test requires too many pages to pass and fails
spuriously even when zswap is working.
Use sysconf(_SC_PAGESIZE) for the zswpin threshold conversion so the
check matches the actual system page size.
Signed-off-by: Li Wang <liwang@redhat.com>
Reviewed-by: Yosry Ahmed <yosry@kernel.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
---
tools/testing/selftests/cgroup/test_zswap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 8bf5fc34bb23..032c36658a15 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -235,7 +235,7 @@ static int test_zswapin(const char *root)
goto out;
}
- if (zswpin < MB(24) / PAGE_SIZE) {
+ if (zswpin < MB(24) / sysconf(_SC_PAGESIZE)) {
ksft_print_msg("at least 24MB should be brought back from zswap\n");
goto out;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 4/7] selftests/cgroup: rename PAGE_SIZE to BUF_SIZE in cgroup_util
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
2026-03-12 4:06 ` [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap Li Wang
2026-03-12 4:06 ` [PATCH v2 3/7] selftests/cgroup: use runtime page size for zswpin check Li Wang
@ 2026-03-12 4:06 ` Li Wang
2026-03-12 4:06 ` [PATCH v2 5/7] selftest/cgroup: fix zswap test_no_invasive_cgroup_shrink on 64K pagesize system Li Wang
` (3 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Johannes Weiner,
Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo, Roman Gushchin,
Shakeel Butt
The cgroup utility code defines a local PAGE_SIZE macro hardcoded to
4096, which is used solely as a generic buffer size for reading cgroup
and proc files. This is misleading because the value has nothing to do
with the actual page size of the system, and on architectures with
larger pages (e.g., 64K on arm64 or ppc64) the name suggests a
relationship that does not exist.
Additionally, the name can shadow or conflict with the PAGE_SIZE
definition from system headers, leading to confusion or subtle bugs.
Rename it to BUF_SIZE to accurately reflect its purpose as a general
I/O buffer size. No functional change.
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
---
.../testing/selftests/cgroup/lib/cgroup_util.c | 18 +++++++++---------
.../selftests/cgroup/lib/include/cgroup_util.h | 4 ++--
tools/testing/selftests/cgroup/test_core.c | 2 +-
tools/testing/selftests/cgroup/test_freezer.c | 2 +-
.../testing/selftests/cgroup/test_memcontrol.c | 14 +++++++-------
5 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c
index ce6c2642fd9b..0be525ed11db 100644
--- a/tools/testing/selftests/cgroup/lib/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c
@@ -125,7 +125,7 @@ int cg_read_strcmp(const char *cgroup, const char *control,
int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
if (cg_read(cgroup, control, buf, sizeof(buf)))
return -1;
@@ -155,7 +155,7 @@ long cg_read_long_fd(int fd)
long cg_read_key_long(const char *cgroup, const char *control, const char *key)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
char *ptr;
if (cg_read(cgroup, control, buf, sizeof(buf)))
@@ -191,7 +191,7 @@ long cg_read_key_long_poll(const char *cgroup, const char *control,
long cg_read_lc(const char *cgroup, const char *control)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
const char delim[] = "\n";
char *line;
long cnt = 0;
@@ -243,7 +243,7 @@ int cg_write_numeric(const char *cgroup, const char *control, long value)
static int cg_find_root(char *root, size_t len, const char *controller,
bool *nsdelegate)
{
- char buf[10 * PAGE_SIZE];
+ char buf[10 * BUF_SIZE];
char *fs, *mount, *type, *options;
const char delim[] = "\n\t ";
@@ -298,7 +298,7 @@ int cg_create(const char *cgroup)
int cg_wait_for_proc_count(const char *cgroup, int count)
{
- char buf[10 * PAGE_SIZE] = {0};
+ char buf[10 * BUF_SIZE] = {0};
int attempts;
char *ptr;
@@ -323,7 +323,7 @@ int cg_wait_for_proc_count(const char *cgroup, int count)
int cg_killall(const char *cgroup)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
char *ptr = buf;
/* If cgroup.kill exists use it. */
@@ -533,7 +533,7 @@ int cg_run_nowait(const char *cgroup,
int proc_mount_contains(const char *option)
{
- char buf[4 * PAGE_SIZE];
+ char buf[4 * BUF_SIZE];
ssize_t read;
read = read_text("/proc/mounts", buf, sizeof(buf));
@@ -545,7 +545,7 @@ int proc_mount_contains(const char *option)
int cgroup_feature(const char *feature)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
ssize_t read;
read = read_text("/sys/kernel/cgroup/features", buf, sizeof(buf));
@@ -572,7 +572,7 @@ ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t
int proc_read_strstr(int pid, bool thread, const char *item, const char *needle)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0)
return -1;
diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
index 77f386dab5e8..ca4a161c17a4 100644
--- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
@@ -2,8 +2,8 @@
#include <stdbool.h>
#include <stdlib.h>
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
+#ifndef BUF_SIZE
+#define BUF_SIZE 4096
#endif
#define MB(x) (x << 20)
diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c
index 102262555a59..df7fac7e5554 100644
--- a/tools/testing/selftests/cgroup/test_core.c
+++ b/tools/testing/selftests/cgroup/test_core.c
@@ -87,7 +87,7 @@ static int test_cgcore_destroy(const char *root)
int ret = KSFT_FAIL;
char *cg_test = NULL;
int child_pid;
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
cg_test = cg_name(root, "cg_test");
diff --git a/tools/testing/selftests/cgroup/test_freezer.c b/tools/testing/selftests/cgroup/test_freezer.c
index 97fae92c8387..160a9e6ad277 100644
--- a/tools/testing/selftests/cgroup/test_freezer.c
+++ b/tools/testing/selftests/cgroup/test_freezer.c
@@ -642,7 +642,7 @@ static int test_cgfreezer_ptrace(const char *root)
*/
static int proc_check_stopped(int pid)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
int len;
len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c
index 2fb096a2a9f9..c01e60b497aa 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -33,7 +33,7 @@ int get_temp_fd(void)
int alloc_pagecache(int fd, size_t size)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
struct stat st;
int i;
@@ -60,7 +60,7 @@ int alloc_anon(const char *cgroup, void *arg)
char *buf, *ptr;
buf = malloc(size);
- for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
+ for (ptr = buf; ptr < buf + size; ptr += BUF_SIZE)
*ptr = 0;
free(buf);
@@ -69,7 +69,7 @@ int alloc_anon(const char *cgroup, void *arg)
int is_swap_enabled(void)
{
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
const char delim[] = "\n";
int cnt = 0;
char *line;
@@ -112,7 +112,7 @@ static int test_memcg_subtree_control(const char *root)
{
char *parent, *child, *parent2 = NULL, *child2 = NULL;
int ret = KSFT_FAIL;
- char buf[PAGE_SIZE];
+ char buf[BUF_SIZE];
/* Create two nested cgroups with the memory controller enabled */
parent = cg_name(root, "memcg_test_0");
@@ -183,7 +183,7 @@ static int alloc_anon_50M_check(const char *cgroup, void *arg)
return -1;
}
- for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
+ for (ptr = buf; ptr < buf + size; ptr += BUF_SIZE)
*ptr = 0;
current = cg_read_long(cgroup, "memory.current");
@@ -413,7 +413,7 @@ static int alloc_anon_noexit(const char *cgroup, void *arg)
return -1;
}
- for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
+ for (ptr = buf; ptr < buf + size; ptr += BUF_SIZE)
*ptr = 0;
while (getppid() == ppid)
@@ -999,7 +999,7 @@ static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
return -1;
}
- for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
+ for (ptr = buf; ptr < buf + size; ptr += BUF_SIZE)
*ptr = 0;
mem_current = cg_read_long(cgroup, "memory.current");
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 5/7] selftest/cgroup: fix zswap test_no_invasive_cgroup_shrink on 64K pagesize system
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
` (2 preceding siblings ...)
2026-03-12 4:06 ` [PATCH v2 4/7] selftests/cgroup: rename PAGE_SIZE to BUF_SIZE in cgroup_util Li Wang
@ 2026-03-12 4:06 ` Li Wang
2026-03-12 4:06 ` [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap Li Wang
` (2 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Johannes Weiner,
Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo, Roman Gushchin,
Shakeel Butt
The test_no_invasive_cgroup_shrink and allocate_bytes use a hardcoded
stride of 4095 bytes when touching allocated pages. On systems with 64K
page size, this results in writing to the same page multiple times
instead of touching all pages, leading to insufficient memory pressure.
Additionally, the original memory limits and allocation sizes are too
small for 64K page size systems. With only 1M memory.max, there are
very few pages available, and a zswap.max of 10K may not provide enough
room to store even a single compressed page. This can cause OOM kills
or false positives due to insufficient zswap writeback being triggered.
Fix these issues by:
- Using sysconf(_SC_PAGESIZE) instead of the hardcoded 4095 stride in
both allocate_bytes() and test_no_invasive_cgroup_shrink().
- Increasing memory.max to 32M for both wb_group and control_group to
ensure enough pages are available regardless of page size.
- Increasing zswap.max from 10K to 64K and allocation sizes from 10M
to 64M to reliably trigger zswap writeback on all configurations.
=== Error Log ===
# getconf PAGESIZE
65536
# ./test_zswap
TAP version 13
...
ok 5 test_zswap_writeback_disabled
ok 6 # SKIP test_no_kmem_bypass
not ok 7 test_no_invasive_cgroup_shrink
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
---
tools/testing/selftests/cgroup/test_zswap.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 032c36658a15..654874fb371b 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -83,12 +83,13 @@ static int allocate_and_read_bytes(const char *cgroup, void *arg)
static int allocate_bytes(const char *cgroup, void *arg)
{
+ long pagesize = sysconf(_SC_PAGESIZE);
size_t size = (size_t)arg;
char *mem = (char *)malloc(size);
if (!mem)
return -1;
- for (int i = 0; i < size; i += 4095)
+ for (int i = 0; i < size; i += pagesize)
mem[i] = 'a';
free(mem);
return 0;
@@ -415,34 +416,41 @@ static int test_zswap_writeback_disabled(const char *root)
static int test_no_invasive_cgroup_shrink(const char *root)
{
int ret = KSFT_FAIL;
- size_t control_allocation_size = MB(10);
+ long pagesize = sysconf(_SC_PAGESIZE);
+ size_t control_allocation_size = MB(64);
char *control_allocation = NULL, *wb_group = NULL, *control_group = NULL;
wb_group = setup_test_group_1M(root, "per_memcg_wb_test1");
if (!wb_group)
return KSFT_FAIL;
- if (cg_write(wb_group, "memory.zswap.max", "10K"))
+ if (cg_write(wb_group, "memory.zswap.max", "64K"))
+ goto out;
+ if (cg_write(wb_group, "memory.max", "32M"))
goto out;
+
control_group = setup_test_group_1M(root, "per_memcg_wb_test2");
if (!control_group)
goto out;
+ if (cg_write(control_group, "memory.max", "32M"))
+ goto out;
/* Push some test_group2 memory into zswap */
if (cg_enter_current(control_group))
goto out;
control_allocation = malloc(control_allocation_size);
- for (int i = 0; i < control_allocation_size; i += 4095)
+ for (int i = 0; i < control_allocation_size; i += pagesize)
control_allocation[i] = 'a';
if (cg_read_key_long(control_group, "memory.stat", "zswapped") < 1)
goto out;
- /* Allocate 10x memory.max to push wb_group memory into zswap and trigger wb */
- if (cg_run(wb_group, allocate_bytes, (void *)MB(10)))
+ /* Allocate 2x memory.max to push wb_group memory into zswap and trigger wb */
+ if (cg_run(wb_group, allocate_bytes, (void *)MB(64)))
goto out;
/* Verify that only zswapped memory from gwb_group has been written back */
if (get_cg_wb_count(wb_group) > 0 && get_cg_wb_count(control_group) == 0)
ret = KSFT_PASS;
+
out:
cg_enter_current(root);
if (control_group) {
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
` (3 preceding siblings ...)
2026-03-12 4:06 ` [PATCH v2 5/7] selftest/cgroup: fix zswap test_no_invasive_cgroup_shrink on 64K pagesize system Li Wang
@ 2026-03-12 4:06 ` Li Wang
2026-03-12 22:44 ` Yosry Ahmed
2026-03-12 4:06 ` [PATCH v2 7/7] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system Li Wang
2026-03-12 18:32 ` [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Nhat Pham
6 siblings, 1 reply; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Johannes Weiner,
Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo, Roman Gushchin,
Shakeel Butt
test_zswap uses hardcoded values of 4095 and 4096 throughout as page
stride and page size, which are only correct on systems with a 4K page
size. On architectures with larger pages (e.g., 64K on arm64 or ppc64),
these constants cause memory to be touched at sub-page granularity,
leading to inefficient access patterns and incorrect page count
calculations, which can cause test failures.
Replace all hardcoded 4095 and 4096 values with a global pagesize
variable initialized from sysconf(_SC_PAGESIZE) at startup, and remove
the redundant local sysconf() calls scattered across individual
functions. No functional change on 4K page size systems.
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
---
tools/testing/selftests/cgroup/test_zswap.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 654874fb371b..4eb88930b45f 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -13,6 +13,8 @@
#include "kselftest.h"
#include "cgroup_util.h"
+static size_t pagesize;
+
static int read_int(const char *path, size_t *value)
{
FILE *file;
@@ -68,11 +70,11 @@ static int allocate_and_read_bytes(const char *cgroup, void *arg)
if (!mem)
return -1;
- for (int i = 0; i < size; i += 4095)
+ for (int i = 0; i < size; i += pagesize)
mem[i] = 'a';
/* Go through the allocated memory to (z)swap in and out pages */
- for (int i = 0; i < size; i += 4095) {
+ for (int i = 0; i < size; i += pagesize) {
if (mem[i] != 'a')
ret = -1;
}
@@ -83,7 +85,6 @@ static int allocate_and_read_bytes(const char *cgroup, void *arg)
static int allocate_bytes(const char *cgroup, void *arg)
{
- long pagesize = sysconf(_SC_PAGESIZE);
size_t size = (size_t)arg;
char *mem = (char *)malloc(size);
@@ -263,7 +264,6 @@ static int test_zswapin(const char *root)
*/
static int attempt_writeback(const char *cgroup, void *arg)
{
- long pagesize = sysconf(_SC_PAGESIZE);
size_t memsize = MB(4);
char buf[pagesize];
long zswap_usage;
@@ -416,7 +416,6 @@ static int test_zswap_writeback_disabled(const char *root)
static int test_no_invasive_cgroup_shrink(const char *root)
{
int ret = KSFT_FAIL;
- long pagesize = sysconf(_SC_PAGESIZE);
size_t control_allocation_size = MB(64);
char *control_allocation = NULL, *wb_group = NULL, *control_group = NULL;
@@ -479,7 +478,7 @@ static int no_kmem_bypass_child(const char *cgroup, void *arg)
values->child_allocated = true;
return -1;
}
- for (long i = 0; i < values->target_alloc_bytes; i += 4095)
+ for (long i = 0; i < values->target_alloc_bytes; i += pagesize)
((char *)allocation)[i] = 'a';
values->child_allocated = true;
pause();
@@ -527,7 +526,7 @@ static int test_no_kmem_bypass(const char *root)
min_free_kb_low = sys_info.totalram / 500000;
values->target_alloc_bytes = (sys_info.totalram - min_free_kb_high * 1000) +
sys_info.totalram * 5 / 100;
- stored_pages_threshold = sys_info.totalram / 5 / 4096;
+ stored_pages_threshold = sys_info.totalram / 5 / pagesize;
trigger_allocation_size = sys_info.totalram / 20;
/* Set up test memcg */
@@ -554,7 +553,7 @@ static int test_no_kmem_bypass(const char *root)
if (!trigger_allocation)
break;
- for (int i = 0; i < trigger_allocation_size; i += 4095)
+ for (int i = 0; i < trigger_allocation_size; i += pagesize)
trigger_allocation[i] = 'b';
usleep(100000);
free(trigger_allocation);
@@ -565,8 +564,8 @@ static int test_no_kmem_bypass(const char *root)
/* If memory was pushed to zswap, verify it belongs to memcg */
if (stored_pages > stored_pages_threshold) {
int zswapped = cg_read_key_long(test_group, "memory.stat", "zswapped ");
- int delta = stored_pages * 4096 - zswapped;
- int result_ok = delta < stored_pages * 4096 / 4;
+ int delta = stored_pages * pagesize - zswapped;
+ int result_ok = delta < stored_pages * pagesize / 4;
ret = result_ok ? KSFT_PASS : KSFT_FAIL;
break;
@@ -622,6 +621,7 @@ int main(int argc, char **argv)
char root[PATH_MAX];
int i, state;
+ pagesize = sysconf(_SC_PAGESIZE);
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 7/7] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
` (4 preceding siblings ...)
2026-03-12 4:06 ` [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap Li Wang
@ 2026-03-12 4:06 ` Li Wang
2026-03-12 22:44 ` Yosry Ahmed
2026-03-12 18:32 ` [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Nhat Pham
6 siblings, 1 reply; 17+ messages in thread
From: Li Wang @ 2026-03-12 4:06 UTC (permalink / raw)
To: mkoutny, yosryahmed
Cc: linux-kselftest, linux-kernel, linux-mm, Johannes Weiner,
Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo, Roman Gushchin,
Shakeel Butt
In attempt_writeback(), a memsize of 4M only covers 64 pages on 64K
page size systems. When memory.reclaim is called, the kernel prefers
reclaiming clean file pages (binary, libc, linker, etc.) over swapping
anonymous pages. With only 64 pages of anonymous memory, the reclaim
target can be largely or entirely satisfied by dropping file pages,
resulting in very few or zero anonymous pages being pushed into zswap.
This causes zswap_usage to be extremely small or zero, making
zswap_usage/2 insufficient to create meaningful writeback pressure.
The test then fails because no writeback is triggered.
On 4K page size systems this is not an issue because 4M covers 1024
pages, and file pages are a small fraction of the reclaim target.
Fix this by always allocating 1024 pages regardless of page size.
This ensures enough anonymous pages to reliably populate zswap and
trigger writeback, while keeping the original 4M allocation on 4K
page size systems.
=== Error Log ===
# uname -rm
6.12.0-211.el10.ppc64le ppc64le
# getconf PAGESIZE
65536
# ./test_zswap
TAP version 13
1..7
ok 1 test_zswap_usage
ok 2 test_swapin_nozswap
ok 3 test_zswapin
not ok 4 test_zswap_writeback_enabled
...
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Yosry Ahmed <yosryahmed@google.com>
---
Notes:
v1 --> v2:
* use pagesize * 1024 which clearly shows the page numbers
tools/testing/selftests/cgroup/test_zswap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 4eb88930b45f..acfa0045d2ab 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -264,7 +264,7 @@ static int test_zswapin(const char *root)
*/
static int attempt_writeback(const char *cgroup, void *arg)
{
- size_t memsize = MB(4);
+ size_t memsize = pagesize * 1024;
char buf[pagesize];
long zswap_usage;
bool wb_enabled = *(bool *) arg;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
` (5 preceding siblings ...)
2026-03-12 4:06 ` [PATCH v2 7/7] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system Li Wang
@ 2026-03-12 18:32 ` Nhat Pham
2026-03-12 21:09 ` Yosry Ahmed
6 siblings, 1 reply; 17+ messages in thread
From: Nhat Pham @ 2026-03-12 18:32 UTC (permalink / raw)
To: Li Wang
Cc: mkoutny, yosryahmed, linux-kselftest, linux-kernel, linux-mm,
Johannes Weiner, Michal Hocko, Muchun Song, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Wed, Mar 11, 2026 at 9:06 PM Li Wang <liwang@redhat.com> wrote:
>
> Currently, test_zswap only checks for the presence of zswap via
> /sys/module/zswap, but it does not account for the global runtime
> state in /sys/module/zswap/parameters/enabled.
>
> If zswap is configured in the kernel but globally disabled at runtime,
> the zswap cgroup tests will run in an invalid environment and fail
> spuriously.
>
> Fix this by reading the 'enabled' parameter and gracefully skipping
> the test when zswap is disabled. A helpful hint is also added to the
> skip message to show users how to enable it.
>
> Signed-off-by: Li Wang <liwang@redhat.com>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Michal Hocko <mhocko@kernel.org>
> Cc: Michal Koutný <mkoutny@suse.com>
> Cc: Muchun Song <muchun.song@linux.dev>
> Cc: Nhat Pham <nphamcs@gmail.com>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Roman Gushchin <roman.gushchin@linux.dev>
> Cc: Shakeel Butt <shakeel.butt@linux.dev>
> Cc: Yosry Ahmed <yosryahmed@google.com>
> ---
>
> Notes:
> v1 --> v2:
> * remove enable/disable_zswap functions
> * skip the test if zswap is not enabled
> * reporting fail when zswap_enabled return -1
>
> tools/testing/selftests/cgroup/test_zswap.c | 28 +++++++++++++++++----
> 1 file changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
> index 64ebc3f3f203..ec64daaa2f5a 100644
> --- a/tools/testing/selftests/cgroup/test_zswap.c
> +++ b/tools/testing/selftests/cgroup/test_zswap.c
> @@ -589,23 +589,41 @@ struct zswap_test {
> };
> #undef T
>
> -static bool zswap_configured(void)
> +static int zswap_enabled(void)
> {
> - return access("/sys/module/zswap", F_OK) == 0;
> + char buf[16];
> + ssize_t n;
> +
> + if (access("/sys/module/zswap", F_OK))
> + ksft_exit_skip("zswap isn't configured\n");
> +
> + n = read_text("/sys/module/zswap/parameters/enabled", buf, sizeof(buf));
> + if (n <= 0)
> + return -1;
> +
> + if (buf[0] == 'Y')
> + return 1;
> + else if (buf[0] == 'N')
> + return 0;
> +
> + return -1;
> }
>
> int main(int argc, char **argv)
> {
> char root[PATH_MAX];
> - int i;
> + int i, state;
>
> ksft_print_header();
> ksft_set_plan(ARRAY_SIZE(tests));
> if (cg_find_unified_root(root, sizeof(root), NULL))
> ksft_exit_skip("cgroup v2 isn't mounted\n");
>
> - if (!zswap_configured())
> - ksft_exit_skip("zswap isn't configured\n");
> + state = zswap_enabled();
> + if (state == 0)
> + ksft_exit_skip("zswap is disabled (hint: echo 1 > /sys/module/zswap/parameters/enabled)\n");
> + else if (state < 0)
> + ksft_exit_fail_msg("Failed to read zswap state\n");
>
> /*
> * Check that memory controller is available:
> --
> 2.53.0
>
Seems a bit convoluted. You ksft_exit_skip() for one case in
zswap_enabled(), but return some value in other cases. This value is
checked in the caller (main()) and then used to decide whether to skip
or fail as well?
Why don't you just consolidate them in one place. If zswap_enabled()
has no other callers, let's just open code it, yeah?
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
2026-03-12 18:32 ` [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Nhat Pham
@ 2026-03-12 21:09 ` Yosry Ahmed
2026-03-13 1:50 ` Li Wang
0 siblings, 1 reply; 17+ messages in thread
From: Yosry Ahmed @ 2026-03-12 21:09 UTC (permalink / raw)
To: Nhat Pham
Cc: Li Wang, mkoutny, yosryahmed, linux-kselftest, linux-kernel,
linux-mm, Johannes Weiner, Michal Hocko, Muchun Song, Tejun Heo,
Roman Gushchin, Shakeel Butt
> > diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
> > index 64ebc3f3f203..ec64daaa2f5a 100644
> > --- a/tools/testing/selftests/cgroup/test_zswap.c
> > +++ b/tools/testing/selftests/cgroup/test_zswap.c
> > @@ -589,23 +589,41 @@ struct zswap_test {
> > };
> > #undef T
> >
> > -static bool zswap_configured(void)
> > +static int zswap_enabled(void)
> > {
> > - return access("/sys/module/zswap", F_OK) == 0;
> > + char buf[16];
> > + ssize_t n;
> > +
> > + if (access("/sys/module/zswap", F_OK))
> > + ksft_exit_skip("zswap isn't configured\n");
> > +
> > + n = read_text("/sys/module/zswap/parameters/enabled", buf, sizeof(buf));
> > + if (n <= 0)
> > + return -1;
> > +
> > + if (buf[0] == 'Y')
> > + return 1;
> > + else if (buf[0] == 'N')
> > + return 0;
> > +
> > + return -1;
> > }
> >
> > int main(int argc, char **argv)
> > {
> > char root[PATH_MAX];
> > - int i;
> > + int i, state;
> >
> > ksft_print_header();
> > ksft_set_plan(ARRAY_SIZE(tests));
> > if (cg_find_unified_root(root, sizeof(root), NULL))
> > ksft_exit_skip("cgroup v2 isn't mounted\n");
> >
> > - if (!zswap_configured())
> > - ksft_exit_skip("zswap isn't configured\n");
> > + state = zswap_enabled();
> > + if (state == 0)
> > + ksft_exit_skip("zswap is disabled (hint: echo 1 > /sys/module/zswap/parameters/enabled)\n");
> > + else if (state < 0)
> > + ksft_exit_fail_msg("Failed to read zswap state\n");
> >
> > /*
> > * Check that memory controller is available:
> > --
> > 2.53.0
> >
>
> Seems a bit convoluted. You ksft_exit_skip() for one case in
> zswap_enabled(), but return some value in other cases. This value is
> checked in the caller (main()) and then used to decide whether to skip
> or fail as well?
>
> Why don't you just consolidate them in one place. If zswap_enabled()
> has no other callers, let's just open code it, yeah?
Hmm I actually like having the helper. What if we move all the
ksft_exit_skip() and ksft_exit_fail_msg() calls inside and rename it
to check_zswap_enabled()? We can also just read one character:
static void check_zswap_enabled(void)
{
char value;
if (access("/sys/module/zswap", F_OK))
ksft_exit_skip("zswap isn't configured\n");
if (read_text("/sys/module/zswap/parameters/enabled", value,
sizeof(value)) <= 0)
ksft_exit_fail_msg("Failed to read
/sys/module/zswap/parameters/enabled\n");
if (value == 'N')
ksft_exit_skip("zswap is disabled (hint: echo 1 >
/sys/module/zswap/parameters/enabled)\n");
}
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap
2026-03-12 4:06 ` [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap Li Wang
@ 2026-03-12 22:41 ` Yosry Ahmed
0 siblings, 0 replies; 17+ messages in thread
From: Yosry Ahmed @ 2026-03-12 22:41 UTC (permalink / raw)
To: Li Wang
Cc: mkoutny, yosryahmed, linux-kselftest, linux-kernel, linux-mm,
Johannes Weiner, Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Wed, Mar 11, 2026 at 9:06 PM Li Wang <liwang@redhat.com> wrote:
>
> test_swapin_nozswap can hit OOM before reaching its assertions on some
> setups. The test currently sets memory.max=8M and then allocates/reads
> 32M with memory.zswap.max=0, which may over-constrain reclaim and kill
> the workload process.
>
> Raise memory.max to 24M so the workload can make forward progress, and
> lower the swap_peak expectation from 24M to 8M to keep the check robust
> across environments.
>
> The test intent is unchanged: verify that swapping happens while zswap
> remains unused when memory.zswap.max=0.
>
> === Error Logs ===
>
> # ./test_zswap
> TAP version 13
> 1..7
> ok 1 test_zswap_usage
> not ok 2 test_swapin_nozswap
> ...
>
> # dmesg
> [271641.879153] test_zswap invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=0
> [271641.879168] CPU: 1 UID: 0 PID: 177372 Comm: test_zswap Kdump: loaded Not tainted 6.12.0-211.el10.ppc64le #1 VOLUNTARY
> [271641.879171] Hardware name: IBM,9009-41A POWER9 (architected) 0x4e0202 0xf000005 of:IBM,FW940.02 (UL940_041) hv:phyp pSeries
> [271641.879173] Call Trace:
> [271641.879174] [c00000037540f730] [c00000000127ec44] dump_stack_lvl+0x88/0xc4 (unreliable)
> [271641.879184] [c00000037540f760] [c0000000005cc594] dump_header+0x5c/0x1e4
> [271641.879188] [c00000037540f7e0] [c0000000005cb464] oom_kill_process+0x324/0x3b0
> [271641.879192] [c00000037540f860] [c0000000005cbe48] out_of_memory+0x118/0x420
> [271641.879196] [c00000037540f8f0] [c00000000070d8ec] mem_cgroup_out_of_memory+0x18c/0x1b0
> [271641.879200] [c00000037540f990] [c000000000713888] try_charge_memcg+0x598/0x890
> [271641.879204] [c00000037540fa70] [c000000000713dbc] charge_memcg+0x5c/0x110
> [271641.879207] [c00000037540faa0] [c0000000007159f8] __mem_cgroup_charge+0x48/0x120
> [271641.879211] [c00000037540fae0] [c000000000641914] alloc_anon_folio+0x2b4/0x5a0
> [271641.879215] [c00000037540fb60] [c000000000641d58] do_anonymous_page+0x158/0x6b0
> [271641.879218] [c00000037540fbd0] [c000000000642f8c] __handle_mm_fault+0x4bc/0x910
> [271641.879221] [c00000037540fcf0] [c000000000643500] handle_mm_fault+0x120/0x3c0
> [271641.879224] [c00000037540fd40] [c00000000014bba0] ___do_page_fault+0x1c0/0x980
> [271641.879228] [c00000037540fdf0] [c00000000014c44c] hash__do_page_fault+0x2c/0xc0
> [271641.879232] [c00000037540fe20] [c0000000001565d8] do_hash_fault+0x128/0x1d0
> [271641.879236] [c00000037540fe50] [c000000000008be0] data_access_common_virt+0x210/0x220
> [271641.879548] Tasks state (memory values in pages):
> ...
> [271641.879550] [ pid ] uid tgid total_vm rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name
> [271641.879555] [ 177372] 0 177372 571 0 0 0 0 51200 96 0 test_zswap
> [271641.879562] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=/,mems_allowed=0,oom_memcg=/no_zswap_test,task_memcg=/no_zswap_test,task=test_zswap,pid=177372,uid=0
> [271641.879578] Memory cgroup out of memory: Killed process 177372 (test_zswap) total-vm:36544kB, anon-rss:0kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:50kB oom_score_adj:0
>
> Signed-off-by: Li Wang <liwang@redhat.com>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Michal Hocko <mhocko@kernel.org>
> Cc: Michal Koutný <mkoutny@suse.com>
> Cc: Muchun Song <muchun.song@linux.dev>
> Cc: Nhat Pham <nphamcs@gmail.com>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Roman Gushchin <roman.gushchin@linux.dev>
> Cc: Shakeel Butt <shakeel.butt@linux.dev>
> Cc: Yosry Ahmed <yosryahmed@google.com>
Acked-by: Yosry Ahmed <yosry@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap
2026-03-12 4:06 ` [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap Li Wang
@ 2026-03-12 22:44 ` Yosry Ahmed
2026-03-13 2:01 ` Li Wang
0 siblings, 1 reply; 17+ messages in thread
From: Yosry Ahmed @ 2026-03-12 22:44 UTC (permalink / raw)
To: Li Wang
Cc: mkoutny, yosryahmed, linux-kselftest, linux-kernel, linux-mm,
Johannes Weiner, Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Wed, Mar 11, 2026 at 9:07 PM Li Wang <liwang@redhat.com> wrote:
>
> test_zswap uses hardcoded values of 4095 and 4096 throughout as page
> stride and page size, which are only correct on systems with a 4K page
> size. On architectures with larger pages (e.g., 64K on arm64 or ppc64),
> these constants cause memory to be touched at sub-page granularity,
> leading to inefficient access patterns and incorrect page count
> calculations, which can cause test failures.
>
> Replace all hardcoded 4095 and 4096 values with a global pagesize
> variable initialized from sysconf(_SC_PAGESIZE) at startup, and remove
> the redundant local sysconf() calls scattered across individual
> functions. No functional change on 4K page size systems.
>
> Signed-off-by: Li Wang <liwang@redhat.com>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Michal Hocko <mhocko@kernel.org>
> Cc: Michal Koutný <mkoutny@suse.com>
> Cc: Muchun Song <muchun.song@linux.dev>
> Cc: Nhat Pham <nphamcs@gmail.com>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Roman Gushchin <roman.gushchin@linux.dev>
> Cc: Shakeel Butt <shakeel.butt@linux.dev>
> Cc: Yosry Ahmed <yosryahmed@google.com>
It makes more sense to have this patch first, replacing all hardcoded
values, then patch 5 doing the necessary changes on top.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 7/7] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system
2026-03-12 4:06 ` [PATCH v2 7/7] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system Li Wang
@ 2026-03-12 22:44 ` Yosry Ahmed
0 siblings, 0 replies; 17+ messages in thread
From: Yosry Ahmed @ 2026-03-12 22:44 UTC (permalink / raw)
To: Li Wang
Cc: mkoutny, yosryahmed, linux-kselftest, linux-kernel, linux-mm,
Johannes Weiner, Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Wed, Mar 11, 2026 at 9:07 PM Li Wang <liwang@redhat.com> wrote:
>
> In attempt_writeback(), a memsize of 4M only covers 64 pages on 64K
> page size systems. When memory.reclaim is called, the kernel prefers
> reclaiming clean file pages (binary, libc, linker, etc.) over swapping
> anonymous pages. With only 64 pages of anonymous memory, the reclaim
> target can be largely or entirely satisfied by dropping file pages,
> resulting in very few or zero anonymous pages being pushed into zswap.
>
> This causes zswap_usage to be extremely small or zero, making
> zswap_usage/2 insufficient to create meaningful writeback pressure.
> The test then fails because no writeback is triggered.
>
> On 4K page size systems this is not an issue because 4M covers 1024
> pages, and file pages are a small fraction of the reclaim target.
>
> Fix this by always allocating 1024 pages regardless of page size.
> This ensures enough anonymous pages to reliably populate zswap and
> trigger writeback, while keeping the original 4M allocation on 4K
> page size systems.
>
> === Error Log ===
> # uname -rm
> 6.12.0-211.el10.ppc64le ppc64le
>
> # getconf PAGESIZE
> 65536
>
> # ./test_zswap
> TAP version 13
> 1..7
> ok 1 test_zswap_usage
> ok 2 test_swapin_nozswap
> ok 3 test_zswapin
> not ok 4 test_zswap_writeback_enabled
> ...
>
> Signed-off-by: Li Wang <liwang@redhat.com>
> Cc: Johannes Weiner <hannes@cmpxchg.org>
> Cc: Michal Hocko <mhocko@kernel.org>
> Cc: Michal Koutný <mkoutny@suse.com>
> Cc: Muchun Song <muchun.song@linux.dev>
> Cc: Nhat Pham <nphamcs@gmail.com>
> Cc: Tejun Heo <tj@kernel.org>
> Cc: Roman Gushchin <roman.gushchin@linux.dev>
> Cc: Shakeel Butt <shakeel.butt@linux.dev>
> Cc: Yosry Ahmed <yosryahmed@google.com>
Acked-by: Yosry Ahmed <yosry@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
2026-03-12 21:09 ` Yosry Ahmed
@ 2026-03-13 1:50 ` Li Wang
2026-03-13 2:58 ` Yosry Ahmed
0 siblings, 1 reply; 17+ messages in thread
From: Li Wang @ 2026-03-13 1:50 UTC (permalink / raw)
To: Yosry Ahmed, Nhat Pham
Cc: mkoutny, yosryahmed, linux-kselftest, linux-kernel, linux-mm,
Johannes Weiner, Michal Hocko, Muchun Song, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Thu, Mar 12, 2026 at 02:09:30PM -0700, Yosry Ahmed wrote:
> > > diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
> > > index 64ebc3f3f203..ec64daaa2f5a 100644
> > > --- a/tools/testing/selftests/cgroup/test_zswap.c
> > > +++ b/tools/testing/selftests/cgroup/test_zswap.c
> > > @@ -589,23 +589,41 @@ struct zswap_test {
> > > };
> > > #undef T
> > >
> > > -static bool zswap_configured(void)
> > > +static int zswap_enabled(void)
> > > {
> > > - return access("/sys/module/zswap", F_OK) == 0;
> > > + char buf[16];
> > > + ssize_t n;
> > > +
> > > + if (access("/sys/module/zswap", F_OK))
> > > + ksft_exit_skip("zswap isn't configured\n");
> > > +
> > > + n = read_text("/sys/module/zswap/parameters/enabled", buf, sizeof(buf));
> > > + if (n <= 0)
> > > + return -1;
> > > +
> > > + if (buf[0] == 'Y')
> > > + return 1;
> > > + else if (buf[0] == 'N')
> > > + return 0;
> > > +
> > > + return -1;
> > > }
> > >
> > > int main(int argc, char **argv)
> > > {
> > > char root[PATH_MAX];
> > > - int i;
> > > + int i, state;
> > >
> > > ksft_print_header();
> > > ksft_set_plan(ARRAY_SIZE(tests));
> > > if (cg_find_unified_root(root, sizeof(root), NULL))
> > > ksft_exit_skip("cgroup v2 isn't mounted\n");
> > >
> > > - if (!zswap_configured())
> > > - ksft_exit_skip("zswap isn't configured\n");
> > > + state = zswap_enabled();
> > > + if (state == 0)
> > > + ksft_exit_skip("zswap is disabled (hint: echo 1 > /sys/module/zswap/parameters/enabled)\n");
> > > + else if (state < 0)
> > > + ksft_exit_fail_msg("Failed to read zswap state\n");
> > >
> > > /*
> > > * Check that memory controller is available:
> > > --
> > > 2.53.0
> > >
> >
> > Seems a bit convoluted. You ksft_exit_skip() for one case in
> > zswap_enabled(), but return some value in other cases. This value is
> > checked in the caller (main()) and then used to decide whether to skip
> > or fail as well?
> >
> > Why don't you just consolidate them in one place. If zswap_enabled()
> > has no other callers, let's just open code it, yeah?
>
> Hmm I actually like having the helper. What if we move all the
> ksft_exit_skip() and ksft_exit_fail_msg() calls inside and rename it
> to check_zswap_enabled()? We can also just read one character:
Yes, this looks more tidy than open it. I'll go this helper in v3.
>
> static void check_zswap_enabled(void)
> {
> char value;
read_text() requires string but not char.
>
> if (access("/sys/module/zswap", F_OK))
> ksft_exit_skip("zswap isn't configured\n");
>
> if (read_text("/sys/module/zswap/parameters/enabled", value,
> sizeof(value)) <= 0)
> ksft_exit_fail_msg("Failed to read
> /sys/module/zswap/parameters/enabled\n");
>
> if (value == 'N')
> ksft_exit_skip("zswap is disabled (hint: echo 1 >
> /sys/module/zswap/parameters/enabled)\n");
> }
>
--
Regards,
Li Wang
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap
2026-03-12 22:44 ` Yosry Ahmed
@ 2026-03-13 2:01 ` Li Wang
0 siblings, 0 replies; 17+ messages in thread
From: Li Wang @ 2026-03-13 2:01 UTC (permalink / raw)
To: Yosry Ahmed
Cc: mkoutny, yosryahmed, linux-kselftest, linux-kernel, linux-mm,
Johannes Weiner, Michal Hocko, Muchun Song, Nhat Pham, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Thu, Mar 12, 2026 at 03:44:09PM -0700, Yosry Ahmed wrote:
> On Wed, Mar 11, 2026 at 9:07 PM Li Wang <liwang@redhat.com> wrote:
> >
> > test_zswap uses hardcoded values of 4095 and 4096 throughout as page
> > stride and page size, which are only correct on systems with a 4K page
> > size. On architectures with larger pages (e.g., 64K on arm64 or ppc64),
> > these constants cause memory to be touched at sub-page granularity,
> > leading to inefficient access patterns and incorrect page count
> > calculations, which can cause test failures.
> >
> > Replace all hardcoded 4095 and 4096 values with a global pagesize
> > variable initialized from sysconf(_SC_PAGESIZE) at startup, and remove
> > the redundant local sysconf() calls scattered across individual
> > functions. No functional change on 4K page size systems.
> >
> > Signed-off-by: Li Wang <liwang@redhat.com>
> > Cc: Johannes Weiner <hannes@cmpxchg.org>
> > Cc: Michal Hocko <mhocko@kernel.org>
> > Cc: Michal Koutný <mkoutny@suse.com>
> > Cc: Muchun Song <muchun.song@linux.dev>
> > Cc: Nhat Pham <nphamcs@gmail.com>
> > Cc: Tejun Heo <tj@kernel.org>
> > Cc: Roman Gushchin <roman.gushchin@linux.dev>
> > Cc: Shakeel Butt <shakeel.butt@linux.dev>
> > Cc: Yosry Ahmed <yosryahmed@google.com>
>
> It makes more sense to have this patch first, replacing all hardcoded
> values, then patch 5 doing the necessary changes on top.
Make sense.
--
Regards,
Li Wang
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
2026-03-13 1:50 ` Li Wang
@ 2026-03-13 2:58 ` Yosry Ahmed
2026-03-13 3:23 ` Li Wang
0 siblings, 1 reply; 17+ messages in thread
From: Yosry Ahmed @ 2026-03-13 2:58 UTC (permalink / raw)
To: Li Wang
Cc: Nhat Pham, mkoutny, yosryahmed, linux-kselftest, linux-kernel,
linux-mm, Johannes Weiner, Michal Hocko, Muchun Song, Tejun Heo,
Roman Gushchin, Shakeel Butt
> Yes, this looks more tidy than open it. I'll go this helper in v3.
>
> >
> > static void check_zswap_enabled(void)
> > {
> > char value;
>
> read_text() requires string but not char.
I was just illustrating. This should be passed in as &value. That should work?
>
> >
> > if (access("/sys/module/zswap", F_OK))
> > ksft_exit_skip("zswap isn't configured\n");
> >
> > if (read_text("/sys/module/zswap/parameters/enabled", value,
> > sizeof(value)) <= 0)
> > ksft_exit_fail_msg("Failed to read
> > /sys/module/zswap/parameters/enabled\n");
> >
> > if (value == 'N')
> > ksft_exit_skip("zswap is disabled (hint: echo 1 >
> > /sys/module/zswap/parameters/enabled)\n");
> > }
> >
>
> --
> Regards,
> Li Wang
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
2026-03-13 2:58 ` Yosry Ahmed
@ 2026-03-13 3:23 ` Li Wang
2026-03-13 3:35 ` Li Wang
0 siblings, 1 reply; 17+ messages in thread
From: Li Wang @ 2026-03-13 3:23 UTC (permalink / raw)
To: Yosry Ahmed
Cc: Nhat Pham, mkoutny, yosryahmed, linux-kselftest, linux-kernel,
linux-mm, Johannes Weiner, Michal Hocko, Muchun Song, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Thu, Mar 12, 2026 at 07:58:28PM -0700, Yosry Ahmed wrote:
> > Yes, this looks more tidy than open it. I'll go this helper in v3.
> >
> > >
> > > static void check_zswap_enabled(void)
> > > {
> > > char value;
> >
> > read_text() requires string but not char.
>
> I was just illustrating. This should be passed in as &value. That should work?
Yes, pass it as &value works well.
Patchset v3 is on the way :).
--
Regards,
Li Wang
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled
2026-03-13 3:23 ` Li Wang
@ 2026-03-13 3:35 ` Li Wang
0 siblings, 0 replies; 17+ messages in thread
From: Li Wang @ 2026-03-13 3:35 UTC (permalink / raw)
To: Yosry Ahmed
Cc: Nhat Pham, mkoutny, yosryahmed, linux-kselftest, linux-kernel,
linux-mm, Johannes Weiner, Michal Hocko, Muchun Song, Tejun Heo,
Roman Gushchin, Shakeel Butt
On Fri, Mar 13, 2026 at 11:23:50AM +0800, Li Wang wrote:
> On Thu, Mar 12, 2026 at 07:58:28PM -0700, Yosry Ahmed wrote:
> > > Yes, this looks more tidy than open it. I'll go this helper in v3.
> > >
> > > >
> > > > static void check_zswap_enabled(void)
> > > > {
> > > > char value;
> > >
> > > read_text() requires string but not char.
> >
> > I was just illustrating. This should be passed in as &value. That should work?
>
> Yes, pass it as &value works well.
And use sizeof(&value) just get the pointer's size but not
the buffer size, so I would recommend: char value[2].
--
Regards,
Li Wang
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-03-13 3:35 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-12 4:06 [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Li Wang
2026-03-12 4:06 ` [PATCH v2 2/7] selftests/cgroup: avoid OOM in test_swapin_nozswap Li Wang
2026-03-12 22:41 ` Yosry Ahmed
2026-03-12 4:06 ` [PATCH v2 3/7] selftests/cgroup: use runtime page size for zswpin check Li Wang
2026-03-12 4:06 ` [PATCH v2 4/7] selftests/cgroup: rename PAGE_SIZE to BUF_SIZE in cgroup_util Li Wang
2026-03-12 4:06 ` [PATCH v2 5/7] selftest/cgroup: fix zswap test_no_invasive_cgroup_shrink on 64K pagesize system Li Wang
2026-03-12 4:06 ` [PATCH v2 6/7] selftests/cgroup: replace hardcoded page size values in test_zswap Li Wang
2026-03-12 22:44 ` Yosry Ahmed
2026-03-13 2:01 ` Li Wang
2026-03-12 4:06 ` [PATCH v2 7/7] selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system Li Wang
2026-03-12 22:44 ` Yosry Ahmed
2026-03-12 18:32 ` [PATCH v2 1/7] selftests/cgroup: skip test_zswap if zswap is globally disabled Nhat Pham
2026-03-12 21:09 ` Yosry Ahmed
2026-03-13 1:50 ` Li Wang
2026-03-13 2:58 ` Yosry Ahmed
2026-03-13 3:23 ` Li Wang
2026-03-13 3:35 ` Li Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox