From 1d224e7cbeac00292f15564310cdfa3976785b0e Mon Sep 17 00:00:00 2001 From: Li Wang Date: Wed, 22 Apr 2026 16:04:45 +0800 Subject: selftests/mm: respect build verbosity settings for 32/64-bit targets Patch series "selftests/mm: clean up build output and verbosity", v3. Currently, the build process for the mm selftests is unnecessarily noisy. First, it leaks raw compiler errors during the liburing feature probe if the headers are missing, which is confusing since the build system already handles this gracefully with a clear warning. Second, the specific 32-bit and 64-bit compilation targets ignore the standard kbuild verbosity settings, always printing their full compiler commands even during a default quiet build. This patch (of 2): The 32-bit and 64-bit compilation rules invoke $(CC) directly, bypassing the $(Q) quiet prefix and $(call msg,...) helper used by the rest of the selftests build system. This causes these rules to always print the full compiler command line, even when V=0 (the default). Wrap the commands with $(Q) and $(call msg,CC,,$@) to match the convention used by lib.mk, so that quiet and verbose builds behave consistently across all targets. ==== Build logs ==== ... CC merge CC rmap CC soft-dirty gcc -Wall -O2 -I /usr/src/25/tools/testing/selftests/../../.. -isystem /usr/src/25/tools/testing/selftests/../../../usr/include -isystem /usr/src/25/tools/testing/selftests/../../../tools/include/uapi -Wunreachable-code -U_FORTIFY_SOURCE -no-pie -D_GNU_SOURCE= -I/usr/src/25/tools/testing/selftests/../../../tools/testing/selftests -m32 -mxsave protection_keys.c vm_util.c thp_settings.c pkey_util.c -lrt -lpthread -lm -lrt -ldl -lm -o /usr/src/25/tools/testing/selftests/mm/protection_keys_32 gcc -Wall -O2 -I /usr/src/25/tools/testing/selftests/../../.. -isystem /usr/src/25/tools/testing/selftests/../../../usr/include -isystem /usr/src/25/tools/testing/selftests/../../../tools/include/uapi -Wunreachable-code -U_FORTIFY_SOURCE -no-pie -D_GNU_SOURCE= -I/usr/src/25/tools/testing/selftests/../../../tools/testing/selftests -m32 -mxsave pkey_sighandler_tests.c vm_util.c thp_settings.c pkey_util.c -lrt -lpthread -lm -lrt -ldl -lm -o /usr/src/25/tools/testing/selftests/mm/pkey_sighandler_tests_32 ... Link: https://lore.kernel.org/20260422080446.26020-1-wangli.ahau@gmail.com Link: https://lore.kernel.org/20260422080446.26020-2-wangli.ahau@gmail.com Signed-off-by: Li Wang Reported-by: Andrew Morton Tested-by: Andrew Morton Tested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index cd24596cdd27..6195770eba6e 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -216,7 +216,8 @@ ifeq ($(CAN_BUILD_I386),1) $(BINARIES_32): CFLAGS += -m32 -mxsave $(BINARIES_32): LDLIBS += -lrt -ldl -lm $(BINARIES_32): $(OUTPUT)/%_32: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ + $(call msg,CC,,$@) + $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ $(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-32,$(t)))) endif @@ -224,7 +225,8 @@ ifeq ($(CAN_BUILD_X86_64),1) $(BINARIES_64): CFLAGS += -m64 -mxsave $(BINARIES_64): LDLIBS += -lrt -ldl $(BINARIES_64): $(OUTPUT)/%_64: %.c - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ + $(call msg,CC,,$@) + $(Q)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(notdir $^) $(LDLIBS) -o $@ $(foreach t,$(VMTARGETS),$(eval $(call gen-target-rule-64,$(t)))) endif -- cgit v1.2.3 From 04cf82a741e096bf74e77bb8cf11e66481b4dcdf Mon Sep 17 00:00:00 2001 From: Li Wang Date: Wed, 22 Apr 2026 16:04:46 +0800 Subject: selftests/mm: suppress compiler error in liburing check When building the mm selftests on a system without liburing development headers, check_config.sh leaks a raw compiler error: /tmp/tmp.kIIOIqwe3n.c:2:10: fatal error: liburing.h: No such file or directory 2 | #include | ^~~~~~~~~~~~ Since this is an expected failure during the configuration probe, redirect the compiler output to /dev/null to hide it. And the build system prints a clear warning when this occurs: Warning: missing liburing support. Some tests will be skipped. Because the user is properly notified about the missing dependency, the raw compiler error is redundant and only confuse users. Additionally, update the Makefile to use $(Q) and $(call msg,...) for the check_config.sh execution. This aligns the probe with standard kbuild output formatting, providing a clean "CHK" message instead of printing the raw command during the build. Link: https://lore.kernel.org/20260422080446.26020-3-wangli.ahau@gmail.com Signed-off-by: Li Wang Tested-by: David Hildenbrand (Arm) Acked-by: David Hildenbrand (Arm) Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/Makefile | 3 ++- tools/testing/selftests/mm/check_config.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 6195770eba6e..18779045b7f6 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -263,7 +263,8 @@ $(OUTPUT)/migration: LDLIBS += -lnuma $(OUTPUT)/rmap: LDLIBS += -lnuma local_config.mk local_config.h: check_config.sh - CC="$(CC)" CFLAGS="$(CFLAGS)" ./check_config.sh + $(call msg,CHK,config,$@) + $(Q)CC="$(CC)" CFLAGS="$(CFLAGS)" ./check_config.sh EXTRA_CLEAN += local_config.mk local_config.h diff --git a/tools/testing/selftests/mm/check_config.sh b/tools/testing/selftests/mm/check_config.sh index b84c82bbf875..32beaefe279e 100755 --- a/tools/testing/selftests/mm/check_config.sh +++ b/tools/testing/selftests/mm/check_config.sh @@ -16,7 +16,7 @@ echo "#include " > $tmpfile_c echo "#include " >> $tmpfile_c echo "int func(void) { return 0; }" >> $tmpfile_c -$CC $CFLAGS -c $tmpfile_c -o $tmpfile_o +$CC $CFLAGS -c $tmpfile_c -o $tmpfile_o >/dev/null 2>&1 if [ -f $tmpfile_o ]; then echo "#define LOCAL_CONFIG_HAVE_LIBURING 1" > $OUTPUT_H_FILE -- cgit v1.2.3 From 588f08518fa2bb3b9ef20b5fbb20e27b39e5a257 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:58 -0700 Subject: selftests/damon/_damon_sysfs: support failed region quota charge ratio Extend _damon_sysfs.py for DAMOS action failed regions quota charge ratio setup, so that we can add kselftest for the new feature. Link: https://lore.kernel.org/20260428013402.115171-10-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/_damon_sysfs.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/damon/_damon_sysfs.py b/tools/testing/selftests/damon/_damon_sysfs.py index 2b4df655d9fd..0f13512fa5e6 100644 --- a/tools/testing/selftests/damon/_damon_sysfs.py +++ b/tools/testing/selftests/damon/_damon_sysfs.py @@ -132,14 +132,17 @@ class DamosQuota: goals = None # quota goals goal_tuner = None # quota goal tuner reset_interval_ms = None # quota reset interval + fail_charge_num = None + fail_charge_denom = None weight_sz_permil = None weight_nr_accesses_permil = None weight_age_permil = None scheme = None # owner scheme def __init__(self, sz=0, ms=0, goals=None, goal_tuner='consist', - reset_interval_ms=0, weight_sz_permil=0, - weight_nr_accesses_permil=0, weight_age_permil=0): + reset_interval_ms=0, fail_charge_num=0, fail_charge_denom=0, + weight_sz_permil=0, weight_nr_accesses_permil=0, + weight_age_permil=0): self.sz = sz self.ms = ms self.reset_interval_ms = reset_interval_ms @@ -151,6 +154,8 @@ class DamosQuota: for idx, goal in enumerate(self.goals): goal.idx = idx goal.quota = self + self.fail_charge_num = fail_charge_num + self.fail_charge_denom = fail_charge_denom def sysfs_dir(self): return os.path.join(self.scheme.sysfs_dir(), 'quotas') @@ -197,6 +202,18 @@ class DamosQuota: os.path.join(self.sysfs_dir(), 'goal_tuner'), self.goal_tuner) if err is not None: return err + + err = write_file( + os.path.join(self.sysfs_dir(), 'fail_charge_num'), + self.fail_charge_num) + if err is not None: + return err + err = write_file( + os.path.join(self.sysfs_dir(), 'fail_charge_denom'), + self.fail_charge_denom) + if err is not None: + return err + return None class DamosWatermarks: -- cgit v1.2.3 From bcd8d68c6ba1ef918294d96ab64726eeef00b37c Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:33:59 -0700 Subject: selftests/damon/drgn_dump_damon_status: support failed region quota charge ratio Extend drgn_dump_damon_status.py to dump DAMON internal state for DAMOS action failed regions quota charge ratio, to be able to show if the internal state for the feature is working, with future DAMON selftests. Link: https://lore.kernel.org/20260428013402.115171-11-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/drgn_dump_damon_status.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/damon/drgn_dump_damon_status.py b/tools/testing/selftests/damon/drgn_dump_damon_status.py index af99b07a4f56..b5c56233a923 100755 --- a/tools/testing/selftests/damon/drgn_dump_damon_status.py +++ b/tools/testing/selftests/damon/drgn_dump_damon_status.py @@ -112,6 +112,8 @@ def damos_quota_to_dict(quota): ['goals', damos_quota_goals_to_list], ['goal_tuner', int], ['esz', int], + ['fail_charge_num', int], + ['fail_charge_denom', int], ['weight_sz', int], ['weight_nr_accesses', int], ['weight_age', int], -- cgit v1.2.3 From 8d21446a6c7feb2d93b3ea4f54ffd7f4eb64f2bc Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 27 Apr 2026 18:34:00 -0700 Subject: selftests/damon/sysfs.py: test failed region quota charge ratio Extend sysfs.py DAMON selftest to setup DAMOS action failed region quota charge ratio and assert the setup is made into DAMON internal state. Link: https://lore.kernel.org/20260428013402.115171-12-sj@kernel.org Signed-off-by: SeongJae Park Cc: Brendan Higgins Cc: David Hildenbrand Cc: Jonathan Corbet Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- tools/testing/selftests/damon/sysfs.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools') diff --git a/tools/testing/selftests/damon/sysfs.py b/tools/testing/selftests/damon/sysfs.py index 3aa5c91548a5..9067945f16ca 100755 --- a/tools/testing/selftests/damon/sysfs.py +++ b/tools/testing/selftests/damon/sysfs.py @@ -73,6 +73,10 @@ def assert_quota_committed(quota, dump): } assert_true(dump['goal_tuner'] == tuner_val[quota.goal_tuner], 'goal_tuner', dump) + assert_true(dump['fail_charge_num'] == quota.fail_charge_num, + 'fail_charge_num', dump) + assert_true(dump['fail_charge_denom'] == quota.fail_charge_denom, + 'fail_charge_denom', dump) assert_true(dump['weight_sz'] == quota.weight_sz_permil, 'weight_sz', dump) assert_true(dump['weight_nr_accesses'] == quota.weight_nr_accesses_permil, 'weight_nr_accesses', dump) @@ -239,6 +243,8 @@ def main(): nid=1)], goal_tuner='temporal', reset_interval_ms=1500, + fail_charge_num=1, + fail_charge_denom=4096, weight_sz_permil=20, weight_nr_accesses_permil=200, weight_age_permil=1000), -- cgit v1.2.3 From 9f40c3cdf0fa3011c3a15f8acc0b9ffb3ed11171 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:52 +0800 Subject: selftests/cgroup: skip test_zswap if zswap is globally disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch series "selftests/cgroup: improve zswap tests robustness and support large page sizes", v7. This patchset aims to fix various spurious failures and improve the overall robustness of the cgroup zswap selftests. The primary motivation is to make the tests compatible with architectures that use non-4K page sizes (such as 64K on ppc64le and arm64). Currently, the tests rely heavily on hardcoded 4K page sizes and fixed memory limits. On 64K page size systems, these hardcoded values lead to sub-page granularity accesses, incorrect page count calculations, and insufficient memory pressure to trigger zswap writeback, ultimately causing the tests to fail. Additionally, this series addresses OOM kills occurring in test_swapin_nozswap by dynamically scaling memory limits, and prevents spurious test failures when zswap is built into the kernel but globally disabled. This patch (of 8): test_zswap currently only checks whether zswap is present by testing /sys/module/zswap. This misses the runtime global state exposed in /sys/module/zswap/parameters/enabled. When zswap is built/loaded but globally disabled, the zswap cgroup selftests run in an invalid environment and may fail spuriously. Check the runtime enabled state before running the tests: - skip if zswap is not configured, - fail if the enabled knob cannot be read, - skip if zswap is globally disabled. Also print a hint in the skip message on how to enable zswap. Link: https://lore.kernel.org/20260424040059.12940-1-li.wang@linux.dev Link: https://lore.kernel.org/20260424040059.12940-2-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index a7bdcdd09d62..a94238a2e048 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -15,6 +15,9 @@ #include "kselftest.h" #include "cgroup_util.h" +#define PATH_ZSWAP "/sys/module/zswap" +#define PATH_ZSWAP_ENABLED "/sys/module/zswap/parameters/enabled" + static int read_int(const char *path, size_t *value) { FILE *file; @@ -725,9 +728,18 @@ struct zswap_test { }; #undef T -static bool zswap_configured(void) +static void check_zswap_enabled(void) { - return access("/sys/module/zswap", F_OK) == 0; + char value[2]; + + if (access(PATH_ZSWAP, F_OK)) + ksft_exit_skip("zswap isn't configured\n"); + + if (read_text(PATH_ZSWAP_ENABLED, value, sizeof(value)) <= 0) + ksft_exit_fail_msg("Failed to read " PATH_ZSWAP_ENABLED "\n"); + + if (value[0] == 'N') + ksft_exit_skip("zswap is disabled (hint: echo 1 > " PATH_ZSWAP_ENABLED ")\n"); } int main(int argc, char **argv) @@ -740,8 +752,7 @@ int main(int argc, char **argv) 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"); + check_zswap_enabled(); /* * Check that memory controller is available: -- cgit v1.2.3 From 0d38cded3c6294b0dfa38e3fc92077b5d381951e Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:53 +0800 Subject: selftests/cgroup: avoid OOM in test_swapin_nozswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Replace hardcoded sizes with PAGE_SIZE-based values: - control_allocation_size = PAGE_SIZE * 512 - memory.max = control_allocation_size * 3 / 4 - minimum expected swap = control_allocation_size / 4 This keeps the test pressure model intact (allocate/read beyond memory.max to force swap-in/out) while making it more robust across different environments. The test intent is unchanged: confirm that swapping occurs 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 Link: https://lore.kernel.org/20260424040059.12940-3-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index a94238a2e048..47709cbdcdf1 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -165,21 +165,25 @@ out: static int test_swapin_nozswap(const char *root) { int ret = KSFT_FAIL; - char *test_group; - long swap_peak, zswpout; + char *test_group, mem_max_buf[32]; + long swap_peak, zswpout, min_swap; + size_t allocation_size = sysconf(_SC_PAGESIZE) * 512; + + min_swap = allocation_size / 4; + snprintf(mem_max_buf, sizeof(mem_max_buf), "%zu", allocation_size * 3/4); test_group = cg_name(root, "no_zswap_test"); if (!test_group) goto out; if (cg_create(test_group)) goto out; - if (cg_write(test_group, "memory.max", "8M")) + if (cg_write(test_group, "memory.max", mem_max_buf)) goto out; if (cg_write(test_group, "memory.zswap.max", "0")) goto out; /* Allocate and read more than memory.max to trigger swapin */ - if (cg_run(test_group, allocate_and_read_bytes, (void *)MB(32))) + if (cg_run(test_group, allocate_and_read_bytes, (void *)allocation_size)) goto out; /* Verify that pages are swapped out, but no zswap happened */ @@ -189,8 +193,9 @@ 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 < min_swap) { + ksft_print_msg("at least %ldKB of memory should be swapped out\n", + min_swap / 1024); goto out; } -- cgit v1.2.3 From b19ee588e159c71f0d314246a944dcfc3e2a6009 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:54 +0800 Subject: selftests/cgroup: use runtime page size for zswpin check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Link: https://lore.kernel.org/20260424040059.12940-4-li.wang@linux.dev Signed-off-by: Li Wang Reviewed-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 47709cbdcdf1..37aa83c2f1bf 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -245,7 +245,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; } -- cgit v1.2.3 From 6e9f5c2eecd107cf9a10fd22d311b2c49026f474 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:55 +0800 Subject: selftests/cgroup: rename PAGE_SIZE to BUF_SIZE in cgroup_util MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cgroup utility code defines a local PAGE_SIZE macro hardcoded to 4096, which is used primarily as a generic buffer size for reading cgroup and proc files. This naming is misleading because the value has nothing to do with the actual page size of the system. 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 PAGE_SIZE definitions from system headers, leading to confusion or subtle bugs. To resolve this, rename the macro to BUF_SIZE to accurately reflect its purpose as a general I/O buffer size. Furthermore, test_memcontrol currently relies on this hardcoded 4K value to stride through memory and trigger page faults. Update this logic to use the actual system page size dynamically. This micro-optimizes the memory faulting process by ensuring it iterates correctly and efficiently based on the underlying architecture's true page size. (This part from Waiman) Link: https://lore.kernel.org/20260424040059.12940-5-li.wang@linux.dev Signed-off-by: Li Wang Signed-off-by: Waiman Long Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Yosry Ahmed Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/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 +- tools/testing/selftests/cgroup/test_memcontrol.c | 19 ++++++++++++------- 5 files changed, 25 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c index 42f54936f4bb..f1ec7de58ae3 100644 --- a/tools/testing/selftests/cgroup/lib/cgroup_util.c +++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c @@ -141,7 +141,7 @@ int cg_read_strcmp_wait(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; @@ -171,7 +171,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))) @@ -207,7 +207,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; @@ -259,7 +259,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 "; @@ -314,7 +314,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; @@ -339,7 +339,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. */ @@ -549,7 +549,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)); @@ -561,7 +561,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)); @@ -588,7 +588,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 567b1082974c..febc1723d090 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 #include -#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 7b83c7e7c9d4..88ca832d4fc1 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 @@ cleanup: */ 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 b43da9bc20c4..44338dbaee81 100644 --- a/tools/testing/selftests/cgroup/test_memcontrol.c +++ b/tools/testing/selftests/cgroup/test_memcontrol.c @@ -26,6 +26,7 @@ static bool has_localevents; static bool has_recursiveprot; +static int page_size; int get_temp_fd(void) { @@ -34,7 +35,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; @@ -61,7 +62,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 += page_size) *ptr = 0; free(buf); @@ -70,7 +71,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; @@ -113,7 +114,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"); @@ -184,7 +185,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 += page_size) *ptr = 0; current = cg_read_long(cgroup, "memory.current"); @@ -414,7 +415,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 += page_size) *ptr = 0; while (getppid() == ppid) @@ -1000,7 +1001,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 += page_size) *ptr = 0; mem_current = cg_read_long(cgroup, "memory.current"); @@ -1791,6 +1792,10 @@ int main(int argc, char **argv) char root[PATH_MAX]; int i, proc_status; + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size <= 0) + page_size = BUF_SIZE; + ksft_print_header(); ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) -- cgit v1.2.3 From 43743cc516684e40faf15afbb123eccd3d90e244 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:56 +0800 Subject: selftests/cgroup: replace hardcoded page size values in test_zswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. Link: https://lore.kernel.org/20260424040059.12940-6-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Reviewed-by: Jiayuan Chen Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Nhat Pham Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 45 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 37aa83c2f1bf..23ff11390a33 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -15,6 +15,8 @@ #include "kselftest.h" #include "cgroup_util.h" +static int page_size; + #define PATH_ZSWAP "/sys/module/zswap" #define PATH_ZSWAP_ENABLED "/sys/module/zswap/parameters/enabled" @@ -73,11 +75,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 += page_size) 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 += page_size) { if (mem[i] != 'a') ret = -1; } @@ -93,7 +95,7 @@ static int allocate_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 += page_size) mem[i] = 'a'; free(mem); return 0; @@ -167,7 +169,7 @@ static int test_swapin_nozswap(const char *root) int ret = KSFT_FAIL; char *test_group, mem_max_buf[32]; long swap_peak, zswpout, min_swap; - size_t allocation_size = sysconf(_SC_PAGESIZE) * 512; + size_t allocation_size = page_size * 512; min_swap = allocation_size / 4; snprintf(mem_max_buf, sizeof(mem_max_buf), "%zu", allocation_size * 3/4); @@ -245,7 +247,7 @@ static int test_zswapin(const char *root) goto out; } - if (zswpin < MB(24) / sysconf(_SC_PAGESIZE)) { + if (zswpin < MB(24) / page_size) { ksft_print_msg("at least 24MB should be brought back from zswap\n"); goto out; } @@ -272,9 +274,8 @@ out: */ static int attempt_writeback(const char *cgroup, void *arg) { - long pagesize = sysconf(_SC_PAGESIZE); size_t memsize = MB(4); - char buf[pagesize]; + char buf[page_size]; long zswap_usage; bool wb_enabled = *(bool *) arg; int ret = -1; @@ -289,11 +290,11 @@ static int attempt_writeback(const char *cgroup, void *arg) * half empty, this will result in data that is still compressible * and ends up in zswap, with material zswap usage. */ - for (int i = 0; i < pagesize; i++) - buf[i] = i < pagesize/2 ? (char) i : 0; + for (int i = 0; i < page_size; i++) + buf[i] = i < page_size/2 ? (char) i : 0; - for (int i = 0; i < memsize; i += pagesize) - memcpy(&mem[i], buf, pagesize); + for (int i = 0; i < memsize; i += page_size) + memcpy(&mem[i], buf, page_size); /* Try and reclaim allocated memory */ if (cg_write_numeric(cgroup, "memory.reclaim", memsize)) { @@ -304,8 +305,8 @@ static int attempt_writeback(const char *cgroup, void *arg) zswap_usage = cg_read_long(cgroup, "memory.zswap.current"); /* zswpin */ - for (int i = 0; i < memsize; i += pagesize) { - if (memcmp(&mem[i], buf, pagesize)) { + for (int i = 0; i < memsize; i += page_size) { + if (memcmp(&mem[i], buf, page_size)) { ksft_print_msg("invalid memory\n"); goto out; } @@ -441,7 +442,7 @@ static int test_no_invasive_cgroup_shrink(const char *root) 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 += page_size) control_allocation[i] = 'a'; if (cg_read_key_long(control_group, "memory.stat", "zswapped") < 1) goto out; @@ -481,7 +482,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 += page_size) ((char *)allocation)[i] = 'a'; values->child_allocated = true; pause(); @@ -529,7 +530,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 / page_size; trigger_allocation_size = sys_info.totalram / 20; /* Set up test memcg */ @@ -556,7 +557,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 += page_size) trigger_allocation[i] = 'b'; usleep(100000); free(trigger_allocation); @@ -567,8 +568,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 * page_size - zswapped; + int result_ok = delta < stored_pages * page_size / 4; ret = result_ok ? KSFT_PASS : KSFT_FAIL; break; @@ -622,7 +623,7 @@ static int allocate_random_and_wait(const char *cgroup, void *arg) close(fd); /* Touch all pages to ensure they're faulted in */ - for (size_t i = 0; i < size; i += PAGE_SIZE) + for (size_t i = 0; i < size; i += page_size) mem[i] = mem[i]; /* Use MADV_PAGEOUT to push pages into zswap */ @@ -752,6 +753,10 @@ int main(int argc, char **argv) char root[PATH_MAX]; int i; + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size <= 0) + page_size = BUF_SIZE; + ksft_print_header(); ksft_set_plan(ARRAY_SIZE(tests)); if (cg_find_unified_root(root, sizeof(root), NULL)) -- cgit v1.2.3 From a19b474927519c8822193f8bdc010641ec6ba404 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:57 +0800 Subject: selftest/cgroup: fix zswap test_no_invasive_cgroup_shrink on large pagesize system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_no_invasive_cgroup_shrink sets up two cgroups: wb_group, which is expected to trigger zswap writeback, and a control group (renamed to zw_group), which should only have pages sitting in zswap without any writeback. There are two problems with the current test: 1) The data patterns are reversed. wb_group uses allocate_bytes(), which writes only a single byte per page — trivially compressible, especially by zstd — so compressed pages fit within zswap.max and writeback is never triggered. Meanwhile, the control group uses getrandom() to produce hard-to-compress data, but it is the group that does *not* need writeback. 2) The test uses fixed sizes (10K zswap.max, 10MB allocation) that are too small on systems with large PAGE_SIZE (e.g. 64K), failing to build enough memory pressure to trigger writeback reliably. Fix both issues by: - Swapping the data patterns: fill wb_group pages with partially random data (getrandom for page_size/4 bytes) to resist compression and trigger writeback, and fill zw_group pages with simple repeated data to stay compressed in zswap. - Making all size parameters PAGE_SIZE-aware: set allocation size to PAGE_SIZE * 1024, memory.zswap.max to PAGE_SIZE, and memory.max to allocation_size / 2 for both cgroups. - Allocating memory inline instead of via cg_run() so the pages remain resident throughout the test. === 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 Link: https://lore.kernel.org/20260424040059.12940-7-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Yosry Ahmed Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 70 ++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 21 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 23ff11390a33..8f0478923bd0 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "kselftest.h" #include "cgroup_util.h" @@ -426,44 +427,71 @@ 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); - char *control_allocation = NULL, *wb_group = NULL, *control_group = NULL; + unsigned int off; + size_t allocation_size = page_size * 1024; + unsigned int nr_pages = allocation_size / page_size; + char zswap_max_buf[32], mem_max_buf[32]; + char *zw_allocation = NULL, *wb_allocation = NULL; + char *zw_group = NULL, *wb_group = NULL; + + snprintf(zswap_max_buf, sizeof(zswap_max_buf), "%d", page_size); + snprintf(mem_max_buf, sizeof(mem_max_buf), "%zu", allocation_size / 2); 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", zswap_max_buf)) + goto out; + if (cg_write(wb_group, "memory.max", mem_max_buf)) + goto out; + + zw_group = setup_test_group_1M(root, "per_memcg_wb_test2"); + if (!zw_group) goto out; - control_group = setup_test_group_1M(root, "per_memcg_wb_test2"); - if (!control_group) + if (cg_write(zw_group, "memory.max", mem_max_buf)) goto out; - /* Push some test_group2 memory into zswap */ - if (cg_enter_current(control_group)) + /* Push some zw_group memory into zswap (simple data, easy to compress) */ + if (cg_enter_current(zw_group)) goto out; - control_allocation = malloc(control_allocation_size); - for (int i = 0; i < control_allocation_size; i += page_size) - control_allocation[i] = 'a'; - if (cg_read_key_long(control_group, "memory.stat", "zswapped") < 1) + zw_allocation = malloc(allocation_size); + for (int i = 0; i < nr_pages; i++) { + off = (unsigned long)i * page_size; + memset(&zw_allocation[off], 0, page_size); + memset(&zw_allocation[off], 'a', page_size/4); + } + if (cg_read_key_long(zw_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))) + /* Push wb_group memory into zswap with hard-to-compress data to trigger wb */ + if (cg_enter_current(wb_group)) + goto out; + wb_allocation = malloc(allocation_size); + if (!wb_allocation) goto out; + for (int i = 0; i < nr_pages; i++) { + off = (unsigned long)i * page_size; + memset(&wb_allocation[off], 0, page_size); + getrandom(&wb_allocation[off], page_size/4, 0); + } /* 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) + if (get_cg_wb_count(wb_group) > 0 && get_cg_wb_count(zw_group) == 0) ret = KSFT_PASS; out: cg_enter_current(root); - if (control_group) { - cg_destroy(control_group); - free(control_group); + if (zw_group) { + cg_destroy(zw_group); + free(zw_group); + } + if (wb_group) { + cg_destroy(wb_group); + free(wb_group); } - cg_destroy(wb_group); - free(wb_group); - if (control_allocation) - free(control_allocation); + if (zw_allocation) + free(zw_allocation); + if (wb_allocation) + free(wb_allocation); return ret; } -- cgit v1.2.3 From 883015a9c328eaeac48395db36f9e5f864f6473d Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:58 +0800 Subject: selftest/cgroup: fix zswap attempt_writeback() on 64K pagesize system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/4 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 systems. - Setting zswap.max to zswap_usage/4 instead of zswap_usage/2 to create stronger writeback pressure, ensuring reclaim reliably triggers writeback even on large 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 ... Link: https://lore.kernel.org/20260424040059.12940-8-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Yosry Ahmed Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 8f0478923bd0..5fe0cffb5575 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -268,14 +268,14 @@ out: This will move it into zswap. * 3. Save current zswap usage. * 4. Move the memory allocated in step 1 back in from zswap. - * 5. Set zswap.max to half the amount that was recorded in step 3. + * 5. Set zswap.max to 1/4 of the amount that was recorded in step 3. * 6. Attempt to reclaim memory equal to the amount that was allocated, this will either trigger writeback if it's enabled, or reclamation will fail if writeback is disabled as there isn't enough zswap space. */ static int attempt_writeback(const char *cgroup, void *arg) { - size_t memsize = MB(4); + size_t memsize = page_size * 1024; char buf[page_size]; long zswap_usage; bool wb_enabled = *(bool *) arg; @@ -313,12 +313,12 @@ static int attempt_writeback(const char *cgroup, void *arg) } } - if (cg_write_numeric(cgroup, "memory.zswap.max", zswap_usage/2)) + if (cg_write_numeric(cgroup, "memory.zswap.max", zswap_usage/4)) goto out; /* * If writeback is enabled, trying to reclaim memory now will trigger a - * writeback as zswap.max is half of what was needed when reclaim ran the first time. + * writeback as zswap.max is 1/4 of what was needed when reclaim ran the first time. * If writeback is disabled, memory reclaim will fail as zswap is limited and * it can't writeback to swap. */ -- cgit v1.2.3 From e5ab892d05ca1a6b032dbc4c9795372daf226415 Mon Sep 17 00:00:00 2001 From: Li Wang Date: Fri, 24 Apr 2026 12:00:59 +0800 Subject: selftests/cgroup: test_zswap: wait for asynchronous writeback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zswap writeback is asynchronous, but test_zswap.c checks writeback counters immediately after reclaim/trigger paths. On some platforms (e.g. ppc64le), this can race with background writeback and cause spurious failures even when behavior is correct. Add wait_for_writeback() to poll get_cg_wb_count() with a bounded timeout, and use it in: test_zswap_writeback_one() when writeback is expected test_no_invasive_cgroup_shrink() for the wb_group check This keeps the original before/after assertion style while making the tests robust against writeback completion latency. No test behavior change, selftest stability improvement only. Link: https://lore.kernel.org/20260424040059.12940-9-li.wang@linux.dev Signed-off-by: Li Wang Acked-by: Nhat Pham Cc: Johannes Weiner Cc: Michal Hocko Cc: Michal Koutný Cc: Muchun Song Cc: Tejun Heo Cc: Roman Gushchin Cc: Shakeel Butt Cc: Yosry Ahmed Cc: Chengming Zhou Cc: Jiayuan Chen Cc: Waiman Long Cc: Yosry Ahmed Signed-off-by: Andrew Morton --- tools/testing/selftests/cgroup/test_zswap.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c index 5fe0cffb5575..49b36ee79160 100644 --- a/tools/testing/selftests/cgroup/test_zswap.c +++ b/tools/testing/selftests/cgroup/test_zswap.c @@ -120,6 +120,27 @@ fail: return NULL; } +/* + * Writeback is asynchronous; poll until at least one writeback has + * been recorded for @cg, or until @timeout_ms has elapsed. + */ +static long wait_for_writeback(const char *cg, int timeout_ms) +{ + long elapsed, count; + for (elapsed = 0; elapsed < timeout_ms; elapsed += 100) { + count = get_cg_wb_count(cg); + + if (count < 0) + return -1; + if (count > 0) + return count; + + usleep(100000); + } + + return 0; +} + /* * Sanity test to check that pages are written into zswap. */ @@ -345,7 +366,10 @@ static int test_zswap_writeback_one(const char *cgroup, bool wb) return -1; /* Verify that zswap writeback occurred only if writeback was enabled */ - zswpwb_after = get_cg_wb_count(cgroup); + if (wb) + zswpwb_after = wait_for_writeback(cgroup, 5000); + else + zswpwb_after = get_cg_wb_count(cgroup); if (zswpwb_after < 0) return -1; @@ -476,7 +500,7 @@ static int test_no_invasive_cgroup_shrink(const char *root) } /* Verify that only zswapped memory from gwb_group has been written back */ - if (get_cg_wb_count(wb_group) > 0 && get_cg_wb_count(zw_group) == 0) + if (wait_for_writeback(wb_group, 5000) > 0 && get_cg_wb_count(zw_group) == 0) ret = KSFT_PASS; out: cg_enter_current(root); -- cgit v1.2.3 From c02dd57c57a6ae7dd05fdf8b861f1a76e1e4f8bc Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:38 -0700 Subject: selftests/mm: verify droppable mappings cannot be locked For configs that support MAP_DROPPABLE verify that a mapping created with MAP_DROPPABLE cannot be locked via mlock(), and that it will not be locked if it's created after mlockall(MCL_FUTURE). Link: https://lore.kernel.org/20260416033939.49981-3-anthony.yznaga@oracle.com Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/mlock2-tests.c | 84 +++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/testing/selftests/mm/mlock2-tests.c b/tools/testing/selftests/mm/mlock2-tests.c index b474f2b20def..e16e288cc7c1 100644 --- a/tools/testing/selftests/mm/mlock2-tests.c +++ b/tools/testing/selftests/mm/mlock2-tests.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE #include +#include #include #include #include @@ -163,14 +164,17 @@ static int lock_check(unsigned long addr) return (vma_rss == vma_size); } -static int unlock_lock_check(char *map) +static int unlock_lock_check(char *map, bool mlock_supported) { - if (is_vmflag_set((unsigned long)map, LOCKED)) { + if (!is_vmflag_set((unsigned long)map, LOCKED)) + return 0; + + if (mlock_supported) ksft_print_msg("VMA flag %s is present on page 1 after unlock\n", LOCKED); - return 1; - } + else + ksft_print_msg("VMA flag %s is present on an unsupported VMA\n", LOCKED); - return 0; + return 1; } static void test_mlock_lock(void) @@ -196,7 +200,7 @@ static void test_mlock_lock(void) ksft_exit_fail_msg("munlock(): %s\n", strerror(errno)); } - ksft_test_result(!unlock_lock_check(map), "%s: Unlocked\n", __func__); + ksft_test_result(!unlock_lock_check(map, true), "%s: Unlocked\n", __func__); munmap(map, 2 * page_size); } @@ -296,7 +300,7 @@ static void test_munlockall0(void) ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno)); } - ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__); + ksft_test_result(!unlock_lock_check(map, true), "%s: No locked memory\n", __func__); munmap(map, 2 * page_size); } @@ -336,7 +340,67 @@ static void test_munlockall1(void) ksft_exit_fail_msg("munlockall() %s\n", strerror(errno)); } - ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__); + ksft_test_result(!unlock_lock_check(map, true), "%s: No locked memory\n", __func__); + munmap(map, 2 * page_size); +} + +/* Droppable memory should not be lockable. */ +static void test_mlock_droppable(void) +{ + char *map; + unsigned long page_size = getpagesize(); + + /* Ensure MCL_FUTURE is not set. */ + if (munlockall()) { + ksft_test_result_fail("munlockall() %s\n", strerror(errno)); + return; + } + + map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); + if (map == MAP_FAILED) { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) + ksft_test_result_skip("%s: MAP_DROPPABLE not supported\n", __func__); + else + ksft_test_result_fail("mmap error: %s\n", strerror(errno)); + return; + } + + if (mlock2_(map, 2 * page_size, 0)) + ksft_test_result_fail("mlock2(0): %s\n", strerror(errno)); + else + ksft_test_result(!unlock_lock_check(map, false), + "%s: droppable memory not locked\n", __func__); + + munmap(map, 2 * page_size); +} + +static void test_mlockall_future_droppable(void) +{ + char *map; + unsigned long page_size = getpagesize(); + + if (mlockall(MCL_CURRENT | MCL_FUTURE)) { + ksft_test_result_fail("mlockall(MCL_CURRENT | MCL_FUTURE): %s\n", strerror(errno)); + return; + } + + map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); + + if (map == MAP_FAILED) { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) + ksft_test_result_skip("%s: MAP_DROPPABLE not supported\n", __func__); + else + ksft_test_result_fail("mmap error: %s\n", strerror(errno)); + munlockall(); + return; + } + + ksft_test_result(!unlock_lock_check(map, false), "%s: droppable memory not locked\n", + __func__); + + munlockall(); munmap(map, 2 * page_size); } @@ -442,7 +506,7 @@ int main(int argc, char **argv) munmap(map, size); - ksft_set_plan(13); + ksft_set_plan(15); test_mlock_lock(); test_mlock_onfault(); @@ -451,6 +515,8 @@ int main(int argc, char **argv) test_lock_onfault_of_present(); test_vma_management(true); test_mlockall(); + test_mlock_droppable(); + test_mlockall_future_droppable(); ksft_finished(); } -- cgit v1.2.3 From 303c6bdfe7cb51658fe632e31ee5a5d526c88435 Mon Sep 17 00:00:00 2001 From: Anthony Yznaga Date: Wed, 15 Apr 2026 20:39:39 -0700 Subject: selftests/mm: run the MAP_DROPPABLE selftest The test was not being run by the selftest framework so it was never noticed that it would fail with an assertion failure on configs without support for MAP_DROPPABLE. Update the test so that it is skipped instead when MAP_DROPPABLE is not supported, and add it to the mmap category so that the test is run by the framework. Link: https://lore.kernel.org/20260416033939.49981-4-anthony.yznaga@oracle.com Signed-off-by: Anthony Yznaga Acked-by: David Hildenbrand (Arm) Cc: Jann Horn Cc: Jason A. Donenfeld Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Pedro Falcato Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Mark Brown Cc: Vlastimil Babka (SUSE) Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/droppable.c | 9 ++++++++- tools/testing/selftests/mm/run_vmtests.sh | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/testing/selftests/mm/droppable.c b/tools/testing/selftests/mm/droppable.c index 44940f75c461..30c8be37fcb9 100644 --- a/tools/testing/selftests/mm/droppable.c +++ b/tools/testing/selftests/mm/droppable.c @@ -26,7 +26,14 @@ int main(int argc, char *argv[]) ksft_set_plan(1); alloc = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_DROPPABLE, -1, 0); - assert(alloc != MAP_FAILED); + if (alloc == MAP_FAILED) { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) { + ksft_test_result_skip("MAP_DROPPABLE not supported\n"); + exit(KSFT_SKIP); + } + ksft_test_result_fail("mmap error: %s\n", strerror(errno)); + exit(KSFT_FAIL); + } memset(alloc, 'A', alloc_size); for (size_t i = 0; i < alloc_size; i += page_size) assert(*(uint8_t *)(alloc + i)); diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index c17b133a81d2..3b61677fe984 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -382,6 +382,7 @@ else fi CATEGORY="mmap" run_test ./map_populate +CATEGORY="mmap" run_test ./droppable CATEGORY="mlock" run_test ./mlock-random-test -- cgit v1.2.3 From 7e8983f317ab0efd13aa573e166d7ad69e36a429 Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Wed, 15 Apr 2026 10:15:09 +0530 Subject: selftests/mm: simplify byte pattern checking in mremap_test The original version of mremap_test (7df666253f26: "kselftests: vm: add mremap tests") validated remapped contents byte-by-byte and printed a mismatc