File Index
Files and directories
Sign in
FILE

va_high_addr_switch.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/mm/va_high_addr_switch.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *
  4 * Authors: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
  5 * Authors: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
  6 */
  7
  8#include <stdio.h>
  9#include <sys/mman.h>
 10#include <string.h>
 11
 12#include "vm_util.h"
 13#include "kselftest.h"
 14
 15/*
 16 * The hint addr value is used to allocate addresses
 17 * beyond the high address switch boundary.
 18 */
 19
 20#define ADDR_MARK_128TB	(1UL << 47)
 21#define ADDR_MARK_256TB	(1UL << 48)
 22
 23#define HIGH_ADDR_128TB	(1UL << 48)
 24#define HIGH_ADDR_256TB	(1UL << 49)
 25
 26struct testcase {
 27	void *addr;
 28	unsigned long size;
 29	unsigned long flags;
 30	const char *msg;
 31	unsigned int low_addr_required:1;
 32	unsigned int keep_mapped:1;
 33};
 34
 35static struct testcase *testcases;
 36static struct testcase *hugetlb_testcases;
 37static int sz_testcases, sz_hugetlb_testcases;
 38static unsigned long switch_hint;
 39
 40/* Initialize testcases inside a function to compute parameters at runtime */
 41void testcases_init(void)
 42{
 43	unsigned long pagesize = getpagesize();
 44	unsigned long hugepagesize = default_huge_page_size();
 45	unsigned long low_addr = (1UL << 30);
 46	unsigned long addr_switch_hint = ADDR_MARK_128TB;
 47	unsigned long high_addr = HIGH_ADDR_128TB;
 48
 49#ifdef __aarch64__
 50
 51	/* Post LPA2, the lower userspace VA on a 16K pagesize is 47 bits. */
 52	if (pagesize != (16UL << 10)) {
 53		addr_switch_hint = ADDR_MARK_256TB;
 54		high_addr = HIGH_ADDR_256TB;
 55	}
 56#endif
 57
 58	struct testcase t[] = {
 59		{
 60			/*
 61			 * If stack is moved, we could possibly allocate
 62			 * this at the requested address.
 63			 */
 64			.addr = ((void *)(addr_switch_hint - pagesize)),
 65			.size = pagesize,
 66			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
 67			.msg = "mmap(addr_switch_hint - pagesize, pagesize)",
 68			.low_addr_required = 1,
 69		},
 70		{
 71			/*
 72			 * Unless MAP_FIXED is specified, allocation based on hint
 73			 * addr is never at requested address or above it, which is
 74			 * beyond high address switch boundary in this case. Instead,
 75			 * a suitable allocation is found in lower address space.
 76			 */
 77			.addr = ((void *)(addr_switch_hint - pagesize)),
 78			.size = 2 * pagesize,
 79			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
 80			.msg = "mmap(addr_switch_hint - pagesize, (2 * pagesize))",
 81			.low_addr_required = 1,
 82		},
 83		{
 84			/*
 85			 * Exact mapping at high address switch boundary, should
 86			 * be obtained even without MAP_FIXED as area is free.
 87			 */
 88			.addr = ((void *)(addr_switch_hint)),
 89			.size = pagesize,
 90			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
 91			.msg = "mmap(addr_switch_hint, pagesize)",
 92			.keep_mapped = 1,
 93		},
 94		{
 95			.addr = (void *)(addr_switch_hint),
 96			.size = 2 * pagesize,
 97			.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
 98			.msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)",
 99		},
100		{
101			.addr = NULL,
102			.size = 2 * pagesize,
103			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
104			.msg = "mmap(NULL)",
105			.low_addr_required = 1,
106		},
107		{
108			.addr = (void *)low_addr,
109			.size = 2 * pagesize,
110			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
111			.msg = "mmap(low_addr)",
112			.low_addr_required = 1,
113		},
114		{
115			.addr = (void *)high_addr,
116			.size = 2 * pagesize,
117			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
118			.msg = "mmap(high_addr)",
119			.keep_mapped = 1,
120		},
121		{
122			.addr = (void *)high_addr,
123			.size = 2 * pagesize,
124			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
125			.msg = "mmap(high_addr) again",
126			.keep_mapped = 1,
127		},
128		{
129			.addr = (void *)high_addr,
130			.size = 2 * pagesize,
131			.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
132			.msg = "mmap(high_addr, MAP_FIXED)",
133		},
134		{
135			.addr = (void *) -1,
136			.size = 2 * pagesize,
137			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
138			.msg = "mmap(-1)",
139			.keep_mapped = 1,
140		},
141		{
142			.addr = (void *) -1,
143			.size = 2 * pagesize,
144			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
145			.msg = "mmap(-1) again",
146		},
147		{
148			.addr = ((void *)(addr_switch_hint - pagesize)),
149			.size = pagesize,
150			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
151			.msg = "mmap(addr_switch_hint - pagesize, pagesize)",
152			.low_addr_required = 1,
153		},
154		{
155			.addr = (void *)(addr_switch_hint - pagesize),
156			.size = 2 * pagesize,
157			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
158			.msg = "mmap(addr_switch_hint - pagesize, 2 * pagesize)",
159			.low_addr_required = 1,
160			.keep_mapped = 1,
161		},
162		{
163			.addr = (void *)(addr_switch_hint - pagesize / 2),
164			.size = 2 * pagesize,
165			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
166			.msg = "mmap(addr_switch_hint - pagesize/2 , 2 * pagesize)",
167			.low_addr_required = 1,
168			.keep_mapped = 1,
169		},
170		{
171			.addr = ((void *)(addr_switch_hint)),
172			.size = pagesize,
173			.flags = MAP_PRIVATE | MAP_ANONYMOUS,
174			.msg = "mmap(addr_switch_hint, pagesize)",
175		},
176		{
177			.addr = (void *)(addr_switch_hint),
178			.size = 2 * pagesize,
179			.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
180			.msg = "mmap(addr_switch_hint, 2 * pagesize, MAP_FIXED)",
181		},
182	};
183
184	struct testcase ht[] = {
185		{
186			.addr = NULL,
187			.size = hugepagesize,
188			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
189			.msg = "mmap(NULL, MAP_HUGETLB)",
190			.low_addr_required = 1,
191		},
192		{
193			.addr = (void *)low_addr,
194			.size = hugepagesize,
195			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
196			.msg = "mmap(low_addr, MAP_HUGETLB)",
197			.low_addr_required = 1,
198		},
199		{
200			.addr = (void *)high_addr,
201			.size = hugepagesize,
202			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
203			.msg = "mmap(high_addr, MAP_HUGETLB)",
204			.keep_mapped = 1,
205		},
206		{
207			.addr = (void *)high_addr,
208			.size = hugepagesize,
209			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
210			.msg = "mmap(high_addr, MAP_HUGETLB) again",
211			.keep_mapped = 1,
212		},
213		{
214			.addr = (void *)high_addr,
215			.size = hugepagesize,
216			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
217			.msg = "mmap(high_addr, MAP_FIXED | MAP_HUGETLB)",
218		},
219		{
220			.addr = (void *) -1,
221			.size = hugepagesize,
222			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
223			.msg = "mmap(-1, MAP_HUGETLB)",
224			.keep_mapped = 1,
225		},
226		{
227			.addr = (void *) -1,
228			.size = hugepagesize,
229			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
230			.msg = "mmap(-1, MAP_HUGETLB) again",
231		},
232		{
233			.addr = (void *)(addr_switch_hint - hugepagesize),
234			.size = 2 * hugepagesize,
235			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
236			.msg = "mmap(addr_switch_hint - hugepagesize, 2*hugepagesize, MAP_HUGETLB)",
237			.low_addr_required = 1,
238			.keep_mapped = 1,
239		},
240		{
241			.addr = (void *)(addr_switch_hint),
242			.size = 2 * hugepagesize,
243			.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
244			.msg = "mmap(addr_switch_hint , 2*hugepagesize, MAP_FIXED | MAP_HUGETLB)",
245		},
246	};
247
248	testcases = malloc(sizeof(t));
249	hugetlb_testcases = malloc(sizeof(ht));
250
251	/* Copy into global arrays */
252	memcpy(testcases, t, sizeof(t));
253	memcpy(hugetlb_testcases, ht, sizeof(ht));
254
255	sz_testcases = ARRAY_SIZE(t);
256	sz_hugetlb_testcases = ARRAY_SIZE(ht);
257	switch_hint = addr_switch_hint;
258}
259
260static int run_test(struct testcase *test, int count)
261{
262	void *p;
263	int i, ret = KSFT_PASS;
264
265	for (i = 0; i < count; i++) {
266		struct testcase *t = test + i;
267
268		p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
269
270		printf("%s: %p - ", t->msg, p);
271
272		if (p == MAP_FAILED) {
273			printf("FAILED\n");
274			ret = KSFT_FAIL;
275			continue;
276		}
277
278		if (t->low_addr_required && p >= (void *)(switch_hint)) {
279			printf("FAILED\n");
280			ret = KSFT_FAIL;
281		} else {
282			/*
283			 * Do a dereference of the address returned so that we catch
284			 * bugs in page fault handling
285			 */
286			memset(p, 0, t->size);
287			printf("OK\n");
288		}
289		if (!t->keep_mapped)
290			munmap(p, t->size);
291	}
292
293	return ret;
294}
295
296#ifdef __aarch64__
297/* Check if userspace VA > 48 bits */
298static int high_address_present(void)
299{
300	void *ptr = mmap((void *)(1UL << 50), 1, PROT_READ | PROT_WRITE,
301			 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
302	if (ptr == MAP_FAILED)
303		return 0;
304
305	munmap(ptr, 1);
306	return 1;
307}
308#endif
309
310static int supported_arch(void)
311{
312#if defined(__powerpc64__)
313	return 1;
314#elif defined(__x86_64__)
315	return 1;
316#elif defined(__aarch64__)
317	return high_address_present();
318#else
319	return 0;
320#endif
321}
322
323int main(int argc, char **argv)
324{
325	int ret;
326
327	if (!supported_arch())
328		return KSFT_SKIP;
329
330	testcases_init();
331
332	ret = run_test(testcases, sz_testcases);
333	if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
334		ret = run_test(hugetlb_testcases, sz_hugetlb_testcases);
335	return ret;
336}
~/ Read-only file preview.