Files
linux/tools/testing/selftests/mm/write_to_hugetlbfs.c
Li Wang 8e46adb62f selftests/mm/write_to_hugetlbfs: parse -s as size_t
Patch series "selftests/mm: hugetlb cgroup charging: robustness fixes", v3.

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.


This patch (of 3):

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 the size variable to size_t and parse -s with sscanf("%zu", ...). 
Also print the size using %zu.

This avoids incorrect behavior with large -s values and makes the utility
more robust.

Link: https://lkml.kernel.org/r/20251221122639.3168038-1-liwang@redhat.com
Link: https://lkml.kernel.org/r/20251221122639.3168038-2-liwang@redhat.com
Signed-off-by: Li Wang <liwang@redhat.com>
Acked-by: David Hildenbrand (Red Hat) <david@kernel.org>
Acked-by: Waiman Long <longman@redhat.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2026-01-20 19:24:50 -08:00

247 lines
4.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* This program reserves and uses hugetlb memory, supporting a bunch of
* scenarios needed by the charged_reserved_hugetlb.sh test.
*/
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
/* Global definitions. */
enum method {
HUGETLBFS,
MMAP_MAP_HUGETLB,
SHM,
MAX_METHOD
};
/* Global variables. */
static const char *self;
static int *shmaddr;
static int shmid;
/*
* Show usage and exit.
*/
static void exit_usage(void)
{
printf("Usage: %s -p <path to hugetlbfs file> -s <size to map> "
"[-m <0=hugetlbfs | 1=mmap(MAP_HUGETLB)>] [-l] [-r] "
"[-o] [-w] [-n]\n",
self);
exit(EXIT_FAILURE);
}
void sig_handler(int signo)
{
printf("Received %d.\n", signo);
if (signo == SIGINT) {
if (shmaddr) {
printf("Deleting the memory\n");
if (shmdt((const void *)shmaddr) != 0) {
perror("Detach failure");
shmctl(shmid, IPC_RMID, NULL);
exit(4);
}
shmctl(shmid, IPC_RMID, NULL);
printf("Done deleting the memory\n");
}
}
exit(2);
}
int main(int argc, char **argv)
{
int fd = 0;
int key = 0;
int *ptr = NULL;
int c = 0;
size_t size = 0;
char path[256] = "";
enum method method = MAX_METHOD;
int want_sleep = 0, private = 0;
int populate = 0;
int write = 0;
int reserve = 1;
if (signal(SIGINT, sig_handler) == SIG_ERR)
err(1, "\ncan't catch SIGINT\n");
/* Parse command-line arguments. */
setvbuf(stdout, NULL, _IONBF, 0);
self = argv[0];
while ((c = getopt(argc, argv, "s:p:m:owlrn")) != -1) {
switch (c) {
case 's':
if (sscanf(optarg, "%zu", &size) != 1) {
perror("Invalid -s.");
exit_usage();
}
break;
case 'p':
strncpy(path, optarg, sizeof(path) - 1);
break;
case 'm':
if (atoi(optarg) >= MAX_METHOD) {
errno = EINVAL;
perror("Invalid -m.");
exit_usage();
}
method = atoi(optarg);
break;
case 'o':
populate = 1;
break;
case 'w':
write = 1;
break;
case 'l':
want_sleep = 1;
break;
case 'r':
private
= 1;
break;
case 'n':
reserve = 0;
break;
default:
errno = EINVAL;
perror("Invalid arg");
exit_usage();
}
}
if (strncmp(path, "", sizeof(path)) != 0) {
printf("Writing to this path: %s\n", path);
} else {
errno = EINVAL;
perror("path not found");
exit_usage();
}
if (size != 0) {
printf("Writing this size: %zu\n", size);
} else {
errno = EINVAL;
perror("size not found");
exit_usage();
}
if (!populate)
printf("Not populating.\n");
else
printf("Populating.\n");
if (!write)
printf("Not writing to memory.\n");
if (method == MAX_METHOD) {
errno = EINVAL;
perror("-m Invalid");
exit_usage();
} else
printf("Using method=%d\n", method);
if (!private)
printf("Shared mapping.\n");
else
printf("Private mapping.\n");
if (!reserve)
printf("NO_RESERVE mapping.\n");
else
printf("RESERVE mapping.\n");
switch (method) {
case HUGETLBFS:
printf("Allocating using HUGETLBFS.\n");
fd = open(path, O_CREAT | O_RDWR, 0777);
if (fd == -1)
err(1, "Failed to open file.");
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
(private ? MAP_PRIVATE : MAP_SHARED) |
(populate ? MAP_POPULATE : 0) |
(reserve ? 0 : MAP_NORESERVE),
fd, 0);
if (ptr == MAP_FAILED) {
close(fd);
err(1, "Error mapping the file");
}
break;
case MMAP_MAP_HUGETLB:
printf("Allocating using MAP_HUGETLB.\n");
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
(private ? (MAP_PRIVATE | MAP_ANONYMOUS) :
MAP_SHARED) |
MAP_HUGETLB | (populate ? MAP_POPULATE : 0) |
(reserve ? 0 : MAP_NORESERVE),
-1, 0);
if (ptr == MAP_FAILED)
err(1, "mmap");
printf("Returned address is %p\n", ptr);
break;
case SHM:
printf("Allocating using SHM.\n");
shmid = shmget(key, size,
SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
if (shmid < 0) {
shmid = shmget(++key, size,
SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
if (shmid < 0)
err(1, "shmget");
}
printf("shmid: 0x%x, shmget key:%d\n", shmid, key);
ptr = shmat(shmid, NULL, 0);
if (ptr == (int *)-1) {
perror("Shared memory attach failure");
shmctl(shmid, IPC_RMID, NULL);
exit(2);
}
shmaddr = ptr;
printf("shmaddr: %p\n", shmaddr);
break;
default:
errno = EINVAL;
err(1, "Invalid method.");
}
if (write) {
printf("Writing to memory.\n");
memset(ptr, 1, size);
}
if (want_sleep) {
/* Signal to caller that we're done. */
printf("DONE\n");
/* Hold memory until external kill signal is delivered. */
while (1)
sleep(100);
}
if (method == HUGETLBFS)
close(fd);
return 0;
}