* [PATCH 0/3] selftests/mm: hugetlb cgroup charging: robustness fixes
@ 2025-12-20 11:16 Li Wang
2025-12-20 11:16 ` [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t Li Wang
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Li Wang @ 2025-12-20 11:16 UTC (permalink / raw)
To: linux-kselftest, akpm, linux-kernel, linux-mm
This series fixes a few issues in the hugetlb cgroup charging selftests
(write_to_hugetlbfs.c + charge_reserved_hugetlb.sh) that show up on systems
with large hugepages (e.g. 512MB) and when failures cause the test to wait
indefinitely.
On an aarch64 64k page kernel with 512MB hugepages, the test consistently
fails in write_to_hugetlbfs with ENOMEM and then hangs waiting for the
expected usage values. The root cause is that charge_reserved_hugetlb.sh
mounts hugetlbfs with a fixed size=256M, which is smaller than a single
hugepage, resulting in a mount with size=0 capacity.
In addition, write_to_hugetlbfs previously parsed -s via atoi() into an
int, which can overflow and print negative sizes.
Reproducer / environment:
- Kernel: 6.12.0-xxx.el10.aarch64+64k
- Hugepagesize: 524288 kB (512MB)
- ./charge_reserved_hugetlb.sh -cgroup-v2
- Observed mount: pagesize=512M,size=0 before this series
After applying the series, the test completes successfully on the above setup.
Li Wang (3):
selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t
selftests/mm/charge_reserved_hugetlb.sh: add waits with timeout helper
selftests/mm/charge_reserved_hugetlb: fix hugetlbfs mount size for
large hugepages
.../selftests/mm/charge_reserved_hugetlb.sh | 49 ++++++++++---------
.../testing/selftests/mm/write_to_hugetlbfs.c | 19 +++++--
2 files changed, 43 insertions(+), 25 deletions(-)
--
2.49.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t
2025-12-20 11:16 [PATCH 0/3] selftests/mm: hugetlb cgroup charging: robustness fixes Li Wang
@ 2025-12-20 11:16 ` Li Wang
2025-12-20 18:58 ` Andrew Morton
2025-12-20 11:16 ` [PATCH 2/3] selftests/mm/charge_reserved_hugetlb.sh: add waits with timeout helper Li Wang
2025-12-20 11:16 ` [PATCH 3/3] selftests/mm/charge_reserved_hugetlb: fix hugetlbfs mount size for large hugepages Li Wang
2 siblings, 1 reply; 6+ messages in thread
From: Li Wang @ 2025-12-20 11:16 UTC (permalink / raw)
To: linux-kselftest, akpm, linux-kernel, linux-mm
Cc: David Hildenbrand, Mark Brown, Shuah Khan, Waiman Long
write_to_hugetlbfs currently parses the -s size argument with atoi()
into an int. This silently accepts malformed input, cannot report overflow,
and can truncate large sizes.
--- Error log ---
# uname -r
6.12.0-xxx.el10.aarch64+64k
# ls /sys/kernel/mm/hugepages/hugepages-*
hugepages-16777216kB/ hugepages-2048kB/ hugepages-524288kB/
#./charge_reserved_hugetlb.sh -cgroup-v2
# -----------------------------------------
...
# nr hugepages = 10
# writing cgroup limit: 5368709120
# writing reseravation limit: 5368709120
...
# Writing to this path: /mnt/huge/test
# Writing this size: -1610612736 <--------
Switch size to size_t and parse -s using strtoull() with proper validation.
Also print the size using %zu.
This makes the test utility more robust and avoids undefined/incorrect
behavior with large or invalid -s values.
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Waiman Long <longman@redhat.com>
---
.../testing/selftests/mm/write_to_hugetlbfs.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/mm/write_to_hugetlbfs.c b/tools/testing/selftests/mm/write_to_hugetlbfs.c
index 34c91f7e6128..61f34b35dc27 100644
--- a/tools/testing/selftests/mm/write_to_hugetlbfs.c
+++ b/tools/testing/selftests/mm/write_to_hugetlbfs.c
@@ -68,7 +68,7 @@ int main(int argc, char **argv)
int key = 0;
int *ptr = NULL;
int c = 0;
- int size = 0;
+ size_t size = 0;
char path[256] = "";
enum method method = MAX_METHOD;
int want_sleep = 0, private = 0;
@@ -86,7 +86,20 @@ int main(int argc, char **argv)
while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) {
switch (c) {
case 's':
- size = atoi(optarg);
+ errno = 0;
+ char *end = NULL;
+ unsigned long long tmp = strtoull(optarg, &end, 10);
+ if (errno || end == optarg || *end != '\0') {
+ errno = EINVAL;
+ perror("Invalid -s size");
+ exit_usage();
+ }
+ if (tmp == 0) {
+ errno = EINVAL;
+ perror("size not found");
+ exit_usage();
+ }
+ size = (size_t)tmp;
break;
case 'p':
strncpy(path, optarg, sizeof(path) - 1);
@@ -131,7 +144,7 @@ int main(int argc, char **argv)
}
if (size != 0) {
- printf("Writing this size: %d\n", size);
+ printf("Writing this size: %zu\n", size);
} else {
errno = EINVAL;
perror("size not found");
--
2.49.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/3] selftests/mm/charge_reserved_hugetlb.sh: add waits with timeout helper
2025-12-20 11:16 [PATCH 0/3] selftests/mm: hugetlb cgroup charging: robustness fixes Li Wang
2025-12-20 11:16 ` [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t Li Wang
@ 2025-12-20 11:16 ` Li Wang
2025-12-20 11:16 ` [PATCH 3/3] selftests/mm/charge_reserved_hugetlb: fix hugetlbfs mount size for large hugepages Li Wang
2 siblings, 0 replies; 6+ messages in thread
From: Li Wang @ 2025-12-20 11:16 UTC (permalink / raw)
To: linux-kselftest, akpm, linux-kernel, linux-mm
Cc: David Hildenbrand, Mark Brown, Shuah Khan, Waiman Long
The hugetlb cgroup usage wait loops in charge_reserved_hugetlb.sh were
unbounded and could hang forever if the expected cgroup file value never
appears (e.g. due to bugs, timing issues, or unexpected behavior).
--- Error log ---
# uname -r
6.12.0-xxx.el10.aarch64+64k
# ls /sys/kernel/mm/hugepages/hugepages-*
hugepages-16777216kB/ hugepages-2048kB/ hugepages-524288kB/
#./charge_reserved_hugetlb.sh -cgroup-v2
# -----------------------------------------
...
# nr hugepages = 10
# writing cgroup limit: 5368709120
# writing reseravation limit: 5368709120
...
# write_to_hugetlbfs: Error mapping the file: Cannot allocate memory
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
...
Introduce a small helper, wait_for_file_value(), and use it for:
- waiting for reservation usage to drop to 0,
- waiting for reservation usage to reach a given size,
- waiting for fault usage to reach a given size.
This makes the waits consistent and adds a hard timeout (120 tries with
0.5s sleep) so the test fails instead of stalling indefinitely.
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Waiman Long <longman@redhat.com>
---
.../selftests/mm/charge_reserved_hugetlb.sh | 45 ++++++++++---------
1 file changed, 25 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
index e1fe16bcbbe8..6aa4ce66922c 100755
--- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
+++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
@@ -112,41 +112,46 @@ function setup_cgroup() {
fi
}
+function wait_for_file_value() {
+ local path="$1"
+ local expect="$2"
+ local max_tries="120"
+
+ local i cur
+ for ((i=1; i<=max_tries; i++)); do
+ cur="$(cat "$path")"
+ if [[ "$cur" == "$expect" ]]; then
+ return 0
+ fi
+ echo "Waiting for $path to become '$expect' (current: '$cur') (try $i/$max_tries)"
+ sleep 0.5
+ done
+
+ echo "ERROR: timeout waiting for $path to become '$expect' (last: '$cur')"
+ return 1
+}
+
function wait_for_hugetlb_memory_to_get_depleted() {
local cgroup="$1"
local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file"
- # Wait for hugetlbfs memory to get depleted.
- while [ $(cat $path) != 0 ]; do
- echo Waiting for hugetlb memory to get depleted.
- cat $path
- sleep 0.5
- done
+
+ wait_for_file_value "$path" "0"
}
function wait_for_hugetlb_memory_to_get_reserved() {
local cgroup="$1"
local size="$2"
-
local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$reservation_usage_file"
- # Wait for hugetlbfs memory to get written.
- while [ $(cat $path) != $size ]; do
- echo Waiting for hugetlb memory reservation to reach size $size.
- cat $path
- sleep 0.5
- done
+
+ wait_for_file_value "$path" "$size"
}
function wait_for_hugetlb_memory_to_get_written() {
local cgroup="$1"
local size="$2"
-
local path="$cgroup_path/$cgroup/hugetlb.${MB}MB.$fault_usage_file"
- # Wait for hugetlbfs memory to get written.
- while [ $(cat $path) != $size ]; do
- echo Waiting for hugetlb memory to reach size $size.
- cat $path
- sleep 0.5
- done
+
+ wait_for_file_value "$path" "$size"
}
function write_hugetlbfs_and_get_usage() {
--
2.49.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/3] selftests/mm/charge_reserved_hugetlb: fix hugetlbfs mount size for large hugepages
2025-12-20 11:16 [PATCH 0/3] selftests/mm: hugetlb cgroup charging: robustness fixes Li Wang
2025-12-20 11:16 ` [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t Li Wang
2025-12-20 11:16 ` [PATCH 2/3] selftests/mm/charge_reserved_hugetlb.sh: add waits with timeout helper Li Wang
@ 2025-12-20 11:16 ` Li Wang
2 siblings, 0 replies; 6+ messages in thread
From: Li Wang @ 2025-12-20 11:16 UTC (permalink / raw)
To: linux-kselftest, akpm, linux-kernel, linux-mm
Cc: David Hildenbrand, Mark Brown, Shuah Khan, Waiman Long
charge_reserved_hugetlb.sh mounts a hugetlbfs instance at /mnt/huge with
a fixed size of 256M. On systems with large base hugepages (e.g. 512MB),
this is smaller than a single hugepage, so the hugetlbfs mount ends up
with effectively zero capacity (often visible as size=0 in mount output).
As a result, write_to_hugetlbfs fails with ENOMEM and the test can hang
waiting for progress.
--- Error log ---
# uname -r
6.12.0-xxx.el10.aarch64+64k
#./charge_reserved_hugetlb.sh -cgroup-v2
# -----------------------------------------
...
# nr hugepages = 10
# writing cgroup limit: 5368709120
# writing reseravation limit: 5368709120
...
# write_to_hugetlbfs: Error mapping the file: Cannot allocate memory
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
# Waiting for hugetlb memory reservation to reach size 2684354560.
# 0
...
# mount |grep /mnt/huge
none on /mnt/huge type hugetlbfs (rw,relatime,seclabel,pagesize=512M,size=0)
# grep -i huge /proc/meminfo
...
HugePages_Total: 10
HugePages_Free: 10
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 524288 kB
Hugetlb: 5242880 kB
Mount hugetlbfs with size=${size}, the number of bytes the test will
reserve/write, so the filesystem capacity is sufficient regardless of
HugeTLB page size.
Signed-off-by: Li Wang <liwang@redhat.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Waiman Long <longman@redhat.com>
---
tools/testing/selftests/mm/charge_reserved_hugetlb.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
index 6aa4ce66922c..9deb3dc2cbce 100755
--- a/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
+++ b/tools/testing/selftests/mm/charge_reserved_hugetlb.sh
@@ -295,7 +295,7 @@ function run_test() {
setup_cgroup "hugetlb_cgroup_test" "$cgroup_limit" "$reservation_limit"
mkdir -p /mnt/huge
- mount -t hugetlbfs -o pagesize=${MB}M,size=256M none /mnt/huge
+ mount -t hugetlbfs -o pagesize=${MB}M,size=${size} none /mnt/huge
write_hugetlbfs_and_get_usage "hugetlb_cgroup_test" "$size" "$populate" \
"$write" "/mnt/huge/test" "$method" "$private" "$expect_failure" \
@@ -349,7 +349,7 @@ function run_multiple_cgroup_test() {
setup_cgroup "hugetlb_cgroup_test2" "$cgroup_limit2" "$reservation_limit2"
mkdir -p /mnt/huge
- mount -t hugetlbfs -o pagesize=${MB}M,size=256M none /mnt/huge
+ mount -t hugetlbfs -o pagesize=${MB}M,size=${size} none /mnt/huge
write_hugetlbfs_and_get_usage "hugetlb_cgroup_test1" "$size1" \
"$populate1" "$write1" "/mnt/huge/test1" "$method" "$private" \
--
2.49.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t
2025-12-20 11:16 ` [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t Li Wang
@ 2025-12-20 18:58 ` Andrew Morton
2025-12-21 2:35 ` Li Wang
0 siblings, 1 reply; 6+ messages in thread
From: Andrew Morton @ 2025-12-20 18:58 UTC (permalink / raw)
To: Li Wang
Cc: linux-kselftest, linux-kernel, linux-mm, David Hildenbrand,
Mark Brown, Shuah Khan, Waiman Long
On Sat, 20 Dec 2025 19:16:43 +0800 Li Wang <liwang@redhat.com> wrote:
> write_to_hugetlbfs currently parses the -s size argument with atoi()
> into an int. This silently accepts malformed input, cannot report overflow,
> and can truncate large sizes.
>
> --- Error log ---
> # uname -r
> 6.12.0-xxx.el10.aarch64+64k
>
> # ls /sys/kernel/mm/hugepages/hugepages-*
> hugepages-16777216kB/ hugepages-2048kB/ hugepages-524288kB/
>
> #./charge_reserved_hugetlb.sh -cgroup-v2
> # -----------------------------------------
> ...
> # nr hugepages = 10
> # writing cgroup limit: 5368709120
> # writing reseravation limit: 5368709120
Can we fix that typo while we're in there? "reservation".
> ...
> # Writing to this path: /mnt/huge/test
> # Writing this size: -1610612736 <--------
>
> Switch size to size_t and parse -s using strtoull() with proper validation.
> Also print the size using %zu.
>
> This makes the test utility more robust and avoids undefined/incorrect
> behavior with large or invalid -s values.
>
> ...
>
> --- a/tools/testing/selftests/mm/write_to_hugetlbfs.c
> +++ b/tools/testing/selftests/mm/write_to_hugetlbfs.c
> @@ -68,7 +68,7 @@ int main(int argc, char **argv)
> int key = 0;
> int *ptr = NULL;
> int c = 0;
> - int size = 0;
> + size_t size = 0;
> char path[256] = "";
> enum method method = MAX_METHOD;
> int want_sleep = 0, private = 0;
> @@ -86,7 +86,20 @@ int main(int argc, char **argv)
> while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) {
> switch (c) {
> case 's':
> - size = atoi(optarg);
> + errno = 0;
> + char *end = NULL;
> + unsigned long long tmp = strtoull(optarg, &end, 10);
Coding-style nits: we do accept c99-style definitions nowadays but I do
think our eyes prefer the less surprising "definitions come before
code" style. So the above could be
char *end = NULL;
unsigned long long tmp = strtoull(optarg, &end, 10);
errno = 0;
Also, `errno' belongs to libc. It seems wrong to be altering it from
within our client code.
> + if (errno || end == optarg || *end != '\0') {
> + errno = EINVAL;
> + perror("Invalid -s size");
> + exit_usage();
> + }
> + if (tmp == 0) {
> + errno = EINVAL;
> + perror("size not found");
> + exit_usage();
> + }
> + size = (size_t)tmp;
> break;
I'm not really clear on what problems we're trying to solve here, but
this all seems like a lot of fuss. Can we just do
if (sscanf(optarg, "%zu", &size) != 1)
?
> case 'p':
> strncpy(path, optarg, sizeof(path) - 1);
> @@ -131,7 +144,7 @@ int main(int argc, char **argv)
> }
>
> if (size != 0) {
> - printf("Writing this size: %d\n", size);
> + printf("Writing this size: %zu\n", size);
> } else {
> errno = EINVAL;
> perror("size not found");
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t
2025-12-20 18:58 ` Andrew Morton
@ 2025-12-21 2:35 ` Li Wang
0 siblings, 0 replies; 6+ messages in thread
From: Li Wang @ 2025-12-21 2:35 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kselftest, linux-kernel, linux-mm, David Hildenbrand,
Mark Brown, Shuah Khan, Waiman Long
[-- Attachment #1: Type: text/plain, Size: 3503 bytes --]
Andrew Morton <akpm@linux-foundation.org> wrote:
> #./charge_reserved_hugetlb.sh -cgroup-v2
> > # -----------------------------------------
> > ...
> > # nr hugepages = 10
> > # writing cgroup limit: 5368709120
> > # writing reseravation limit: 5368709120
>
> Can we fix that typo while we're in there? "reservation".
>
Good catch, let's fix it in patch 2/3.
> > while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) {
> > switch (c) {
> > case 's':
> > - size = atoi(optarg);
> > + errno = 0;
> > + char *end = NULL;
> > + unsigned long long tmp = strtoull(optarg, &end,
> 10);
>
> Coding-style nits: we do accept c99-style definitions nowadays but I do
> think our eyes prefer the less surprising "definitions come before
> code" style. So the above could be
>
> char *end = NULL;
> unsigned long long tmp = strtoull(optarg, &end,
> 10);
>
> errno = 0;
>
> Also, `errno' belongs to libc. It seems wrong to be altering it from
> within our client code.
>
Indeed, since I saw 'errono' modified in many places in this test,
so to avoid other effects, I set it to zero. However, we can abandon
this method and use sscanf() instead, thanks!
>
> > + if (errno || end == optarg || *end != '\0') {
> > + errno = EINVAL;
> > + perror("Invalid -s size");
> > + exit_usage();
> > + }
> > + if (tmp == 0) {
> > + errno = EINVAL;
> > + perror("size not found");
> > + exit_usage();
> > + }
> > + size = (size_t)tmp;
> > break;
>
> I'm not really clear on what problems we're trying to solve here, but
> this all seems like a lot of fuss. Can we just do
>
> if (sscanf(optarg, "%zu", &size) != 1)
>
Yes, I admit I was overthinking the int overflow handling.
This is nice, so the V2 should be simply like:
--- a/tools/testing/selftests/mm/write_to_hugetlbfs.c
+++ b/tools/testing/selftests/mm/write_to_hugetlbfs.c
@@ -68,7 +68,7 @@ int main(int argc, char **argv)
int key = 0;
int *ptr = NULL;
int c = 0;
- int size = 0;
+ size_t size = 0;
char path[256] = "";
enum method method = MAX_METHOD;
int want_sleep = 0, private = 0;
@@ -86,7 +86,10 @@ int main(int argc, char **argv)
while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) {
switch (c) {
case 's':
- size = atoi(optarg);
+ if (sscanf(optarg, "%zu", &size) != 1) {
+ perror("Invalid -s.");
+ exit_usage();
+ }
break;
case 'p':
strncpy(path, optarg, sizeof(path) - 1);
@@ -131,7 +134,7 @@ int main(int argc, char **argv)
}
if (size != 0) {
- printf("Writing this size: %d\n", size);
+ printf("Writing this size: %zu\n", size);
} else {
errno = EINVAL;
perror("size not found");
And, if no more comments on the rest of patchset, I will send v2 later
today.
--
Regards,
Li Wang
[-- Attachment #2: Type: text/html, Size: 5928 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-12-21 2:35 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-20 11:16 [PATCH 0/3] selftests/mm: hugetlb cgroup charging: robustness fixes Li Wang
2025-12-20 11:16 ` [PATCH 1/3] selftests/mm/write_to_hugetlbfs: parse -s with strtoull and use size_t Li Wang
2025-12-20 18:58 ` Andrew Morton
2025-12-21 2:35 ` Li Wang
2025-12-20 11:16 ` [PATCH 2/3] selftests/mm/charge_reserved_hugetlb.sh: add waits with timeout helper Li Wang
2025-12-20 11:16 ` [PATCH 3/3] selftests/mm/charge_reserved_hugetlb: fix hugetlbfs mount size for large hugepages Li Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox