* [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 *)¶m))
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 *)¶m))
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 *)¶m))
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.