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