1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * hugepage-mremap:
4  *
5  * Example of remapping huge page memory in a user application using the
6  * mremap system call.  Code assumes a hugetlbfs filesystem is mounted
7  * at './huge'.  The code will use 10MB worth of huge pages.
8  */
9 
10 #define _GNU_SOURCE
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <sys/mman.h>
15 #include <errno.h>
16 #include <fcntl.h> /* Definition of O_* constants */
17 #include <sys/syscall.h> /* Definition of SYS_* constants */
18 #include <linux/userfaultfd.h>
19 #include <sys/ioctl.h>
20 
21 #define LENGTH (1UL * 1024 * 1024 * 1024)
22 
23 #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC)
24 #define FLAGS (MAP_SHARED | MAP_ANONYMOUS)
25 
check_bytes(char * addr)26 static void check_bytes(char *addr)
27 {
28 	printf("First hex is %x\n", *((unsigned int *)addr));
29 }
30 
write_bytes(char * addr)31 static void write_bytes(char *addr)
32 {
33 	unsigned long i;
34 
35 	for (i = 0; i < LENGTH; i++)
36 		*(addr + i) = (char)i;
37 }
38 
read_bytes(char * addr)39 static int read_bytes(char *addr)
40 {
41 	unsigned long i;
42 
43 	check_bytes(addr);
44 	for (i = 0; i < LENGTH; i++)
45 		if (*(addr + i) != (char)i) {
46 			printf("Mismatch at %lu\n", i);
47 			return 1;
48 		}
49 	return 0;
50 }
51 
register_region_with_uffd(char * addr,size_t len)52 static void register_region_with_uffd(char *addr, size_t len)
53 {
54 	long uffd; /* userfaultfd file descriptor */
55 	struct uffdio_api uffdio_api;
56 	struct uffdio_register uffdio_register;
57 
58 	/* Create and enable userfaultfd object. */
59 
60 	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
61 	if (uffd == -1) {
62 		perror("userfaultfd");
63 		exit(1);
64 	}
65 
66 	uffdio_api.api = UFFD_API;
67 	uffdio_api.features = 0;
68 	if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) {
69 		perror("ioctl-UFFDIO_API");
70 		exit(1);
71 	}
72 
73 	/* Create a private anonymous mapping. The memory will be
74 	 * demand-zero paged--that is, not yet allocated. When we
75 	 * actually touch the memory, it will be allocated via
76 	 * the userfaultfd.
77 	 */
78 
79 	addr = mmap(NULL, len, PROT_READ | PROT_WRITE,
80 		    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
81 	if (addr == MAP_FAILED) {
82 		perror("mmap");
83 		exit(1);
84 	}
85 
86 	printf("Address returned by mmap() = %p\n", addr);
87 
88 	/* Register the memory range of the mapping we just created for
89 	 * handling by the userfaultfd object. In mode, we request to track
90 	 * missing pages (i.e., pages that have not yet been faulted in).
91 	 */
92 
93 	uffdio_register.range.start = (unsigned long)addr;
94 	uffdio_register.range.len = len;
95 	uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
96 	if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) {
97 		perror("ioctl-UFFDIO_REGISTER");
98 		exit(1);
99 	}
100 }
101 
main(void)102 int main(void)
103 {
104 	int ret = 0;
105 
106 	int fd = open("/huge/test", O_CREAT | O_RDWR, 0755);
107 
108 	if (fd < 0) {
109 		perror("Open failed");
110 		exit(1);
111 	}
112 
113 	/* mmap to a PUD aligned address to hopefully trigger pmd sharing. */
114 	unsigned long suggested_addr = 0x7eaa40000000;
115 	void *haddr = mmap((void *)suggested_addr, LENGTH, PROTECTION,
116 			   MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
117 	printf("Map haddr: Returned address is %p\n", haddr);
118 	if (haddr == MAP_FAILED) {
119 		perror("mmap1");
120 		exit(1);
121 	}
122 
123 	/* mmap again to a dummy address to hopefully trigger pmd sharing. */
124 	suggested_addr = 0x7daa40000000;
125 	void *daddr = mmap((void *)suggested_addr, LENGTH, PROTECTION,
126 			   MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0);
127 	printf("Map daddr: Returned address is %p\n", daddr);
128 	if (daddr == MAP_FAILED) {
129 		perror("mmap3");
130 		exit(1);
131 	}
132 
133 	suggested_addr = 0x7faa40000000;
134 	void *vaddr =
135 		mmap((void *)suggested_addr, LENGTH, PROTECTION, FLAGS, -1, 0);
136 	printf("Map vaddr: Returned address is %p\n", vaddr);
137 	if (vaddr == MAP_FAILED) {
138 		perror("mmap2");
139 		exit(1);
140 	}
141 
142 	register_region_with_uffd(haddr, LENGTH);
143 
144 	void *addr = mremap(haddr, LENGTH, LENGTH,
145 			    MREMAP_MAYMOVE | MREMAP_FIXED, vaddr);
146 	if (addr == MAP_FAILED) {
147 		perror("mremap");
148 		exit(1);
149 	}
150 
151 	printf("Mremap: Returned address is %p\n", addr);
152 	check_bytes(addr);
153 	write_bytes(addr);
154 	ret = read_bytes(addr);
155 
156 	munmap(addr, LENGTH);
157 
158 	return ret;
159 }
160