Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH v6 0/3] selftests/cgroup: fixes for test_zswap on single core VM
@ 2026-09-04 22:45 Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 1/3] selftests/cgroup: test_zswap: wait for cgroup to unpopulate in test_zswap_writeback Wilson Felipe Pereira
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Wilson Felipe Pereira @ 2026-09-04 22:45 UTC (permalink / raw)
  To: Andrew Morton, Johannes Weiner, Yosry Ahmed, Nhat Pham,
	Chengming Zhou, Tejun Heo, Michal Koutný, Shuah Khan
  Cc: linux-mm, cgroups, linux-kselftest, linux-kernel,
	Wilson Felipe Pereira

This series fixes two test failures in test_zswap observed when running on
a single-core VM (-smp 1) with 4GB of RAM.

Patch 1 addresses a race condition in test_zswap_writeback() where
waitpid() returns before the exiting child process is switched away by the
kernel, causing an immediate write of "+memory" to cgroup.subtree_control
to fail with -EBUSY. We fix this by waiting for cgroup.events to report
"populated 0".

Patch 2 standardizes cgroup key-value stat and event parsers on 64-bit
types by replacing cg_read_key_long() with cg_read_key_s64() across the
entire cgroup selftest suite, avoiding 32-bit integer overflows on systems
with large memory or high counter values.

Patch 3 fixes an implicit unsigned conversion bug in test_no_kmem_bypass()
where small negative timing differences between debugfs stored_pages and
cgroup zswapped bytes caused the comparison to falsely fail due to
unsigned promotion.

v5 -> v6:
- Now it is a 3-patch series.
- Patch 1: wait for cgroup to unpopulate in test_zswap_writeback
  (unchanged, retains Michal's Acked-by).
- Patch 2: convert cg_read_key_*() helpers to s64, define s64 as
  signed long long in cgroup_util.h, update poll/reporting helpers
  (values_close, values_close_report with llabs), helper functions,
  and all callers across cgroup selftests to avoid 32-bit
  overflow/truncation (suggested by Michal Koutný).
  - signed long long for s64 was based on powerpc/include/utils.h
- Patch 3: test_zswap: fix implicit unsigned promotion bug in
  test_no_kmem_bypass, updated to use s64 for delta and max_delta.
- Verify warning-free builds on both 64-bit and 32-bit (gcc -m32).

v4 -> v5:
- Patch 2: Introduce cg_read_key_long_long() to return a long long
  value, and have cg_read_key_long use it and convert to long.
- Patch 2: Make zswapped a long long. This prevents an overflow on
  32-bit platforms (Andrew Morton, Sashiko).

v3 -> v4:
- Patch 2: Use long long for delta and max_delta and explicitly cast
  stored_pages to avoid integer overflow and implicit unsigned promotion
  warnings on 32-bit platforms (Andrew Morton, Sashiko).

v2 -> v3:
- Patch 1: Collect Acked-by from Michal Koutný.
- Patch 2: Explicitly cast stored_pages to (long) in delta calculation and
  inline the return comparison (Michal Koutný).

v1 -> v2:
- Patch 1: Replace EBUSY retry loop with cg_read_strcmp_wait() waiting for
  cgroup.events "populated 0" (Michal Koutný).
- Patch 1: Clarify task lifecycle in commit description (Yosry Ahmed).
- Patch 2: Remove abs() and declare delta/zswapped as signed longs with a
  signed threshold comparison (Michal Koutný).
- Patch 2: Add Fixes tag (Michal Koutný).

v5: https://lore.kernel.org/all/20260901052250.154532-1-wfelipe@google.com/
v4: https://lore.kernel.org/all/20260828033741.2184560-1-wfelipe@google.com/
v3: https://lore.kernel.org/all/20260827034807.2822234-1-wfelipe@google.com/
v2: https://lore.kernel.org/all/20260824033533.2147900-1-wfelipe@google.com/
v1: https://lore.kernel.org/all/20260804042053.56940-1-wfelipe@google.com/

Wilson Felipe Pereira (3):
  selftests/cgroup: test_zswap: wait for cgroup to unpopulate in
    test_zswap_writeback
  selftests/cgroup: convert cg_read_key_*() helpers to s64
  selftests/cgroup: test_zswap: fix implicit unsigned promotion bug in
    test_no_kmem_bypass

 .../selftests/cgroup/lib/cgroup_util.c        | 14 ++--
 .../cgroup/lib/include/cgroup_util.h          | 22 +++---
 tools/testing/selftests/cgroup/test_cpu.c     | 47 ++++++-------
 tools/testing/selftests/cgroup/test_freezer.c | 16 ++---
 tools/testing/selftests/cgroup/test_kmem.c    | 56 ++++++++-------
 .../selftests/cgroup/test_memcontrol.c        | 69 ++++++++++---------
 tools/testing/selftests/cgroup/test_pids.c    |  5 +-
 tools/testing/selftests/cgroup/test_zswap.c   | 63 +++++++++--------
 8 files changed, 154 insertions(+), 138 deletions(-)

--
2.55.0.979.g7e5102b832-goog

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v6 1/3] selftests/cgroup: test_zswap: wait for cgroup to unpopulate in test_zswap_writeback
  2026-09-04 22:45 [PATCH v6 0/3] selftests/cgroup: fixes for test_zswap on single core VM Wilson Felipe Pereira
@ 2026-09-04 22:45 ` Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 2/3] selftests/cgroup: convert cg_read_key_*() helpers to s64 Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 3/3] selftests/cgroup: test_zswap: fix implicit unsigned promotion bug in test_no_kmem_bypass Wilson Felipe Pereira
  2 siblings, 0 replies; 4+ messages in thread
From: Wilson Felipe Pereira @ 2026-09-04 22:45 UTC (permalink / raw)
  To: Andrew Morton, Johannes Weiner, Yosry Ahmed, Nhat Pham,
	Chengming Zhou, Tejun Heo, Michal Koutný, Shuah Khan
  Cc: linux-mm, cgroups, linux-kselftest, linux-kernel,
	Wilson Felipe Pereira

When running test_zswap on a single-core VM (-smp 1) with 4GB of RAM,
test_zswap_writeback intermittently fails on the initial run after boot.

In test_zswap_writeback(), after waitpid() reaps the child process created
by test_zswap_writeback_one(), writing "+memory" to cgroup.subtree_control
can fail with -EBUSY. Under cgroup v2, enabling domain subtree controllers
is forbidden while any tasks remain in cgroup.procs.

When a child process exits, exit_notify() wakes the parent process,
allowing waitpid() to return immediately. However, the cgroup populated
task count (nr_populated_csets) is only decremented when the exiting
task is switched away via finish_task_switch() -> cgroup_task_dead(). On
single-core systems, the parent runs before the dead child has been
switched out, causing "+memory" to fail with -EBUSY if written immediately
after waitpid() returns.

Fix this by waiting for cgroup.events to report "populated 0\n" via
cg_read_strcmp_wait() before enabling subtree control.

Signed-off-by: Wilson Felipe Pereira <wfelipe@google.com>
Acked-by: Michal Koutný <mkoutny@suse.com>
---
 tools/testing/selftests/cgroup/test_zswap.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 609c48f38524..8f2c9aa4776c 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -408,6 +408,8 @@ static int test_zswap_writeback(const char *root, bool wb)
 	 * Thus, the parent's setting shall be what's in effect. */
 	if (cg_write(test_group, "memory.zswap.max", "max"))
 		goto out;
+	if (cg_read_strcmp_wait(test_group, "cgroup.events", "populated 0\n"))
+		goto out;
 	if (cg_write(test_group, "cgroup.subtree_control", "+memory"))
 		goto out;
 
-- 
2.55.0.979.g7e5102b832-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v6 2/3] selftests/cgroup: convert cg_read_key_*() helpers to s64
  2026-09-04 22:45 [PATCH v6 0/3] selftests/cgroup: fixes for test_zswap on single core VM Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 1/3] selftests/cgroup: test_zswap: wait for cgroup to unpopulate in test_zswap_writeback Wilson Felipe Pereira
@ 2026-09-04 22:45 ` Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 3/3] selftests/cgroup: test_zswap: fix implicit unsigned promotion bug in test_no_kmem_bypass Wilson Felipe Pereira
  2 siblings, 0 replies; 4+ messages in thread
From: Wilson Felipe Pereira @ 2026-09-04 22:45 UTC (permalink / raw)
  To: Andrew Morton, Johannes Weiner, Yosry Ahmed, Nhat Pham,
	Chengming Zhou, Tejun Heo, Michal Koutný, Shuah Khan
  Cc: linux-mm, cgroups, linux-kselftest, linux-kernel,
	Wilson Felipe Pereira

In the kernel, cgroup statistics and event counters in multi-key control
files (such as memory.stat, cpu.stat, and memory.events) are internally
stored and exported as 64-bit values.

On 32-bit platforms, cg_read_key_long() relies on atol(), which returns a
32-bit signed long. When values in memory.stat or event counters exceed
2.14GB (such as on 32-bit systems with large memory), the parsed value
overflows and wraps into negative numbers, leading to subtle bugs and
false test failures.

Standardize on explicitly-sized 64-bit helpers and types across the
cgroup selftests:

- Define s64 in cgroup_util.h as signed long long.
- Convert cg_read_key_long() to cg_read_key_s64() using atoll().
- Convert cg_read_key_long_poll() to cg_read_key_s64_poll(), updating
  both the return type and the expected argument to s64.
- Update values_close() and values_close_report() to operate on s64 and
  use llabs() to prevent downcasting/truncation of 64-bit differences.
- Migrate helper functions (get_cg_wb_count(), get_zswpout(),
  get_zswap_incomp(), wait_for_writeback(), and cg_check_freezetime())
  and their callers to s64.
- Migrate all callers across test_cpu, test_kmem, test_memcontrol,
  test_pids, test_freezer, and test_zswap to cg_read_key_s64().
- Update corresponding printf formats to %lld and difference checks to
  llabs() for s64.

Suggested-by: Michal Koutný <mkoutny@suse.com>
Signed-off-by: Wilson Felipe Pereira <wfelipe@google.com>
---
 .../selftests/cgroup/lib/cgroup_util.c        | 14 ++--
 .../cgroup/lib/include/cgroup_util.h          | 22 +++---
 tools/testing/selftests/cgroup/test_cpu.c     | 47 ++++++-------
 tools/testing/selftests/cgroup/test_freezer.c | 16 ++---
 tools/testing/selftests/cgroup/test_kmem.c    | 56 ++++++++-------
 .../selftests/cgroup/test_memcontrol.c        | 69 ++++++++++---------
 tools/testing/selftests/cgroup/test_pids.c    |  5 +-
 tools/testing/selftests/cgroup/test_zswap.c   | 54 ++++++++-------
 8 files changed, 148 insertions(+), 135 deletions(-)

diff --git a/tools/testing/selftests/cgroup/lib/cgroup_util.c b/tools/testing/selftests/cgroup/lib/cgroup_util.c
index 2596c12cd864..ed3c4893833a 100644
--- a/tools/testing/selftests/cgroup/lib/cgroup_util.c
+++ b/tools/testing/selftests/cgroup/lib/cgroup_util.c
@@ -172,7 +172,7 @@ long cg_read_long_fd(int fd)
 	return atol(buf);
 }
 
-long cg_read_key_long(const char *cgroup, const char *control, const char *key)
+s64 cg_read_key_s64(const char *cgroup, const char *control, const char *key)
 {
 	char buf[BUF_SIZE];
 	char *ptr;
@@ -184,18 +184,18 @@ long cg_read_key_long(const char *cgroup, const char *control, const char *key)
 	if (!ptr)
 		return -1;
 
-	return atol(ptr + strlen(key));
+	return atoll(ptr + strlen(key));
 }
 
-long cg_read_key_long_poll(const char *cgroup, const char *control,
-			   const char *key, long expected, int retries,
-			   useconds_t wait_interval_us)
+s64 cg_read_key_s64_poll(const char *cgroup, const char *control,
+			 const char *key, s64 expected, int retries,
+			 useconds_t wait_interval_us)
 {
-	long val = -1;
+	s64 val = -1;
 	int i;
 
 	for (i = 0; i < retries; i++) {
-		val = cg_read_key_long(cgroup, control, key);
+		val = cg_read_key_s64(cgroup, control, key);
 		if (val < 0)
 			return val;
 
diff --git a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
index 5d39c709ac7a..ddb05932d9c7 100644
--- a/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
+++ b/tools/testing/selftests/cgroup/lib/include/cgroup_util.h
@@ -2,6 +2,8 @@
 #include <stdbool.h>
 #include <stdlib.h>
 
+typedef signed long long s64;
+
 #ifndef BUF_SIZE
 #define BUF_SIZE 4096
 #endif
@@ -24,25 +26,25 @@
 /*
  * Checks if two given values differ by less than err% of their sum.
  */
-static inline int values_close(long a, long b, int err)
+static inline int values_close(s64 a, s64 b, int err)
 {
-	return labs(a - b) <= (a + b) / 100 * err;
+	return llabs(a - b) <= (a + b) / 100 * err;
 }
 
 /*
  * Checks if two given values differ by less than err% of their sum and assert
  * with detailed debug info if not.
  */
-static inline int values_close_report(long a, long b, int err)
+static inline int values_close_report(s64 a, s64 b, int err)
 {
-	long diff  = labs(a - b);
-	long limit = (a + b) / 100 * err;
+	s64 diff  = llabs(a - b);
+	s64 limit = (a + b) / 100 * err;
 	double actual_err = (a + b) ? (100.0 * diff / (a + b)) : 0.0;
 	int close = diff <= limit;
 
 	if (!close)
 		fprintf(stderr,
-			"[FAIL] actual=%ld expected=%ld | diff=%ld | limit=%ld | "
+			"[FAIL] actual=%lld expected=%lld | diff=%lld | limit=%lld | "
 			"tolerance=%d%% | actual_error=%.2f%%\n",
 			a, b, diff, limit, err, actual_err);
 
@@ -69,10 +71,10 @@ extern int cg_read_strstr(const char *cgroup, const char *control,
 			  const char *needle);
 extern long cg_read_long(const char *cgroup, const char *control);
 extern long cg_read_long_fd(int fd);
-long cg_read_key_long(const char *cgroup, const char *control, const char *key);
-long cg_read_key_long_poll(const char *cgroup, const char *control,
-			   const char *key, long expected, int retries,
-			   useconds_t wait_interval_us);
+s64 cg_read_key_s64(const char *cgroup, const char *control, const char *key);
+s64 cg_read_key_s64_poll(const char *cgroup, const char *control,
+			 const char *key, s64 expected, int retries,
+			 useconds_t wait_interval_us);
 extern long cg_read_lc(const char *cgroup, const char *control);
 extern int cg_write(const char *cgroup, const char *control, char *buf);
 extern int cg_open(const char *cgroup, const char *control, int flags);
diff --git a/tools/testing/selftests/cgroup/test_cpu.c b/tools/testing/selftests/cgroup/test_cpu.c
index 735a53bb222b..11da1de9cdeb 100644
--- a/tools/testing/selftests/cgroup/test_cpu.c
+++ b/tools/testing/selftests/cgroup/test_cpu.c
@@ -24,7 +24,7 @@ enum hog_clock_type {
 struct cpu_hogger {
 	char *cgroup;
 	pid_t pid;
-	long usage;
+	s64 usage;
 };
 
 struct cpu_hog_func_param {
@@ -185,7 +185,7 @@ static int hog_cpus_timed(const char *cgroup, void *arg)
 static int test_cpucg_stats(const char *root)
 {
 	int ret = KSFT_FAIL;
-	long usage_usec, user_usec, system_usec;
+	s64 usage_usec, user_usec, system_usec;
 	long usage_seconds = 2;
 	long expected_usage_usec = usage_seconds * USEC_PER_SEC;
 	char *cpucg;
@@ -197,9 +197,9 @@ static int test_cpucg_stats(const char *root)
 	if (cg_create(cpucg))
 		goto cleanup;
 
-	usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec");
-	user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec");
-	system_usec = cg_read_key_long(cpucg, "cpu.stat", "system_usec");
+	usage_usec = cg_read_key_s64(cpucg, "cpu.stat", "usage_usec");
+	user_usec = cg_read_key_s64(cpucg, "cpu.stat", "user_usec");
+	system_usec = cg_read_key_s64(cpucg, "cpu.stat", "system_usec");
 	if (usage_usec != 0 || user_usec != 0 || system_usec != 0)
 		goto cleanup;
 
@@ -214,8 +214,8 @@ static int test_cpucg_stats(const char *root)
 	if (cg_run(cpucg, hog_cpus_timed, (void *)&param))
 		goto cleanup;
 
-	usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec");
-	user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec");
+	usage_usec = cg_read_key_s64(cpucg, "cpu.stat", "usage_usec");
+	user_usec = cg_read_key_s64(cpucg, "cpu.stat", "user_usec");
 	if (user_usec <= 0)
 		goto cleanup;
 
@@ -239,7 +239,7 @@ static int test_cpucg_nice(const char *root)
 {
 	int ret = KSFT_FAIL;
 	int status;
-	long user_usec, nice_usec;
+	s64 user_usec, nice_usec;
 	long usage_seconds = 2;
 	long expected_nice_usec = usage_seconds * USEC_PER_SEC;
 	char *cpucg;
@@ -252,8 +252,8 @@ static int test_cpucg_nice(const char *root)
 	if (cg_create(cpucg))
 		goto cleanup;
 
-	user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec");
-	nice_usec = cg_read_key_long(cpucg, "cpu.stat", "nice_usec");
+	user_usec = cg_read_key_s64(cpucg, "cpu.stat", "user_usec");
+	nice_usec = cg_read_key_s64(cpucg, "cpu.stat", "nice_usec");
 	if (nice_usec == -1)
 		ret = KSFT_SKIP;
 	if (user_usec != 0 || nice_usec != 0)
@@ -289,8 +289,8 @@ static int test_cpucg_nice(const char *root)
 		if (!WIFEXITED(status))
 			goto cleanup;
 
-		user_usec = cg_read_key_long(cpucg, "cpu.stat", "user_usec");
-		nice_usec = cg_read_key_long(cpucg, "cpu.stat", "nice_usec");
+		user_usec = cg_read_key_s64(cpucg, "cpu.stat", "user_usec");
+		nice_usec = cg_read_key_s64(cpucg, "cpu.stat", "nice_usec");
 		if (user_usec <= 0)
 			goto cleanup;
 		if (!values_close_report(nice_usec, expected_nice_usec, 1))
@@ -357,8 +357,8 @@ run_cpucg_weight_test(
 	}
 
 	for (i = 0; i < ARRAY_SIZE(children); i++)
-		children[i].usage = cg_read_key_long(children[i].cgroup,
-				"cpu.stat", "usage_usec");
+		children[i].usage = cg_read_key_s64(children[i].cgroup,
+						    "cpu.stat", "usage_usec");
 
 	if (validate(children, ARRAY_SIZE(children)))
 		goto cleanup;
@@ -486,7 +486,7 @@ run_cpucg_nested_weight_test(const char *root, bool overprovisioned)
 	int ret = KSFT_FAIL, i;
 	char *parent = NULL, *child = NULL;
 	struct cpu_hogger leaf[3] = {};
-	long nested_leaf_usage, child_usage;
+	s64 nested_leaf_usage, child_usage;
 	int nprocs = get_nprocs();
 
 	if (!overprovisioned) {
@@ -567,8 +567,8 @@ run_cpucg_nested_weight_test(const char *root, bool overprovisioned)
 	}
 
 	for (i = 0; i < ARRAY_SIZE(leaf); i++) {
-		leaf[i].usage = cg_read_key_long(leaf[i].cgroup,
-				"cpu.stat", "usage_usec");
+		leaf[i].usage = cg_read_key_s64(leaf[i].cgroup, "cpu.stat",
+						"usage_usec");
 		if (leaf[i].usage <= 0)
 			goto cleanup;
 	}
@@ -580,8 +580,7 @@ run_cpucg_nested_weight_test(const char *root, bool overprovisioned)
 	} else if (!values_close_report(leaf[0].usage * 2, nested_leaf_usage, 15))
 		goto cleanup;
 
-
-	child_usage = cg_read_key_long(child, "cpu.stat", "usage_usec");
+	child_usage = cg_read_key_s64(child, "cpu.stat", "usage_usec");
 	if (child_usage <= 0)
 		goto cleanup;
 	if (!values_close_report(child_usage, nested_leaf_usage, 1))
@@ -679,7 +678,8 @@ static int test_cpucg_max(const char *root)
 	long duration_seconds = 1;
 
 	long duration_usec;
-	long usage_usec, n_periods, remainder_usec, expected_usage_usec;
+	long n_periods, remainder_usec, expected_usage_usec;
+	s64 usage_usec;
 	char *cpucg;
 	char quota_buf[32];
 
@@ -708,7 +708,7 @@ static int test_cpucg_max(const char *root)
 	if (cg_run(cpucg, hog_cpus_timed, (void *)&param))
 		goto cleanup;
 
-	usage_usec = cg_read_key_long(cpucg, "cpu.stat", "usage_usec");
+	usage_usec = cg_read_key_s64(cpucg, "cpu.stat", "usage_usec");
 	if (usage_usec <= 0)
 		goto cleanup;
 
@@ -746,7 +746,8 @@ static int test_cpucg_max_nested(const char *root)
 	long duration_seconds = 1;
 
 	long duration_usec;
-	long usage_usec, n_periods, remainder_usec, expected_usage_usec;
+	long n_periods, remainder_usec, expected_usage_usec;
+	s64 usage_usec;
 	char *parent, *child;
 	char quota_buf[32];
 
@@ -782,7 +783,7 @@ static int test_cpucg_max_nested(const char *root)
 	if (cg_run(child, hog_cpus_timed, (void *)&param))
 		goto cleanup;
 
-	usage_usec = cg_read_key_long(child, "cpu.stat", "usage_usec");
+	usage_usec = cg_read_key_s64(child, "cpu.stat", "usage_usec");
 	if (usage_usec <= 0)
 		goto cleanup;
 
diff --git a/tools/testing/selftests/cgroup/test_freezer.c b/tools/testing/selftests/cgroup/test_freezer.c
index f28bb02e9783..f4f082c13d4b 100644
--- a/tools/testing/selftests/cgroup/test_freezer.c
+++ b/tools/testing/selftests/cgroup/test_freezer.c
@@ -807,10 +807,9 @@ static int test_cgfreezer_vfork(const char *root)
 /*
  * Get the current frozen_usec for the cgroup.
  */
-static long cg_check_freezetime(const char *cgroup)
+static s64 cg_check_freezetime(const char *cgroup)
 {
-	return cg_read_key_long(cgroup, "cgroup.stat.local",
-				"frozen_usec ");
+	return cg_read_key_s64(cgroup, "cgroup.stat.local", "frozen_usec ");
 }
 
 /*
@@ -820,7 +819,7 @@ static int test_cgfreezer_time_empty(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *cgroup = NULL;
-	long prev, curr;
+	s64 prev, curr;
 
 	cgroup = cg_name(root, "cg_time_test_empty");
 	if (!cgroup)
@@ -839,7 +838,7 @@ static int test_cgfreezer_time_empty(const char *root)
 		goto cleanup;
 	}
 	if (curr > 0) {
-		debug("Expect time (%ld) to be 0\n", curr);
+		debug("Expect time (%lld) to be 0\n", curr);
 		goto cleanup;
 	}
 
@@ -853,8 +852,7 @@ static int test_cgfreezer_time_empty(const char *root)
 	usleep(1000);
 	curr = cg_check_freezetime(cgroup);
 	if (curr < 1000) {
-		debug("Expect time (%ld) to be at least 1000 us\n",
-		      curr);
+		debug("Expect time (%lld) to be at least 1000 us\n", curr);
 		goto cleanup;
 	}
 
@@ -867,7 +865,7 @@ static int test_cgfreezer_time_empty(const char *root)
 	prev = curr;
 	curr = cg_check_freezetime(cgroup);
 	if (curr <= prev) {
-		debug("Expect time (%ld) to be more than previous check (%ld)\n",
+		debug("Expect time (%lld) to be more than previous check (%lld)\n",
 		      curr, prev);
 		goto cleanup;
 	}
@@ -879,7 +877,7 @@ static int test_cgfreezer_time_empty(const char *root)
 	prev = curr;
 	curr = cg_check_freezetime(cgroup);
 	if (curr != prev) {
-		debug("Expect time (%ld) to be unchanged from previous check (%ld)\n",
+		debug("Expect time (%lld) to be unchanged from previous check (%lld)\n",
 		      curr, prev);
 		goto cleanup;
 	}
diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c
index 437f2d35f205..249898f3b81e 100644
--- a/tools/testing/selftests/cgroup/test_kmem.c
+++ b/tools/testing/selftests/cgroup/test_kmem.c
@@ -54,7 +54,8 @@ static int test_kmem_basic(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *cg = NULL;
-	long slab0, slab1, current;
+	s64 slab0, slab1;
+	long current;
 
 	cg = cg_name(root, "kmem_basic_test");
 	if (!cg)
@@ -66,7 +67,7 @@ static int test_kmem_basic(const char *root)
 	if (cg_run(cg, alloc_dcache, (void *)100000))
 		goto cleanup;
 
-	slab0 = cg_read_key_long(cg, "memory.stat", "slab ");
+	slab0 = cg_read_key_s64(cg, "memory.stat", "slab ");
 	if (slab0 < (1 << 20))
 		goto cleanup;
 
@@ -75,7 +76,7 @@ static int test_kmem_basic(const char *root)
 	/* wait for RCU freeing */
 	sleep(1);
 
-	slab1 = cg_read_key_long(cg, "memory.stat", "slab ");
+	slab1 = cg_read_key_s64(cg, "memory.stat", "slab ");
 	if (slab1 < 0)
 		goto cleanup;
 
@@ -167,7 +168,8 @@ static int cg_run_in_subcgroups(const char *parent,
  */
 static int test_kmem_memcg_deletion(const char *root)
 {
-	long current, anon, file, kernel, sock, sum;
+	long current;
+	s64 anon, file, kernel, sock, sum;
 	int ret = KSFT_FAIL;
 	char *parent;
 
@@ -185,23 +187,23 @@ static int test_kmem_memcg_deletion(const char *root)
 		goto cleanup;
 
 	current = cg_read_long(parent, "memory.current");
-	anon = cg_read_key_long(parent, "memory.stat", "anon ");
-	file = cg_read_key_long(parent, "memory.stat", "file ");
-	kernel = cg_read_key_long(parent, "memory.stat", "kernel ");
-	sock = cg_read_key_long(parent, "memory.stat", "sock ");
+	anon = cg_read_key_s64(parent, "memory.stat", "anon ");
+	file = cg_read_key_s64(parent, "memory.stat", "file ");
+	kernel = cg_read_key_s64(parent, "memory.stat", "kernel ");
+	sock = cg_read_key_s64(parent, "memory.stat", "sock ");
 	if (current < 0 || anon < 0 || file < 0 || kernel < 0 || sock < 0)
 		goto cleanup;
 
 	sum = anon + file + kernel + sock;
-	if (labs(sum - current) < MAX_VMSTAT_ERROR) {
+	if (llabs(sum - current) < MAX_VMSTAT_ERROR) {
 		ret = KSFT_PASS;
 	} else {
 		printf("memory.current = %ld\n", current);
-		printf("anon + file + kernel + sock = %ld\n", sum);
-		printf("anon = %ld\n", anon);
-		printf("file = %ld\n", file);
-		printf("kernel = %ld\n", kernel);
-		printf("sock = %ld\n", sock);
+		printf("anon + file + kernel + sock = %lld\n", sum);
+		printf("anon = %lld\n", anon);
+		printf("file = %lld\n", file);
+		printf("kernel = %lld\n", kernel);
+		printf("sock = %lld\n", sock);
 	}
 
 cleanup:
@@ -248,7 +250,7 @@ static int spawn_1000_threads(const char *cgroup, void *arg)
 	int nr_threads = 1000;
 	pthread_t *tinfo;
 	unsigned long i;
-	long stack;
+	s64 stack;
 	int ret = -1;
 
 	tinfo = calloc(nr_threads, sizeof(pthread_t));
@@ -263,7 +265,7 @@ static int spawn_1000_threads(const char *cgroup, void *arg)
 		}
 	}
 
-	stack = cg_read_key_long(cgroup, "memory.stat", "kernel_stack ");
+	stack = cg_read_key_s64(cgroup, "memory.stat", "kernel_stack ");
 	if (stack >= 4096 * 1000)
 		ret = 0;
 
@@ -307,7 +309,7 @@ static int test_kmem_dead_cgroups(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *parent;
-	long dead = -1;
+	s64 dead = -1;
 
 	parent = cg_name(root, "kmem_dead_cgroups_test");
 	if (!parent)
@@ -328,9 +330,10 @@ static int test_kmem_dead_cgroups(const char *root)
 	 * from a specific kernel constant, and can be adjusted if reclaim
 	 * behavior changes in the future.
 	 */
-	dead = cg_read_key_long_poll(parent, "cgroup.stat",
-					"nr_dying_descendants ", 0, KMEM_DEAD_WAIT_RETRIES,
-					DEFAULT_WAIT_INTERVAL_US);
+	dead = cg_read_key_s64_poll(parent, "cgroup.stat",
+				    "nr_dying_descendants ", 0,
+				    KMEM_DEAD_WAIT_RETRIES,
+				    DEFAULT_WAIT_INTERVAL_US);
 	if (dead)
 		goto cleanup;
 
@@ -353,7 +356,8 @@ static int test_percpu_basic(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *parent, *child;
-	long current, percpu, slab;
+	long current;
+	s64 percpu, slab;
 	int i;
 
 	parent = cg_name(root, "percpu_basic_test");
@@ -382,15 +386,15 @@ static int test_percpu_basic(const char *root)
 	}
 
 	current = cg_read_long(parent, "memory.current");
-	percpu = cg_read_key_long(parent, "memory.stat", "percpu ");
-	slab = cg_read_key_long(parent, "memory.stat", "slab ");
+	percpu = cg_read_key_s64(parent, "memory.stat", "percpu ");
+	slab = cg_read_key_s64(parent, "memory.stat", "slab ");
 
 	if (current > 0 && percpu > 0 && slab >= 0 &&
-			labs(current - (percpu + slab)) < MAX_VMSTAT_ERROR)
+	    llabs(current - (percpu + slab)) < MAX_VMSTAT_ERROR)
 		ret = KSFT_PASS;
 	else
-		printf("memory.current %ld\npercpu %ld\nslab %ld\ndelta %ld\n",
-			current, percpu, slab, current - (percpu + slab));
+		printf("memory.current %ld\npercpu %lld\nslab %lld\ndelta %lld\n",
+		       current, percpu, slab, current - (percpu + slab));
 
 cleanup_children:
 	for (i = 0; i < 1000; i++) {
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c
index 3a84d068fbf3..539650c1d562 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -192,7 +192,8 @@ static int alloc_anon_50M_check(const char *cgroup, void *arg)
 {
 	size_t size = MB(50);
 	char *buf;
-	long anon, current;
+	s64 anon;
+	long current;
 	int ret = -1;
 
 	buf = alloc_and_populate_anon(size);
@@ -206,7 +207,7 @@ static int alloc_anon_50M_check(const char *cgroup, void *arg)
 	if (!values_close(size, current, 3))
 		goto cleanup;
 
-	anon = cg_read_key_long(cgroup, "memory.stat", "anon ");
+	anon = cg_read_key_s64(cgroup, "memory.stat", "anon ");
 	if (anon < 0)
 		goto cleanup;
 
@@ -223,7 +224,8 @@ static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
 {
 	size_t size = MB(50);
 	int ret = -1;
-	long current, file;
+	long current;
+	s64 file;
 	int fd;
 
 	fd = get_temp_fd();
@@ -237,7 +239,7 @@ static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
 	if (current < size)
 		goto cleanup;
 
-	file = cg_read_key_long(cgroup, "memory.stat", "file ");
+	file = cg_read_key_s64(cgroup, "memory.stat", "file ");
 	if (file < 0)
 		goto cleanup;
 
@@ -623,10 +625,10 @@ static int test_memcg_protection(const char *root, bool min)
 	for (i = 0; i < ARRAY_SIZE(children); i++) {
 		int ignore_low_events_index = has_recursiveprot ? 2 : -1;
 		int no_low_events_index = 1;
-		long low, oom;
+		s64 low, oom;
 
-		oom = cg_read_key_long(children[i], "memory.events", "oom ");
-		low = cg_read_key_long(children[i], "memory.events", "low ");
+		oom = cg_read_key_s64(children[i], "memory.events", "oom ");
+		low = cg_read_key_s64(children[i], "memory.events", "low ");
 
 		if (oom)
 			goto cleanup;
@@ -711,7 +713,7 @@ static int test_memcg_high(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *memcg;
-	long high;
+	s64 high;
 
 	memcg = cg_name(root, "memcg_test");
 	if (!memcg)
@@ -738,7 +740,7 @@ static int test_memcg_high(const char *root)
 	if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
 		goto cleanup;
 
-	high = cg_read_key_long(memcg, "memory.events", "high ");
+	high = cg_read_key_s64(memcg, "memory.events", "high ");
 	if (high <= 0)
 		goto cleanup;
 
@@ -774,8 +776,8 @@ static int test_memcg_high_sync(const char *root)
 {
 	int ret = KSFT_FAIL, pid, fd = -1;
 	char *memcg;
-	long pre_high, pre_max;
-	long post_high, post_max;
+	s64 pre_high, pre_max;
+	s64 post_high, post_max;
 
 	memcg = cg_name(root, "memcg_test");
 	if (!memcg)
@@ -784,8 +786,8 @@ static int test_memcg_high_sync(const char *root)
 	if (cg_create(memcg))
 		goto cleanup;
 
-	pre_high = cg_read_key_long(memcg, "memory.events", "high ");
-	pre_max = cg_read_key_long(memcg, "memory.events", "max ");
+	pre_high = cg_read_key_s64(memcg, "memory.events", "high ");
+	pre_max = cg_read_key_s64(memcg, "memory.events", "max ");
 	if (pre_high < 0 || pre_max < 0)
 		goto cleanup;
 
@@ -808,8 +810,8 @@ static int test_memcg_high_sync(const char *root)
 
 	cg_wait_for(fd);
 
-	post_high = cg_read_key_long(memcg, "memory.events", "high ");
-	post_max = cg_read_key_long(memcg, "memory.events", "max ");
+	post_high = cg_read_key_s64(memcg, "memory.events", "high ");
+	post_max = cg_read_key_s64(memcg, "memory.events", "max ");
 	if (post_high < 0 || post_max < 0)
 		goto cleanup;
 
@@ -836,7 +838,8 @@ static int test_memcg_max(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *memcg;
-	long current, max;
+	long current;
+	s64 max;
 
 	memcg = cg_name(root, "memcg_test");
 	if (!memcg)
@@ -865,7 +868,7 @@ static int test_memcg_max(const char *root)
 	if (current > MB(30) || !current)
 		goto cleanup;
 
-	max = cg_read_key_long(memcg, "memory.events", "max ");
+	max = cg_read_key_s64(memcg, "memory.events", "max ");
 	if (max <= 0)
 		goto cleanup;
 
@@ -1029,7 +1032,8 @@ static int test_memcg_swap_max_peak(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *memcg;
-	long max, peak;
+	s64 max;
+	long peak;
 	struct stat ss;
 	int swap_peak_fd = -1, mem_peak_fd = -1;
 
@@ -1122,10 +1126,10 @@ static int test_memcg_swap_max_peak(const char *root)
 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
 		goto cleanup;
 
-	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
+	if (cg_read_key_s64(memcg, "memory.events", "oom ") != 1)
 		goto cleanup;
 
-	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
+	if (cg_read_key_s64(memcg, "memory.events", "oom_kill ") != 1)
 		goto cleanup;
 
 	peak = cg_read_long(memcg, "memory.peak");
@@ -1186,7 +1190,7 @@ static int test_memcg_swap_max_peak(const char *root)
 	if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30)))
 		goto cleanup;
 
-	max = cg_read_key_long(memcg, "memory.events", "max ");
+	max = cg_read_key_s64(memcg, "memory.events", "max ");
 	if (max <= 0)
 		goto cleanup;
 
@@ -1248,10 +1252,10 @@ static int test_memcg_oom_events(const char *root)
 	if (cg_read_strcmp(memcg, "cgroup.procs", ""))
 		goto cleanup;
 
-	if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
+	if (cg_read_key_s64(memcg, "memory.events", "oom ") != 1)
 		goto cleanup;
 
-	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
+	if (cg_read_key_s64(memcg, "memory.events", "oom_kill ") != 1)
 		goto cleanup;
 
 	ret = KSFT_PASS;
@@ -1354,13 +1358,14 @@ static int tcp_client(const char *cgroup, unsigned short port)
 	ret = KSFT_FAIL;
 	while (retries--) {
 		uint8_t buf[0x100000];
-		long current, sock;
+		long current;
+		s64 sock;
 
 		if (read(sk, buf, sizeof(buf)) <= 0)
 			goto close_sk;
 
 		current = cg_read_long(cgroup, "memory.current");
-		sock = cg_read_key_long(cgroup, "memory.stat", "sock ");
+		sock = cg_read_key_s64(cgroup, "memory.stat", "sock ");
 
 		if (current < 0 || sock < 0)
 			goto close_sk;
@@ -1392,7 +1397,7 @@ static int test_memcg_sock(const char *root)
 	int bind_retries = 5, ret = KSFT_FAIL, pid, err;
 	unsigned short port;
 	char *memcg;
-	long sock_post = -1;
+	s64 sock_post = -1;
 
 	memcg = cg_name(root, "memcg_test");
 	if (!memcg)
@@ -1459,7 +1464,7 @@ static int test_memcg_sock(const char *root)
 	 * scheduling slack) and require that the "sock " counter
 	 * eventually drops to zero.
 	 */
-	sock_post = cg_read_key_long_poll(memcg, "memory.stat", "sock ", 0,
+	sock_post = cg_read_key_s64_poll(memcg, "memory.stat", "sock ", 0,
 					 MEMCG_SOCKSTAT_WAIT_RETRIES,
 					 DEFAULT_WAIT_INTERVAL_US);
 	if (sock_post)
@@ -1484,7 +1489,7 @@ static int test_memcg_oom_group_leaf_events(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *parent, *child;
-	long parent_oom_events;
+	s64 parent_oom_events;
 
 	parent = cg_name(root, "memcg_test_0");
 	child = cg_name(root, "memcg_test_0/memcg_test_1");
@@ -1519,11 +1524,11 @@ static int test_memcg_oom_group_leaf_events(const char *root)
 	if (cg_test_proc_killed(child))
 		goto cleanup;
 
-	if (cg_read_key_long(child, "memory.events", "oom_kill ") <= 0)
+	if (cg_read_key_s64(child, "memory.events", "oom_kill ") <= 0)
 		goto cleanup;
 
-	parent_oom_events = cg_read_key_long(
-			parent, "memory.events", "oom_kill ");
+	parent_oom_events =
+		cg_read_key_s64(parent, "memory.events", "oom_kill ");
 	/*
 	 * If memory_localevents is not enabled (the default), the parent should
 	 * count OOM events in its children groups. Otherwise, it should not
@@ -1639,7 +1644,7 @@ static int test_memcg_oom_group_score_events(const char *root)
 	if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
 		goto cleanup;
 
-	if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 3)
+	if (cg_read_key_s64(memcg, "memory.events", "oom_kill ") != 3)
 		goto cleanup;
 
 	if (kill(safe_pid, SIGKILL))
diff --git a/tools/testing/selftests/cgroup/test_pids.c b/tools/testing/selftests/cgroup/test_pids.c
index 710109b53dfe..f4d5bc9f5f24 100644
--- a/tools/testing/selftests/cgroup/test_pids.c
+++ b/tools/testing/selftests/cgroup/test_pids.c
@@ -111,12 +111,11 @@ static int test_pids_events(const char *root)
 	if (kill(pid, SIGINT))
 		goto cleanup;
 
-	if (cg_read_key_long(cg_child, "pids.events", "max ") != 0)
+	if (cg_read_key_s64(cg_child, "pids.events", "max ") != 0)
 		goto cleanup;
-	if (cg_read_key_long(cg_parent, "pids.events", "max ") != 1)
+	if (cg_read_key_s64(cg_parent, "pids.events", "max ") != 1)
 		goto cleanup;
 
-
 	ret = KSFT_PASS;
 
 cleanup:
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index 8f2c9aa4776c..f4ee440b3c66 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -59,14 +59,14 @@ static int get_zswap_stored_pages(size_t *value)
 	return read_int(PATH_ZSWAP_STORED_PAGES, value);
 }
 
-static long get_cg_wb_count(const char *cg)
+static s64 get_cg_wb_count(const char *cg)
 {
-	return cg_read_key_long(cg, "memory.stat", "zswpwb");
+	return cg_read_key_s64(cg, "memory.stat", "zswpwb");
 }
 
-static long get_zswpout(const char *cgroup)
+static s64 get_zswpout(const char *cgroup)
 {
-	return cg_read_key_long(cgroup, "memory.stat", "zswpout ");
+	return cg_read_key_s64(cgroup, "memory.stat", "zswpout ");
 }
 
 static int allocate_and_read_bytes(const char *cgroup, void *arg)
@@ -125,9 +125,10 @@ static char *setup_test_group_1M(const char *root, const char *name)
  * 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)
+static s64 wait_for_writeback(const char *cg, int timeout_ms)
 {
-	long elapsed, count;
+	long elapsed;
+	s64 count;
 	for (elapsed = 0; elapsed < timeout_ms; elapsed += 100) {
 		count = get_cg_wb_count(cg);
 
@@ -147,7 +148,7 @@ static long wait_for_writeback(const char *cg, int timeout_ms)
  */
 static int test_zswap_usage(const char *root)
 {
-	long zswpout_before, zswpout_after;
+	s64 zswpout_before, zswpout_after;
 	int ret = KSFT_FAIL;
 	char *test_group;
 
@@ -191,7 +192,8 @@ 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;
+	long swap_peak, min_swap;
+	s64 zswpout;
 	size_t allocation_size = page_size * 512;
 
 	min_swap = allocation_size / 4;
@@ -248,7 +250,7 @@ static int test_zswapin(const char *root)
 {
 	int ret = KSFT_FAIL;
 	char *test_group;
-	long zswpin;
+	s64 zswpin;
 
 	test_group = cg_name(root, "zswapin_test");
 	if (!test_group)
@@ -264,7 +266,7 @@ static int test_zswapin(const char *root)
 	if (cg_run(test_group, allocate_and_read_bytes, (void *)MB(32)))
 		goto out;
 
-	zswpin = cg_read_key_long(test_group, "memory.stat", "zswpin ");
+	zswpin = cg_read_key_s64(test_group, "memory.stat", "zswpin ");
 	if (zswpin < 0) {
 		ksft_print_msg("failed to get zswpin\n");
 		goto out;
@@ -355,11 +357,11 @@ static int attempt_writeback(const char *cgroup, void *arg)
 
 static int test_zswap_writeback_one(const char *cgroup, bool wb)
 {
-	long zswpwb_before, zswpwb_after;
+	s64 zswpwb_before, zswpwb_after;
 
 	zswpwb_before = get_cg_wb_count(cgroup);
 	if (zswpwb_before != 0) {
-		ksft_print_msg("zswpwb_before = %ld instead of 0\n", zswpwb_before);
+		ksft_print_msg("zswpwb_before = %lld instead of 0\n", zswpwb_before);
 		return -1;
 	}
 
@@ -375,7 +377,7 @@ static int test_zswap_writeback_one(const char *cgroup, bool wb)
 		return -1;
 
 	if (wb != !!zswpwb_after) {
-		ksft_print_msg("zswpwb_after is %ld while wb is %s\n",
+		ksft_print_msg("zswpwb_after is %lld while wb is %s\n",
 				zswpwb_after, wb ? "enabled" : "disabled");
 		return -1;
 	}
@@ -487,7 +489,7 @@ static int test_no_invasive_cgroup_shrink(const char *root)
 		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)
+	if (cg_read_key_s64(zw_group, "memory.stat", "zswapped") < 1)
 		goto out;
 
 	/* Push wb_group memory into zswap with hard-to-compress data to trigger wb */
@@ -630,7 +632,7 @@ static int test_no_kmem_bypass(const char *root)
 			break;
 		/* 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 ");
+			s64 zswapped = cg_read_key_s64(test_group, "memory.stat", "zswapped ");
 			int delta = stored_pages * page_size - zswapped;
 			int result_ok = delta < stored_pages * page_size / 4;
 
@@ -703,9 +705,9 @@ static int allocate_random_and_wait(const char *cgroup, void *arg)
 	return 0;
 }
 
-static long get_zswap_incomp(const char *cgroup)
+static s64 get_zswap_incomp(const char *cgroup)
 {
-	return cg_read_key_long(cgroup, "memory.stat", "zswap_incomp ");
+	return cg_read_key_s64(cgroup, "memory.stat", "zswap_incomp ");
 }
 
 /*
@@ -721,7 +723,7 @@ static int test_zswap_incompressible(const char *root)
 	int ret = KSFT_FAIL;
 	struct incomp_child_args *values;
 	char *test_group;
-	long zswap_incomp;
+	s64 zswap_incomp;
 	pid_t child_pid;
 	int child_status;
 	char buf;
@@ -757,13 +759,15 @@ static int test_zswap_incompressible(const char *root)
 
 	zswap_incomp = get_zswap_incomp(test_group);
 	if (zswap_incomp <= 0) {
-		long zswpout = get_zswpout(test_group);
-		long zswapped = cg_read_key_long(test_group, "memory.stat", "zswapped ");
-		long zswap_b = cg_read_key_long(test_group, "memory.stat", "zswap ");
-
-		ksft_print_msg("zswap_incomp not increased: %ld\n", zswap_incomp);
-		ksft_print_msg("debug: zswpout=%ld zswapped=%ld zswap_b=%ld\n",
-			       zswpout, zswapped, zswap_b);
+		s64 zswpout = get_zswpout(test_group);
+		s64 zswapped =
+			cg_read_key_s64(test_group, "memory.stat", "zswapped ");
+		s64 zswap_b =
+			cg_read_key_s64(test_group, "memory.stat", "zswap ");
+
+		ksft_print_msg("zswap_incomp not increased: %lld\n", zswap_incomp);
+		ksft_print_msg("debug: zswpout=%lld zswapped=%lld zswap_b=%lld\n",
+				zswpout, zswapped, zswap_b);
 		ksft_print_msg("debug: madvise ret=%d errno=%d\n",
 			       values->madvise_ret, values->madvise_errno);
 		goto out_kill;
-- 
2.55.0.979.g7e5102b832-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v6 3/3] selftests/cgroup: test_zswap: fix implicit unsigned promotion bug in test_no_kmem_bypass
  2026-09-04 22:45 [PATCH v6 0/3] selftests/cgroup: fixes for test_zswap on single core VM Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 1/3] selftests/cgroup: test_zswap: wait for cgroup to unpopulate in test_zswap_writeback Wilson Felipe Pereira
  2026-09-04 22:45 ` [PATCH v6 2/3] selftests/cgroup: convert cg_read_key_*() helpers to s64 Wilson Felipe Pereira
@ 2026-09-04 22:45 ` Wilson Felipe Pereira
  2 siblings, 0 replies; 4+ messages in thread
From: Wilson Felipe Pereira @ 2026-09-04 22:45 UTC (permalink / raw)
  To: Andrew Morton, Johannes Weiner, Yosry Ahmed, Nhat Pham,
	Chengming Zhou, Tejun Heo, Michal Koutný, Shuah Khan
  Cc: linux-mm, cgroups, linux-kselftest, linux-kernel,
	Wilson Felipe Pereira

In test_no_kmem_bypass(), delta (stored_pages * page_size - zswapped) is
checked against stored_pages * page_size / 4 to verify that the pages
pushed to zswap belong to the test memory cgroup.

Due to slight stat update timing differences, delta can evaluate to a small
negative number (e.g. -5MB out of 1GB). Because delta was declared as a
signed int and stored_pages is an unsigned size_t, C's usual arithmetic
conversions implicitly promote a negative delta to a large unsigned 64-bit
integer, causing `delta < stored_pages * page_size / 4` to falsely evaluate
to 0 and fail the test.

Fix this by declaring delta and max_delta as signed 64-bit values (s64),
explicitly casting stored_pages, and comparing against a signed threshold,
ensuring negative deltas correctly evaluate to true.

Fixes: a549f9f31561 ("selftests: cgroup: add test_zswap with no kmem bypass test")
Signed-off-by: Wilson Felipe Pereira <wfelipe@google.com>
---
 tools/testing/selftests/cgroup/test_zswap.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index f4ee440b3c66..6e7ee11bc97a 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -632,11 +632,12 @@ static int test_no_kmem_bypass(const char *root)
 			break;
 		/* If memory was pushed to zswap, verify it belongs to memcg */
 		if (stored_pages > stored_pages_threshold) {
-			s64 zswapped = cg_read_key_s64(test_group, "memory.stat", "zswapped ");
-			int delta = stored_pages * page_size - zswapped;
-			int result_ok = delta < stored_pages * page_size / 4;
+			s64 zswapped = cg_read_key_s64(
+				test_group, "memory.stat", "zswapped ");
+			s64 delta = (s64)stored_pages * page_size - zswapped;
+			s64 max_delta = (s64)stored_pages * page_size / 4;
 
-			ret = result_ok ? KSFT_PASS : KSFT_FAIL;
+			ret = (delta < max_delta) ? KSFT_PASS : KSFT_FAIL;
 			break;
 		}
 	}
-- 
2.55.0.979.g7e5102b832-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-04 22:51 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 22:45 [PATCH v6 0/3] selftests/cgroup: fixes for test_zswap on single core VM Wilson Felipe Pereira
2026-09-04 22:45 ` [PATCH v6 1/3] selftests/cgroup: test_zswap: wait for cgroup to unpopulate in test_zswap_writeback Wilson Felipe Pereira
2026-09-04 22:45 ` [PATCH v6 2/3] selftests/cgroup: convert cg_read_key_*() helpers to s64 Wilson Felipe Pereira
2026-09-04 22:45 ` [PATCH v6 3/3] selftests/cgroup: test_zswap: fix implicit unsigned promotion bug in test_no_kmem_bypass Wilson Felipe Pereira

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox