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