| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- // 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), NULL))
- 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;
- }
|