selftests: add a selftest to verify hugetlb usage in memcg
This patch add a new kselftest to demonstrate and verify the new hugetlb memcg accounting behavior. Link: https://lkml.kernel.org/r/20231006184629.155543-5-nphamcs@gmail.com Signed-off-by: Nhat Pham <nphamcs@gmail.com> Cc: Frank van der Linden <fvdl@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Mike Kravetz <mike.kravetz@oracle.com> Cc: Muchun Song <muchun.song@linux.dev> Cc: Rik van Riel <riel@surriel.com> Cc: Roman Gushchin <roman.gushchin@linux.dev> Cc: Shakeel Butt <shakeelb@google.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Tejun heo <tj@kernel.org> Cc: Yosry Ahmed <yosryahmed@google.com> Cc: Zefan Li <lizefan.x@bytedance.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
8cba9576df
commit
c0dddb7aa5
|
@ -5269,6 +5269,7 @@ S: Maintained
|
|||
F: mm/memcontrol.c
|
||||
F: mm/swap_cgroup.c
|
||||
F: tools/testing/selftests/cgroup/memcg_protection.m
|
||||
F: tools/testing/selftests/cgroup/test_hugetlb_memcg.c
|
||||
F: tools/testing/selftests/cgroup/test_kmem.c
|
||||
F: tools/testing/selftests/cgroup/test_memcontrol.c
|
||||
|
||||
|
@ -9652,6 +9653,7 @@ F: include/linux/hugetlb.h
|
|||
F: mm/hugetlb.c
|
||||
F: mm/hugetlb_vmemmap.c
|
||||
F: mm/hugetlb_vmemmap.h
|
||||
F: tools/testing/selftests/cgroup/test_hugetlb_memcg.c
|
||||
|
||||
HVA ST MEDIA DRIVER
|
||||
M: Jean-Christophe Trotin <jean-christophe.trotin@foss.st.com>
|
||||
|
|
|
@ -7,4 +7,5 @@ test_kill
|
|||
test_cpu
|
||||
test_cpuset
|
||||
test_zswap
|
||||
test_hugetlb_memcg
|
||||
wait_inotify
|
||||
|
|
|
@ -14,6 +14,7 @@ TEST_GEN_PROGS += test_kill
|
|||
TEST_GEN_PROGS += test_cpu
|
||||
TEST_GEN_PROGS += test_cpuset
|
||||
TEST_GEN_PROGS += test_zswap
|
||||
TEST_GEN_PROGS += test_hugetlb_memcg
|
||||
|
||||
LOCAL_HDRS += $(selfdir)/clone3/clone3_selftests.h $(selfdir)/pidfd/pidfd.h
|
||||
|
||||
|
@ -27,3 +28,4 @@ $(OUTPUT)/test_kill: cgroup_util.c
|
|||
$(OUTPUT)/test_cpu: cgroup_util.c
|
||||
$(OUTPUT)/test_cpuset: cgroup_util.c
|
||||
$(OUTPUT)/test_zswap: cgroup_util.c
|
||||
$(OUTPUT)/test_hugetlb_memcg: cgroup_util.c
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include "../kselftest.h"
|
||||
#include "cgroup_util.h"
|
||||
|
||||
#define ADDR ((void *)(0x0UL))
|
||||
#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB)
|
||||
/* mapping 8 MBs == 4 hugepages */
|
||||
#define LENGTH (8UL*1024*1024)
|
||||
#define PROTECTION (PROT_READ | PROT_WRITE)
|
||||
|
||||
/* borrowed from mm/hmm-tests.c */
|
||||
static long get_hugepage_size(void)
|
||||
{
|
||||
int fd;
|
||||
char buf[2048];
|
||||
int len;
|
||||
char *p, *q, *path = "/proc/meminfo", *tag = "Hugepagesize:";
|
||||
long val;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
/* Error opening the file */
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
if (len < 0) {
|
||||
/* Error in reading the file */
|
||||
return -1;
|
||||
}
|
||||
if (len == sizeof(buf)) {
|
||||
/* Error file is too large */
|
||||
return -1;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
|
||||
/* Search for a tag if provided */
|
||||
if (tag) {
|
||||
p = strstr(buf, tag);
|
||||
if (!p)
|
||||
return -1; /* looks like the line we want isn't there */
|
||||
p += strlen(tag);
|
||||
} else
|
||||
p = buf;
|
||||
|
||||
val = strtol(p, &q, 0);
|
||||
if (*q != ' ') {
|
||||
/* Error parsing the file */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int set_file(const char *path, long value)
|
||||
{
|
||||
FILE *file;
|
||||
int ret;
|
||||
|
||||
file = fopen(path, "w");
|
||||
if (!file)
|
||||
return -1;
|
||||
ret = fprintf(file, "%ld\n", value);
|
||||
fclose(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int set_nr_hugepages(long value)
|
||||
{
|
||||
return set_file("/proc/sys/vm/nr_hugepages", value);
|
||||
}
|
||||
|
||||
static unsigned int check_first(char *addr)
|
||||
{
|
||||
return *(unsigned int *)addr;
|
||||
}
|
||||
|
||||
static void write_data(char *addr)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < LENGTH; i++)
|
||||
*(addr + i) = (char)i;
|
||||
}
|
||||
|
||||
static int hugetlb_test_program(const char *cgroup, void *arg)
|
||||
{
|
||||
char *test_group = (char *)arg;
|
||||
void *addr;
|
||||
long old_current, expected_current, current;
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
old_current = cg_read_long(test_group, "memory.current");
|
||||
set_nr_hugepages(20);
|
||||
current = cg_read_long(test_group, "memory.current");
|
||||
if (current - old_current >= MB(2)) {
|
||||
ksft_print_msg(
|
||||
"setting nr_hugepages should not increase hugepage usage.\n");
|
||||
ksft_print_msg("before: %ld, after: %ld\n", old_current, current);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, 0, 0);
|
||||
if (addr == MAP_FAILED) {
|
||||
ksft_print_msg("fail to mmap.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
current = cg_read_long(test_group, "memory.current");
|
||||
if (current - old_current >= MB(2)) {
|
||||
ksft_print_msg("mmap should not increase hugepage usage.\n");
|
||||
ksft_print_msg("before: %ld, after: %ld\n", old_current, current);
|
||||
goto out_failed_munmap;
|
||||
}
|
||||
old_current = current;
|
||||
|
||||
/* read the first page */
|
||||
check_first(addr);
|
||||
expected_current = old_current + MB(2);
|
||||
current = cg_read_long(test_group, "memory.current");
|
||||
if (!values_close(expected_current, current, 5)) {
|
||||
ksft_print_msg("memory usage should increase by around 2MB.\n");
|
||||
ksft_print_msg(
|
||||
"expected memory: %ld, actual memory: %ld\n",
|
||||
expected_current, current);
|
||||
goto out_failed_munmap;
|
||||
}
|
||||
|
||||
/* write to the whole range */
|
||||
write_data(addr);
|
||||
current = cg_read_long(test_group, "memory.current");
|
||||
expected_current = old_current + MB(8);
|
||||
if (!values_close(expected_current, current, 5)) {
|
||||
ksft_print_msg("memory usage should increase by around 8MB.\n");
|
||||
ksft_print_msg(
|
||||
"expected memory: %ld, actual memory: %ld\n",
|
||||
expected_current, current);
|
||||
goto out_failed_munmap;
|
||||
}
|
||||
|
||||
/* unmap the whole range */
|
||||
munmap(addr, LENGTH);
|
||||
current = cg_read_long(test_group, "memory.current");
|
||||
expected_current = old_current;
|
||||
if (!values_close(expected_current, current, 5)) {
|
||||
ksft_print_msg("memory usage should go back down.\n");
|
||||
ksft_print_msg(
|
||||
"expected memory: %ld, actual memory: %ld\n",
|
||||
expected_current, current);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
return ret;
|
||||
|
||||
out_failed_munmap:
|
||||
munmap(addr, LENGTH);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_hugetlb_memcg(char *root)
|
||||
{
|
||||
int ret = KSFT_FAIL;
|
||||
char *test_group;
|
||||
|
||||
test_group = cg_name(root, "hugetlb_memcg_test");
|
||||
if (!test_group || cg_create(test_group)) {
|
||||
ksft_print_msg("fail to create cgroup.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cg_write(test_group, "memory.max", "100M")) {
|
||||
ksft_print_msg("fail to set cgroup memory limit.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* disable swap */
|
||||
if (cg_write(test_group, "memory.swap.max", "0")) {
|
||||
ksft_print_msg("fail to disable swap.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!cg_run(test_group, hugetlb_test_program, (void *)test_group))
|
||||
ret = KSFT_PASS;
|
||||
out:
|
||||
cg_destroy(test_group);
|
||||
free(test_group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char root[PATH_MAX];
|
||||
int ret = EXIT_SUCCESS, has_memory_hugetlb_acc;
|
||||
|
||||
has_memory_hugetlb_acc = proc_mount_contains("memory_hugetlb_accounting");
|
||||
if (has_memory_hugetlb_acc < 0)
|
||||
ksft_exit_skip("Failed to query cgroup mount option\n");
|
||||
else if (!has_memory_hugetlb_acc)
|
||||
ksft_exit_skip("memory hugetlb accounting is disabled\n");
|
||||
|
||||
/* Unit is kB! */
|
||||
if (get_hugepage_size() != 2048) {
|
||||
ksft_print_msg("test_hugetlb_memcg requires 2MB hugepages\n");
|
||||
ksft_test_result_skip("test_hugetlb_memcg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cg_find_unified_root(root, sizeof(root)))
|
||||
ksft_exit_skip("cgroup v2 isn't mounted\n");
|
||||
|
||||
switch (test_hugetlb_memcg(root)) {
|
||||
case KSFT_PASS:
|
||||
ksft_test_result_pass("test_hugetlb_memcg\n");
|
||||
break;
|
||||
case KSFT_SKIP:
|
||||
ksft_test_result_skip("test_hugetlb_memcg\n");
|
||||
break;
|
||||
default:
|
||||
ret = EXIT_FAILURE;
|
||||
ksft_test_result_fail("test_hugetlb_memcg\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue