xref: /linux/tools/testing/selftests/mm/thuge-gen.c (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Test selecting other page sizes for mmap/shmget. */
3 
4 #define _GNU_SOURCE
5 #include <sys/mman.h>
6 #include <linux/mman.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <sys/ipc.h>
10 #include <sys/shm.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include "vm_util.h"
16 #include "kselftest.h"
17 #include "hugepage_settings.h"
18 
19 #if !defined(MAP_HUGETLB)
20 #define MAP_HUGETLB	0x40000
21 #endif
22 
23 #define SHM_HUGETLB     04000   /* segment will use huge TLB pages */
24 #ifndef SHM_HUGE_SHIFT
25 #define SHM_HUGE_SHIFT  26
26 #endif
27 
28 #define NUM_PAGESIZES   5
29 #define NUM_PAGES 4
30 
31 unsigned long page_sizes[NUM_PAGESIZES];
32 int num_page_sizes;
33 
34 int ilog2(unsigned long v)
35 {
36 	int l = 0;
37 	while ((1UL << l) < v)
38 		l++;
39 	return l;
40 }
41 
42 void show(unsigned long ps)
43 {
44 	if (ps == getpagesize())
45 		return;
46 
47 	ksft_print_msg("%luMB: %lu\n", ps >> 20, hugetlb_free_pages(ps));
48 }
49 
50 void test_mmap(unsigned long size, unsigned flags)
51 {
52 	char *map;
53 	unsigned long before, after;
54 
55 	before = hugetlb_free_pages(size);
56 	map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE,
57 			MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, -1, 0);
58 	if (map == MAP_FAILED)
59 		ksft_exit_fail_msg("mmap: %s\n", strerror(errno));
60 
61 	memset(map, 0xff, size*NUM_PAGES);
62 	after = hugetlb_free_pages(size);
63 
64 	show(size);
65 	ksft_test_result(size == getpagesize() || (before - after) == NUM_PAGES,
66 			 "%s mmap %lu %x\n", __func__, size, flags);
67 
68 	if (munmap(map, size * NUM_PAGES))
69 		ksft_exit_fail_msg("%s: unmap %s\n", __func__, strerror(errno));
70 }
71 
72 void test_shmget(unsigned long size, unsigned flags)
73 {
74 	/* values for PAGE_SIZE test */
75 	unsigned long before = NUM_PAGES;
76 	unsigned long after = 0;
77 	struct shm_info i;
78 	char *map;
79 	int id;
80 
81 	if (size != getpagesize())
82 		before = hugetlb_free_pages(size);
83 
84 	id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags);
85 	if (id < 0) {
86 		if (errno == EPERM) {
87 			ksft_test_result_skip("shmget requires root privileges: %s\n",
88 					      strerror(errno));
89 			return;
90 		}
91 		ksft_exit_fail_msg("shmget: %s\n", strerror(errno));
92 	}
93 
94 	if (shmctl(id, SHM_INFO, (void *)&i) < 0)
95 		ksft_exit_fail_msg("shmctl: %s\n", strerror(errno));
96 
97 	map = shmat(id, NULL, 0600);
98 	if (map == MAP_FAILED)
99 		ksft_exit_fail_msg("shmat: %s\n", strerror(errno));
100 
101 	shmctl(id, IPC_RMID, NULL);
102 
103 	memset(map, 0xff, size*NUM_PAGES);
104 	if (size != getpagesize())
105 		after = hugetlb_free_pages(size);
106 
107 	show(size);
108 	ksft_test_result((before - after) == NUM_PAGES,
109 			 "%s: mmap %lu %x\n", __func__, size, flags);
110 	if (shmdt(map))
111 		ksft_exit_fail_msg("%s: shmdt: %s\n", __func__, strerror(errno));
112 }
113 
114 void find_pagesizes(void)
115 {
116 	unsigned long largest = getpagesize();
117 	int i;
118 
119 	num_page_sizes = hugetlb_setup(NUM_PAGES, page_sizes, ARRAY_SIZE(page_sizes));
120 
121 	for (i = 0; i < num_page_sizes; i++)
122 		if (page_sizes[i] > largest)
123 			largest = page_sizes[i];
124 
125 	shm_limits_prepare(NUM_PAGES * largest);
126 }
127 
128 int main(void)
129 {
130 	unsigned default_hps = default_huge_page_size();
131 	int i;
132 
133 	ksft_print_header();
134 
135 	find_pagesizes();
136 
137 	if (!num_page_sizes)
138 		ksft_finished();
139 
140 	ksft_set_plan(2 * num_page_sizes + 3);
141 
142 	for (i = 0; i < num_page_sizes; i++) {
143 		unsigned long ps = page_sizes[i];
144 		int arg = ilog2(ps) << MAP_HUGE_SHIFT;
145 
146 		ksft_print_msg("Testing %luMB mmap with shift %x\n", ps >> 20, arg);
147 		test_mmap(ps, MAP_HUGETLB | arg);
148 	}
149 
150 	ksft_print_msg("Testing default huge mmap\n");
151 	test_mmap(default_hps, MAP_HUGETLB);
152 
153 	ksft_print_msg("Testing non-huge shmget\n");
154 	test_shmget(getpagesize(), 0);
155 
156 	for (i = 0; i < num_page_sizes; i++) {
157 		unsigned long ps = page_sizes[i];
158 		int arg = ilog2(ps) << SHM_HUGE_SHIFT;
159 		ksft_print_msg("Testing %luMB shmget with shift %x\n", ps >> 20, arg);
160 		test_shmget(ps, SHM_HUGETLB | arg);
161 	}
162 
163 	ksft_print_msg("default huge shmget\n");
164 	test_shmget(default_hps, SHM_HUGETLB);
165 
166 	ksft_finished();
167 }
168 
169 SHM_LIMITS_RESTORE()
170