1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2baa489faSSeongJae Park /* Test selecting other page sizes for mmap/shmget.
3baa489faSSeongJae Park 
4baa489faSSeongJae Park    Before running this huge pages for each huge page size must have been
5baa489faSSeongJae Park    reserved.
6baa489faSSeongJae Park    For large pages beyond MAX_ORDER (like 1GB on x86) boot options must be used.
7baa489faSSeongJae Park    Also shmmax must be increased.
8baa489faSSeongJae Park    And you need to run as root to work around some weird permissions in shm.
9baa489faSSeongJae Park    And nothing using huge pages should run in parallel.
10baa489faSSeongJae Park    When the program aborts you may need to clean up the shm segments with
11baa489faSSeongJae Park    ipcrm -m by hand, like this
12baa489faSSeongJae Park    sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m
13baa489faSSeongJae Park    (warning this will remove all if someone else uses them) */
14baa489faSSeongJae Park 
15baa489faSSeongJae Park #define _GNU_SOURCE 1
16baa489faSSeongJae Park #include <sys/mman.h>
17baa489faSSeongJae Park #include <stdlib.h>
18baa489faSSeongJae Park #include <stdio.h>
19baa489faSSeongJae Park #include <sys/ipc.h>
20baa489faSSeongJae Park #include <sys/shm.h>
21baa489faSSeongJae Park #include <sys/stat.h>
22baa489faSSeongJae Park #include <glob.h>
23baa489faSSeongJae Park #include <assert.h>
24baa489faSSeongJae Park #include <unistd.h>
25baa489faSSeongJae Park #include <stdarg.h>
26baa489faSSeongJae Park #include <string.h>
27bd4d67e7SPeter Xu #include "vm_util.h"
28baa489faSSeongJae Park 
29baa489faSSeongJae Park #define err(x) perror(x), exit(1)
30baa489faSSeongJae Park 
31baa489faSSeongJae Park #define MAP_HUGE_2MB    (21 << MAP_HUGE_SHIFT)
32baa489faSSeongJae Park #define MAP_HUGE_1GB    (30 << MAP_HUGE_SHIFT)
33baa489faSSeongJae Park #define MAP_HUGE_SHIFT  26
34baa489faSSeongJae Park #define MAP_HUGE_MASK   0x3f
35baa489faSSeongJae Park #if !defined(MAP_HUGETLB)
36baa489faSSeongJae Park #define MAP_HUGETLB	0x40000
37baa489faSSeongJae Park #endif
38baa489faSSeongJae Park 
39baa489faSSeongJae Park #define SHM_HUGETLB     04000   /* segment will use huge TLB pages */
40baa489faSSeongJae Park #define SHM_HUGE_SHIFT  26
41baa489faSSeongJae Park #define SHM_HUGE_MASK   0x3f
42baa489faSSeongJae Park #define SHM_HUGE_2MB    (21 << SHM_HUGE_SHIFT)
43baa489faSSeongJae Park #define SHM_HUGE_1GB    (30 << SHM_HUGE_SHIFT)
44baa489faSSeongJae Park 
45baa489faSSeongJae Park #define NUM_PAGESIZES   5
46baa489faSSeongJae Park 
47baa489faSSeongJae Park #define NUM_PAGES 4
48baa489faSSeongJae Park 
49baa489faSSeongJae Park #define Dprintf(fmt...) // printf(fmt)
50baa489faSSeongJae Park 
51baa489faSSeongJae Park unsigned long page_sizes[NUM_PAGESIZES];
52baa489faSSeongJae Park int num_page_sizes;
53baa489faSSeongJae Park 
ilog2(unsigned long v)54baa489faSSeongJae Park int ilog2(unsigned long v)
55baa489faSSeongJae Park {
56baa489faSSeongJae Park 	int l = 0;
57baa489faSSeongJae Park 	while ((1UL << l) < v)
58baa489faSSeongJae Park 		l++;
59baa489faSSeongJae Park 	return l;
60baa489faSSeongJae Park }
61baa489faSSeongJae Park 
find_pagesizes(void)62baa489faSSeongJae Park void find_pagesizes(void)
63baa489faSSeongJae Park {
64baa489faSSeongJae Park 	glob_t g;
65baa489faSSeongJae Park 	int i;
66baa489faSSeongJae Park 	glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g);
67baa489faSSeongJae Park 	assert(g.gl_pathc <= NUM_PAGESIZES);
68baa489faSSeongJae Park 	for (i = 0; i < g.gl_pathc; i++) {
69baa489faSSeongJae Park 		sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB",
70baa489faSSeongJae Park 				&page_sizes[i]);
71baa489faSSeongJae Park 		page_sizes[i] <<= 10;
72baa489faSSeongJae Park 		printf("Found %luMB\n", page_sizes[i] >> 20);
73baa489faSSeongJae Park 	}
74baa489faSSeongJae Park 	num_page_sizes = g.gl_pathc;
75baa489faSSeongJae Park 	globfree(&g);
76baa489faSSeongJae Park }
77baa489faSSeongJae Park 
show(unsigned long ps)78baa489faSSeongJae Park void show(unsigned long ps)
79baa489faSSeongJae Park {
80baa489faSSeongJae Park 	char buf[100];
81baa489faSSeongJae Park 	if (ps == getpagesize())
82baa489faSSeongJae Park 		return;
83baa489faSSeongJae Park 	printf("%luMB: ", ps >> 20);
84baa489faSSeongJae Park 	fflush(stdout);
85baa489faSSeongJae Park 	snprintf(buf, sizeof buf,
86baa489faSSeongJae Park 		"cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages",
87baa489faSSeongJae Park 		ps >> 10);
88baa489faSSeongJae Park 	system(buf);
89baa489faSSeongJae Park }
90baa489faSSeongJae Park 
read_sysfs(int warn,char * fmt,...)91baa489faSSeongJae Park unsigned long read_sysfs(int warn, char *fmt, ...)
92baa489faSSeongJae Park {
93baa489faSSeongJae Park 	char *line = NULL;
94baa489faSSeongJae Park 	size_t linelen = 0;
95baa489faSSeongJae Park 	char buf[100];
96baa489faSSeongJae Park 	FILE *f;
97baa489faSSeongJae Park 	va_list ap;
98baa489faSSeongJae Park 	unsigned long val = 0;
99baa489faSSeongJae Park 
100baa489faSSeongJae Park 	va_start(ap, fmt);
101baa489faSSeongJae Park 	vsnprintf(buf, sizeof buf, fmt, ap);
102baa489faSSeongJae Park 	va_end(ap);
103baa489faSSeongJae Park 
104baa489faSSeongJae Park 	f = fopen(buf, "r");
105baa489faSSeongJae Park 	if (!f) {
106baa489faSSeongJae Park 		if (warn)
107baa489faSSeongJae Park 			printf("missing %s\n", buf);
108baa489faSSeongJae Park 		return 0;
109baa489faSSeongJae Park 	}
110baa489faSSeongJae Park 	if (getline(&line, &linelen, f) > 0) {
111baa489faSSeongJae Park 		sscanf(line, "%lu", &val);
112baa489faSSeongJae Park 	}
113baa489faSSeongJae Park 	fclose(f);
114baa489faSSeongJae Park 	free(line);
115baa489faSSeongJae Park 	return val;
116baa489faSSeongJae Park }
117baa489faSSeongJae Park 
read_free(unsigned long ps)118baa489faSSeongJae Park unsigned long read_free(unsigned long ps)
119baa489faSSeongJae Park {
120baa489faSSeongJae Park 	return read_sysfs(ps != getpagesize(),
121baa489faSSeongJae Park 			"/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages",
122baa489faSSeongJae Park 			ps >> 10);
123baa489faSSeongJae Park }
124baa489faSSeongJae Park 
test_mmap(unsigned long size,unsigned flags)125baa489faSSeongJae Park void test_mmap(unsigned long size, unsigned flags)
126baa489faSSeongJae Park {
127baa489faSSeongJae Park 	char *map;
128baa489faSSeongJae Park 	unsigned long before, after;
129baa489faSSeongJae Park 	int err;
130baa489faSSeongJae Park 
131baa489faSSeongJae Park 	before = read_free(size);
132baa489faSSeongJae Park 	map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE,
133baa489faSSeongJae Park 			MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0);
134baa489faSSeongJae Park 
135baa489faSSeongJae Park 	if (map == (char *)-1) err("mmap");
136baa489faSSeongJae Park 	memset(map, 0xff, size*NUM_PAGES);
137baa489faSSeongJae Park 	after = read_free(size);
138baa489faSSeongJae Park 	Dprintf("before %lu after %lu diff %ld size %lu\n",
139baa489faSSeongJae Park 		before, after, before - after, size);
140baa489faSSeongJae Park 	assert(size == getpagesize() || (before - after) == NUM_PAGES);
141baa489faSSeongJae Park 	show(size);
142*6e16f513SRyan Roberts 	err = munmap(map, size * NUM_PAGES);
143baa489faSSeongJae Park 	assert(!err);
144baa489faSSeongJae Park }
145baa489faSSeongJae Park 
test_shmget(unsigned long size,unsigned flags)146baa489faSSeongJae Park void test_shmget(unsigned long size, unsigned flags)
147baa489faSSeongJae Park {
148baa489faSSeongJae Park 	int id;
149baa489faSSeongJae Park 	unsigned long before, after;
150baa489faSSeongJae Park 	int err;
151baa489faSSeongJae Park 
152baa489faSSeongJae Park 	before = read_free(size);
153baa489faSSeongJae Park 	id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags);
154baa489faSSeongJae Park 	if (id < 0) err("shmget");
155baa489faSSeongJae Park 
156baa489faSSeongJae Park 	struct shm_info i;
157baa489faSSeongJae Park 	if (shmctl(id, SHM_INFO, (void *)&i) < 0) err("shmctl");
158baa489faSSeongJae Park 	Dprintf("alloc %lu res %lu\n", i.shm_tot, i.shm_rss);
159baa489faSSeongJae Park 
160baa489faSSeongJae Park 
161baa489faSSeongJae Park 	Dprintf("id %d\n", id);
162baa489faSSeongJae Park 	char *map = shmat(id, NULL, 0600);
163baa489faSSeongJae Park 	if (map == (char*)-1) err("shmat");
164baa489faSSeongJae Park 
165baa489faSSeongJae Park 	shmctl(id, IPC_RMID, NULL);
166baa489faSSeongJae Park 
167baa489faSSeongJae Park 	memset(map, 0xff, size*NUM_PAGES);
168baa489faSSeongJae Park 	after = read_free(size);
169baa489faSSeongJae Park 
170baa489faSSeongJae Park 	Dprintf("before %lu after %lu diff %ld size %lu\n",
171baa489faSSeongJae Park 		before, after, before - after, size);
172baa489faSSeongJae Park 	assert(size == getpagesize() || (before - after) == NUM_PAGES);
173baa489faSSeongJae Park 	show(size);
174baa489faSSeongJae Park 	err = shmdt(map);
175baa489faSSeongJae Park 	assert(!err);
176baa489faSSeongJae Park }
177baa489faSSeongJae Park 
sanity_checks(void)178baa489faSSeongJae Park void sanity_checks(void)
179baa489faSSeongJae Park {
180baa489faSSeongJae Park 	int i;
181baa489faSSeongJae Park 	unsigned long largest = getpagesize();
182baa489faSSeongJae Park 
183baa489faSSeongJae Park 	for (i = 0; i < num_page_sizes; i++) {
184baa489faSSeongJae Park 		if (page_sizes[i] > largest)
185baa489faSSeongJae Park 			largest = page_sizes[i];
186baa489faSSeongJae Park 
187baa489faSSeongJae Park 		if (read_free(page_sizes[i]) < NUM_PAGES) {
188baa489faSSeongJae Park 			printf("Not enough huge pages for page size %lu MB, need %u\n",
189baa489faSSeongJae Park 				page_sizes[i] >> 20,
190baa489faSSeongJae Park 				NUM_PAGES);
191baa489faSSeongJae Park 			exit(0);
192baa489faSSeongJae Park 		}
193baa489faSSeongJae Park 	}
194baa489faSSeongJae Park 
195baa489faSSeongJae Park 	if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) {
196baa489faSSeongJae Park 		printf("Please do echo %lu > /proc/sys/kernel/shmmax", largest * NUM_PAGES);
197baa489faSSeongJae Park 		exit(0);
198baa489faSSeongJae Park 	}
199baa489faSSeongJae Park 
200baa489faSSeongJae Park #if defined(__x86_64__)
201baa489faSSeongJae Park 	if (largest != 1U<<30) {
202baa489faSSeongJae Park 		printf("No GB pages available on x86-64\n"
203baa489faSSeongJae Park 		       "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES);
204baa489faSSeongJae Park 		exit(0);
205baa489faSSeongJae Park 	}
206baa489faSSeongJae Park #endif
207baa489faSSeongJae Park }
208baa489faSSeongJae Park 
main(void)209baa489faSSeongJae Park int main(void)
210baa489faSSeongJae Park {
211baa489faSSeongJae Park 	int i;
212baa489faSSeongJae Park 	unsigned default_hps = default_huge_page_size();
213baa489faSSeongJae Park 
214baa489faSSeongJae Park 	find_pagesizes();
215baa489faSSeongJae Park 
216baa489faSSeongJae Park 	sanity_checks();
217baa489faSSeongJae Park 
218baa489faSSeongJae Park 	for (i = 0; i < num_page_sizes; i++) {
219baa489faSSeongJae Park 		unsigned long ps = page_sizes[i];
220baa489faSSeongJae Park 		int arg = ilog2(ps) << MAP_HUGE_SHIFT;
221baa489faSSeongJae Park 		printf("Testing %luMB mmap with shift %x\n", ps >> 20, arg);
222baa489faSSeongJae Park 		test_mmap(ps, MAP_HUGETLB | arg);
223baa489faSSeongJae Park 	}
224baa489faSSeongJae Park 	printf("Testing default huge mmap\n");
225*6e16f513SRyan Roberts 	test_mmap(default_hps, MAP_HUGETLB);
226baa489faSSeongJae Park 
227baa489faSSeongJae Park 	puts("Testing non-huge shmget");
228baa489faSSeongJae Park 	test_shmget(getpagesize(), 0);
229baa489faSSeongJae Park 
230baa489faSSeongJae Park 	for (i = 0; i < num_page_sizes; i++) {
231baa489faSSeongJae Park 		unsigned long ps = page_sizes[i];
232baa489faSSeongJae Park 		int arg = ilog2(ps) << SHM_HUGE_SHIFT;
233baa489faSSeongJae Park 		printf("Testing %luMB shmget with shift %x\n", ps >> 20, arg);
234baa489faSSeongJae Park 		test_shmget(ps, SHM_HUGETLB | arg);
235baa489faSSeongJae Park 	}
236baa489faSSeongJae Park 	puts("default huge shmget");
237baa489faSSeongJae Park 	test_shmget(default_hps, SHM_HUGETLB);
238baa489faSSeongJae Park 
239baa489faSSeongJae Park 	return 0;
240baa489faSSeongJae Park }
241