235 lines · c
1// SPDX-License-Identifier: GPL-2.02#define _GNU_SOURCE3 4#include <linux/limits.h>5#include <sys/mman.h>6#include <stdio.h>7#include <stdlib.h>8#include <string.h>9#include <fcntl.h>10#include "../kselftest.h"11#include "cgroup_util.h"12 13#define ADDR ((void *)(0x0UL))14#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB)15/* mapping 8 MBs == 4 hugepages */16#define LENGTH (8UL*1024*1024)17#define PROTECTION (PROT_READ | PROT_WRITE)18 19/* borrowed from mm/hmm-tests.c */20static long get_hugepage_size(void)21{22 int fd;23 char buf[2048];24 int len;25 char *p, *q, *path = "/proc/meminfo", *tag = "Hugepagesize:";26 long val;27 28 fd = open(path, O_RDONLY);29 if (fd < 0) {30 /* Error opening the file */31 return -1;32 }33 34 len = read(fd, buf, sizeof(buf));35 close(fd);36 if (len < 0) {37 /* Error in reading the file */38 return -1;39 }40 if (len == sizeof(buf)) {41 /* Error file is too large */42 return -1;43 }44 buf[len] = '\0';45 46 /* Search for a tag if provided */47 if (tag) {48 p = strstr(buf, tag);49 if (!p)50 return -1; /* looks like the line we want isn't there */51 p += strlen(tag);52 } else53 p = buf;54 55 val = strtol(p, &q, 0);56 if (*q != ' ') {57 /* Error parsing the file */58 return -1;59 }60 61 return val;62}63 64static int set_file(const char *path, long value)65{66 FILE *file;67 int ret;68 69 file = fopen(path, "w");70 if (!file)71 return -1;72 ret = fprintf(file, "%ld\n", value);73 fclose(file);74 return ret;75}76 77static int set_nr_hugepages(long value)78{79 return set_file("/proc/sys/vm/nr_hugepages", value);80}81 82static unsigned int check_first(char *addr)83{84 return *(unsigned int *)addr;85}86 87static void write_data(char *addr)88{89 unsigned long i;90 91 for (i = 0; i < LENGTH; i++)92 *(addr + i) = (char)i;93}94 95static int hugetlb_test_program(const char *cgroup, void *arg)96{97 char *test_group = (char *)arg;98 void *addr;99 long old_current, expected_current, current;100 int ret = EXIT_FAILURE;101 102 old_current = cg_read_long(test_group, "memory.current");103 set_nr_hugepages(20);104 current = cg_read_long(test_group, "memory.current");105 if (current - old_current >= MB(2)) {106 ksft_print_msg(107 "setting nr_hugepages should not increase hugepage usage.\n");108 ksft_print_msg("before: %ld, after: %ld\n", old_current, current);109 return EXIT_FAILURE;110 }111 112 addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, 0, 0);113 if (addr == MAP_FAILED) {114 ksft_print_msg("fail to mmap.\n");115 return EXIT_FAILURE;116 }117 current = cg_read_long(test_group, "memory.current");118 if (current - old_current >= MB(2)) {119 ksft_print_msg("mmap should not increase hugepage usage.\n");120 ksft_print_msg("before: %ld, after: %ld\n", old_current, current);121 goto out_failed_munmap;122 }123 old_current = current;124 125 /* read the first page */126 check_first(addr);127 expected_current = old_current + MB(2);128 current = cg_read_long(test_group, "memory.current");129 if (!values_close(expected_current, current, 5)) {130 ksft_print_msg("memory usage should increase by around 2MB.\n");131 ksft_print_msg(132 "expected memory: %ld, actual memory: %ld\n",133 expected_current, current);134 goto out_failed_munmap;135 }136 137 /* write to the whole range */138 write_data(addr);139 current = cg_read_long(test_group, "memory.current");140 expected_current = old_current + MB(8);141 if (!values_close(expected_current, current, 5)) {142 ksft_print_msg("memory usage should increase by around 8MB.\n");143 ksft_print_msg(144 "expected memory: %ld, actual memory: %ld\n",145 expected_current, current);146 goto out_failed_munmap;147 }148 149 /* unmap the whole range */150 munmap(addr, LENGTH);151 current = cg_read_long(test_group, "memory.current");152 expected_current = old_current;153 if (!values_close(expected_current, current, 5)) {154 ksft_print_msg("memory usage should go back down.\n");155 ksft_print_msg(156 "expected memory: %ld, actual memory: %ld\n",157 expected_current, current);158 return ret;159 }160 161 ret = EXIT_SUCCESS;162 return ret;163 164out_failed_munmap:165 munmap(addr, LENGTH);166 return ret;167}168 169static int test_hugetlb_memcg(char *root)170{171 int ret = KSFT_FAIL;172 char *test_group;173 174 test_group = cg_name(root, "hugetlb_memcg_test");175 if (!test_group || cg_create(test_group)) {176 ksft_print_msg("fail to create cgroup.\n");177 goto out;178 }179 180 if (cg_write(test_group, "memory.max", "100M")) {181 ksft_print_msg("fail to set cgroup memory limit.\n");182 goto out;183 }184 185 /* disable swap */186 if (cg_write(test_group, "memory.swap.max", "0")) {187 ksft_print_msg("fail to disable swap.\n");188 goto out;189 }190 191 if (!cg_run(test_group, hugetlb_test_program, (void *)test_group))192 ret = KSFT_PASS;193out:194 cg_destroy(test_group);195 free(test_group);196 return ret;197}198 199int main(int argc, char **argv)200{201 char root[PATH_MAX];202 int ret = EXIT_SUCCESS, has_memory_hugetlb_acc;203 204 has_memory_hugetlb_acc = proc_mount_contains("memory_hugetlb_accounting");205 if (has_memory_hugetlb_acc < 0)206 ksft_exit_skip("Failed to query cgroup mount option\n");207 else if (!has_memory_hugetlb_acc)208 ksft_exit_skip("memory hugetlb accounting is disabled\n");209 210 /* Unit is kB! */211 if (get_hugepage_size() != 2048) {212 ksft_print_msg("test_hugetlb_memcg requires 2MB hugepages\n");213 ksft_test_result_skip("test_hugetlb_memcg\n");214 return ret;215 }216 217 if (cg_find_unified_root(root, sizeof(root), NULL))218 ksft_exit_skip("cgroup v2 isn't mounted\n");219 220 switch (test_hugetlb_memcg(root)) {221 case KSFT_PASS:222 ksft_test_result_pass("test_hugetlb_memcg\n");223 break;224 case KSFT_SKIP:225 ksft_test_result_skip("test_hugetlb_memcg\n");226 break;227 default:228 ret = EXIT_FAILURE;229 ksft_test_result_fail("test_hugetlb_memcg\n");230 break;231 }232 233 return ret;234}235