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. The path to a file in a hugetlbfs filesystem must |
7 | * be passed as the last argument to this test. The amount of memory used |
8 | * by this test in MBs can optionally be passed as an argument. If no memory |
9 | * amount is passed, the default amount is 10MB. |
10 | * |
11 | * To make sure the test triggers pmd sharing and goes through the 'unshare' |
12 | * path in the mremap code use 1GB (1024) or more. |
13 | */ |
14 | |
15 | #define _GNU_SOURCE |
16 | #include <stdlib.h> |
17 | #include <stdio.h> |
18 | #include <unistd.h> |
19 | #include <sys/mman.h> |
20 | #include <errno.h> |
21 | #include <fcntl.h> /* Definition of O_* constants */ |
22 | #include <sys/syscall.h> /* Definition of SYS_* constants */ |
23 | #include <linux/userfaultfd.h> |
24 | #include <sys/ioctl.h> |
25 | #include <string.h> |
26 | #include <stdbool.h> |
27 | #include "../kselftest.h" |
28 | #include "vm_util.h" |
29 | |
30 | #define DEFAULT_LENGTH_MB 10UL |
31 | #define MB_TO_BYTES(x) (x * 1024 * 1024) |
32 | |
33 | #define PROTECTION (PROT_READ | PROT_WRITE | PROT_EXEC) |
34 | #define FLAGS (MAP_SHARED | MAP_ANONYMOUS) |
35 | |
36 | static void check_bytes(char *addr) |
37 | { |
38 | ksft_print_msg(msg: "First hex is %x\n" , *((unsigned int *)addr)); |
39 | } |
40 | |
41 | static void write_bytes(char *addr, size_t len) |
42 | { |
43 | unsigned long i; |
44 | |
45 | for (i = 0; i < len; i++) |
46 | *(addr + i) = (char)i; |
47 | } |
48 | |
49 | static int read_bytes(char *addr, size_t len) |
50 | { |
51 | unsigned long i; |
52 | |
53 | check_bytes(addr); |
54 | for (i = 0; i < len; i++) |
55 | if (*(addr + i) != (char)i) { |
56 | ksft_print_msg(msg: "Mismatch at %lu\n" , i); |
57 | return 1; |
58 | } |
59 | return 0; |
60 | } |
61 | |
62 | static void register_region_with_uffd(char *addr, size_t len) |
63 | { |
64 | long uffd; /* userfaultfd file descriptor */ |
65 | struct uffdio_api uffdio_api; |
66 | |
67 | /* Create and enable userfaultfd object. */ |
68 | |
69 | uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); |
70 | if (uffd == -1) |
71 | ksft_exit_fail_msg(msg: "userfaultfd: %s\n" , strerror(errno)); |
72 | |
73 | uffdio_api.api = UFFD_API; |
74 | uffdio_api.features = 0; |
75 | if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) |
76 | ksft_exit_fail_msg(msg: "ioctl-UFFDIO_API: %s\n" , strerror(errno)); |
77 | |
78 | /* Create a private anonymous mapping. The memory will be |
79 | * demand-zero paged--that is, not yet allocated. When we |
80 | * actually touch the memory, it will be allocated via |
81 | * the userfaultfd. |
82 | */ |
83 | |
84 | addr = mmap(NULL, len, PROT_READ | PROT_WRITE, |
85 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
86 | if (addr == MAP_FAILED) |
87 | ksft_exit_fail_msg(msg: "mmap: %s\n" , strerror(errno)); |
88 | |
89 | ksft_print_msg(msg: "Address returned by mmap() = %p\n" , addr); |
90 | |
91 | /* Register the memory range of the mapping we just created for |
92 | * handling by the userfaultfd object. In mode, we request to track |
93 | * missing pages (i.e., pages that have not yet been faulted in). |
94 | */ |
95 | if (uffd_register(uffd, addr, len, miss: true, wp: false, minor: false)) |
96 | ksft_exit_fail_msg(msg: "ioctl-UFFDIO_REGISTER: %s\n" , strerror(errno)); |
97 | } |
98 | |
99 | int main(int argc, char *argv[]) |
100 | { |
101 | size_t length = 0; |
102 | int ret = 0, fd; |
103 | |
104 | ksft_print_header(); |
105 | ksft_set_plan(plan: 1); |
106 | |
107 | if (argc >= 2 && !strcmp(argv[1], "-h" )) |
108 | ksft_exit_fail_msg(msg: "Usage: %s [length_in_MB]\n" , argv[0]); |
109 | |
110 | /* Read memory length as the first arg if valid, otherwise fallback to |
111 | * the default length. |
112 | */ |
113 | if (argc >= 2) |
114 | length = (size_t)atoi(argv[1]); |
115 | else |
116 | length = DEFAULT_LENGTH_MB; |
117 | |
118 | length = MB_TO_BYTES(length); |
119 | fd = memfd_create(argv[0], MFD_HUGETLB); |
120 | if (fd < 0) |
121 | ksft_exit_fail_msg(msg: "Open failed: %s\n" , strerror(errno)); |
122 | |
123 | /* mmap to a PUD aligned address to hopefully trigger pmd sharing. */ |
124 | unsigned long suggested_addr = 0x7eaa40000000; |
125 | void *haddr = mmap((void *)suggested_addr, length, PROTECTION, |
126 | MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); |
127 | ksft_print_msg(msg: "Map haddr: Returned address is %p\n" , haddr); |
128 | if (haddr == MAP_FAILED) |
129 | ksft_exit_fail_msg(msg: "mmap1: %s\n" , strerror(errno)); |
130 | |
131 | /* mmap again to a dummy address to hopefully trigger pmd sharing. */ |
132 | suggested_addr = 0x7daa40000000; |
133 | void *daddr = mmap((void *)suggested_addr, length, PROTECTION, |
134 | MAP_HUGETLB | MAP_SHARED | MAP_POPULATE, fd, 0); |
135 | ksft_print_msg(msg: "Map daddr: Returned address is %p\n" , daddr); |
136 | if (daddr == MAP_FAILED) |
137 | ksft_exit_fail_msg(msg: "mmap3: %s\n" , strerror(errno)); |
138 | |
139 | suggested_addr = 0x7faa40000000; |
140 | void *vaddr = |
141 | mmap((void *)suggested_addr, length, PROTECTION, FLAGS, -1, 0); |
142 | ksft_print_msg(msg: "Map vaddr: Returned address is %p\n" , vaddr); |
143 | if (vaddr == MAP_FAILED) |
144 | ksft_exit_fail_msg(msg: "mmap2: %s\n" , strerror(errno)); |
145 | |
146 | register_region_with_uffd(addr: haddr, len: length); |
147 | |
148 | void *addr = mremap(haddr, length, length, |
149 | MREMAP_MAYMOVE | MREMAP_FIXED, vaddr); |
150 | if (addr == MAP_FAILED) |
151 | ksft_exit_fail_msg(msg: "mremap: %s\n" , strerror(errno)); |
152 | |
153 | ksft_print_msg(msg: "Mremap: Returned address is %p\n" , addr); |
154 | check_bytes(addr); |
155 | write_bytes(addr, len: length); |
156 | ret = read_bytes(addr, len: length); |
157 | |
158 | munmap(addr, length); |
159 | |
160 | addr = mremap(addr, length, length, 0); |
161 | if (addr != MAP_FAILED) |
162 | ksft_exit_fail_msg(msg: "mremap: Expected failure, but call succeeded\n" ); |
163 | |
164 | close(fd); |
165 | |
166 | ksft_test_result(!ret, "Read same data\n" ); |
167 | ksft_exit(!ret); |
168 | } |
169 | |