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