Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm/hmm/test: add selftests for HMM

Add some basic stand alone self tests for HMM.
The test program and shell scripts use the test_hmm.ko driver to exercise
HMM functionality in the kernel.

Link: https://lore.kernel.org/r/20200422195028.3684-3-rcampbell@nvidia.com
Signed-off-by: Ralph Campbell <rcampbell@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>

authored by

Ralph Campbell and committed by
Jason Gunthorpe
fee9f6d1 b2ef9f5a

+1478
+1
tools/testing/selftests/vm/.gitignore
··· 16 16 va_128TBswitch 17 17 map_fixed_noreplace 18 18 write_to_hugetlbfs 19 + hmm-tests
+3
tools/testing/selftests/vm/Makefile
··· 7 7 LDLIBS = -lrt 8 8 TEST_GEN_FILES = compaction_test 9 9 TEST_GEN_FILES += gup_benchmark 10 + TEST_GEN_FILES += hmm-tests 10 11 TEST_GEN_FILES += hugepage-mmap 11 12 TEST_GEN_FILES += hugepage-shm 12 13 TEST_GEN_FILES += map_hugetlb ··· 33 32 34 33 KSFT_KHDR_INSTALL := 1 35 34 include ../lib.mk 35 + 36 + $(OUTPUT)/hmm-tests: LDLIBS += -lhugetlbfs -lpthread 36 37 37 38 $(OUTPUT)/userfaultfd: LDLIBS += -lpthread 38 39
+2
tools/testing/selftests/vm/config
··· 1 1 CONFIG_SYSVIPC=y 2 2 CONFIG_USERFAULTFD=y 3 3 CONFIG_TEST_VMALLOC=m 4 + CONFIG_DEVICE_PRIVATE=y 5 + CONFIG_TEST_HMM=m
+1359
tools/testing/selftests/vm/hmm-tests.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * HMM stands for Heterogeneous Memory Management, it is a helper layer inside 4 + * the linux kernel to help device drivers mirror a process address space in 5 + * the device. This allows the device to use the same address space which 6 + * makes communication and data exchange a lot easier. 7 + * 8 + * This framework's sole purpose is to exercise various code paths inside 9 + * the kernel to make sure that HMM performs as expected and to flush out any 10 + * bugs. 11 + */ 12 + 13 + #include "../kselftest_harness.h" 14 + 15 + #include <errno.h> 16 + #include <fcntl.h> 17 + #include <stdio.h> 18 + #include <stdlib.h> 19 + #include <stdint.h> 20 + #include <unistd.h> 21 + #include <strings.h> 22 + #include <time.h> 23 + #include <pthread.h> 24 + #include <hugetlbfs.h> 25 + #include <sys/types.h> 26 + #include <sys/stat.h> 27 + #include <sys/mman.h> 28 + #include <sys/ioctl.h> 29 + 30 + /* 31 + * This is a private UAPI to the kernel test module so it isn't exported 32 + * in the usual include/uapi/... directory. 33 + */ 34 + #include "../../../../lib/test_hmm_uapi.h" 35 + 36 + struct hmm_buffer { 37 + void *ptr; 38 + void *mirror; 39 + unsigned long size; 40 + int fd; 41 + uint64_t cpages; 42 + uint64_t faults; 43 + }; 44 + 45 + #define TWOMEG (1 << 21) 46 + #define HMM_BUFFER_SIZE (1024 << 12) 47 + #define HMM_PATH_MAX 64 48 + #define NTIMES 256 49 + 50 + #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1))) 51 + 52 + FIXTURE(hmm) 53 + { 54 + int fd; 55 + unsigned int page_size; 56 + unsigned int page_shift; 57 + }; 58 + 59 + FIXTURE(hmm2) 60 + { 61 + int fd0; 62 + int fd1; 63 + unsigned int page_size; 64 + unsigned int page_shift; 65 + }; 66 + 67 + static int hmm_open(int unit) 68 + { 69 + char pathname[HMM_PATH_MAX]; 70 + int fd; 71 + 72 + snprintf(pathname, sizeof(pathname), "/dev/hmm_dmirror%d", unit); 73 + fd = open(pathname, O_RDWR, 0); 74 + if (fd < 0) 75 + fprintf(stderr, "could not open hmm dmirror driver (%s)\n", 76 + pathname); 77 + return fd; 78 + } 79 + 80 + FIXTURE_SETUP(hmm) 81 + { 82 + self->page_size = sysconf(_SC_PAGE_SIZE); 83 + self->page_shift = ffs(self->page_size) - 1; 84 + 85 + self->fd = hmm_open(0); 86 + ASSERT_GE(self->fd, 0); 87 + } 88 + 89 + FIXTURE_SETUP(hmm2) 90 + { 91 + self->page_size = sysconf(_SC_PAGE_SIZE); 92 + self->page_shift = ffs(self->page_size) - 1; 93 + 94 + self->fd0 = hmm_open(0); 95 + ASSERT_GE(self->fd0, 0); 96 + self->fd1 = hmm_open(1); 97 + ASSERT_GE(self->fd1, 0); 98 + } 99 + 100 + FIXTURE_TEARDOWN(hmm) 101 + { 102 + int ret = close(self->fd); 103 + 104 + ASSERT_EQ(ret, 0); 105 + self->fd = -1; 106 + } 107 + 108 + FIXTURE_TEARDOWN(hmm2) 109 + { 110 + int ret = close(self->fd0); 111 + 112 + ASSERT_EQ(ret, 0); 113 + self->fd0 = -1; 114 + 115 + ret = close(self->fd1); 116 + ASSERT_EQ(ret, 0); 117 + self->fd1 = -1; 118 + } 119 + 120 + static int hmm_dmirror_cmd(int fd, 121 + unsigned long request, 122 + struct hmm_buffer *buffer, 123 + unsigned long npages) 124 + { 125 + struct hmm_dmirror_cmd cmd; 126 + int ret; 127 + 128 + /* Simulate a device reading system memory. */ 129 + cmd.addr = (__u64)buffer->ptr; 130 + cmd.ptr = (__u64)buffer->mirror; 131 + cmd.npages = npages; 132 + 133 + for (;;) { 134 + ret = ioctl(fd, request, &cmd); 135 + if (ret == 0) 136 + break; 137 + if (errno == EINTR) 138 + continue; 139 + return -errno; 140 + } 141 + buffer->cpages = cmd.cpages; 142 + buffer->faults = cmd.faults; 143 + 144 + return 0; 145 + } 146 + 147 + static void hmm_buffer_free(struct hmm_buffer *buffer) 148 + { 149 + if (buffer == NULL) 150 + return; 151 + 152 + if (buffer->ptr) 153 + munmap(buffer->ptr, buffer->size); 154 + free(buffer->mirror); 155 + free(buffer); 156 + } 157 + 158 + /* 159 + * Create a temporary file that will be deleted on close. 160 + */ 161 + static int hmm_create_file(unsigned long size) 162 + { 163 + char path[HMM_PATH_MAX]; 164 + int fd; 165 + 166 + strcpy(path, "/tmp"); 167 + fd = open(path, O_TMPFILE | O_EXCL | O_RDWR, 0600); 168 + if (fd >= 0) { 169 + int r; 170 + 171 + do { 172 + r = ftruncate(fd, size); 173 + } while (r == -1 && errno == EINTR); 174 + if (!r) 175 + return fd; 176 + close(fd); 177 + } 178 + return -1; 179 + } 180 + 181 + /* 182 + * Return a random unsigned number. 183 + */ 184 + static unsigned int hmm_random(void) 185 + { 186 + static int fd = -1; 187 + unsigned int r; 188 + 189 + if (fd < 0) { 190 + fd = open("/dev/urandom", O_RDONLY); 191 + if (fd < 0) { 192 + fprintf(stderr, "%s:%d failed to open /dev/urandom\n", 193 + __FILE__, __LINE__); 194 + return ~0U; 195 + } 196 + } 197 + read(fd, &r, sizeof(r)); 198 + return r; 199 + } 200 + 201 + static void hmm_nanosleep(unsigned int n) 202 + { 203 + struct timespec t; 204 + 205 + t.tv_sec = 0; 206 + t.tv_nsec = n; 207 + nanosleep(&t, NULL); 208 + } 209 + 210 + /* 211 + * Simple NULL test of device open/close. 212 + */ 213 + TEST_F(hmm, open_close) 214 + { 215 + } 216 + 217 + /* 218 + * Read private anonymous memory. 219 + */ 220 + TEST_F(hmm, anon_read) 221 + { 222 + struct hmm_buffer *buffer; 223 + unsigned long npages; 224 + unsigned long size; 225 + unsigned long i; 226 + int *ptr; 227 + int ret; 228 + int val; 229 + 230 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 231 + ASSERT_NE(npages, 0); 232 + size = npages << self->page_shift; 233 + 234 + buffer = malloc(sizeof(*buffer)); 235 + ASSERT_NE(buffer, NULL); 236 + 237 + buffer->fd = -1; 238 + buffer->size = size; 239 + buffer->mirror = malloc(size); 240 + ASSERT_NE(buffer->mirror, NULL); 241 + 242 + buffer->ptr = mmap(NULL, size, 243 + PROT_READ | PROT_WRITE, 244 + MAP_PRIVATE | MAP_ANONYMOUS, 245 + buffer->fd, 0); 246 + ASSERT_NE(buffer->ptr, MAP_FAILED); 247 + 248 + /* 249 + * Initialize buffer in system memory but leave the first two pages 250 + * zero (pte_none and pfn_zero). 251 + */ 252 + i = 2 * self->page_size / sizeof(*ptr); 253 + for (ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 254 + ptr[i] = i; 255 + 256 + /* Set buffer permission to read-only. */ 257 + ret = mprotect(buffer->ptr, size, PROT_READ); 258 + ASSERT_EQ(ret, 0); 259 + 260 + /* Populate the CPU page table with a special zero page. */ 261 + val = *(int *)(buffer->ptr + self->page_size); 262 + ASSERT_EQ(val, 0); 263 + 264 + /* Simulate a device reading system memory. */ 265 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages); 266 + ASSERT_EQ(ret, 0); 267 + ASSERT_EQ(buffer->cpages, npages); 268 + ASSERT_EQ(buffer->faults, 1); 269 + 270 + /* Check what the device read. */ 271 + ptr = buffer->mirror; 272 + for (i = 0; i < 2 * self->page_size / sizeof(*ptr); ++i) 273 + ASSERT_EQ(ptr[i], 0); 274 + for (; i < size / sizeof(*ptr); ++i) 275 + ASSERT_EQ(ptr[i], i); 276 + 277 + hmm_buffer_free(buffer); 278 + } 279 + 280 + /* 281 + * Read private anonymous memory which has been protected with 282 + * mprotect() PROT_NONE. 283 + */ 284 + TEST_F(hmm, anon_read_prot) 285 + { 286 + struct hmm_buffer *buffer; 287 + unsigned long npages; 288 + unsigned long size; 289 + unsigned long i; 290 + int *ptr; 291 + int ret; 292 + 293 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 294 + ASSERT_NE(npages, 0); 295 + size = npages << self->page_shift; 296 + 297 + buffer = malloc(sizeof(*buffer)); 298 + ASSERT_NE(buffer, NULL); 299 + 300 + buffer->fd = -1; 301 + buffer->size = size; 302 + buffer->mirror = malloc(size); 303 + ASSERT_NE(buffer->mirror, NULL); 304 + 305 + buffer->ptr = mmap(NULL, size, 306 + PROT_READ | PROT_WRITE, 307 + MAP_PRIVATE | MAP_ANONYMOUS, 308 + buffer->fd, 0); 309 + ASSERT_NE(buffer->ptr, MAP_FAILED); 310 + 311 + /* Initialize buffer in system memory. */ 312 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 313 + ptr[i] = i; 314 + 315 + /* Initialize mirror buffer so we can verify it isn't written. */ 316 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 317 + ptr[i] = -i; 318 + 319 + /* Protect buffer from reading. */ 320 + ret = mprotect(buffer->ptr, size, PROT_NONE); 321 + ASSERT_EQ(ret, 0); 322 + 323 + /* Simulate a device reading system memory. */ 324 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages); 325 + ASSERT_EQ(ret, -EFAULT); 326 + 327 + /* Allow CPU to read the buffer so we can check it. */ 328 + ret = mprotect(buffer->ptr, size, PROT_READ); 329 + ASSERT_EQ(ret, 0); 330 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 331 + ASSERT_EQ(ptr[i], i); 332 + 333 + /* Check what the device read. */ 334 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 335 + ASSERT_EQ(ptr[i], -i); 336 + 337 + hmm_buffer_free(buffer); 338 + } 339 + 340 + /* 341 + * Write private anonymous memory. 342 + */ 343 + TEST_F(hmm, anon_write) 344 + { 345 + struct hmm_buffer *buffer; 346 + unsigned long npages; 347 + unsigned long size; 348 + unsigned long i; 349 + int *ptr; 350 + int ret; 351 + 352 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 353 + ASSERT_NE(npages, 0); 354 + size = npages << self->page_shift; 355 + 356 + buffer = malloc(sizeof(*buffer)); 357 + ASSERT_NE(buffer, NULL); 358 + 359 + buffer->fd = -1; 360 + buffer->size = size; 361 + buffer->mirror = malloc(size); 362 + ASSERT_NE(buffer->mirror, NULL); 363 + 364 + buffer->ptr = mmap(NULL, size, 365 + PROT_READ | PROT_WRITE, 366 + MAP_PRIVATE | MAP_ANONYMOUS, 367 + buffer->fd, 0); 368 + ASSERT_NE(buffer->ptr, MAP_FAILED); 369 + 370 + /* Initialize data that the device will write to buffer->ptr. */ 371 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 372 + ptr[i] = i; 373 + 374 + /* Simulate a device writing system memory. */ 375 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 376 + ASSERT_EQ(ret, 0); 377 + ASSERT_EQ(buffer->cpages, npages); 378 + ASSERT_EQ(buffer->faults, 1); 379 + 380 + /* Check what the device wrote. */ 381 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 382 + ASSERT_EQ(ptr[i], i); 383 + 384 + hmm_buffer_free(buffer); 385 + } 386 + 387 + /* 388 + * Write private anonymous memory which has been protected with 389 + * mprotect() PROT_READ. 390 + */ 391 + TEST_F(hmm, anon_write_prot) 392 + { 393 + struct hmm_buffer *buffer; 394 + unsigned long npages; 395 + unsigned long size; 396 + unsigned long i; 397 + int *ptr; 398 + int ret; 399 + 400 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 401 + ASSERT_NE(npages, 0); 402 + size = npages << self->page_shift; 403 + 404 + buffer = malloc(sizeof(*buffer)); 405 + ASSERT_NE(buffer, NULL); 406 + 407 + buffer->fd = -1; 408 + buffer->size = size; 409 + buffer->mirror = malloc(size); 410 + ASSERT_NE(buffer->mirror, NULL); 411 + 412 + buffer->ptr = mmap(NULL, size, 413 + PROT_READ, 414 + MAP_PRIVATE | MAP_ANONYMOUS, 415 + buffer->fd, 0); 416 + ASSERT_NE(buffer->ptr, MAP_FAILED); 417 + 418 + /* Simulate a device reading a zero page of memory. */ 419 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1); 420 + ASSERT_EQ(ret, 0); 421 + ASSERT_EQ(buffer->cpages, 1); 422 + ASSERT_EQ(buffer->faults, 1); 423 + 424 + /* Initialize data that the device will write to buffer->ptr. */ 425 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 426 + ptr[i] = i; 427 + 428 + /* Simulate a device writing system memory. */ 429 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 430 + ASSERT_EQ(ret, -EPERM); 431 + 432 + /* Check what the device wrote. */ 433 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 434 + ASSERT_EQ(ptr[i], 0); 435 + 436 + /* Now allow writing and see that the zero page is replaced. */ 437 + ret = mprotect(buffer->ptr, size, PROT_WRITE | PROT_READ); 438 + ASSERT_EQ(ret, 0); 439 + 440 + /* Simulate a device writing system memory. */ 441 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 442 + ASSERT_EQ(ret, 0); 443 + ASSERT_EQ(buffer->cpages, npages); 444 + ASSERT_EQ(buffer->faults, 1); 445 + 446 + /* Check what the device wrote. */ 447 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 448 + ASSERT_EQ(ptr[i], i); 449 + 450 + hmm_buffer_free(buffer); 451 + } 452 + 453 + /* 454 + * Check that a device writing an anonymous private mapping 455 + * will copy-on-write if a child process inherits the mapping. 456 + */ 457 + TEST_F(hmm, anon_write_child) 458 + { 459 + struct hmm_buffer *buffer; 460 + unsigned long npages; 461 + unsigned long size; 462 + unsigned long i; 463 + int *ptr; 464 + pid_t pid; 465 + int child_fd; 466 + int ret; 467 + 468 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 469 + ASSERT_NE(npages, 0); 470 + size = npages << self->page_shift; 471 + 472 + buffer = malloc(sizeof(*buffer)); 473 + ASSERT_NE(buffer, NULL); 474 + 475 + buffer->fd = -1; 476 + buffer->size = size; 477 + buffer->mirror = malloc(size); 478 + ASSERT_NE(buffer->mirror, NULL); 479 + 480 + buffer->ptr = mmap(NULL, size, 481 + PROT_READ | PROT_WRITE, 482 + MAP_PRIVATE | MAP_ANONYMOUS, 483 + buffer->fd, 0); 484 + ASSERT_NE(buffer->ptr, MAP_FAILED); 485 + 486 + /* Initialize buffer->ptr so we can tell if it is written. */ 487 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 488 + ptr[i] = i; 489 + 490 + /* Initialize data that the device will write to buffer->ptr. */ 491 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 492 + ptr[i] = -i; 493 + 494 + pid = fork(); 495 + if (pid == -1) 496 + ASSERT_EQ(pid, 0); 497 + if (pid != 0) { 498 + waitpid(pid, &ret, 0); 499 + ASSERT_EQ(WIFEXITED(ret), 1); 500 + 501 + /* Check that the parent's buffer did not change. */ 502 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 503 + ASSERT_EQ(ptr[i], i); 504 + return; 505 + } 506 + 507 + /* Check that we see the parent's values. */ 508 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 509 + ASSERT_EQ(ptr[i], i); 510 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 511 + ASSERT_EQ(ptr[i], -i); 512 + 513 + /* The child process needs its own mirror to its own mm. */ 514 + child_fd = hmm_open(0); 515 + ASSERT_GE(child_fd, 0); 516 + 517 + /* Simulate a device writing system memory. */ 518 + ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages); 519 + ASSERT_EQ(ret, 0); 520 + ASSERT_EQ(buffer->cpages, npages); 521 + ASSERT_EQ(buffer->faults, 1); 522 + 523 + /* Check what the device wrote. */ 524 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 525 + ASSERT_EQ(ptr[i], -i); 526 + 527 + close(child_fd); 528 + exit(0); 529 + } 530 + 531 + /* 532 + * Check that a device writing an anonymous shared mapping 533 + * will not copy-on-write if a child process inherits the mapping. 534 + */ 535 + TEST_F(hmm, anon_write_child_shared) 536 + { 537 + struct hmm_buffer *buffer; 538 + unsigned long npages; 539 + unsigned long size; 540 + unsigned long i; 541 + int *ptr; 542 + pid_t pid; 543 + int child_fd; 544 + int ret; 545 + 546 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 547 + ASSERT_NE(npages, 0); 548 + size = npages << self->page_shift; 549 + 550 + buffer = malloc(sizeof(*buffer)); 551 + ASSERT_NE(buffer, NULL); 552 + 553 + buffer->fd = -1; 554 + buffer->size = size; 555 + buffer->mirror = malloc(size); 556 + ASSERT_NE(buffer->mirror, NULL); 557 + 558 + buffer->ptr = mmap(NULL, size, 559 + PROT_READ | PROT_WRITE, 560 + MAP_SHARED | MAP_ANONYMOUS, 561 + buffer->fd, 0); 562 + ASSERT_NE(buffer->ptr, MAP_FAILED); 563 + 564 + /* Initialize buffer->ptr so we can tell if it is written. */ 565 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 566 + ptr[i] = i; 567 + 568 + /* Initialize data that the device will write to buffer->ptr. */ 569 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 570 + ptr[i] = -i; 571 + 572 + pid = fork(); 573 + if (pid == -1) 574 + ASSERT_EQ(pid, 0); 575 + if (pid != 0) { 576 + waitpid(pid, &ret, 0); 577 + ASSERT_EQ(WIFEXITED(ret), 1); 578 + 579 + /* Check that the parent's buffer did change. */ 580 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 581 + ASSERT_EQ(ptr[i], -i); 582 + return; 583 + } 584 + 585 + /* Check that we see the parent's values. */ 586 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 587 + ASSERT_EQ(ptr[i], i); 588 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 589 + ASSERT_EQ(ptr[i], -i); 590 + 591 + /* The child process needs its own mirror to its own mm. */ 592 + child_fd = hmm_open(0); 593 + ASSERT_GE(child_fd, 0); 594 + 595 + /* Simulate a device writing system memory. */ 596 + ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages); 597 + ASSERT_EQ(ret, 0); 598 + ASSERT_EQ(buffer->cpages, npages); 599 + ASSERT_EQ(buffer->faults, 1); 600 + 601 + /* Check what the device wrote. */ 602 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 603 + ASSERT_EQ(ptr[i], -i); 604 + 605 + close(child_fd); 606 + exit(0); 607 + } 608 + 609 + /* 610 + * Write private anonymous huge page. 611 + */ 612 + TEST_F(hmm, anon_write_huge) 613 + { 614 + struct hmm_buffer *buffer; 615 + unsigned long npages; 616 + unsigned long size; 617 + unsigned long i; 618 + void *old_ptr; 619 + void *map; 620 + int *ptr; 621 + int ret; 622 + 623 + size = 2 * TWOMEG; 624 + 625 + buffer = malloc(sizeof(*buffer)); 626 + ASSERT_NE(buffer, NULL); 627 + 628 + buffer->fd = -1; 629 + buffer->size = size; 630 + buffer->mirror = malloc(size); 631 + ASSERT_NE(buffer->mirror, NULL); 632 + 633 + buffer->ptr = mmap(NULL, size, 634 + PROT_READ | PROT_WRITE, 635 + MAP_PRIVATE | MAP_ANONYMOUS, 636 + buffer->fd, 0); 637 + ASSERT_NE(buffer->ptr, MAP_FAILED); 638 + 639 + size = TWOMEG; 640 + npages = size >> self->page_shift; 641 + map = (void *)ALIGN((uintptr_t)buffer->ptr, size); 642 + ret = madvise(map, size, MADV_HUGEPAGE); 643 + ASSERT_EQ(ret, 0); 644 + old_ptr = buffer->ptr; 645 + buffer->ptr = map; 646 + 647 + /* Initialize data that the device will write to buffer->ptr. */ 648 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 649 + ptr[i] = i; 650 + 651 + /* Simulate a device writing system memory. */ 652 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 653 + ASSERT_EQ(ret, 0); 654 + ASSERT_EQ(buffer->cpages, npages); 655 + ASSERT_EQ(buffer->faults, 1); 656 + 657 + /* Check what the device wrote. */ 658 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 659 + ASSERT_EQ(ptr[i], i); 660 + 661 + buffer->ptr = old_ptr; 662 + hmm_buffer_free(buffer); 663 + } 664 + 665 + /* 666 + * Write huge TLBFS page. 667 + */ 668 + TEST_F(hmm, anon_write_hugetlbfs) 669 + { 670 + struct hmm_buffer *buffer; 671 + unsigned long npages; 672 + unsigned long size; 673 + unsigned long i; 674 + int *ptr; 675 + int ret; 676 + long pagesizes[4]; 677 + int n, idx; 678 + 679 + /* Skip test if we can't allocate a hugetlbfs page. */ 680 + 681 + n = gethugepagesizes(pagesizes, 4); 682 + if (n <= 0) 683 + return; 684 + for (idx = 0; --n > 0; ) { 685 + if (pagesizes[n] < pagesizes[idx]) 686 + idx = n; 687 + } 688 + size = ALIGN(TWOMEG, pagesizes[idx]); 689 + npages = size >> self->page_shift; 690 + 691 + buffer = malloc(sizeof(*buffer)); 692 + ASSERT_NE(buffer, NULL); 693 + 694 + buffer->ptr = get_hugepage_region(size, GHR_STRICT); 695 + if (buffer->ptr == NULL) { 696 + free(buffer); 697 + return; 698 + } 699 + 700 + buffer->fd = -1; 701 + buffer->size = size; 702 + buffer->mirror = malloc(size); 703 + ASSERT_NE(buffer->mirror, NULL); 704 + 705 + /* Initialize data that the device will write to buffer->ptr. */ 706 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 707 + ptr[i] = i; 708 + 709 + /* Simulate a device writing system memory. */ 710 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 711 + ASSERT_EQ(ret, 0); 712 + ASSERT_EQ(buffer->cpages, npages); 713 + ASSERT_EQ(buffer->faults, 1); 714 + 715 + /* Check what the device wrote. */ 716 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 717 + ASSERT_EQ(ptr[i], i); 718 + 719 + free_hugepage_region(buffer->ptr); 720 + buffer->ptr = NULL; 721 + hmm_buffer_free(buffer); 722 + } 723 + 724 + /* 725 + * Read mmap'ed file memory. 726 + */ 727 + TEST_F(hmm, file_read) 728 + { 729 + struct hmm_buffer *buffer; 730 + unsigned long npages; 731 + unsigned long size; 732 + unsigned long i; 733 + int *ptr; 734 + int ret; 735 + int fd; 736 + ssize_t len; 737 + 738 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 739 + ASSERT_NE(npages, 0); 740 + size = npages << self->page_shift; 741 + 742 + fd = hmm_create_file(size); 743 + ASSERT_GE(fd, 0); 744 + 745 + buffer = malloc(sizeof(*buffer)); 746 + ASSERT_NE(buffer, NULL); 747 + 748 + buffer->fd = fd; 749 + buffer->size = size; 750 + buffer->mirror = malloc(size); 751 + ASSERT_NE(buffer->mirror, NULL); 752 + 753 + /* Write initial contents of the file. */ 754 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 755 + ptr[i] = i; 756 + len = pwrite(fd, buffer->mirror, size, 0); 757 + ASSERT_EQ(len, size); 758 + memset(buffer->mirror, 0, size); 759 + 760 + buffer->ptr = mmap(NULL, size, 761 + PROT_READ, 762 + MAP_SHARED, 763 + buffer->fd, 0); 764 + ASSERT_NE(buffer->ptr, MAP_FAILED); 765 + 766 + /* Simulate a device reading system memory. */ 767 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages); 768 + ASSERT_EQ(ret, 0); 769 + ASSERT_EQ(buffer->cpages, npages); 770 + ASSERT_EQ(buffer->faults, 1); 771 + 772 + /* Check what the device read. */ 773 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 774 + ASSERT_EQ(ptr[i], i); 775 + 776 + hmm_buffer_free(buffer); 777 + } 778 + 779 + /* 780 + * Write mmap'ed file memory. 781 + */ 782 + TEST_F(hmm, file_write) 783 + { 784 + struct hmm_buffer *buffer; 785 + unsigned long npages; 786 + unsigned long size; 787 + unsigned long i; 788 + int *ptr; 789 + int ret; 790 + int fd; 791 + ssize_t len; 792 + 793 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 794 + ASSERT_NE(npages, 0); 795 + size = npages << self->page_shift; 796 + 797 + fd = hmm_create_file(size); 798 + ASSERT_GE(fd, 0); 799 + 800 + buffer = malloc(sizeof(*buffer)); 801 + ASSERT_NE(buffer, NULL); 802 + 803 + buffer->fd = fd; 804 + buffer->size = size; 805 + buffer->mirror = malloc(size); 806 + ASSERT_NE(buffer->mirror, NULL); 807 + 808 + buffer->ptr = mmap(NULL, size, 809 + PROT_READ | PROT_WRITE, 810 + MAP_SHARED, 811 + buffer->fd, 0); 812 + ASSERT_NE(buffer->ptr, MAP_FAILED); 813 + 814 + /* Initialize data that the device will write to buffer->ptr. */ 815 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 816 + ptr[i] = i; 817 + 818 + /* Simulate a device writing system memory. */ 819 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 820 + ASSERT_EQ(ret, 0); 821 + ASSERT_EQ(buffer->cpages, npages); 822 + ASSERT_EQ(buffer->faults, 1); 823 + 824 + /* Check what the device wrote. */ 825 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 826 + ASSERT_EQ(ptr[i], i); 827 + 828 + /* Check that the device also wrote the file. */ 829 + len = pread(fd, buffer->mirror, size, 0); 830 + ASSERT_EQ(len, size); 831 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 832 + ASSERT_EQ(ptr[i], i); 833 + 834 + hmm_buffer_free(buffer); 835 + } 836 + 837 + /* 838 + * Migrate anonymous memory to device private memory. 839 + */ 840 + TEST_F(hmm, migrate) 841 + { 842 + struct hmm_buffer *buffer; 843 + unsigned long npages; 844 + unsigned long size; 845 + unsigned long i; 846 + int *ptr; 847 + int ret; 848 + 849 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 850 + ASSERT_NE(npages, 0); 851 + size = npages << self->page_shift; 852 + 853 + buffer = malloc(sizeof(*buffer)); 854 + ASSERT_NE(buffer, NULL); 855 + 856 + buffer->fd = -1; 857 + buffer->size = size; 858 + buffer->mirror = malloc(size); 859 + ASSERT_NE(buffer->mirror, NULL); 860 + 861 + buffer->ptr = mmap(NULL, size, 862 + PROT_READ | PROT_WRITE, 863 + MAP_PRIVATE | MAP_ANONYMOUS, 864 + buffer->fd, 0); 865 + ASSERT_NE(buffer->ptr, MAP_FAILED); 866 + 867 + /* Initialize buffer in system memory. */ 868 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 869 + ptr[i] = i; 870 + 871 + /* Migrate memory to device. */ 872 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages); 873 + ASSERT_EQ(ret, 0); 874 + ASSERT_EQ(buffer->cpages, npages); 875 + 876 + /* Check what the device read. */ 877 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 878 + ASSERT_EQ(ptr[i], i); 879 + 880 + hmm_buffer_free(buffer); 881 + } 882 + 883 + /* 884 + * Migrate anonymous memory to device private memory and fault it back to system 885 + * memory. 886 + */ 887 + TEST_F(hmm, migrate_fault) 888 + { 889 + struct hmm_buffer *buffer; 890 + unsigned long npages; 891 + unsigned long size; 892 + unsigned long i; 893 + int *ptr; 894 + int ret; 895 + 896 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 897 + ASSERT_NE(npages, 0); 898 + size = npages << self->page_shift; 899 + 900 + buffer = malloc(sizeof(*buffer)); 901 + ASSERT_NE(buffer, NULL); 902 + 903 + buffer->fd = -1; 904 + buffer->size = size; 905 + buffer->mirror = malloc(size); 906 + ASSERT_NE(buffer->mirror, NULL); 907 + 908 + buffer->ptr = mmap(NULL, size, 909 + PROT_READ | PROT_WRITE, 910 + MAP_PRIVATE | MAP_ANONYMOUS, 911 + buffer->fd, 0); 912 + ASSERT_NE(buffer->ptr, MAP_FAILED); 913 + 914 + /* Initialize buffer in system memory. */ 915 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 916 + ptr[i] = i; 917 + 918 + /* Migrate memory to device. */ 919 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages); 920 + ASSERT_EQ(ret, 0); 921 + ASSERT_EQ(buffer->cpages, npages); 922 + 923 + /* Check what the device read. */ 924 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 925 + ASSERT_EQ(ptr[i], i); 926 + 927 + /* Fault pages back to system memory and check them. */ 928 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 929 + ASSERT_EQ(ptr[i], i); 930 + 931 + hmm_buffer_free(buffer); 932 + } 933 + 934 + /* 935 + * Try to migrate various memory types to device private memory. 936 + */ 937 + TEST_F(hmm2, migrate_mixed) 938 + { 939 + struct hmm_buffer *buffer; 940 + unsigned long npages; 941 + unsigned long size; 942 + int *ptr; 943 + unsigned char *p; 944 + int ret; 945 + int val; 946 + 947 + npages = 6; 948 + size = npages << self->page_shift; 949 + 950 + buffer = malloc(sizeof(*buffer)); 951 + ASSERT_NE(buffer, NULL); 952 + 953 + buffer->fd = -1; 954 + buffer->size = size; 955 + buffer->mirror = malloc(size); 956 + ASSERT_NE(buffer->mirror, NULL); 957 + 958 + /* Reserve a range of addresses. */ 959 + buffer->ptr = mmap(NULL, size, 960 + PROT_NONE, 961 + MAP_PRIVATE | MAP_ANONYMOUS, 962 + buffer->fd, 0); 963 + ASSERT_NE(buffer->ptr, MAP_FAILED); 964 + p = buffer->ptr; 965 + 966 + /* Migrating a protected area should be an error. */ 967 + ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, npages); 968 + ASSERT_EQ(ret, -EINVAL); 969 + 970 + /* Punch a hole after the first page address. */ 971 + ret = munmap(buffer->ptr + self->page_size, self->page_size); 972 + ASSERT_EQ(ret, 0); 973 + 974 + /* We expect an error if the vma doesn't cover the range. */ 975 + ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 3); 976 + ASSERT_EQ(ret, -EINVAL); 977 + 978 + /* Page 2 will be a read-only zero page. */ 979 + ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size, 980 + PROT_READ); 981 + ASSERT_EQ(ret, 0); 982 + ptr = (int *)(buffer->ptr + 2 * self->page_size); 983 + val = *ptr + 3; 984 + ASSERT_EQ(val, 3); 985 + 986 + /* Page 3 will be read-only. */ 987 + ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 988 + PROT_READ | PROT_WRITE); 989 + ASSERT_EQ(ret, 0); 990 + ptr = (int *)(buffer->ptr + 3 * self->page_size); 991 + *ptr = val; 992 + ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 993 + PROT_READ); 994 + ASSERT_EQ(ret, 0); 995 + 996 + /* Page 4-5 will be read-write. */ 997 + ret = mprotect(buffer->ptr + 4 * self->page_size, 2 * self->page_size, 998 + PROT_READ | PROT_WRITE); 999 + ASSERT_EQ(ret, 0); 1000 + ptr = (int *)(buffer->ptr + 4 * self->page_size); 1001 + *ptr = val; 1002 + ptr = (int *)(buffer->ptr + 5 * self->page_size); 1003 + *ptr = val; 1004 + 1005 + /* Now try to migrate pages 2-5 to device 1. */ 1006 + buffer->ptr = p + 2 * self->page_size; 1007 + ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 4); 1008 + ASSERT_EQ(ret, 0); 1009 + ASSERT_EQ(buffer->cpages, 4); 1010 + 1011 + /* Page 5 won't be migrated to device 0 because it's on device 1. */ 1012 + buffer->ptr = p + 5 * self->page_size; 1013 + ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_MIGRATE, buffer, 1); 1014 + ASSERT_EQ(ret, -ENOENT); 1015 + buffer->ptr = p; 1016 + 1017 + buffer->ptr = p; 1018 + hmm_buffer_free(buffer); 1019 + } 1020 + 1021 + /* 1022 + * Migrate anonymous memory to device private memory and fault it back to system 1023 + * memory multiple times. 1024 + */ 1025 + TEST_F(hmm, migrate_multiple) 1026 + { 1027 + struct hmm_buffer *buffer; 1028 + unsigned long npages; 1029 + unsigned long size; 1030 + unsigned long i; 1031 + unsigned long c; 1032 + int *ptr; 1033 + int ret; 1034 + 1035 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1036 + ASSERT_NE(npages, 0); 1037 + size = npages << self->page_shift; 1038 + 1039 + for (c = 0; c < NTIMES; c++) { 1040 + buffer = malloc(sizeof(*buffer)); 1041 + ASSERT_NE(buffer, NULL); 1042 + 1043 + buffer->fd = -1; 1044 + buffer->size = size; 1045 + buffer->mirror = malloc(size); 1046 + ASSERT_NE(buffer->mirror, NULL); 1047 + 1048 + buffer->ptr = mmap(NULL, size, 1049 + PROT_READ | PROT_WRITE, 1050 + MAP_PRIVATE | MAP_ANONYMOUS, 1051 + buffer->fd, 0); 1052 + ASSERT_NE(buffer->ptr, MAP_FAILED); 1053 + 1054 + /* Initialize buffer in system memory. */ 1055 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1056 + ptr[i] = i; 1057 + 1058 + /* Migrate memory to device. */ 1059 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, 1060 + npages); 1061 + ASSERT_EQ(ret, 0); 1062 + ASSERT_EQ(buffer->cpages, npages); 1063 + 1064 + /* Check what the device read. */ 1065 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1066 + ASSERT_EQ(ptr[i], i); 1067 + 1068 + /* Fault pages back to system memory and check them. */ 1069 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1070 + ASSERT_EQ(ptr[i], i); 1071 + 1072 + hmm_buffer_free(buffer); 1073 + } 1074 + } 1075 + 1076 + /* 1077 + * Read anonymous memory multiple times. 1078 + */ 1079 + TEST_F(hmm, anon_read_multiple) 1080 + { 1081 + struct hmm_buffer *buffer; 1082 + unsigned long npages; 1083 + unsigned long size; 1084 + unsigned long i; 1085 + unsigned long c; 1086 + int *ptr; 1087 + int ret; 1088 + 1089 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1090 + ASSERT_NE(npages, 0); 1091 + size = npages << self->page_shift; 1092 + 1093 + for (c = 0; c < NTIMES; c++) { 1094 + buffer = malloc(sizeof(*buffer)); 1095 + ASSERT_NE(buffer, NULL); 1096 + 1097 + buffer->fd = -1; 1098 + buffer->size = size; 1099 + buffer->mirror = malloc(size); 1100 + ASSERT_NE(buffer->mirror, NULL); 1101 + 1102 + buffer->ptr = mmap(NULL, size, 1103 + PROT_READ | PROT_WRITE, 1104 + MAP_PRIVATE | MAP_ANONYMOUS, 1105 + buffer->fd, 0); 1106 + ASSERT_NE(buffer->ptr, MAP_FAILED); 1107 + 1108 + /* Initialize buffer in system memory. */ 1109 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1110 + ptr[i] = i + c; 1111 + 1112 + /* Simulate a device reading system memory. */ 1113 + ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1114 + npages); 1115 + ASSERT_EQ(ret, 0); 1116 + ASSERT_EQ(buffer->cpages, npages); 1117 + ASSERT_EQ(buffer->faults, 1); 1118 + 1119 + /* Check what the device read. */ 1120 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1121 + ASSERT_EQ(ptr[i], i + c); 1122 + 1123 + hmm_buffer_free(buffer); 1124 + } 1125 + } 1126 + 1127 + void *unmap_buffer(void *p) 1128 + { 1129 + struct hmm_buffer *buffer = p; 1130 + 1131 + /* Delay for a bit and then unmap buffer while it is being read. */ 1132 + hmm_nanosleep(hmm_random() % 32000); 1133 + munmap(buffer->ptr + buffer->size / 2, buffer->size / 2); 1134 + buffer->ptr = NULL; 1135 + 1136 + return NULL; 1137 + } 1138 + 1139 + /* 1140 + * Try reading anonymous memory while it is being unmapped. 1141 + */ 1142 + TEST_F(hmm, anon_teardown) 1143 + { 1144 + unsigned long npages; 1145 + unsigned long size; 1146 + unsigned long c; 1147 + void *ret; 1148 + 1149 + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1150 + ASSERT_NE(npages, 0); 1151 + size = npages << self->page_shift; 1152 + 1153 + for (c = 0; c < NTIMES; ++c) { 1154 + pthread_t thread; 1155 + struct hmm_buffer *buffer; 1156 + unsigned long i; 1157 + int *ptr; 1158 + int rc; 1159 + 1160 + buffer = malloc(sizeof(*buffer)); 1161 + ASSERT_NE(buffer, NULL); 1162 + 1163 + buffer->fd = -1; 1164 + buffer->size = size; 1165 + buffer->mirror = malloc(size); 1166 + ASSERT_NE(buffer->mirror, NULL); 1167 + 1168 + buffer->ptr = mmap(NULL, size, 1169 + PROT_READ | PROT_WRITE, 1170 + MAP_PRIVATE | MAP_ANONYMOUS, 1171 + buffer->fd, 0); 1172 + ASSERT_NE(buffer->ptr, MAP_FAILED); 1173 + 1174 + /* Initialize buffer in system memory. */ 1175 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1176 + ptr[i] = i + c; 1177 + 1178 + rc = pthread_create(&thread, NULL, unmap_buffer, buffer); 1179 + ASSERT_EQ(rc, 0); 1180 + 1181 + /* Simulate a device reading system memory. */ 1182 + rc = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1183 + npages); 1184 + if (rc == 0) { 1185 + ASSERT_EQ(buffer->cpages, npages); 1186 + ASSERT_EQ(buffer->faults, 1); 1187 + 1188 + /* Check what the device read. */ 1189 + for (i = 0, ptr = buffer->mirror; 1190 + i < size / sizeof(*ptr); 1191 + ++i) 1192 + ASSERT_EQ(ptr[i], i + c); 1193 + } 1194 + 1195 + pthread_join(thread, &ret); 1196 + hmm_buffer_free(buffer); 1197 + } 1198 + } 1199 + 1200 + /* 1201 + * Test memory snapshot without faulting in pages accessed by the device. 1202 + */ 1203 + TEST_F(hmm2, snapshot) 1204 + { 1205 + struct hmm_buffer *buffer; 1206 + unsigned long npages; 1207 + unsigned long size; 1208 + int *ptr; 1209 + unsigned char *p; 1210 + unsigned char *m; 1211 + int ret; 1212 + int val; 1213 + 1214 + npages = 7; 1215 + size = npages << self->page_shift; 1216 + 1217 + buffer = malloc(sizeof(*buffer)); 1218 + ASSERT_NE(buffer, NULL); 1219 + 1220 + buffer->fd = -1; 1221 + buffer->size = size; 1222 + buffer->mirror = malloc(npages); 1223 + ASSERT_NE(buffer->mirror, NULL); 1224 + 1225 + /* Reserve a range of addresses. */ 1226 + buffer->ptr = mmap(NULL, size, 1227 + PROT_NONE, 1228 + MAP_PRIVATE | MAP_ANONYMOUS, 1229 + buffer->fd, 0); 1230 + ASSERT_NE(buffer->ptr, MAP_FAILED); 1231 + p = buffer->ptr; 1232 + 1233 + /* Punch a hole after the first page address. */ 1234 + ret = munmap(buffer->ptr + self->page_size, self->page_size); 1235 + ASSERT_EQ(ret, 0); 1236 + 1237 + /* Page 2 will be read-only zero page. */ 1238 + ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size, 1239 + PROT_READ); 1240 + ASSERT_EQ(ret, 0); 1241 + ptr = (int *)(buffer->ptr + 2 * self->page_size); 1242 + val = *ptr + 3; 1243 + ASSERT_EQ(val, 3); 1244 + 1245 + /* Page 3 will be read-only. */ 1246 + ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 1247 + PROT_READ | PROT_WRITE); 1248 + ASSERT_EQ(ret, 0); 1249 + ptr = (int *)(buffer->ptr + 3 * self->page_size); 1250 + *ptr = val; 1251 + ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 1252 + PROT_READ); 1253 + ASSERT_EQ(ret, 0); 1254 + 1255 + /* Page 4-6 will be read-write. */ 1256 + ret = mprotect(buffer->ptr + 4 * self->page_size, 3 * self->page_size, 1257 + PROT_READ | PROT_WRITE); 1258 + ASSERT_EQ(ret, 0); 1259 + ptr = (int *)(buffer->ptr + 4 * self->page_size); 1260 + *ptr = val; 1261 + 1262 + /* Page 5 will be migrated to device 0. */ 1263 + buffer->ptr = p + 5 * self->page_size; 1264 + ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_MIGRATE, buffer, 1); 1265 + ASSERT_EQ(ret, 0); 1266 + ASSERT_EQ(buffer->cpages, 1); 1267 + 1268 + /* Page 6 will be migrated to device 1. */ 1269 + buffer->ptr = p + 6 * self->page_size; 1270 + ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 1); 1271 + ASSERT_EQ(ret, 0); 1272 + ASSERT_EQ(buffer->cpages, 1); 1273 + 1274 + /* Simulate a device snapshotting CPU pagetables. */ 1275 + buffer->ptr = p; 1276 + ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_SNAPSHOT, buffer, npages); 1277 + ASSERT_EQ(ret, 0); 1278 + ASSERT_EQ(buffer->cpages, npages); 1279 + 1280 + /* Check what the device saw. */ 1281 + m = buffer->mirror; 1282 + ASSERT_EQ(m[0], HMM_DMIRROR_PROT_ERROR); 1283 + ASSERT_EQ(m[1], HMM_DMIRROR_PROT_ERROR); 1284 + ASSERT_EQ(m[2], HMM_DMIRROR_PROT_ZERO | HMM_DMIRROR_PROT_READ); 1285 + ASSERT_EQ(m[3], HMM_DMIRROR_PROT_READ); 1286 + ASSERT_EQ(m[4], HMM_DMIRROR_PROT_WRITE); 1287 + ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL | 1288 + HMM_DMIRROR_PROT_WRITE); 1289 + ASSERT_EQ(m[6], HMM_DMIRROR_PROT_NONE); 1290 + 1291 + hmm_buffer_free(buffer); 1292 + } 1293 + 1294 + /* 1295 + * Test two devices reading the same memory (double mapped). 1296 + */ 1297 + TEST_F(hmm2, double_map) 1298 + { 1299 + struct hmm_buffer *buffer; 1300 + unsigned long npages; 1301 + unsigned long size; 1302 + unsigned long i; 1303 + int *ptr; 1304 + int ret; 1305 + 1306 + npages = 6; 1307 + size = npages << self->page_shift; 1308 + 1309 + buffer = malloc(sizeof(*buffer)); 1310 + ASSERT_NE(buffer, NULL); 1311 + 1312 + buffer->fd = -1; 1313 + buffer->size = size; 1314 + buffer->mirror = malloc(npages); 1315 + ASSERT_NE(buffer->mirror, NULL); 1316 + 1317 + /* Reserve a range of addresses. */ 1318 + buffer->ptr = mmap(NULL, size, 1319 + PROT_READ | PROT_WRITE, 1320 + MAP_PRIVATE | MAP_ANONYMOUS, 1321 + buffer->fd, 0); 1322 + ASSERT_NE(buffer->ptr, MAP_FAILED); 1323 + 1324 + /* Initialize buffer in system memory. */ 1325 + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1326 + ptr[i] = i; 1327 + 1328 + /* Make region read-only. */ 1329 + ret = mprotect(buffer->ptr, size, PROT_READ); 1330 + ASSERT_EQ(ret, 0); 1331 + 1332 + /* Simulate device 0 reading system memory. */ 1333 + ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages); 1334 + ASSERT_EQ(ret, 0); 1335 + ASSERT_EQ(buffer->cpages, npages); 1336 + ASSERT_EQ(buffer->faults, 1); 1337 + 1338 + /* Check what the device read. */ 1339 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1340 + ASSERT_EQ(ptr[i], i); 1341 + 1342 + /* Simulate device 1 reading system memory. */ 1343 + ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_READ, buffer, npages); 1344 + ASSERT_EQ(ret, 0); 1345 + ASSERT_EQ(buffer->cpages, npages); 1346 + ASSERT_EQ(buffer->faults, 1); 1347 + 1348 + /* Check what the device read. */ 1349 + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1350 + ASSERT_EQ(ptr[i], i); 1351 + 1352 + /* Punch a hole after the first page address. */ 1353 + ret = munmap(buffer->ptr + self->page_size, self->page_size); 1354 + ASSERT_EQ(ret, 0); 1355 + 1356 + hmm_buffer_free(buffer); 1357 + } 1358 + 1359 + TEST_HARNESS_MAIN
+16
tools/testing/selftests/vm/run_vmtests
··· 307 307 echo "[FAIL]" 308 308 exitcode=1 309 309 fi 310 + 311 + echo "running HMM smoke test" 312 + echo "------------------------------------" 313 + ./test_hmm.sh smoke 314 + ret_val=$? 315 + 316 + if [ $ret_val -eq 0 ]; then 317 + echo "[PASS]" 318 + elif [ $ret_val -eq $ksft_skip ]; then 319 + echo "[SKIP]" 320 + exitcode=$ksft_skip 321 + else 322 + echo "[FAIL]" 323 + exitcode=1 324 + fi 325 + 310 326 exit $exitcode
+97
tools/testing/selftests/vm/test_hmm.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Copyright (C) 2018 Uladzislau Rezki (Sony) <urezki@gmail.com> 5 + # 6 + # This is a test script for the kernel test driver to analyse vmalloc 7 + # allocator. Therefore it is just a kernel module loader. You can specify 8 + # and pass different parameters in order to: 9 + # a) analyse performance of vmalloc allocations; 10 + # b) stressing and stability check of vmalloc subsystem. 11 + 12 + TEST_NAME="test_hmm" 13 + DRIVER="test_hmm" 14 + 15 + # 1 if fails 16 + exitcode=1 17 + 18 + # Kselftest framework requirement - SKIP code is 4. 19 + ksft_skip=4 20 + 21 + check_test_requirements() 22 + { 23 + uid=$(id -u) 24 + if [ $uid -ne 0 ]; then 25 + echo "$0: Must be run as root" 26 + exit $ksft_skip 27 + fi 28 + 29 + if ! which modprobe > /dev/null 2>&1; then 30 + echo "$0: You need modprobe installed" 31 + exit $ksft_skip 32 + fi 33 + 34 + if ! modinfo $DRIVER > /dev/null 2>&1; then 35 + echo "$0: You must have the following enabled in your kernel:" 36 + echo "CONFIG_TEST_HMM=m" 37 + exit $ksft_skip 38 + fi 39 + } 40 + 41 + load_driver() 42 + { 43 + modprobe $DRIVER > /dev/null 2>&1 44 + if [ $? == 0 ]; then 45 + major=$(awk "\$2==\"HMM_DMIRROR\" {print \$1}" /proc/devices) 46 + mknod /dev/hmm_dmirror0 c $major 0 47 + mknod /dev/hmm_dmirror1 c $major 1 48 + fi 49 + } 50 + 51 + unload_driver() 52 + { 53 + modprobe -r $DRIVER > /dev/null 2>&1 54 + rm -f /dev/hmm_dmirror? 55 + } 56 + 57 + run_smoke() 58 + { 59 + echo "Running smoke test. Note, this test provides basic coverage." 60 + 61 + load_driver 62 + $(dirname "${BASH_SOURCE[0]}")/hmm-tests 63 + unload_driver 64 + } 65 + 66 + usage() 67 + { 68 + echo -n "Usage: $0" 69 + echo 70 + echo "Example usage:" 71 + echo 72 + echo "# Shows help message" 73 + echo "./${TEST_NAME}.sh" 74 + echo 75 + echo "# Smoke testing" 76 + echo "./${TEST_NAME}.sh smoke" 77 + echo 78 + exit 0 79 + } 80 + 81 + function run_test() 82 + { 83 + if [ $# -eq 0 ]; then 84 + usage 85 + else 86 + if [ "$1" = "smoke" ]; then 87 + run_smoke 88 + else 89 + usage 90 + fi 91 + fi 92 + } 93 + 94 + check_test_requirements 95 + run_test $@ 96 + 97 + exit 0