.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Stress userfaultfd syscall. |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2015 Red Hat, Inc. |
---|
5 | | - * |
---|
6 | | - * This work is licensed under the terms of the GNU GPL, version 2. See |
---|
7 | | - * the COPYING file in the top-level directory. |
---|
8 | 6 | * |
---|
9 | 7 | * This test allocates two virtual areas and bounces the physical |
---|
10 | 8 | * memory across the two virtual areas (from area_src to area_dst) |
---|
.. | .. |
---|
34 | 32 | * per-CPU threads 1 by triggering userfaults inside |
---|
35 | 33 | * pthread_mutex_lock will also verify the atomicity of the memory |
---|
36 | 34 | * transfer (UFFDIO_COPY). |
---|
37 | | - * |
---|
38 | | - * The program takes two parameters: the amounts of physical memory in |
---|
39 | | - * megabytes (MiB) of the area and the number of bounces to execute. |
---|
40 | | - * |
---|
41 | | - * # 100MiB 99999 bounces |
---|
42 | | - * ./userfaultfd 100 99999 |
---|
43 | | - * |
---|
44 | | - * # 1GiB 99 bounces |
---|
45 | | - * ./userfaultfd 1000 99 |
---|
46 | | - * |
---|
47 | | - * # 10MiB-~6GiB 999 bounces, continue forever unless an error triggers |
---|
48 | | - * while ./userfaultfd $[RANDOM % 6000 + 10] 999; do true; done |
---|
49 | 35 | */ |
---|
50 | 36 | |
---|
51 | 37 | #define _GNU_SOURCE |
---|
.. | .. |
---|
60 | 46 | #include <signal.h> |
---|
61 | 47 | #include <poll.h> |
---|
62 | 48 | #include <string.h> |
---|
| 49 | +#include <linux/mman.h> |
---|
63 | 50 | #include <sys/mman.h> |
---|
64 | 51 | #include <sys/syscall.h> |
---|
65 | 52 | #include <sys/ioctl.h> |
---|
.. | .. |
---|
68 | 55 | #include <linux/userfaultfd.h> |
---|
69 | 56 | #include <setjmp.h> |
---|
70 | 57 | #include <stdbool.h> |
---|
| 58 | +#include <assert.h> |
---|
71 | 59 | |
---|
72 | 60 | #include "../kselftest.h" |
---|
73 | 61 | |
---|
.. | .. |
---|
90 | 78 | #define ALARM_INTERVAL_SECS 10 |
---|
91 | 79 | static volatile bool test_uffdio_copy_eexist = true; |
---|
92 | 80 | static volatile bool test_uffdio_zeropage_eexist = true; |
---|
| 81 | +/* Whether to test uffd write-protection */ |
---|
| 82 | +static bool test_uffdio_wp = false; |
---|
| 83 | +/* Whether to test uffd minor faults */ |
---|
| 84 | +static bool test_uffdio_minor = false; |
---|
93 | 85 | |
---|
94 | 86 | static bool map_shared; |
---|
| 87 | +static int shm_fd; |
---|
95 | 88 | static int huge_fd; |
---|
96 | 89 | static char *huge_fd_off0; |
---|
97 | 90 | static unsigned long long *count_verify; |
---|
98 | | -static int uffd, uffd_flags, finished, *pipefd; |
---|
| 91 | +static int uffd = -1; |
---|
| 92 | +static int uffd_flags, finished, *pipefd; |
---|
99 | 93 | static char *area_src, *area_src_alias, *area_dst, *area_dst_alias; |
---|
100 | 94 | static char *zeropage; |
---|
101 | 95 | pthread_attr_t attr; |
---|
| 96 | + |
---|
| 97 | +/* Userfaultfd test statistics */ |
---|
| 98 | +struct uffd_stats { |
---|
| 99 | + int cpu; |
---|
| 100 | + unsigned long missing_faults; |
---|
| 101 | + unsigned long wp_faults; |
---|
| 102 | + unsigned long minor_faults; |
---|
| 103 | +}; |
---|
102 | 104 | |
---|
103 | 105 | /* pthread_mutex_t starts at page offset 0 */ |
---|
104 | 106 | #define area_mutex(___area, ___nr) \ |
---|
.. | .. |
---|
115 | 117 | ~(unsigned long)(sizeof(unsigned long long) \ |
---|
116 | 118 | - 1))) |
---|
117 | 119 | |
---|
118 | | -static int anon_release_pages(char *rel_area) |
---|
119 | | -{ |
---|
120 | | - int ret = 0; |
---|
| 120 | +const char *examples = |
---|
| 121 | + "# Run anonymous memory test on 100MiB region with 99999 bounces:\n" |
---|
| 122 | + "./userfaultfd anon 100 99999\n\n" |
---|
| 123 | + "# Run share memory test on 1GiB region with 99 bounces:\n" |
---|
| 124 | + "./userfaultfd shmem 1000 99\n\n" |
---|
| 125 | + "# Run hugetlb memory test on 256MiB region with 50 bounces (using /dev/hugepages/hugefile):\n" |
---|
| 126 | + "./userfaultfd hugetlb 256 50 /dev/hugepages/hugefile\n\n" |
---|
| 127 | + "# Run the same hugetlb test but using shmem:\n" |
---|
| 128 | + "./userfaultfd hugetlb_shared 256 50 /dev/hugepages/hugefile\n\n" |
---|
| 129 | + "# 10MiB-~6GiB 999 bounces anonymous test, " |
---|
| 130 | + "continue forever unless an error triggers\n" |
---|
| 131 | + "while ./userfaultfd anon $[RANDOM % 6000 + 10] 999; do true; done\n\n"; |
---|
121 | 132 | |
---|
122 | | - if (madvise(rel_area, nr_pages * page_size, MADV_DONTNEED)) { |
---|
123 | | - perror("madvise"); |
---|
124 | | - ret = 1; |
---|
| 133 | +static void usage(void) |
---|
| 134 | +{ |
---|
| 135 | + fprintf(stderr, "\nUsage: ./userfaultfd <test type> <MiB> <bounces> " |
---|
| 136 | + "[hugetlbfs_file]\n\n"); |
---|
| 137 | + fprintf(stderr, "Supported <test type>: anon, hugetlb, " |
---|
| 138 | + "hugetlb_shared, shmem\n\n"); |
---|
| 139 | + fprintf(stderr, "Examples:\n\n"); |
---|
| 140 | + fprintf(stderr, "%s", examples); |
---|
| 141 | + exit(1); |
---|
| 142 | +} |
---|
| 143 | + |
---|
| 144 | +#define _err(fmt, ...) \ |
---|
| 145 | + do { \ |
---|
| 146 | + int ret = errno; \ |
---|
| 147 | + fprintf(stderr, "ERROR: " fmt, ##__VA_ARGS__); \ |
---|
| 148 | + fprintf(stderr, " (errno=%d, line=%d)\n", \ |
---|
| 149 | + ret, __LINE__); \ |
---|
| 150 | + } while (0) |
---|
| 151 | + |
---|
| 152 | +#define err(fmt, ...) \ |
---|
| 153 | + do { \ |
---|
| 154 | + _err(fmt, ##__VA_ARGS__); \ |
---|
| 155 | + exit(1); \ |
---|
| 156 | + } while (0) |
---|
| 157 | + |
---|
| 158 | +static void uffd_stats_reset(struct uffd_stats *uffd_stats, |
---|
| 159 | + unsigned long n_cpus) |
---|
| 160 | +{ |
---|
| 161 | + int i; |
---|
| 162 | + |
---|
| 163 | + for (i = 0; i < n_cpus; i++) { |
---|
| 164 | + uffd_stats[i].cpu = i; |
---|
| 165 | + uffd_stats[i].missing_faults = 0; |
---|
| 166 | + uffd_stats[i].wp_faults = 0; |
---|
| 167 | + uffd_stats[i].minor_faults = 0; |
---|
| 168 | + } |
---|
| 169 | +} |
---|
| 170 | + |
---|
| 171 | +static void uffd_stats_report(struct uffd_stats *stats, int n_cpus) |
---|
| 172 | +{ |
---|
| 173 | + int i; |
---|
| 174 | + unsigned long long miss_total = 0, wp_total = 0, minor_total = 0; |
---|
| 175 | + |
---|
| 176 | + for (i = 0; i < n_cpus; i++) { |
---|
| 177 | + miss_total += stats[i].missing_faults; |
---|
| 178 | + wp_total += stats[i].wp_faults; |
---|
| 179 | + minor_total += stats[i].minor_faults; |
---|
125 | 180 | } |
---|
126 | 181 | |
---|
127 | | - return ret; |
---|
| 182 | + printf("userfaults: "); |
---|
| 183 | + if (miss_total) { |
---|
| 184 | + printf("%llu missing (", miss_total); |
---|
| 185 | + for (i = 0; i < n_cpus; i++) |
---|
| 186 | + printf("%lu+", stats[i].missing_faults); |
---|
| 187 | + printf("\b) "); |
---|
| 188 | + } |
---|
| 189 | + if (wp_total) { |
---|
| 190 | + printf("%llu wp (", wp_total); |
---|
| 191 | + for (i = 0; i < n_cpus; i++) |
---|
| 192 | + printf("%lu+", stats[i].wp_faults); |
---|
| 193 | + printf("\b) "); |
---|
| 194 | + } |
---|
| 195 | + if (minor_total) { |
---|
| 196 | + printf("%llu minor (", minor_total); |
---|
| 197 | + for (i = 0; i < n_cpus; i++) |
---|
| 198 | + printf("%lu+", stats[i].minor_faults); |
---|
| 199 | + printf("\b)"); |
---|
| 200 | + } |
---|
| 201 | + printf("\n"); |
---|
| 202 | +} |
---|
| 203 | + |
---|
| 204 | +static void anon_release_pages(char *rel_area) |
---|
| 205 | +{ |
---|
| 206 | + if (madvise(rel_area, nr_pages * page_size, MADV_DONTNEED)) |
---|
| 207 | + err("madvise(MADV_DONTNEED) failed"); |
---|
128 | 208 | } |
---|
129 | 209 | |
---|
130 | 210 | static void anon_allocate_area(void **alloc_area) |
---|
131 | 211 | { |
---|
132 | 212 | *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, |
---|
133 | 213 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
---|
134 | | - if (*alloc_area == MAP_FAILED) { |
---|
135 | | - fprintf(stderr, "mmap of anonymous memory failed"); |
---|
136 | | - *alloc_area = NULL; |
---|
137 | | - } |
---|
| 214 | + if (*alloc_area == MAP_FAILED) |
---|
| 215 | + err("posix_memalign() failed"); |
---|
138 | 216 | } |
---|
139 | 217 | |
---|
140 | 218 | static void noop_alias_mapping(__u64 *start, size_t len, unsigned long offset) |
---|
141 | 219 | { |
---|
142 | 220 | } |
---|
143 | 221 | |
---|
144 | | -/* HugeTLB memory */ |
---|
145 | | -static int hugetlb_release_pages(char *rel_area) |
---|
| 222 | +static void hugetlb_release_pages(char *rel_area) |
---|
146 | 223 | { |
---|
147 | | - int ret = 0; |
---|
148 | | - |
---|
149 | 224 | if (fallocate(huge_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, |
---|
150 | | - rel_area == huge_fd_off0 ? 0 : |
---|
151 | | - nr_pages * page_size, |
---|
152 | | - nr_pages * page_size)) { |
---|
153 | | - perror("fallocate"); |
---|
154 | | - ret = 1; |
---|
155 | | - } |
---|
156 | | - |
---|
157 | | - return ret; |
---|
| 225 | + rel_area == huge_fd_off0 ? 0 : nr_pages * page_size, |
---|
| 226 | + nr_pages * page_size)) |
---|
| 227 | + err("fallocate() failed"); |
---|
158 | 228 | } |
---|
159 | | - |
---|
160 | 229 | |
---|
161 | 230 | static void hugetlb_allocate_area(void **alloc_area) |
---|
162 | 231 | { |
---|
163 | 232 | void *area_alias = NULL; |
---|
164 | 233 | char **alloc_area_alias; |
---|
| 234 | + |
---|
165 | 235 | *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, |
---|
166 | 236 | (map_shared ? MAP_SHARED : MAP_PRIVATE) | |
---|
167 | 237 | MAP_HUGETLB, |
---|
168 | 238 | huge_fd, *alloc_area == area_src ? 0 : |
---|
169 | 239 | nr_pages * page_size); |
---|
170 | | - if (*alloc_area == MAP_FAILED) { |
---|
171 | | - fprintf(stderr, "mmap of hugetlbfs file failed\n"); |
---|
172 | | - *alloc_area = NULL; |
---|
173 | | - } |
---|
| 240 | + if (*alloc_area == MAP_FAILED) |
---|
| 241 | + err("mmap of hugetlbfs file failed"); |
---|
174 | 242 | |
---|
175 | 243 | if (map_shared) { |
---|
176 | 244 | area_alias = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, |
---|
177 | 245 | MAP_SHARED | MAP_HUGETLB, |
---|
178 | 246 | huge_fd, *alloc_area == area_src ? 0 : |
---|
179 | 247 | nr_pages * page_size); |
---|
180 | | - if (area_alias == MAP_FAILED) { |
---|
181 | | - if (munmap(*alloc_area, nr_pages * page_size) < 0) |
---|
182 | | - perror("hugetlb munmap"), exit(1); |
---|
183 | | - *alloc_area = NULL; |
---|
184 | | - return; |
---|
185 | | - } |
---|
| 248 | + if (area_alias == MAP_FAILED) |
---|
| 249 | + err("mmap of hugetlb file alias failed"); |
---|
186 | 250 | } |
---|
| 251 | + |
---|
187 | 252 | if (*alloc_area == area_src) { |
---|
188 | 253 | huge_fd_off0 = *alloc_area; |
---|
189 | 254 | alloc_area_alias = &area_src_alias; |
---|
190 | 255 | } else { |
---|
191 | 256 | alloc_area_alias = &area_dst_alias; |
---|
192 | 257 | } |
---|
| 258 | + |
---|
193 | 259 | if (area_alias) |
---|
194 | 260 | *alloc_area_alias = area_alias; |
---|
195 | 261 | } |
---|
.. | .. |
---|
207 | 273 | *start = (unsigned long) area_dst_alias + offset; |
---|
208 | 274 | } |
---|
209 | 275 | |
---|
210 | | -/* Shared memory */ |
---|
211 | | -static int shmem_release_pages(char *rel_area) |
---|
| 276 | +static void shmem_release_pages(char *rel_area) |
---|
212 | 277 | { |
---|
213 | | - int ret = 0; |
---|
214 | | - |
---|
215 | | - if (madvise(rel_area, nr_pages * page_size, MADV_REMOVE)) { |
---|
216 | | - perror("madvise"); |
---|
217 | | - ret = 1; |
---|
218 | | - } |
---|
219 | | - |
---|
220 | | - return ret; |
---|
| 278 | + if (madvise(rel_area, nr_pages * page_size, MADV_REMOVE)) |
---|
| 279 | + err("madvise(MADV_REMOVE) failed"); |
---|
221 | 280 | } |
---|
222 | 281 | |
---|
223 | 282 | static void shmem_allocate_area(void **alloc_area) |
---|
224 | 283 | { |
---|
| 284 | + void *area_alias = NULL; |
---|
| 285 | + bool is_src = alloc_area == (void **)&area_src; |
---|
| 286 | + unsigned long offset = is_src ? 0 : nr_pages * page_size; |
---|
| 287 | + |
---|
225 | 288 | *alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, |
---|
226 | | - MAP_ANONYMOUS | MAP_SHARED, -1, 0); |
---|
227 | | - if (*alloc_area == MAP_FAILED) { |
---|
228 | | - fprintf(stderr, "shared memory mmap failed\n"); |
---|
229 | | - *alloc_area = NULL; |
---|
230 | | - } |
---|
| 289 | + MAP_SHARED, shm_fd, offset); |
---|
| 290 | + if (*alloc_area == MAP_FAILED) |
---|
| 291 | + err("mmap of memfd failed"); |
---|
| 292 | + |
---|
| 293 | + area_alias = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, |
---|
| 294 | + MAP_SHARED, shm_fd, offset); |
---|
| 295 | + if (area_alias == MAP_FAILED) |
---|
| 296 | + err("mmap of memfd alias failed"); |
---|
| 297 | + |
---|
| 298 | + if (is_src) |
---|
| 299 | + area_src_alias = area_alias; |
---|
| 300 | + else |
---|
| 301 | + area_dst_alias = area_alias; |
---|
| 302 | +} |
---|
| 303 | + |
---|
| 304 | +static void shmem_alias_mapping(__u64 *start, size_t len, unsigned long offset) |
---|
| 305 | +{ |
---|
| 306 | + *start = (unsigned long)area_dst_alias + offset; |
---|
231 | 307 | } |
---|
232 | 308 | |
---|
233 | 309 | struct uffd_test_ops { |
---|
234 | 310 | unsigned long expected_ioctls; |
---|
235 | 311 | void (*allocate_area)(void **alloc_area); |
---|
236 | | - int (*release_pages)(char *rel_area); |
---|
| 312 | + void (*release_pages)(char *rel_area); |
---|
237 | 313 | void (*alias_mapping)(__u64 *start, size_t len, unsigned long offset); |
---|
238 | 314 | }; |
---|
239 | 315 | |
---|
240 | | -#define ANON_EXPECTED_IOCTLS ((1 << _UFFDIO_WAKE) | \ |
---|
| 316 | +#define SHMEM_EXPECTED_IOCTLS ((1 << _UFFDIO_WAKE) | \ |
---|
241 | 317 | (1 << _UFFDIO_COPY) | \ |
---|
242 | 318 | (1 << _UFFDIO_ZEROPAGE)) |
---|
| 319 | + |
---|
| 320 | +#define ANON_EXPECTED_IOCTLS ((1 << _UFFDIO_WAKE) | \ |
---|
| 321 | + (1 << _UFFDIO_COPY) | \ |
---|
| 322 | + (1 << _UFFDIO_ZEROPAGE) | \ |
---|
| 323 | + (1 << _UFFDIO_WRITEPROTECT)) |
---|
243 | 324 | |
---|
244 | 325 | static struct uffd_test_ops anon_uffd_test_ops = { |
---|
245 | 326 | .expected_ioctls = ANON_EXPECTED_IOCTLS, |
---|
.. | .. |
---|
249 | 330 | }; |
---|
250 | 331 | |
---|
251 | 332 | static struct uffd_test_ops shmem_uffd_test_ops = { |
---|
252 | | - .expected_ioctls = ANON_EXPECTED_IOCTLS, |
---|
| 333 | + .expected_ioctls = SHMEM_EXPECTED_IOCTLS, |
---|
253 | 334 | .allocate_area = shmem_allocate_area, |
---|
254 | 335 | .release_pages = shmem_release_pages, |
---|
255 | | - .alias_mapping = noop_alias_mapping, |
---|
| 336 | + .alias_mapping = shmem_alias_mapping, |
---|
256 | 337 | }; |
---|
257 | 338 | |
---|
258 | 339 | static struct uffd_test_ops hugetlb_uffd_test_ops = { |
---|
259 | | - .expected_ioctls = UFFD_API_RANGE_IOCTLS_BASIC, |
---|
| 340 | + .expected_ioctls = UFFD_API_RANGE_IOCTLS_BASIC & ~(1 << _UFFDIO_CONTINUE), |
---|
260 | 341 | .allocate_area = hugetlb_allocate_area, |
---|
261 | 342 | .release_pages = hugetlb_release_pages, |
---|
262 | 343 | .alias_mapping = hugetlb_alias_mapping, |
---|
263 | 344 | }; |
---|
264 | 345 | |
---|
265 | 346 | static struct uffd_test_ops *uffd_test_ops; |
---|
| 347 | + |
---|
| 348 | +static void userfaultfd_open(uint64_t *features) |
---|
| 349 | +{ |
---|
| 350 | + struct uffdio_api uffdio_api; |
---|
| 351 | + |
---|
| 352 | + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); |
---|
| 353 | + if (uffd < 0) |
---|
| 354 | + err("userfaultfd syscall not available in this kernel"); |
---|
| 355 | + uffd_flags = fcntl(uffd, F_GETFD, NULL); |
---|
| 356 | + |
---|
| 357 | + uffdio_api.api = UFFD_API; |
---|
| 358 | + uffdio_api.features = *features; |
---|
| 359 | + if (ioctl(uffd, UFFDIO_API, &uffdio_api)) |
---|
| 360 | + err("UFFDIO_API failed.\nPlease make sure to " |
---|
| 361 | + "run with either root or ptrace capability."); |
---|
| 362 | + if (uffdio_api.api != UFFD_API) |
---|
| 363 | + err("UFFDIO_API error: %" PRIu64, (uint64_t)uffdio_api.api); |
---|
| 364 | + |
---|
| 365 | + *features = uffdio_api.features; |
---|
| 366 | +} |
---|
| 367 | + |
---|
| 368 | +static inline void munmap_area(void **area) |
---|
| 369 | +{ |
---|
| 370 | + if (*area) |
---|
| 371 | + if (munmap(*area, nr_pages * page_size)) |
---|
| 372 | + err("munmap"); |
---|
| 373 | + |
---|
| 374 | + *area = NULL; |
---|
| 375 | +} |
---|
| 376 | + |
---|
| 377 | +static void uffd_test_ctx_clear(void) |
---|
| 378 | +{ |
---|
| 379 | + size_t i; |
---|
| 380 | + |
---|
| 381 | + if (pipefd) { |
---|
| 382 | + for (i = 0; i < nr_cpus * 2; ++i) { |
---|
| 383 | + if (close(pipefd[i])) |
---|
| 384 | + err("close pipefd"); |
---|
| 385 | + } |
---|
| 386 | + free(pipefd); |
---|
| 387 | + pipefd = NULL; |
---|
| 388 | + } |
---|
| 389 | + |
---|
| 390 | + if (count_verify) { |
---|
| 391 | + free(count_verify); |
---|
| 392 | + count_verify = NULL; |
---|
| 393 | + } |
---|
| 394 | + |
---|
| 395 | + if (uffd != -1) { |
---|
| 396 | + if (close(uffd)) |
---|
| 397 | + err("close uffd"); |
---|
| 398 | + uffd = -1; |
---|
| 399 | + } |
---|
| 400 | + |
---|
| 401 | + huge_fd_off0 = NULL; |
---|
| 402 | + munmap_area((void **)&area_src); |
---|
| 403 | + munmap_area((void **)&area_src_alias); |
---|
| 404 | + munmap_area((void **)&area_dst); |
---|
| 405 | + munmap_area((void **)&area_dst_alias); |
---|
| 406 | +} |
---|
| 407 | + |
---|
| 408 | +static void uffd_test_ctx_init_ext(uint64_t *features) |
---|
| 409 | +{ |
---|
| 410 | + unsigned long nr, cpu; |
---|
| 411 | + |
---|
| 412 | + uffd_test_ctx_clear(); |
---|
| 413 | + |
---|
| 414 | + uffd_test_ops->allocate_area((void **)&area_src); |
---|
| 415 | + uffd_test_ops->allocate_area((void **)&area_dst); |
---|
| 416 | + |
---|
| 417 | + userfaultfd_open(features); |
---|
| 418 | + |
---|
| 419 | + count_verify = malloc(nr_pages * sizeof(unsigned long long)); |
---|
| 420 | + if (!count_verify) |
---|
| 421 | + err("count_verify"); |
---|
| 422 | + |
---|
| 423 | + for (nr = 0; nr < nr_pages; nr++) { |
---|
| 424 | + *area_mutex(area_src, nr) = |
---|
| 425 | + (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; |
---|
| 426 | + count_verify[nr] = *area_count(area_src, nr) = 1; |
---|
| 427 | + /* |
---|
| 428 | + * In the transition between 255 to 256, powerpc will |
---|
| 429 | + * read out of order in my_bcmp and see both bytes as |
---|
| 430 | + * zero, so leave a placeholder below always non-zero |
---|
| 431 | + * after the count, to avoid my_bcmp to trigger false |
---|
| 432 | + * positives. |
---|
| 433 | + */ |
---|
| 434 | + *(area_count(area_src, nr) + 1) = 1; |
---|
| 435 | + } |
---|
| 436 | + |
---|
| 437 | + /* |
---|
| 438 | + * After initialization of area_src, we must explicitly release pages |
---|
| 439 | + * for area_dst to make sure it's fully empty. Otherwise we could have |
---|
| 440 | + * some area_dst pages be errornously initialized with zero pages, |
---|
| 441 | + * hence we could hit memory corruption later in the test. |
---|
| 442 | + * |
---|
| 443 | + * One example is when THP is globally enabled, above allocate_area() |
---|
| 444 | + * calls could have the two areas merged into a single VMA (as they |
---|
| 445 | + * will have the same VMA flags so they're mergeable). When we |
---|
| 446 | + * initialize the area_src above, it's possible that some part of |
---|
| 447 | + * area_dst could have been faulted in via one huge THP that will be |
---|
| 448 | + * shared between area_src and area_dst. It could cause some of the |
---|
| 449 | + * area_dst won't be trapped by missing userfaults. |
---|
| 450 | + * |
---|
| 451 | + * This release_pages() will guarantee even if that happened, we'll |
---|
| 452 | + * proactively split the thp and drop any accidentally initialized |
---|
| 453 | + * pages within area_dst. |
---|
| 454 | + */ |
---|
| 455 | + uffd_test_ops->release_pages(area_dst); |
---|
| 456 | + |
---|
| 457 | + pipefd = malloc(sizeof(int) * nr_cpus * 2); |
---|
| 458 | + if (!pipefd) |
---|
| 459 | + err("pipefd"); |
---|
| 460 | + for (cpu = 0; cpu < nr_cpus; cpu++) |
---|
| 461 | + if (pipe2(&pipefd[cpu * 2], O_CLOEXEC | O_NONBLOCK)) |
---|
| 462 | + err("pipe"); |
---|
| 463 | +} |
---|
| 464 | + |
---|
| 465 | +static inline void uffd_test_ctx_init(uint64_t features) |
---|
| 466 | +{ |
---|
| 467 | + uffd_test_ctx_init_ext(&features); |
---|
| 468 | +} |
---|
266 | 469 | |
---|
267 | 470 | static int my_bcmp(char *str1, char *str2, size_t n) |
---|
268 | 471 | { |
---|
.. | .. |
---|
271 | 474 | if (str1[i] != str2[i]) |
---|
272 | 475 | return 1; |
---|
273 | 476 | return 0; |
---|
| 477 | +} |
---|
| 478 | + |
---|
| 479 | +static void wp_range(int ufd, __u64 start, __u64 len, bool wp) |
---|
| 480 | +{ |
---|
| 481 | + struct uffdio_writeprotect prms = { 0 }; |
---|
| 482 | + |
---|
| 483 | + /* Write protection page faults */ |
---|
| 484 | + prms.range.start = start; |
---|
| 485 | + prms.range.len = len; |
---|
| 486 | + /* Undo write-protect, do wakeup after that */ |
---|
| 487 | + prms.mode = wp ? UFFDIO_WRITEPROTECT_MODE_WP : 0; |
---|
| 488 | + |
---|
| 489 | + if (ioctl(ufd, UFFDIO_WRITEPROTECT, &prms)) |
---|
| 490 | + err("clear WP failed: address=0x%"PRIx64, (uint64_t)start); |
---|
| 491 | +} |
---|
| 492 | + |
---|
| 493 | +static void continue_range(int ufd, __u64 start, __u64 len) |
---|
| 494 | +{ |
---|
| 495 | + struct uffdio_continue req; |
---|
| 496 | + int ret; |
---|
| 497 | + |
---|
| 498 | + req.range.start = start; |
---|
| 499 | + req.range.len = len; |
---|
| 500 | + req.mode = 0; |
---|
| 501 | + |
---|
| 502 | + if (ioctl(ufd, UFFDIO_CONTINUE, &req)) |
---|
| 503 | + err("UFFDIO_CONTINUE failed for address 0x%" PRIx64, |
---|
| 504 | + (uint64_t)start); |
---|
| 505 | + |
---|
| 506 | + /* |
---|
| 507 | + * Error handling within the kernel for continue is subtly different |
---|
| 508 | + * from copy or zeropage, so it may be a source of bugs. Trigger an |
---|
| 509 | + * error (-EEXIST) on purpose, to verify doing so doesn't cause a BUG. |
---|
| 510 | + */ |
---|
| 511 | + req.mapped = 0; |
---|
| 512 | + ret = ioctl(ufd, UFFDIO_CONTINUE, &req); |
---|
| 513 | + if (ret >= 0 || req.mapped != -EEXIST) |
---|
| 514 | + err("failed to exercise UFFDIO_CONTINUE error handling, ret=%d, mapped=%" PRId64, |
---|
| 515 | + ret, (int64_t) req.mapped); |
---|
274 | 516 | } |
---|
275 | 517 | |
---|
276 | 518 | static void *locking_thread(void *arg) |
---|
.. | .. |
---|
282 | 524 | unsigned long long count; |
---|
283 | 525 | char randstate[64]; |
---|
284 | 526 | unsigned int seed; |
---|
285 | | - time_t start; |
---|
286 | 527 | |
---|
287 | 528 | if (bounces & BOUNCE_RANDOM) { |
---|
288 | 529 | seed = (unsigned int) time(NULL) - bounces; |
---|
.. | .. |
---|
291 | 532 | bzero(&rand, sizeof(rand)); |
---|
292 | 533 | bzero(&randstate, sizeof(randstate)); |
---|
293 | 534 | if (initstate_r(seed, randstate, sizeof(randstate), &rand)) |
---|
294 | | - fprintf(stderr, "srandom_r error\n"), exit(1); |
---|
| 535 | + err("initstate_r failed"); |
---|
295 | 536 | } else { |
---|
296 | 537 | page_nr = -bounces; |
---|
297 | 538 | if (!(bounces & BOUNCE_RACINGFAULTS)) |
---|
.. | .. |
---|
301 | 542 | while (!finished) { |
---|
302 | 543 | if (bounces & BOUNCE_RANDOM) { |
---|
303 | 544 | if (random_r(&rand, &rand_nr)) |
---|
304 | | - fprintf(stderr, "random_r 1 error\n"), exit(1); |
---|
| 545 | + err("random_r failed"); |
---|
305 | 546 | page_nr = rand_nr; |
---|
306 | 547 | if (sizeof(page_nr) > sizeof(rand_nr)) { |
---|
307 | 548 | if (random_r(&rand, &rand_nr)) |
---|
308 | | - fprintf(stderr, "random_r 2 error\n"), exit(1); |
---|
| 549 | + err("random_r failed"); |
---|
309 | 550 | page_nr |= (((unsigned long) rand_nr) << 16) << |
---|
310 | 551 | 16; |
---|
311 | 552 | } |
---|
312 | 553 | } else |
---|
313 | 554 | page_nr += 1; |
---|
314 | 555 | page_nr %= nr_pages; |
---|
315 | | - |
---|
316 | | - start = time(NULL); |
---|
317 | | - if (bounces & BOUNCE_VERIFY) { |
---|
318 | | - count = *area_count(area_dst, page_nr); |
---|
319 | | - if (!count) |
---|
320 | | - fprintf(stderr, |
---|
321 | | - "page_nr %lu wrong count %Lu %Lu\n", |
---|
322 | | - page_nr, count, |
---|
323 | | - count_verify[page_nr]), exit(1); |
---|
324 | | - |
---|
325 | | - |
---|
326 | | - /* |
---|
327 | | - * We can't use bcmp (or memcmp) because that |
---|
328 | | - * returns 0 erroneously if the memory is |
---|
329 | | - * changing under it (even if the end of the |
---|
330 | | - * page is never changing and always |
---|
331 | | - * different). |
---|
332 | | - */ |
---|
333 | | -#if 1 |
---|
334 | | - if (!my_bcmp(area_dst + page_nr * page_size, zeropage, |
---|
335 | | - page_size)) |
---|
336 | | - fprintf(stderr, |
---|
337 | | - "my_bcmp page_nr %lu wrong count %Lu %Lu\n", |
---|
338 | | - page_nr, count, |
---|
339 | | - count_verify[page_nr]), exit(1); |
---|
340 | | -#else |
---|
341 | | - unsigned long loops; |
---|
342 | | - |
---|
343 | | - loops = 0; |
---|
344 | | - /* uncomment the below line to test with mutex */ |
---|
345 | | - /* pthread_mutex_lock(area_mutex(area_dst, page_nr)); */ |
---|
346 | | - while (!bcmp(area_dst + page_nr * page_size, zeropage, |
---|
347 | | - page_size)) { |
---|
348 | | - loops += 1; |
---|
349 | | - if (loops > 10) |
---|
350 | | - break; |
---|
351 | | - } |
---|
352 | | - /* uncomment below line to test with mutex */ |
---|
353 | | - /* pthread_mutex_unlock(area_mutex(area_dst, page_nr)); */ |
---|
354 | | - if (loops) { |
---|
355 | | - fprintf(stderr, |
---|
356 | | - "page_nr %lu all zero thread %lu %p %lu\n", |
---|
357 | | - page_nr, cpu, area_dst + page_nr * page_size, |
---|
358 | | - loops); |
---|
359 | | - if (loops > 10) |
---|
360 | | - exit(1); |
---|
361 | | - } |
---|
362 | | -#endif |
---|
363 | | - } |
---|
364 | | - |
---|
365 | 556 | pthread_mutex_lock(area_mutex(area_dst, page_nr)); |
---|
366 | 557 | count = *area_count(area_dst, page_nr); |
---|
367 | | - if (count != count_verify[page_nr]) { |
---|
368 | | - fprintf(stderr, |
---|
369 | | - "page_nr %lu memory corruption %Lu %Lu\n", |
---|
370 | | - page_nr, count, |
---|
371 | | - count_verify[page_nr]), exit(1); |
---|
372 | | - } |
---|
| 558 | + if (count != count_verify[page_nr]) |
---|
| 559 | + err("page_nr %lu memory corruption %llu %llu", |
---|
| 560 | + page_nr, count, count_verify[page_nr]); |
---|
373 | 561 | count++; |
---|
374 | 562 | *area_count(area_dst, page_nr) = count_verify[page_nr] = count; |
---|
375 | 563 | pthread_mutex_unlock(area_mutex(area_dst, page_nr)); |
---|
376 | | - |
---|
377 | | - if (time(NULL) - start > 1) |
---|
378 | | - fprintf(stderr, |
---|
379 | | - "userfault too slow %ld " |
---|
380 | | - "possible false positive with overcommit\n", |
---|
381 | | - time(NULL) - start); |
---|
382 | 564 | } |
---|
383 | 565 | |
---|
384 | 566 | return NULL; |
---|
.. | .. |
---|
393 | 575 | if (ioctl(ufd, UFFDIO_COPY, uffdio_copy)) { |
---|
394 | 576 | /* real retval in ufdio_copy.copy */ |
---|
395 | 577 | if (uffdio_copy->copy != -EEXIST) |
---|
396 | | - fprintf(stderr, "UFFDIO_COPY retry error %Ld\n", |
---|
397 | | - uffdio_copy->copy), exit(1); |
---|
| 578 | + err("UFFDIO_COPY retry error: %"PRId64, |
---|
| 579 | + (int64_t)uffdio_copy->copy); |
---|
398 | 580 | } else { |
---|
399 | | - fprintf(stderr, "UFFDIO_COPY retry unexpected %Ld\n", |
---|
400 | | - uffdio_copy->copy), exit(1); |
---|
| 581 | + err("UFFDIO_COPY retry unexpected: %"PRId64, |
---|
| 582 | + (int64_t)uffdio_copy->copy); |
---|
401 | 583 | } |
---|
402 | 584 | } |
---|
403 | 585 | |
---|
.. | .. |
---|
406 | 588 | struct uffdio_copy uffdio_copy; |
---|
407 | 589 | |
---|
408 | 590 | if (offset >= nr_pages * page_size) |
---|
409 | | - fprintf(stderr, "unexpected offset %lu\n", |
---|
410 | | - offset), exit(1); |
---|
| 591 | + err("unexpected offset %lu\n", offset); |
---|
411 | 592 | uffdio_copy.dst = (unsigned long) area_dst + offset; |
---|
412 | 593 | uffdio_copy.src = (unsigned long) area_src + offset; |
---|
413 | 594 | uffdio_copy.len = page_size; |
---|
414 | | - uffdio_copy.mode = 0; |
---|
| 595 | + if (test_uffdio_wp) |
---|
| 596 | + uffdio_copy.mode = UFFDIO_COPY_MODE_WP; |
---|
| 597 | + else |
---|
| 598 | + uffdio_copy.mode = 0; |
---|
415 | 599 | uffdio_copy.copy = 0; |
---|
416 | 600 | if (ioctl(ufd, UFFDIO_COPY, &uffdio_copy)) { |
---|
417 | 601 | /* real retval in ufdio_copy.copy */ |
---|
418 | 602 | if (uffdio_copy.copy != -EEXIST) |
---|
419 | | - fprintf(stderr, "UFFDIO_COPY error %Ld\n", |
---|
420 | | - uffdio_copy.copy), exit(1); |
---|
| 603 | + err("UFFDIO_COPY error: %"PRId64, |
---|
| 604 | + (int64_t)uffdio_copy.copy); |
---|
421 | 605 | } else if (uffdio_copy.copy != page_size) { |
---|
422 | | - fprintf(stderr, "UFFDIO_COPY unexpected copy %Ld\n", |
---|
423 | | - uffdio_copy.copy), exit(1); |
---|
| 606 | + err("UFFDIO_COPY error: %"PRId64, (int64_t)uffdio_copy.copy); |
---|
424 | 607 | } else { |
---|
425 | 608 | if (test_uffdio_copy_eexist && retry) { |
---|
426 | 609 | test_uffdio_copy_eexist = false; |
---|
.. | .. |
---|
441 | 624 | return __copy_page(ufd, offset, false); |
---|
442 | 625 | } |
---|
443 | 626 | |
---|
| 627 | +static int uffd_read_msg(int ufd, struct uffd_msg *msg) |
---|
| 628 | +{ |
---|
| 629 | + int ret = read(uffd, msg, sizeof(*msg)); |
---|
| 630 | + |
---|
| 631 | + if (ret != sizeof(*msg)) { |
---|
| 632 | + if (ret < 0) { |
---|
| 633 | + if (errno == EAGAIN) |
---|
| 634 | + return 1; |
---|
| 635 | + err("blocking read error"); |
---|
| 636 | + } else { |
---|
| 637 | + err("short read"); |
---|
| 638 | + } |
---|
| 639 | + } |
---|
| 640 | + |
---|
| 641 | + return 0; |
---|
| 642 | +} |
---|
| 643 | + |
---|
| 644 | +static void uffd_handle_page_fault(struct uffd_msg *msg, |
---|
| 645 | + struct uffd_stats *stats) |
---|
| 646 | +{ |
---|
| 647 | + unsigned long offset; |
---|
| 648 | + |
---|
| 649 | + if (msg->event != UFFD_EVENT_PAGEFAULT) |
---|
| 650 | + err("unexpected msg event %u", msg->event); |
---|
| 651 | + |
---|
| 652 | + if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) { |
---|
| 653 | + /* Write protect page faults */ |
---|
| 654 | + wp_range(uffd, msg->arg.pagefault.address, page_size, false); |
---|
| 655 | + stats->wp_faults++; |
---|
| 656 | + } else if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_MINOR) { |
---|
| 657 | + uint8_t *area; |
---|
| 658 | + int b; |
---|
| 659 | + |
---|
| 660 | + /* |
---|
| 661 | + * Minor page faults |
---|
| 662 | + * |
---|
| 663 | + * To prove we can modify the original range for testing |
---|
| 664 | + * purposes, we're going to bit flip this range before |
---|
| 665 | + * continuing. |
---|
| 666 | + * |
---|
| 667 | + * Note that this requires all minor page fault tests operate on |
---|
| 668 | + * area_dst (non-UFFD-registered) and area_dst_alias |
---|
| 669 | + * (UFFD-registered). |
---|
| 670 | + */ |
---|
| 671 | + |
---|
| 672 | + area = (uint8_t *)(area_dst + |
---|
| 673 | + ((char *)msg->arg.pagefault.address - |
---|
| 674 | + area_dst_alias)); |
---|
| 675 | + for (b = 0; b < page_size; ++b) |
---|
| 676 | + area[b] = ~area[b]; |
---|
| 677 | + continue_range(uffd, msg->arg.pagefault.address, page_size); |
---|
| 678 | + stats->minor_faults++; |
---|
| 679 | + } else { |
---|
| 680 | + /* Missing page faults */ |
---|
| 681 | + if (msg->arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) |
---|
| 682 | + err("unexpected write fault"); |
---|
| 683 | + |
---|
| 684 | + offset = (char *)(unsigned long)msg->arg.pagefault.address - area_dst; |
---|
| 685 | + offset &= ~(page_size-1); |
---|
| 686 | + |
---|
| 687 | + if (copy_page(uffd, offset)) |
---|
| 688 | + stats->missing_faults++; |
---|
| 689 | + } |
---|
| 690 | +} |
---|
| 691 | + |
---|
444 | 692 | static void *uffd_poll_thread(void *arg) |
---|
445 | 693 | { |
---|
446 | | - unsigned long cpu = (unsigned long) arg; |
---|
| 694 | + struct uffd_stats *stats = (struct uffd_stats *)arg; |
---|
| 695 | + unsigned long cpu = stats->cpu; |
---|
447 | 696 | struct pollfd pollfd[2]; |
---|
448 | 697 | struct uffd_msg msg; |
---|
449 | 698 | struct uffdio_register uffd_reg; |
---|
450 | 699 | int ret; |
---|
451 | | - unsigned long offset; |
---|
452 | 700 | char tmp_chr; |
---|
453 | | - unsigned long userfaults = 0; |
---|
454 | 701 | |
---|
455 | 702 | pollfd[0].fd = uffd; |
---|
456 | 703 | pollfd[0].events = POLLIN; |
---|
.. | .. |
---|
459 | 706 | |
---|
460 | 707 | for (;;) { |
---|
461 | 708 | ret = poll(pollfd, 2, -1); |
---|
462 | | - if (!ret) |
---|
463 | | - fprintf(stderr, "poll error %d\n", ret), exit(1); |
---|
464 | | - if (ret < 0) |
---|
465 | | - perror("poll"), exit(1); |
---|
| 709 | + if (ret <= 0) |
---|
| 710 | + err("poll error: %d", ret); |
---|
466 | 711 | if (pollfd[1].revents & POLLIN) { |
---|
467 | 712 | if (read(pollfd[1].fd, &tmp_chr, 1) != 1) |
---|
468 | | - fprintf(stderr, "read pipefd error\n"), |
---|
469 | | - exit(1); |
---|
| 713 | + err("read pipefd error"); |
---|
470 | 714 | break; |
---|
471 | 715 | } |
---|
472 | 716 | if (!(pollfd[0].revents & POLLIN)) |
---|
473 | | - fprintf(stderr, "pollfd[0].revents %d\n", |
---|
474 | | - pollfd[0].revents), exit(1); |
---|
475 | | - ret = read(uffd, &msg, sizeof(msg)); |
---|
476 | | - if (ret < 0) { |
---|
477 | | - if (errno == EAGAIN) |
---|
478 | | - continue; |
---|
479 | | - perror("nonblocking read error"), exit(1); |
---|
480 | | - } |
---|
| 717 | + err("pollfd[0].revents %d", pollfd[0].revents); |
---|
| 718 | + if (uffd_read_msg(uffd, &msg)) |
---|
| 719 | + continue; |
---|
481 | 720 | switch (msg.event) { |
---|
482 | 721 | default: |
---|
483 | | - fprintf(stderr, "unexpected msg event %u\n", |
---|
484 | | - msg.event), exit(1); |
---|
| 722 | + err("unexpected msg event %u\n", msg.event); |
---|
485 | 723 | break; |
---|
486 | 724 | case UFFD_EVENT_PAGEFAULT: |
---|
487 | | - if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) |
---|
488 | | - fprintf(stderr, "unexpected write fault\n"), exit(1); |
---|
489 | | - offset = (char *)(unsigned long)msg.arg.pagefault.address - |
---|
490 | | - area_dst; |
---|
491 | | - offset &= ~(page_size-1); |
---|
492 | | - if (copy_page(uffd, offset)) |
---|
493 | | - userfaults++; |
---|
| 725 | + uffd_handle_page_fault(&msg, stats); |
---|
494 | 726 | break; |
---|
495 | 727 | case UFFD_EVENT_FORK: |
---|
496 | 728 | close(uffd); |
---|
.. | .. |
---|
502 | 734 | uffd_reg.range.len = msg.arg.remove.end - |
---|
503 | 735 | msg.arg.remove.start; |
---|
504 | 736 | if (ioctl(uffd, UFFDIO_UNREGISTER, &uffd_reg.range)) |
---|
505 | | - fprintf(stderr, "remove failure\n"), exit(1); |
---|
| 737 | + err("remove failure"); |
---|
506 | 738 | break; |
---|
507 | 739 | case UFFD_EVENT_REMAP: |
---|
508 | 740 | area_dst = (char *)(unsigned long)msg.arg.remap.to; |
---|
509 | 741 | break; |
---|
510 | 742 | } |
---|
511 | 743 | } |
---|
512 | | - return (void *)userfaults; |
---|
| 744 | + |
---|
| 745 | + return NULL; |
---|
513 | 746 | } |
---|
514 | 747 | |
---|
515 | 748 | pthread_mutex_t uffd_read_mutex = PTHREAD_MUTEX_INITIALIZER; |
---|
516 | 749 | |
---|
517 | 750 | static void *uffd_read_thread(void *arg) |
---|
518 | 751 | { |
---|
519 | | - unsigned long *this_cpu_userfaults; |
---|
| 752 | + struct uffd_stats *stats = (struct uffd_stats *)arg; |
---|
520 | 753 | struct uffd_msg msg; |
---|
521 | | - unsigned long offset; |
---|
522 | | - int ret; |
---|
523 | | - |
---|
524 | | - this_cpu_userfaults = (unsigned long *) arg; |
---|
525 | | - *this_cpu_userfaults = 0; |
---|
526 | 754 | |
---|
527 | 755 | pthread_mutex_unlock(&uffd_read_mutex); |
---|
528 | 756 | /* from here cancellation is ok */ |
---|
529 | 757 | |
---|
530 | 758 | for (;;) { |
---|
531 | | - ret = read(uffd, &msg, sizeof(msg)); |
---|
532 | | - if (ret != sizeof(msg)) { |
---|
533 | | - if (ret < 0) |
---|
534 | | - perror("blocking read error"), exit(1); |
---|
535 | | - else |
---|
536 | | - fprintf(stderr, "short read\n"), exit(1); |
---|
537 | | - } |
---|
538 | | - if (msg.event != UFFD_EVENT_PAGEFAULT) |
---|
539 | | - fprintf(stderr, "unexpected msg event %u\n", |
---|
540 | | - msg.event), exit(1); |
---|
541 | | - if (bounces & BOUNCE_VERIFY && |
---|
542 | | - msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) |
---|
543 | | - fprintf(stderr, "unexpected write fault\n"), exit(1); |
---|
544 | | - offset = (char *)(unsigned long)msg.arg.pagefault.address - |
---|
545 | | - area_dst; |
---|
546 | | - offset &= ~(page_size-1); |
---|
547 | | - if (copy_page(uffd, offset)) |
---|
548 | | - (*this_cpu_userfaults)++; |
---|
| 759 | + if (uffd_read_msg(uffd, &msg)) |
---|
| 760 | + continue; |
---|
| 761 | + uffd_handle_page_fault(&msg, stats); |
---|
549 | 762 | } |
---|
550 | | - return (void *)NULL; |
---|
| 763 | + |
---|
| 764 | + return NULL; |
---|
551 | 765 | } |
---|
552 | 766 | |
---|
553 | 767 | static void *background_thread(void *arg) |
---|
554 | 768 | { |
---|
555 | 769 | unsigned long cpu = (unsigned long) arg; |
---|
556 | | - unsigned long page_nr; |
---|
| 770 | + unsigned long page_nr, start_nr, mid_nr, end_nr; |
---|
557 | 771 | |
---|
558 | | - for (page_nr = cpu * nr_pages_per_cpu; |
---|
559 | | - page_nr < (cpu+1) * nr_pages_per_cpu; |
---|
560 | | - page_nr++) |
---|
| 772 | + start_nr = cpu * nr_pages_per_cpu; |
---|
| 773 | + end_nr = (cpu+1) * nr_pages_per_cpu; |
---|
| 774 | + mid_nr = (start_nr + end_nr) / 2; |
---|
| 775 | + |
---|
| 776 | + /* Copy the first half of the pages */ |
---|
| 777 | + for (page_nr = start_nr; page_nr < mid_nr; page_nr++) |
---|
| 778 | + copy_page_retry(uffd, page_nr * page_size); |
---|
| 779 | + |
---|
| 780 | + /* |
---|
| 781 | + * If we need to test uffd-wp, set it up now. Then we'll have |
---|
| 782 | + * at least the first half of the pages mapped already which |
---|
| 783 | + * can be write-protected for testing |
---|
| 784 | + */ |
---|
| 785 | + if (test_uffdio_wp) |
---|
| 786 | + wp_range(uffd, (unsigned long)area_dst + start_nr * page_size, |
---|
| 787 | + nr_pages_per_cpu * page_size, true); |
---|
| 788 | + |
---|
| 789 | + /* |
---|
| 790 | + * Continue the 2nd half of the page copying, handling write |
---|
| 791 | + * protection faults if any |
---|
| 792 | + */ |
---|
| 793 | + for (page_nr = mid_nr; page_nr < end_nr; page_nr++) |
---|
561 | 794 | copy_page_retry(uffd, page_nr * page_size); |
---|
562 | 795 | |
---|
563 | 796 | return NULL; |
---|
564 | 797 | } |
---|
565 | 798 | |
---|
566 | | -static int stress(unsigned long *userfaults) |
---|
| 799 | +static int stress(struct uffd_stats *uffd_stats) |
---|
567 | 800 | { |
---|
568 | 801 | unsigned long cpu; |
---|
569 | 802 | pthread_t locking_threads[nr_cpus]; |
---|
570 | 803 | pthread_t uffd_threads[nr_cpus]; |
---|
571 | 804 | pthread_t background_threads[nr_cpus]; |
---|
572 | | - void **_userfaults = (void **) userfaults; |
---|
573 | 805 | |
---|
574 | 806 | finished = 0; |
---|
575 | 807 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
---|
.. | .. |
---|
578 | 810 | return 1; |
---|
579 | 811 | if (bounces & BOUNCE_POLL) { |
---|
580 | 812 | if (pthread_create(&uffd_threads[cpu], &attr, |
---|
581 | | - uffd_poll_thread, (void *)cpu)) |
---|
| 813 | + uffd_poll_thread, |
---|
| 814 | + (void *)&uffd_stats[cpu])) |
---|
582 | 815 | return 1; |
---|
583 | 816 | } else { |
---|
584 | 817 | if (pthread_create(&uffd_threads[cpu], &attr, |
---|
585 | 818 | uffd_read_thread, |
---|
586 | | - &_userfaults[cpu])) |
---|
| 819 | + (void *)&uffd_stats[cpu])) |
---|
587 | 820 | return 1; |
---|
588 | 821 | pthread_mutex_lock(&uffd_read_mutex); |
---|
589 | 822 | } |
---|
.. | .. |
---|
604 | 837 | * UFFDIO_COPY without writing zero pages into area_dst |
---|
605 | 838 | * because the background threads already completed). |
---|
606 | 839 | */ |
---|
607 | | - if (uffd_test_ops->release_pages(area_src)) |
---|
608 | | - return 1; |
---|
| 840 | + uffd_test_ops->release_pages(area_src); |
---|
| 841 | + |
---|
| 842 | + finished = 1; |
---|
| 843 | + for (cpu = 0; cpu < nr_cpus; cpu++) |
---|
| 844 | + if (pthread_join(locking_threads[cpu], NULL)) |
---|
| 845 | + return 1; |
---|
609 | 846 | |
---|
610 | 847 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
---|
611 | 848 | char c; |
---|
612 | 849 | if (bounces & BOUNCE_POLL) { |
---|
613 | | - if (write(pipefd[cpu*2+1], &c, 1) != 1) { |
---|
614 | | - fprintf(stderr, "pipefd write error\n"); |
---|
615 | | - return 1; |
---|
616 | | - } |
---|
617 | | - if (pthread_join(uffd_threads[cpu], &_userfaults[cpu])) |
---|
| 850 | + if (write(pipefd[cpu*2+1], &c, 1) != 1) |
---|
| 851 | + err("pipefd write error"); |
---|
| 852 | + if (pthread_join(uffd_threads[cpu], |
---|
| 853 | + (void *)&uffd_stats[cpu])) |
---|
618 | 854 | return 1; |
---|
619 | 855 | } else { |
---|
620 | 856 | if (pthread_cancel(uffd_threads[cpu])) |
---|
.. | .. |
---|
622 | 858 | if (pthread_join(uffd_threads[cpu], NULL)) |
---|
623 | 859 | return 1; |
---|
624 | 860 | } |
---|
625 | | - } |
---|
626 | | - |
---|
627 | | - finished = 1; |
---|
628 | | - for (cpu = 0; cpu < nr_cpus; cpu++) |
---|
629 | | - if (pthread_join(locking_threads[cpu], NULL)) |
---|
630 | | - return 1; |
---|
631 | | - |
---|
632 | | - return 0; |
---|
633 | | -} |
---|
634 | | - |
---|
635 | | -static int userfaultfd_open(int features) |
---|
636 | | -{ |
---|
637 | | - struct uffdio_api uffdio_api; |
---|
638 | | - |
---|
639 | | - uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); |
---|
640 | | - if (uffd < 0) { |
---|
641 | | - fprintf(stderr, |
---|
642 | | - "userfaultfd syscall not available in this kernel\n"); |
---|
643 | | - return 1; |
---|
644 | | - } |
---|
645 | | - uffd_flags = fcntl(uffd, F_GETFD, NULL); |
---|
646 | | - |
---|
647 | | - uffdio_api.api = UFFD_API; |
---|
648 | | - uffdio_api.features = features; |
---|
649 | | - if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { |
---|
650 | | - fprintf(stderr, "UFFDIO_API\n"); |
---|
651 | | - return 1; |
---|
652 | | - } |
---|
653 | | - if (uffdio_api.api != UFFD_API) { |
---|
654 | | - fprintf(stderr, "UFFDIO_API error %Lu\n", uffdio_api.api); |
---|
655 | | - return 1; |
---|
656 | 861 | } |
---|
657 | 862 | |
---|
658 | 863 | return 0; |
---|
.. | .. |
---|
709 | 914 | memset(&act, 0, sizeof(act)); |
---|
710 | 915 | act.sa_sigaction = sighndl; |
---|
711 | 916 | act.sa_flags = SA_SIGINFO; |
---|
712 | | - if (sigaction(SIGBUS, &act, 0)) { |
---|
713 | | - perror("sigaction"); |
---|
714 | | - return 1; |
---|
715 | | - } |
---|
| 917 | + if (sigaction(SIGBUS, &act, 0)) |
---|
| 918 | + err("sigaction"); |
---|
716 | 919 | lastnr = (unsigned long)-1; |
---|
717 | 920 | } |
---|
718 | 921 | |
---|
719 | 922 | for (nr = 0; nr < split_nr_pages; nr++) { |
---|
| 923 | + int steps = 1; |
---|
| 924 | + unsigned long offset = nr * page_size; |
---|
| 925 | + |
---|
720 | 926 | if (signal_test) { |
---|
721 | 927 | if (sigsetjmp(*sigbuf, 1) != 0) { |
---|
722 | | - if (nr == lastnr) { |
---|
723 | | - fprintf(stderr, "Signal repeated\n"); |
---|
724 | | - return 1; |
---|
725 | | - } |
---|
| 928 | + if (steps == 1 && nr == lastnr) |
---|
| 929 | + err("Signal repeated"); |
---|
726 | 930 | |
---|
727 | 931 | lastnr = nr; |
---|
728 | 932 | if (signal_test == 1) { |
---|
729 | | - if (copy_page(uffd, nr * page_size)) |
---|
730 | | - signalled++; |
---|
| 933 | + if (steps == 1) { |
---|
| 934 | + /* This is a MISSING request */ |
---|
| 935 | + steps++; |
---|
| 936 | + if (copy_page(uffd, offset)) |
---|
| 937 | + signalled++; |
---|
| 938 | + } else { |
---|
| 939 | + /* This is a WP request */ |
---|
| 940 | + assert(steps == 2); |
---|
| 941 | + wp_range(uffd, |
---|
| 942 | + (__u64)area_dst + |
---|
| 943 | + offset, |
---|
| 944 | + page_size, false); |
---|
| 945 | + } |
---|
731 | 946 | } else { |
---|
732 | 947 | signalled++; |
---|
733 | 948 | continue; |
---|
.. | .. |
---|
736 | 951 | } |
---|
737 | 952 | |
---|
738 | 953 | count = *area_count(area_dst, nr); |
---|
739 | | - if (count != count_verify[nr]) { |
---|
740 | | - fprintf(stderr, |
---|
741 | | - "nr %lu memory corruption %Lu %Lu\n", |
---|
742 | | - nr, count, |
---|
743 | | - count_verify[nr]), exit(1); |
---|
744 | | - } |
---|
| 954 | + if (count != count_verify[nr]) |
---|
| 955 | + err("nr %lu memory corruption %llu %llu\n", |
---|
| 956 | + nr, count, count_verify[nr]); |
---|
| 957 | + /* |
---|
| 958 | + * Trigger write protection if there is by writing |
---|
| 959 | + * the same value back. |
---|
| 960 | + */ |
---|
| 961 | + *area_count(area_dst, nr) = count; |
---|
745 | 962 | } |
---|
746 | 963 | |
---|
747 | 964 | if (signal_test) |
---|
.. | .. |
---|
753 | 970 | area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size, |
---|
754 | 971 | MREMAP_MAYMOVE | MREMAP_FIXED, area_src); |
---|
755 | 972 | if (area_dst == MAP_FAILED) |
---|
756 | | - perror("mremap"), exit(1); |
---|
| 973 | + err("mremap"); |
---|
| 974 | + /* Reset area_src since we just clobbered it */ |
---|
| 975 | + area_src = NULL; |
---|
757 | 976 | |
---|
758 | 977 | for (; nr < nr_pages; nr++) { |
---|
759 | 978 | count = *area_count(area_dst, nr); |
---|
760 | 979 | if (count != count_verify[nr]) { |
---|
761 | | - fprintf(stderr, |
---|
762 | | - "nr %lu memory corruption %Lu %Lu\n", |
---|
763 | | - nr, count, |
---|
764 | | - count_verify[nr]), exit(1); |
---|
| 980 | + err("nr %lu memory corruption %llu %llu\n", |
---|
| 981 | + nr, count, count_verify[nr]); |
---|
765 | 982 | } |
---|
| 983 | + /* |
---|
| 984 | + * Trigger write protection if there is by writing |
---|
| 985 | + * the same value back. |
---|
| 986 | + */ |
---|
| 987 | + *area_count(area_dst, nr) = count; |
---|
766 | 988 | } |
---|
767 | 989 | |
---|
768 | | - if (uffd_test_ops->release_pages(area_dst)) |
---|
769 | | - return 1; |
---|
| 990 | + uffd_test_ops->release_pages(area_dst); |
---|
770 | 991 | |
---|
771 | | - for (nr = 0; nr < nr_pages; nr++) { |
---|
| 992 | + for (nr = 0; nr < nr_pages; nr++) |
---|
772 | 993 | if (my_bcmp(area_dst + nr * page_size, zeropage, page_size)) |
---|
773 | | - fprintf(stderr, "nr %lu is not zero\n", nr), exit(1); |
---|
774 | | - } |
---|
| 994 | + err("nr %lu is not zero", nr); |
---|
775 | 995 | |
---|
776 | 996 | return 0; |
---|
777 | 997 | } |
---|
.. | .. |
---|
785 | 1005 | offset); |
---|
786 | 1006 | if (ioctl(ufd, UFFDIO_ZEROPAGE, uffdio_zeropage)) { |
---|
787 | 1007 | if (uffdio_zeropage->zeropage != -EEXIST) |
---|
788 | | - fprintf(stderr, "UFFDIO_ZEROPAGE retry error %Ld\n", |
---|
789 | | - uffdio_zeropage->zeropage), exit(1); |
---|
| 1008 | + err("UFFDIO_ZEROPAGE error: %"PRId64, |
---|
| 1009 | + (int64_t)uffdio_zeropage->zeropage); |
---|
790 | 1010 | } else { |
---|
791 | | - fprintf(stderr, "UFFDIO_ZEROPAGE retry unexpected %Ld\n", |
---|
792 | | - uffdio_zeropage->zeropage), exit(1); |
---|
| 1011 | + err("UFFDIO_ZEROPAGE error: %"PRId64, |
---|
| 1012 | + (int64_t)uffdio_zeropage->zeropage); |
---|
793 | 1013 | } |
---|
794 | 1014 | } |
---|
795 | 1015 | |
---|
.. | .. |
---|
798 | 1018 | struct uffdio_zeropage uffdio_zeropage; |
---|
799 | 1019 | int ret; |
---|
800 | 1020 | unsigned long has_zeropage; |
---|
| 1021 | + __s64 res; |
---|
801 | 1022 | |
---|
802 | 1023 | has_zeropage = uffd_test_ops->expected_ioctls & (1 << _UFFDIO_ZEROPAGE); |
---|
803 | 1024 | |
---|
804 | 1025 | if (offset >= nr_pages * page_size) |
---|
805 | | - fprintf(stderr, "unexpected offset %lu\n", |
---|
806 | | - offset), exit(1); |
---|
| 1026 | + err("unexpected offset %lu", offset); |
---|
807 | 1027 | uffdio_zeropage.range.start = (unsigned long) area_dst + offset; |
---|
808 | 1028 | uffdio_zeropage.range.len = page_size; |
---|
809 | 1029 | uffdio_zeropage.mode = 0; |
---|
810 | 1030 | ret = ioctl(ufd, UFFDIO_ZEROPAGE, &uffdio_zeropage); |
---|
| 1031 | + res = uffdio_zeropage.zeropage; |
---|
811 | 1032 | if (ret) { |
---|
812 | 1033 | /* real retval in ufdio_zeropage.zeropage */ |
---|
813 | | - if (has_zeropage) { |
---|
814 | | - if (uffdio_zeropage.zeropage == -EEXIST) |
---|
815 | | - fprintf(stderr, "UFFDIO_ZEROPAGE -EEXIST\n"), |
---|
816 | | - exit(1); |
---|
817 | | - else |
---|
818 | | - fprintf(stderr, "UFFDIO_ZEROPAGE error %Ld\n", |
---|
819 | | - uffdio_zeropage.zeropage), exit(1); |
---|
820 | | - } else { |
---|
821 | | - if (uffdio_zeropage.zeropage != -EINVAL) |
---|
822 | | - fprintf(stderr, |
---|
823 | | - "UFFDIO_ZEROPAGE not -EINVAL %Ld\n", |
---|
824 | | - uffdio_zeropage.zeropage), exit(1); |
---|
825 | | - } |
---|
| 1034 | + if (has_zeropage) |
---|
| 1035 | + err("UFFDIO_ZEROPAGE error: %"PRId64, (int64_t)res); |
---|
| 1036 | + else if (res != -EINVAL) |
---|
| 1037 | + err("UFFDIO_ZEROPAGE not -EINVAL"); |
---|
826 | 1038 | } else if (has_zeropage) { |
---|
827 | | - if (uffdio_zeropage.zeropage != page_size) { |
---|
828 | | - fprintf(stderr, "UFFDIO_ZEROPAGE unexpected %Ld\n", |
---|
829 | | - uffdio_zeropage.zeropage), exit(1); |
---|
| 1039 | + if (res != page_size) { |
---|
| 1040 | + err("UFFDIO_ZEROPAGE unexpected size"); |
---|
830 | 1041 | } else { |
---|
831 | 1042 | if (test_uffdio_zeropage_eexist && retry) { |
---|
832 | 1043 | test_uffdio_zeropage_eexist = false; |
---|
.. | .. |
---|
835 | 1046 | } |
---|
836 | 1047 | return 1; |
---|
837 | 1048 | } |
---|
838 | | - } else { |
---|
839 | | - fprintf(stderr, |
---|
840 | | - "UFFDIO_ZEROPAGE succeeded %Ld\n", |
---|
841 | | - uffdio_zeropage.zeropage), exit(1); |
---|
842 | | - } |
---|
| 1049 | + } else |
---|
| 1050 | + err("UFFDIO_ZEROPAGE succeeded"); |
---|
843 | 1051 | |
---|
844 | 1052 | return 0; |
---|
845 | 1053 | } |
---|
.. | .. |
---|
858 | 1066 | printf("testing UFFDIO_ZEROPAGE: "); |
---|
859 | 1067 | fflush(stdout); |
---|
860 | 1068 | |
---|
861 | | - if (uffd_test_ops->release_pages(area_dst)) |
---|
862 | | - return 1; |
---|
| 1069 | + uffd_test_ctx_init(0); |
---|
863 | 1070 | |
---|
864 | | - if (userfaultfd_open(0) < 0) |
---|
865 | | - return 1; |
---|
866 | 1071 | uffdio_register.range.start = (unsigned long) area_dst; |
---|
867 | 1072 | uffdio_register.range.len = nr_pages * page_size; |
---|
868 | 1073 | uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; |
---|
| 1074 | + if (test_uffdio_wp) |
---|
| 1075 | + uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP; |
---|
869 | 1076 | if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
---|
870 | | - fprintf(stderr, "register failure\n"), exit(1); |
---|
| 1077 | + err("register failure"); |
---|
871 | 1078 | |
---|
872 | 1079 | expected_ioctls = uffd_test_ops->expected_ioctls; |
---|
873 | | - if ((uffdio_register.ioctls & expected_ioctls) != |
---|
874 | | - expected_ioctls) |
---|
875 | | - fprintf(stderr, |
---|
876 | | - "unexpected missing ioctl for anon memory\n"), |
---|
877 | | - exit(1); |
---|
| 1080 | + if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) |
---|
| 1081 | + err("unexpected missing ioctl for anon memory"); |
---|
878 | 1082 | |
---|
879 | | - if (uffdio_zeropage(uffd, 0)) { |
---|
| 1083 | + if (uffdio_zeropage(uffd, 0)) |
---|
880 | 1084 | if (my_bcmp(area_dst, zeropage, page_size)) |
---|
881 | | - fprintf(stderr, "zeropage is not zero\n"), exit(1); |
---|
882 | | - } |
---|
| 1085 | + err("zeropage is not zero"); |
---|
883 | 1086 | |
---|
884 | | - close(uffd); |
---|
885 | 1087 | printf("done.\n"); |
---|
886 | 1088 | return 0; |
---|
887 | 1089 | } |
---|
.. | .. |
---|
890 | 1092 | { |
---|
891 | 1093 | struct uffdio_register uffdio_register; |
---|
892 | 1094 | unsigned long expected_ioctls; |
---|
893 | | - unsigned long userfaults; |
---|
894 | 1095 | pthread_t uffd_mon; |
---|
895 | 1096 | int err, features; |
---|
896 | 1097 | pid_t pid; |
---|
897 | 1098 | char c; |
---|
| 1099 | + struct uffd_stats stats = { 0 }; |
---|
898 | 1100 | |
---|
899 | 1101 | printf("testing events (fork, remap, remove): "); |
---|
900 | 1102 | fflush(stdout); |
---|
901 | 1103 | |
---|
902 | | - if (uffd_test_ops->release_pages(area_dst)) |
---|
903 | | - return 1; |
---|
904 | | - |
---|
905 | 1104 | features = UFFD_FEATURE_EVENT_FORK | UFFD_FEATURE_EVENT_REMAP | |
---|
906 | 1105 | UFFD_FEATURE_EVENT_REMOVE; |
---|
907 | | - if (userfaultfd_open(features) < 0) |
---|
908 | | - return 1; |
---|
| 1106 | + uffd_test_ctx_init(features); |
---|
| 1107 | + |
---|
909 | 1108 | fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); |
---|
910 | 1109 | |
---|
911 | 1110 | uffdio_register.range.start = (unsigned long) area_dst; |
---|
912 | 1111 | uffdio_register.range.len = nr_pages * page_size; |
---|
913 | 1112 | uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; |
---|
| 1113 | + if (test_uffdio_wp) |
---|
| 1114 | + uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP; |
---|
914 | 1115 | if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
---|
915 | | - fprintf(stderr, "register failure\n"), exit(1); |
---|
| 1116 | + err("register failure"); |
---|
916 | 1117 | |
---|
917 | 1118 | expected_ioctls = uffd_test_ops->expected_ioctls; |
---|
918 | | - if ((uffdio_register.ioctls & expected_ioctls) != |
---|
919 | | - expected_ioctls) |
---|
920 | | - fprintf(stderr, |
---|
921 | | - "unexpected missing ioctl for anon memory\n"), |
---|
922 | | - exit(1); |
---|
| 1119 | + if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) |
---|
| 1120 | + err("unexpected missing ioctl for anon memory"); |
---|
923 | 1121 | |
---|
924 | | - if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) |
---|
925 | | - perror("uffd_poll_thread create"), exit(1); |
---|
| 1122 | + if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats)) |
---|
| 1123 | + err("uffd_poll_thread create"); |
---|
926 | 1124 | |
---|
927 | 1125 | pid = fork(); |
---|
928 | 1126 | if (pid < 0) |
---|
929 | | - perror("fork"), exit(1); |
---|
| 1127 | + err("fork"); |
---|
930 | 1128 | |
---|
931 | 1129 | if (!pid) |
---|
932 | | - return faulting_process(0); |
---|
| 1130 | + exit(faulting_process(0)); |
---|
933 | 1131 | |
---|
934 | 1132 | waitpid(pid, &err, 0); |
---|
935 | 1133 | if (err) |
---|
936 | | - fprintf(stderr, "faulting process failed\n"), exit(1); |
---|
937 | | - |
---|
| 1134 | + err("faulting process failed"); |
---|
938 | 1135 | if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) |
---|
939 | | - perror("pipe write"), exit(1); |
---|
940 | | - if (pthread_join(uffd_mon, (void **)&userfaults)) |
---|
| 1136 | + err("pipe write"); |
---|
| 1137 | + if (pthread_join(uffd_mon, NULL)) |
---|
941 | 1138 | return 1; |
---|
942 | 1139 | |
---|
943 | | - close(uffd); |
---|
944 | | - printf("userfaults: %ld\n", userfaults); |
---|
| 1140 | + uffd_stats_report(&stats, 1); |
---|
945 | 1141 | |
---|
946 | | - return userfaults != nr_pages; |
---|
| 1142 | + return stats.missing_faults != nr_pages; |
---|
947 | 1143 | } |
---|
948 | 1144 | |
---|
949 | 1145 | static int userfaultfd_sig_test(void) |
---|
.. | .. |
---|
955 | 1151 | int err, features; |
---|
956 | 1152 | pid_t pid; |
---|
957 | 1153 | char c; |
---|
| 1154 | + struct uffd_stats stats = { 0 }; |
---|
958 | 1155 | |
---|
959 | 1156 | printf("testing signal delivery: "); |
---|
960 | 1157 | fflush(stdout); |
---|
961 | 1158 | |
---|
962 | | - if (uffd_test_ops->release_pages(area_dst)) |
---|
963 | | - return 1; |
---|
964 | | - |
---|
965 | 1159 | features = UFFD_FEATURE_EVENT_FORK|UFFD_FEATURE_SIGBUS; |
---|
966 | | - if (userfaultfd_open(features) < 0) |
---|
967 | | - return 1; |
---|
| 1160 | + uffd_test_ctx_init(features); |
---|
| 1161 | + |
---|
968 | 1162 | fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); |
---|
969 | 1163 | |
---|
970 | 1164 | uffdio_register.range.start = (unsigned long) area_dst; |
---|
971 | 1165 | uffdio_register.range.len = nr_pages * page_size; |
---|
972 | 1166 | uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; |
---|
| 1167 | + if (test_uffdio_wp) |
---|
| 1168 | + uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP; |
---|
973 | 1169 | if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
---|
974 | | - fprintf(stderr, "register failure\n"), exit(1); |
---|
| 1170 | + err("register failure"); |
---|
975 | 1171 | |
---|
976 | 1172 | expected_ioctls = uffd_test_ops->expected_ioctls; |
---|
977 | | - if ((uffdio_register.ioctls & expected_ioctls) != |
---|
978 | | - expected_ioctls) |
---|
979 | | - fprintf(stderr, |
---|
980 | | - "unexpected missing ioctl for anon memory\n"), |
---|
981 | | - exit(1); |
---|
| 1173 | + if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) |
---|
| 1174 | + err("unexpected missing ioctl for anon memory"); |
---|
982 | 1175 | |
---|
983 | 1176 | if (faulting_process(1)) |
---|
984 | | - fprintf(stderr, "faulting process failed\n"), exit(1); |
---|
| 1177 | + err("faulting process failed"); |
---|
985 | 1178 | |
---|
986 | | - if (uffd_test_ops->release_pages(area_dst)) |
---|
987 | | - return 1; |
---|
| 1179 | + uffd_test_ops->release_pages(area_dst); |
---|
988 | 1180 | |
---|
989 | | - if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) |
---|
990 | | - perror("uffd_poll_thread create"), exit(1); |
---|
| 1181 | + if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats)) |
---|
| 1182 | + err("uffd_poll_thread create"); |
---|
991 | 1183 | |
---|
992 | 1184 | pid = fork(); |
---|
993 | 1185 | if (pid < 0) |
---|
994 | | - perror("fork"), exit(1); |
---|
| 1186 | + err("fork"); |
---|
995 | 1187 | |
---|
996 | 1188 | if (!pid) |
---|
997 | 1189 | exit(faulting_process(2)); |
---|
998 | 1190 | |
---|
999 | 1191 | waitpid(pid, &err, 0); |
---|
1000 | 1192 | if (err) |
---|
1001 | | - fprintf(stderr, "faulting process failed\n"), exit(1); |
---|
1002 | | - |
---|
| 1193 | + err("faulting process failed"); |
---|
1003 | 1194 | if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) |
---|
1004 | | - perror("pipe write"), exit(1); |
---|
| 1195 | + err("pipe write"); |
---|
1005 | 1196 | if (pthread_join(uffd_mon, (void **)&userfaults)) |
---|
1006 | 1197 | return 1; |
---|
1007 | 1198 | |
---|
1008 | 1199 | printf("done.\n"); |
---|
1009 | 1200 | if (userfaults) |
---|
1010 | | - fprintf(stderr, "Signal test failed, userfaults: %ld\n", |
---|
1011 | | - userfaults); |
---|
1012 | | - close(uffd); |
---|
| 1201 | + err("Signal test failed, userfaults: %ld", userfaults); |
---|
| 1202 | + |
---|
1013 | 1203 | return userfaults != 0; |
---|
1014 | 1204 | } |
---|
| 1205 | + |
---|
| 1206 | +static int userfaultfd_minor_test(void) |
---|
| 1207 | +{ |
---|
| 1208 | + struct uffdio_register uffdio_register; |
---|
| 1209 | + unsigned long expected_ioctls; |
---|
| 1210 | + unsigned long p; |
---|
| 1211 | + pthread_t uffd_mon; |
---|
| 1212 | + uint8_t expected_byte; |
---|
| 1213 | + void *expected_page; |
---|
| 1214 | + char c; |
---|
| 1215 | + struct uffd_stats stats = { 0 }; |
---|
| 1216 | + uint64_t req_features, features_out; |
---|
| 1217 | + |
---|
| 1218 | + if (!test_uffdio_minor) |
---|
| 1219 | + return 0; |
---|
| 1220 | + |
---|
| 1221 | + printf("testing minor faults: "); |
---|
| 1222 | + fflush(stdout); |
---|
| 1223 | + |
---|
| 1224 | + if (test_type == TEST_HUGETLB) |
---|
| 1225 | + req_features = UFFD_FEATURE_MINOR_HUGETLBFS; |
---|
| 1226 | + else if (test_type == TEST_SHMEM) |
---|
| 1227 | + req_features = UFFD_FEATURE_MINOR_SHMEM; |
---|
| 1228 | + else |
---|
| 1229 | + return 1; |
---|
| 1230 | + |
---|
| 1231 | + features_out = req_features; |
---|
| 1232 | + uffd_test_ctx_init_ext(&features_out); |
---|
| 1233 | + /* If kernel reports required features aren't supported, skip test. */ |
---|
| 1234 | + if ((features_out & req_features) != req_features) { |
---|
| 1235 | + printf("skipping test due to lack of feature support\n"); |
---|
| 1236 | + fflush(stdout); |
---|
| 1237 | + return 0; |
---|
| 1238 | + } |
---|
| 1239 | + |
---|
| 1240 | + uffdio_register.range.start = (unsigned long)area_dst_alias; |
---|
| 1241 | + uffdio_register.range.len = nr_pages * page_size; |
---|
| 1242 | + uffdio_register.mode = UFFDIO_REGISTER_MODE_MINOR; |
---|
| 1243 | + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
---|
| 1244 | + err("register failure"); |
---|
| 1245 | + |
---|
| 1246 | + expected_ioctls = uffd_test_ops->expected_ioctls; |
---|
| 1247 | + expected_ioctls |= 1 << _UFFDIO_CONTINUE; |
---|
| 1248 | + if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) |
---|
| 1249 | + err("unexpected missing ioctl(s)"); |
---|
| 1250 | + |
---|
| 1251 | + /* |
---|
| 1252 | + * After registering with UFFD, populate the non-UFFD-registered side of |
---|
| 1253 | + * the shared mapping. This should *not* trigger any UFFD minor faults. |
---|
| 1254 | + */ |
---|
| 1255 | + for (p = 0; p < nr_pages; ++p) { |
---|
| 1256 | + memset(area_dst + (p * page_size), p % ((uint8_t)-1), |
---|
| 1257 | + page_size); |
---|
| 1258 | + } |
---|
| 1259 | + |
---|
| 1260 | + if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats)) |
---|
| 1261 | + err("uffd_poll_thread create"); |
---|
| 1262 | + |
---|
| 1263 | + /* |
---|
| 1264 | + * Read each of the pages back using the UFFD-registered mapping. We |
---|
| 1265 | + * expect that the first time we touch a page, it will result in a minor |
---|
| 1266 | + * fault. uffd_poll_thread will resolve the fault by bit-flipping the |
---|
| 1267 | + * page's contents, and then issuing a CONTINUE ioctl. |
---|
| 1268 | + */ |
---|
| 1269 | + |
---|
| 1270 | + if (posix_memalign(&expected_page, page_size, page_size)) |
---|
| 1271 | + err("out of memory"); |
---|
| 1272 | + |
---|
| 1273 | + for (p = 0; p < nr_pages; ++p) { |
---|
| 1274 | + expected_byte = ~((uint8_t)(p % ((uint8_t)-1))); |
---|
| 1275 | + memset(expected_page, expected_byte, page_size); |
---|
| 1276 | + if (my_bcmp(expected_page, area_dst_alias + (p * page_size), |
---|
| 1277 | + page_size)) |
---|
| 1278 | + err("unexpected page contents after minor fault"); |
---|
| 1279 | + } |
---|
| 1280 | + |
---|
| 1281 | + if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) |
---|
| 1282 | + err("pipe write"); |
---|
| 1283 | + if (pthread_join(uffd_mon, NULL)) |
---|
| 1284 | + return 1; |
---|
| 1285 | + |
---|
| 1286 | + uffd_stats_report(&stats, 1); |
---|
| 1287 | + |
---|
| 1288 | + return stats.missing_faults != 0 || stats.minor_faults != nr_pages; |
---|
| 1289 | +} |
---|
| 1290 | + |
---|
1015 | 1291 | static int userfaultfd_stress(void) |
---|
1016 | 1292 | { |
---|
1017 | 1293 | void *area; |
---|
1018 | 1294 | char *tmp_area; |
---|
1019 | 1295 | unsigned long nr; |
---|
1020 | 1296 | struct uffdio_register uffdio_register; |
---|
1021 | | - unsigned long cpu; |
---|
1022 | | - int err; |
---|
1023 | | - unsigned long userfaults[nr_cpus]; |
---|
| 1297 | + struct uffd_stats uffd_stats[nr_cpus]; |
---|
1024 | 1298 | |
---|
1025 | | - uffd_test_ops->allocate_area((void **)&area_src); |
---|
1026 | | - if (!area_src) |
---|
1027 | | - return 1; |
---|
1028 | | - uffd_test_ops->allocate_area((void **)&area_dst); |
---|
1029 | | - if (!area_dst) |
---|
1030 | | - return 1; |
---|
| 1299 | + uffd_test_ctx_init(0); |
---|
1031 | 1300 | |
---|
1032 | | - if (userfaultfd_open(0) < 0) |
---|
1033 | | - return 1; |
---|
1034 | | - |
---|
1035 | | - count_verify = malloc(nr_pages * sizeof(unsigned long long)); |
---|
1036 | | - if (!count_verify) { |
---|
1037 | | - perror("count_verify"); |
---|
1038 | | - return 1; |
---|
1039 | | - } |
---|
1040 | | - |
---|
1041 | | - for (nr = 0; nr < nr_pages; nr++) { |
---|
1042 | | - *area_mutex(area_src, nr) = (pthread_mutex_t) |
---|
1043 | | - PTHREAD_MUTEX_INITIALIZER; |
---|
1044 | | - count_verify[nr] = *area_count(area_src, nr) = 1; |
---|
1045 | | - /* |
---|
1046 | | - * In the transition between 255 to 256, powerpc will |
---|
1047 | | - * read out of order in my_bcmp and see both bytes as |
---|
1048 | | - * zero, so leave a placeholder below always non-zero |
---|
1049 | | - * after the count, to avoid my_bcmp to trigger false |
---|
1050 | | - * positives. |
---|
1051 | | - */ |
---|
1052 | | - *(area_count(area_src, nr) + 1) = 1; |
---|
1053 | | - } |
---|
1054 | | - |
---|
1055 | | - pipefd = malloc(sizeof(int) * nr_cpus * 2); |
---|
1056 | | - if (!pipefd) { |
---|
1057 | | - perror("pipefd"); |
---|
1058 | | - return 1; |
---|
1059 | | - } |
---|
1060 | | - for (cpu = 0; cpu < nr_cpus; cpu++) { |
---|
1061 | | - if (pipe2(&pipefd[cpu*2], O_CLOEXEC | O_NONBLOCK)) { |
---|
1062 | | - perror("pipe"); |
---|
1063 | | - return 1; |
---|
1064 | | - } |
---|
1065 | | - } |
---|
1066 | | - |
---|
1067 | | - if (posix_memalign(&area, page_size, page_size)) { |
---|
1068 | | - fprintf(stderr, "out of memory\n"); |
---|
1069 | | - return 1; |
---|
1070 | | - } |
---|
| 1301 | + if (posix_memalign(&area, page_size, page_size)) |
---|
| 1302 | + err("out of memory"); |
---|
1071 | 1303 | zeropage = area; |
---|
1072 | 1304 | bzero(zeropage, page_size); |
---|
1073 | 1305 | |
---|
.. | .. |
---|
1076 | 1308 | pthread_attr_init(&attr); |
---|
1077 | 1309 | pthread_attr_setstacksize(&attr, 16*1024*1024); |
---|
1078 | 1310 | |
---|
1079 | | - err = 0; |
---|
1080 | 1311 | while (bounces--) { |
---|
1081 | 1312 | unsigned long expected_ioctls; |
---|
1082 | 1313 | |
---|
.. | .. |
---|
1101 | 1332 | uffdio_register.range.start = (unsigned long) area_dst; |
---|
1102 | 1333 | uffdio_register.range.len = nr_pages * page_size; |
---|
1103 | 1334 | uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; |
---|
1104 | | - if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) { |
---|
1105 | | - fprintf(stderr, "register failure\n"); |
---|
1106 | | - return 1; |
---|
1107 | | - } |
---|
| 1335 | + if (test_uffdio_wp) |
---|
| 1336 | + uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP; |
---|
| 1337 | + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
---|
| 1338 | + err("register failure"); |
---|
1108 | 1339 | expected_ioctls = uffd_test_ops->expected_ioctls; |
---|
1109 | 1340 | if ((uffdio_register.ioctls & expected_ioctls) != |
---|
1110 | | - expected_ioctls) { |
---|
1111 | | - fprintf(stderr, |
---|
1112 | | - "unexpected missing ioctl for anon memory\n"); |
---|
1113 | | - return 1; |
---|
1114 | | - } |
---|
| 1341 | + expected_ioctls) |
---|
| 1342 | + err("unexpected missing ioctl for anon memory"); |
---|
1115 | 1343 | |
---|
1116 | 1344 | if (area_dst_alias) { |
---|
1117 | 1345 | uffdio_register.range.start = (unsigned long) |
---|
1118 | 1346 | area_dst_alias; |
---|
1119 | | - if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) { |
---|
1120 | | - fprintf(stderr, "register failure alias\n"); |
---|
1121 | | - return 1; |
---|
1122 | | - } |
---|
| 1347 | + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
---|
| 1348 | + err("register failure alias"); |
---|
1123 | 1349 | } |
---|
1124 | 1350 | |
---|
1125 | 1351 | /* |
---|
.. | .. |
---|
1146 | 1372 | * MADV_DONTNEED only after the UFFDIO_REGISTER, so it's |
---|
1147 | 1373 | * required to MADV_DONTNEED here. |
---|
1148 | 1374 | */ |
---|
1149 | | - if (uffd_test_ops->release_pages(area_dst)) |
---|
1150 | | - return 1; |
---|
| 1375 | + uffd_test_ops->release_pages(area_dst); |
---|
| 1376 | + |
---|
| 1377 | + uffd_stats_reset(uffd_stats, nr_cpus); |
---|
1151 | 1378 | |
---|
1152 | 1379 | /* bounce pass */ |
---|
1153 | | - if (stress(userfaults)) |
---|
| 1380 | + if (stress(uffd_stats)) |
---|
1154 | 1381 | return 1; |
---|
1155 | 1382 | |
---|
| 1383 | + /* Clear all the write protections if there is any */ |
---|
| 1384 | + if (test_uffdio_wp) |
---|
| 1385 | + wp_range(uffd, (unsigned long)area_dst, |
---|
| 1386 | + nr_pages * page_size, false); |
---|
| 1387 | + |
---|
1156 | 1388 | /* unregister */ |
---|
1157 | | - if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_register.range)) { |
---|
1158 | | - fprintf(stderr, "unregister failure\n"); |
---|
1159 | | - return 1; |
---|
1160 | | - } |
---|
| 1389 | + if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_register.range)) |
---|
| 1390 | + err("unregister failure"); |
---|
1161 | 1391 | if (area_dst_alias) { |
---|
1162 | 1392 | uffdio_register.range.start = (unsigned long) area_dst; |
---|
1163 | 1393 | if (ioctl(uffd, UFFDIO_UNREGISTER, |
---|
1164 | | - &uffdio_register.range)) { |
---|
1165 | | - fprintf(stderr, "unregister failure alias\n"); |
---|
1166 | | - return 1; |
---|
1167 | | - } |
---|
| 1394 | + &uffdio_register.range)) |
---|
| 1395 | + err("unregister failure alias"); |
---|
1168 | 1396 | } |
---|
1169 | 1397 | |
---|
1170 | 1398 | /* verification */ |
---|
1171 | | - if (bounces & BOUNCE_VERIFY) { |
---|
1172 | | - for (nr = 0; nr < nr_pages; nr++) { |
---|
1173 | | - if (*area_count(area_dst, nr) != count_verify[nr]) { |
---|
1174 | | - fprintf(stderr, |
---|
1175 | | - "error area_count %Lu %Lu %lu\n", |
---|
1176 | | - *area_count(area_src, nr), |
---|
1177 | | - count_verify[nr], |
---|
1178 | | - nr); |
---|
1179 | | - err = 1; |
---|
1180 | | - bounces = 0; |
---|
1181 | | - } |
---|
1182 | | - } |
---|
1183 | | - } |
---|
| 1399 | + if (bounces & BOUNCE_VERIFY) |
---|
| 1400 | + for (nr = 0; nr < nr_pages; nr++) |
---|
| 1401 | + if (*area_count(area_dst, nr) != count_verify[nr]) |
---|
| 1402 | + err("error area_count %llu %llu %lu\n", |
---|
| 1403 | + *area_count(area_src, nr), |
---|
| 1404 | + count_verify[nr], nr); |
---|
1184 | 1405 | |
---|
1185 | 1406 | /* prepare next bounce */ |
---|
1186 | 1407 | tmp_area = area_src; |
---|
.. | .. |
---|
1191 | 1412 | area_src_alias = area_dst_alias; |
---|
1192 | 1413 | area_dst_alias = tmp_area; |
---|
1193 | 1414 | |
---|
1194 | | - printf("userfaults:"); |
---|
1195 | | - for (cpu = 0; cpu < nr_cpus; cpu++) |
---|
1196 | | - printf(" %lu", userfaults[cpu]); |
---|
1197 | | - printf("\n"); |
---|
| 1415 | + uffd_stats_report(uffd_stats, nr_cpus); |
---|
1198 | 1416 | } |
---|
1199 | 1417 | |
---|
1200 | | - if (err) |
---|
1201 | | - return err; |
---|
1202 | | - |
---|
1203 | | - close(uffd); |
---|
1204 | 1418 | return userfaultfd_zeropage_test() || userfaultfd_sig_test() |
---|
1205 | | - || userfaultfd_events_test(); |
---|
| 1419 | + || userfaultfd_events_test() || userfaultfd_minor_test(); |
---|
1206 | 1420 | } |
---|
1207 | 1421 | |
---|
1208 | 1422 | /* |
---|
.. | .. |
---|
1234 | 1448 | if (!strcmp(type, "anon")) { |
---|
1235 | 1449 | test_type = TEST_ANON; |
---|
1236 | 1450 | uffd_test_ops = &anon_uffd_test_ops; |
---|
| 1451 | + /* Only enable write-protect test for anonymous test */ |
---|
| 1452 | + test_uffdio_wp = true; |
---|
1237 | 1453 | } else if (!strcmp(type, "hugetlb")) { |
---|
1238 | 1454 | test_type = TEST_HUGETLB; |
---|
1239 | 1455 | uffd_test_ops = &hugetlb_uffd_test_ops; |
---|
.. | .. |
---|
1241 | 1457 | map_shared = true; |
---|
1242 | 1458 | test_type = TEST_HUGETLB; |
---|
1243 | 1459 | uffd_test_ops = &hugetlb_uffd_test_ops; |
---|
| 1460 | + /* Minor faults require shared hugetlb; only enable here. */ |
---|
| 1461 | + test_uffdio_minor = true; |
---|
1244 | 1462 | } else if (!strcmp(type, "shmem")) { |
---|
1245 | 1463 | map_shared = true; |
---|
1246 | 1464 | test_type = TEST_SHMEM; |
---|
1247 | 1465 | uffd_test_ops = &shmem_uffd_test_ops; |
---|
| 1466 | + test_uffdio_minor = true; |
---|
1248 | 1467 | } else { |
---|
1249 | | - fprintf(stderr, "Unknown test type: %s\n", type), exit(1); |
---|
| 1468 | + err("Unknown test type: %s", type); |
---|
1250 | 1469 | } |
---|
1251 | 1470 | |
---|
1252 | 1471 | if (test_type == TEST_HUGETLB) |
---|
.. | .. |
---|
1255 | 1474 | page_size = sysconf(_SC_PAGE_SIZE); |
---|
1256 | 1475 | |
---|
1257 | 1476 | if (!page_size) |
---|
1258 | | - fprintf(stderr, "Unable to determine page size\n"), |
---|
1259 | | - exit(2); |
---|
| 1477 | + err("Unable to determine page size"); |
---|
1260 | 1478 | if ((unsigned long) area_count(NULL, 0) + sizeof(unsigned long long) * 2 |
---|
1261 | 1479 | > page_size) |
---|
1262 | | - fprintf(stderr, "Impossible to run this test\n"), exit(2); |
---|
| 1480 | + err("Impossible to run this test"); |
---|
1263 | 1481 | } |
---|
1264 | 1482 | |
---|
1265 | 1483 | static void sigalrm(int sig) |
---|
.. | .. |
---|
1274 | 1492 | int main(int argc, char **argv) |
---|
1275 | 1493 | { |
---|
1276 | 1494 | if (argc < 4) |
---|
1277 | | - fprintf(stderr, "Usage: <test type> <MiB> <bounces> [hugetlbfs_file]\n"), |
---|
1278 | | - exit(1); |
---|
| 1495 | + usage(); |
---|
1279 | 1496 | |
---|
1280 | 1497 | if (signal(SIGALRM, sigalrm) == SIG_ERR) |
---|
1281 | | - fprintf(stderr, "failed to arm SIGALRM"), exit(1); |
---|
| 1498 | + err("failed to arm SIGALRM"); |
---|
1282 | 1499 | alarm(ALARM_INTERVAL_SECS); |
---|
1283 | 1500 | |
---|
1284 | 1501 | set_test_type(argv[1]); |
---|
.. | .. |
---|
1287 | 1504 | nr_pages_per_cpu = atol(argv[2]) * 1024*1024 / page_size / |
---|
1288 | 1505 | nr_cpus; |
---|
1289 | 1506 | if (!nr_pages_per_cpu) { |
---|
1290 | | - fprintf(stderr, "invalid MiB\n"); |
---|
1291 | | - fprintf(stderr, "Usage: <MiB> <bounces>\n"), exit(1); |
---|
| 1507 | + _err("invalid MiB"); |
---|
| 1508 | + usage(); |
---|
1292 | 1509 | } |
---|
1293 | 1510 | |
---|
1294 | 1511 | bounces = atoi(argv[3]); |
---|
1295 | 1512 | if (bounces <= 0) { |
---|
1296 | | - fprintf(stderr, "invalid bounces\n"); |
---|
1297 | | - fprintf(stderr, "Usage: <MiB> <bounces>\n"), exit(1); |
---|
| 1513 | + _err("invalid bounces"); |
---|
| 1514 | + usage(); |
---|
1298 | 1515 | } |
---|
1299 | 1516 | nr_pages = nr_pages_per_cpu * nr_cpus; |
---|
1300 | 1517 | |
---|
1301 | 1518 | if (test_type == TEST_HUGETLB) { |
---|
1302 | 1519 | if (argc < 5) |
---|
1303 | | - fprintf(stderr, "Usage: hugetlb <MiB> <bounces> <hugetlbfs_file>\n"), |
---|
1304 | | - exit(1); |
---|
| 1520 | + usage(); |
---|
1305 | 1521 | huge_fd = open(argv[4], O_CREAT | O_RDWR, 0755); |
---|
1306 | | - if (huge_fd < 0) { |
---|
1307 | | - fprintf(stderr, "Open of %s failed", argv[3]); |
---|
1308 | | - perror("open"); |
---|
1309 | | - exit(1); |
---|
1310 | | - } |
---|
1311 | | - if (ftruncate(huge_fd, 0)) { |
---|
1312 | | - fprintf(stderr, "ftruncate %s to size 0 failed", argv[3]); |
---|
1313 | | - perror("ftruncate"); |
---|
1314 | | - exit(1); |
---|
1315 | | - } |
---|
| 1522 | + if (huge_fd < 0) |
---|
| 1523 | + err("Open of %s failed", argv[4]); |
---|
| 1524 | + if (ftruncate(huge_fd, 0)) |
---|
| 1525 | + err("ftruncate %s to size 0 failed", argv[4]); |
---|
| 1526 | + } else if (test_type == TEST_SHMEM) { |
---|
| 1527 | + shm_fd = memfd_create(argv[0], 0); |
---|
| 1528 | + if (shm_fd < 0) |
---|
| 1529 | + err("memfd_create"); |
---|
| 1530 | + if (ftruncate(shm_fd, nr_pages * page_size * 2)) |
---|
| 1531 | + err("ftruncate"); |
---|
| 1532 | + if (fallocate(shm_fd, |
---|
| 1533 | + FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, |
---|
| 1534 | + nr_pages * page_size * 2)) |
---|
| 1535 | + err("fallocate"); |
---|
1316 | 1536 | } |
---|
1317 | 1537 | printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n", |
---|
1318 | 1538 | nr_pages, nr_pages_per_cpu); |
---|