Linux Audio

Check our new training course

Embedded Linux training

Mar 31-Apr 8, 2025
Register
Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2#define _GNU_SOURCE
  3
  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	} else
 53		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}