1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0 2baa489faSSeongJae Park /* 3baa489faSSeongJae Park * HMM stands for Heterogeneous Memory Management, it is a helper layer inside 4baa489faSSeongJae Park * the linux kernel to help device drivers mirror a process address space in 5baa489faSSeongJae Park * the device. This allows the device to use the same address space which 6baa489faSSeongJae Park * makes communication and data exchange a lot easier. 7baa489faSSeongJae Park * 8baa489faSSeongJae Park * This framework's sole purpose is to exercise various code paths inside 9baa489faSSeongJae Park * the kernel to make sure that HMM performs as expected and to flush out any 10baa489faSSeongJae Park * bugs. 11baa489faSSeongJae Park */ 12baa489faSSeongJae Park 13baa489faSSeongJae Park #include "../kselftest_harness.h" 14baa489faSSeongJae Park 15baa489faSSeongJae Park #include <errno.h> 16baa489faSSeongJae Park #include <fcntl.h> 17baa489faSSeongJae Park #include <stdio.h> 18baa489faSSeongJae Park #include <stdlib.h> 19baa489faSSeongJae Park #include <stdint.h> 20baa489faSSeongJae Park #include <unistd.h> 21baa489faSSeongJae Park #include <strings.h> 22baa489faSSeongJae Park #include <time.h> 23baa489faSSeongJae Park #include <pthread.h> 24baa489faSSeongJae Park #include <sys/types.h> 25baa489faSSeongJae Park #include <sys/stat.h> 26baa489faSSeongJae Park #include <sys/mman.h> 27baa489faSSeongJae Park #include <sys/ioctl.h> 28baa489faSSeongJae Park 29baa489faSSeongJae Park 30baa489faSSeongJae Park /* 31baa489faSSeongJae Park * This is a private UAPI to the kernel test module so it isn't exported 32baa489faSSeongJae Park * in the usual include/uapi/... directory. 33baa489faSSeongJae Park */ 34baa489faSSeongJae Park #include <lib/test_hmm_uapi.h> 35baa489faSSeongJae Park #include <mm/gup_test.h> 36baa489faSSeongJae Park 37baa489faSSeongJae Park struct hmm_buffer { 38baa489faSSeongJae Park void *ptr; 39baa489faSSeongJae Park void *mirror; 40baa489faSSeongJae Park unsigned long size; 41baa489faSSeongJae Park int fd; 42baa489faSSeongJae Park uint64_t cpages; 43baa489faSSeongJae Park uint64_t faults; 44baa489faSSeongJae Park }; 45baa489faSSeongJae Park 46baa489faSSeongJae Park enum { 47baa489faSSeongJae Park HMM_PRIVATE_DEVICE_ONE, 48baa489faSSeongJae Park HMM_PRIVATE_DEVICE_TWO, 49baa489faSSeongJae Park HMM_COHERENCE_DEVICE_ONE, 50baa489faSSeongJae Park HMM_COHERENCE_DEVICE_TWO, 51baa489faSSeongJae Park }; 52baa489faSSeongJae Park 53baa489faSSeongJae Park #define TWOMEG (1 << 21) 54baa489faSSeongJae Park #define HMM_BUFFER_SIZE (1024 << 12) 55baa489faSSeongJae Park #define HMM_PATH_MAX 64 56baa489faSSeongJae Park #define NTIMES 10 57baa489faSSeongJae Park 58baa489faSSeongJae Park #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1))) 59baa489faSSeongJae Park /* Just the flags we need, copied from mm.h: */ 60baa489faSSeongJae Park 611738b949SAyush Jain #ifndef FOLL_WRITE 621738b949SAyush Jain #define FOLL_WRITE 0x01 /* check pte is writable */ 631738b949SAyush Jain #endif 641738b949SAyush Jain 651738b949SAyush Jain #ifndef FOLL_LONGTERM 661738b949SAyush Jain #define FOLL_LONGTERM 0x100 /* mapping lifetime is indefinite */ 671738b949SAyush Jain #endif 68baa489faSSeongJae Park FIXTURE(hmm) 69baa489faSSeongJae Park { 70baa489faSSeongJae Park int fd; 71baa489faSSeongJae Park unsigned int page_size; 72baa489faSSeongJae Park unsigned int page_shift; 73baa489faSSeongJae Park }; 74baa489faSSeongJae Park 75baa489faSSeongJae Park FIXTURE_VARIANT(hmm) 76baa489faSSeongJae Park { 77baa489faSSeongJae Park int device_number; 78baa489faSSeongJae Park }; 79baa489faSSeongJae Park 80baa489faSSeongJae Park FIXTURE_VARIANT_ADD(hmm, hmm_device_private) 81baa489faSSeongJae Park { 82baa489faSSeongJae Park .device_number = HMM_PRIVATE_DEVICE_ONE, 83baa489faSSeongJae Park }; 84baa489faSSeongJae Park 85baa489faSSeongJae Park FIXTURE_VARIANT_ADD(hmm, hmm_device_coherent) 86baa489faSSeongJae Park { 87baa489faSSeongJae Park .device_number = HMM_COHERENCE_DEVICE_ONE, 88baa489faSSeongJae Park }; 89baa489faSSeongJae Park 90baa489faSSeongJae Park FIXTURE(hmm2) 91baa489faSSeongJae Park { 92baa489faSSeongJae Park int fd0; 93baa489faSSeongJae Park int fd1; 94baa489faSSeongJae Park unsigned int page_size; 95baa489faSSeongJae Park unsigned int page_shift; 96baa489faSSeongJae Park }; 97baa489faSSeongJae Park 98baa489faSSeongJae Park FIXTURE_VARIANT(hmm2) 99baa489faSSeongJae Park { 100baa489faSSeongJae Park int device_number0; 101baa489faSSeongJae Park int device_number1; 102baa489faSSeongJae Park }; 103baa489faSSeongJae Park 104baa489faSSeongJae Park FIXTURE_VARIANT_ADD(hmm2, hmm2_device_private) 105baa489faSSeongJae Park { 106baa489faSSeongJae Park .device_number0 = HMM_PRIVATE_DEVICE_ONE, 107baa489faSSeongJae Park .device_number1 = HMM_PRIVATE_DEVICE_TWO, 108baa489faSSeongJae Park }; 109baa489faSSeongJae Park 110baa489faSSeongJae Park FIXTURE_VARIANT_ADD(hmm2, hmm2_device_coherent) 111baa489faSSeongJae Park { 112baa489faSSeongJae Park .device_number0 = HMM_COHERENCE_DEVICE_ONE, 113baa489faSSeongJae Park .device_number1 = HMM_COHERENCE_DEVICE_TWO, 114baa489faSSeongJae Park }; 115baa489faSSeongJae Park 116baa489faSSeongJae Park static int hmm_open(int unit) 117baa489faSSeongJae Park { 118baa489faSSeongJae Park char pathname[HMM_PATH_MAX]; 119baa489faSSeongJae Park int fd; 120baa489faSSeongJae Park 121baa489faSSeongJae Park snprintf(pathname, sizeof(pathname), "/dev/hmm_dmirror%d", unit); 122baa489faSSeongJae Park fd = open(pathname, O_RDWR, 0); 123baa489faSSeongJae Park if (fd < 0) 124baa489faSSeongJae Park fprintf(stderr, "could not open hmm dmirror driver (%s)\n", 125baa489faSSeongJae Park pathname); 126baa489faSSeongJae Park return fd; 127baa489faSSeongJae Park } 128baa489faSSeongJae Park 129baa489faSSeongJae Park static bool hmm_is_coherent_type(int dev_num) 130baa489faSSeongJae Park { 131baa489faSSeongJae Park return (dev_num >= HMM_COHERENCE_DEVICE_ONE); 132baa489faSSeongJae Park } 133baa489faSSeongJae Park 134baa489faSSeongJae Park FIXTURE_SETUP(hmm) 135baa489faSSeongJae Park { 136baa489faSSeongJae Park self->page_size = sysconf(_SC_PAGE_SIZE); 137baa489faSSeongJae Park self->page_shift = ffs(self->page_size) - 1; 138baa489faSSeongJae Park 139baa489faSSeongJae Park self->fd = hmm_open(variant->device_number); 140baa489faSSeongJae Park if (self->fd < 0 && hmm_is_coherent_type(variant->device_number)) 141baa489faSSeongJae Park SKIP(exit(0), "DEVICE_COHERENT not available"); 142baa489faSSeongJae Park ASSERT_GE(self->fd, 0); 143baa489faSSeongJae Park } 144baa489faSSeongJae Park 145baa489faSSeongJae Park FIXTURE_SETUP(hmm2) 146baa489faSSeongJae Park { 147baa489faSSeongJae Park self->page_size = sysconf(_SC_PAGE_SIZE); 148baa489faSSeongJae Park self->page_shift = ffs(self->page_size) - 1; 149baa489faSSeongJae Park 150baa489faSSeongJae Park self->fd0 = hmm_open(variant->device_number0); 151baa489faSSeongJae Park if (self->fd0 < 0 && hmm_is_coherent_type(variant->device_number0)) 152baa489faSSeongJae Park SKIP(exit(0), "DEVICE_COHERENT not available"); 153baa489faSSeongJae Park ASSERT_GE(self->fd0, 0); 154baa489faSSeongJae Park self->fd1 = hmm_open(variant->device_number1); 155baa489faSSeongJae Park ASSERT_GE(self->fd1, 0); 156baa489faSSeongJae Park } 157baa489faSSeongJae Park 158baa489faSSeongJae Park FIXTURE_TEARDOWN(hmm) 159baa489faSSeongJae Park { 160baa489faSSeongJae Park int ret = close(self->fd); 161baa489faSSeongJae Park 162baa489faSSeongJae Park ASSERT_EQ(ret, 0); 163baa489faSSeongJae Park self->fd = -1; 164baa489faSSeongJae Park } 165baa489faSSeongJae Park 166baa489faSSeongJae Park FIXTURE_TEARDOWN(hmm2) 167baa489faSSeongJae Park { 168baa489faSSeongJae Park int ret = close(self->fd0); 169baa489faSSeongJae Park 170baa489faSSeongJae Park ASSERT_EQ(ret, 0); 171baa489faSSeongJae Park self->fd0 = -1; 172baa489faSSeongJae Park 173baa489faSSeongJae Park ret = close(self->fd1); 174baa489faSSeongJae Park ASSERT_EQ(ret, 0); 175baa489faSSeongJae Park self->fd1 = -1; 176baa489faSSeongJae Park } 177baa489faSSeongJae Park 178baa489faSSeongJae Park static int hmm_dmirror_cmd(int fd, 179baa489faSSeongJae Park unsigned long request, 180baa489faSSeongJae Park struct hmm_buffer *buffer, 181baa489faSSeongJae Park unsigned long npages) 182baa489faSSeongJae Park { 183baa489faSSeongJae Park struct hmm_dmirror_cmd cmd; 184baa489faSSeongJae Park int ret; 185baa489faSSeongJae Park 186baa489faSSeongJae Park /* Simulate a device reading system memory. */ 187baa489faSSeongJae Park cmd.addr = (__u64)buffer->ptr; 188baa489faSSeongJae Park cmd.ptr = (__u64)buffer->mirror; 189baa489faSSeongJae Park cmd.npages = npages; 190baa489faSSeongJae Park 191baa489faSSeongJae Park for (;;) { 192baa489faSSeongJae Park ret = ioctl(fd, request, &cmd); 193baa489faSSeongJae Park if (ret == 0) 194baa489faSSeongJae Park break; 195baa489faSSeongJae Park if (errno == EINTR) 196baa489faSSeongJae Park continue; 197baa489faSSeongJae Park return -errno; 198baa489faSSeongJae Park } 199baa489faSSeongJae Park buffer->cpages = cmd.cpages; 200baa489faSSeongJae Park buffer->faults = cmd.faults; 201baa489faSSeongJae Park 202baa489faSSeongJae Park return 0; 203baa489faSSeongJae Park } 204baa489faSSeongJae Park 205baa489faSSeongJae Park static void hmm_buffer_free(struct hmm_buffer *buffer) 206baa489faSSeongJae Park { 207baa489faSSeongJae Park if (buffer == NULL) 208baa489faSSeongJae Park return; 209baa489faSSeongJae Park 210baa489faSSeongJae Park if (buffer->ptr) 211baa489faSSeongJae Park munmap(buffer->ptr, buffer->size); 212baa489faSSeongJae Park free(buffer->mirror); 213baa489faSSeongJae Park free(buffer); 214baa489faSSeongJae Park } 215baa489faSSeongJae Park 216baa489faSSeongJae Park /* 217baa489faSSeongJae Park * Create a temporary file that will be deleted on close. 218baa489faSSeongJae Park */ 219baa489faSSeongJae Park static int hmm_create_file(unsigned long size) 220baa489faSSeongJae Park { 221baa489faSSeongJae Park char path[HMM_PATH_MAX]; 222baa489faSSeongJae Park int fd; 223baa489faSSeongJae Park 224baa489faSSeongJae Park strcpy(path, "/tmp"); 225baa489faSSeongJae Park fd = open(path, O_TMPFILE | O_EXCL | O_RDWR, 0600); 226baa489faSSeongJae Park if (fd >= 0) { 227baa489faSSeongJae Park int r; 228baa489faSSeongJae Park 229baa489faSSeongJae Park do { 230baa489faSSeongJae Park r = ftruncate(fd, size); 231baa489faSSeongJae Park } while (r == -1 && errno == EINTR); 232baa489faSSeongJae Park if (!r) 233baa489faSSeongJae Park return fd; 234baa489faSSeongJae Park close(fd); 235baa489faSSeongJae Park } 236baa489faSSeongJae Park return -1; 237baa489faSSeongJae Park } 238baa489faSSeongJae Park 239baa489faSSeongJae Park /* 240baa489faSSeongJae Park * Return a random unsigned number. 241baa489faSSeongJae Park */ 242baa489faSSeongJae Park static unsigned int hmm_random(void) 243baa489faSSeongJae Park { 244baa489faSSeongJae Park static int fd = -1; 245baa489faSSeongJae Park unsigned int r; 246baa489faSSeongJae Park 247baa489faSSeongJae Park if (fd < 0) { 248baa489faSSeongJae Park fd = open("/dev/urandom", O_RDONLY); 249baa489faSSeongJae Park if (fd < 0) { 250baa489faSSeongJae Park fprintf(stderr, "%s:%d failed to open /dev/urandom\n", 251baa489faSSeongJae Park __FILE__, __LINE__); 252baa489faSSeongJae Park return ~0U; 253baa489faSSeongJae Park } 254baa489faSSeongJae Park } 255baa489faSSeongJae Park read(fd, &r, sizeof(r)); 256baa489faSSeongJae Park return r; 257baa489faSSeongJae Park } 258baa489faSSeongJae Park 259baa489faSSeongJae Park static void hmm_nanosleep(unsigned int n) 260baa489faSSeongJae Park { 261baa489faSSeongJae Park struct timespec t; 262baa489faSSeongJae Park 263baa489faSSeongJae Park t.tv_sec = 0; 264baa489faSSeongJae Park t.tv_nsec = n; 265baa489faSSeongJae Park nanosleep(&t, NULL); 266baa489faSSeongJae Park } 267baa489faSSeongJae Park 268baa489faSSeongJae Park static int hmm_migrate_sys_to_dev(int fd, 269baa489faSSeongJae Park struct hmm_buffer *buffer, 270baa489faSSeongJae Park unsigned long npages) 271baa489faSSeongJae Park { 272baa489faSSeongJae Park return hmm_dmirror_cmd(fd, HMM_DMIRROR_MIGRATE_TO_DEV, buffer, npages); 273baa489faSSeongJae Park } 274baa489faSSeongJae Park 275baa489faSSeongJae Park static int hmm_migrate_dev_to_sys(int fd, 276baa489faSSeongJae Park struct hmm_buffer *buffer, 277baa489faSSeongJae Park unsigned long npages) 278baa489faSSeongJae Park { 279baa489faSSeongJae Park return hmm_dmirror_cmd(fd, HMM_DMIRROR_MIGRATE_TO_SYS, buffer, npages); 280baa489faSSeongJae Park } 281baa489faSSeongJae Park 282baa489faSSeongJae Park /* 283baa489faSSeongJae Park * Simple NULL test of device open/close. 284baa489faSSeongJae Park */ 285baa489faSSeongJae Park TEST_F(hmm, open_close) 286baa489faSSeongJae Park { 287baa489faSSeongJae Park } 288baa489faSSeongJae Park 289baa489faSSeongJae Park /* 290baa489faSSeongJae Park * Read private anonymous memory. 291baa489faSSeongJae Park */ 292baa489faSSeongJae Park TEST_F(hmm, anon_read) 293baa489faSSeongJae Park { 294baa489faSSeongJae Park struct hmm_buffer *buffer; 295baa489faSSeongJae Park unsigned long npages; 296baa489faSSeongJae Park unsigned long size; 297baa489faSSeongJae Park unsigned long i; 298baa489faSSeongJae Park int *ptr; 299baa489faSSeongJae Park int ret; 300baa489faSSeongJae Park int val; 301baa489faSSeongJae Park 302baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 303baa489faSSeongJae Park ASSERT_NE(npages, 0); 304baa489faSSeongJae Park size = npages << self->page_shift; 305baa489faSSeongJae Park 306baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 307baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 308baa489faSSeongJae Park 309baa489faSSeongJae Park buffer->fd = -1; 310baa489faSSeongJae Park buffer->size = size; 311baa489faSSeongJae Park buffer->mirror = malloc(size); 312baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 313baa489faSSeongJae Park 314baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 315baa489faSSeongJae Park PROT_READ | PROT_WRITE, 316baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 317baa489faSSeongJae Park buffer->fd, 0); 318baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 319baa489faSSeongJae Park 320baa489faSSeongJae Park /* 321baa489faSSeongJae Park * Initialize buffer in system memory but leave the first two pages 322baa489faSSeongJae Park * zero (pte_none and pfn_zero). 323baa489faSSeongJae Park */ 324baa489faSSeongJae Park i = 2 * self->page_size / sizeof(*ptr); 325baa489faSSeongJae Park for (ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 326baa489faSSeongJae Park ptr[i] = i; 327baa489faSSeongJae Park 328baa489faSSeongJae Park /* Set buffer permission to read-only. */ 329baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_READ); 330baa489faSSeongJae Park ASSERT_EQ(ret, 0); 331baa489faSSeongJae Park 332baa489faSSeongJae Park /* Populate the CPU page table with a special zero page. */ 333baa489faSSeongJae Park val = *(int *)(buffer->ptr + self->page_size); 334baa489faSSeongJae Park ASSERT_EQ(val, 0); 335baa489faSSeongJae Park 336baa489faSSeongJae Park /* Simulate a device reading system memory. */ 337baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages); 338baa489faSSeongJae Park ASSERT_EQ(ret, 0); 339baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 340baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 341baa489faSSeongJae Park 342baa489faSSeongJae Park /* Check what the device read. */ 343baa489faSSeongJae Park ptr = buffer->mirror; 344baa489faSSeongJae Park for (i = 0; i < 2 * self->page_size / sizeof(*ptr); ++i) 345baa489faSSeongJae Park ASSERT_EQ(ptr[i], 0); 346baa489faSSeongJae Park for (; i < size / sizeof(*ptr); ++i) 347baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 348baa489faSSeongJae Park 349baa489faSSeongJae Park hmm_buffer_free(buffer); 350baa489faSSeongJae Park } 351baa489faSSeongJae Park 352baa489faSSeongJae Park /* 353baa489faSSeongJae Park * Read private anonymous memory which has been protected with 354baa489faSSeongJae Park * mprotect() PROT_NONE. 355baa489faSSeongJae Park */ 356baa489faSSeongJae Park TEST_F(hmm, anon_read_prot) 357baa489faSSeongJae Park { 358baa489faSSeongJae Park struct hmm_buffer *buffer; 359baa489faSSeongJae Park unsigned long npages; 360baa489faSSeongJae Park unsigned long size; 361baa489faSSeongJae Park unsigned long i; 362baa489faSSeongJae Park int *ptr; 363baa489faSSeongJae Park int ret; 364baa489faSSeongJae Park 365baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 366baa489faSSeongJae Park ASSERT_NE(npages, 0); 367baa489faSSeongJae Park size = npages << self->page_shift; 368baa489faSSeongJae Park 369baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 370baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 371baa489faSSeongJae Park 372baa489faSSeongJae Park buffer->fd = -1; 373baa489faSSeongJae Park buffer->size = size; 374baa489faSSeongJae Park buffer->mirror = malloc(size); 375baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 376baa489faSSeongJae Park 377baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 378baa489faSSeongJae Park PROT_READ | PROT_WRITE, 379baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 380baa489faSSeongJae Park buffer->fd, 0); 381baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 382baa489faSSeongJae Park 383baa489faSSeongJae Park /* Initialize buffer in system memory. */ 384baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 385baa489faSSeongJae Park ptr[i] = i; 386baa489faSSeongJae Park 387baa489faSSeongJae Park /* Initialize mirror buffer so we can verify it isn't written. */ 388baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 389baa489faSSeongJae Park ptr[i] = -i; 390baa489faSSeongJae Park 391baa489faSSeongJae Park /* Protect buffer from reading. */ 392baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_NONE); 393baa489faSSeongJae Park ASSERT_EQ(ret, 0); 394baa489faSSeongJae Park 395baa489faSSeongJae Park /* Simulate a device reading system memory. */ 396baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages); 397baa489faSSeongJae Park ASSERT_EQ(ret, -EFAULT); 398baa489faSSeongJae Park 399baa489faSSeongJae Park /* Allow CPU to read the buffer so we can check it. */ 400baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_READ); 401baa489faSSeongJae Park ASSERT_EQ(ret, 0); 402baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 403baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 404baa489faSSeongJae Park 405baa489faSSeongJae Park /* Check what the device read. */ 406baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 407baa489faSSeongJae Park ASSERT_EQ(ptr[i], -i); 408baa489faSSeongJae Park 409baa489faSSeongJae Park hmm_buffer_free(buffer); 410baa489faSSeongJae Park } 411baa489faSSeongJae Park 412baa489faSSeongJae Park /* 413baa489faSSeongJae Park * Write private anonymous memory. 414baa489faSSeongJae Park */ 415baa489faSSeongJae Park TEST_F(hmm, anon_write) 416baa489faSSeongJae Park { 417baa489faSSeongJae Park struct hmm_buffer *buffer; 418baa489faSSeongJae Park unsigned long npages; 419baa489faSSeongJae Park unsigned long size; 420baa489faSSeongJae Park unsigned long i; 421baa489faSSeongJae Park int *ptr; 422baa489faSSeongJae Park int ret; 423baa489faSSeongJae Park 424baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 425baa489faSSeongJae Park ASSERT_NE(npages, 0); 426baa489faSSeongJae Park size = npages << self->page_shift; 427baa489faSSeongJae Park 428baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 429baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 430baa489faSSeongJae Park 431baa489faSSeongJae Park buffer->fd = -1; 432baa489faSSeongJae Park buffer->size = size; 433baa489faSSeongJae Park buffer->mirror = malloc(size); 434baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 435baa489faSSeongJae Park 436baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 437baa489faSSeongJae Park PROT_READ | PROT_WRITE, 438baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 439baa489faSSeongJae Park buffer->fd, 0); 440baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 441baa489faSSeongJae Park 442baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 443baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 444baa489faSSeongJae Park ptr[i] = i; 445baa489faSSeongJae Park 446baa489faSSeongJae Park /* Simulate a device writing system memory. */ 447baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 448baa489faSSeongJae Park ASSERT_EQ(ret, 0); 449baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 450baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 451baa489faSSeongJae Park 452baa489faSSeongJae Park /* Check what the device wrote. */ 453baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 454baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 455baa489faSSeongJae Park 456baa489faSSeongJae Park hmm_buffer_free(buffer); 457baa489faSSeongJae Park } 458baa489faSSeongJae Park 459baa489faSSeongJae Park /* 460baa489faSSeongJae Park * Write private anonymous memory which has been protected with 461baa489faSSeongJae Park * mprotect() PROT_READ. 462baa489faSSeongJae Park */ 463baa489faSSeongJae Park TEST_F(hmm, anon_write_prot) 464baa489faSSeongJae Park { 465baa489faSSeongJae Park struct hmm_buffer *buffer; 466baa489faSSeongJae Park unsigned long npages; 467baa489faSSeongJae Park unsigned long size; 468baa489faSSeongJae Park unsigned long i; 469baa489faSSeongJae Park int *ptr; 470baa489faSSeongJae Park int ret; 471baa489faSSeongJae Park 472baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 473baa489faSSeongJae Park ASSERT_NE(npages, 0); 474baa489faSSeongJae Park size = npages << self->page_shift; 475baa489faSSeongJae Park 476baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 477baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 478baa489faSSeongJae Park 479baa489faSSeongJae Park buffer->fd = -1; 480baa489faSSeongJae Park buffer->size = size; 481baa489faSSeongJae Park buffer->mirror = malloc(size); 482baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 483baa489faSSeongJae Park 484baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 485baa489faSSeongJae Park PROT_READ, 486baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 487baa489faSSeongJae Park buffer->fd, 0); 488baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 489baa489faSSeongJae Park 490baa489faSSeongJae Park /* Simulate a device reading a zero page of memory. */ 491baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1); 492baa489faSSeongJae Park ASSERT_EQ(ret, 0); 493baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, 1); 494baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 495baa489faSSeongJae Park 496baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 497baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 498baa489faSSeongJae Park ptr[i] = i; 499baa489faSSeongJae Park 500baa489faSSeongJae Park /* Simulate a device writing system memory. */ 501baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 502baa489faSSeongJae Park ASSERT_EQ(ret, -EPERM); 503baa489faSSeongJae Park 504baa489faSSeongJae Park /* Check what the device wrote. */ 505baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 506baa489faSSeongJae Park ASSERT_EQ(ptr[i], 0); 507baa489faSSeongJae Park 508baa489faSSeongJae Park /* Now allow writing and see that the zero page is replaced. */ 509baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_WRITE | PROT_READ); 510baa489faSSeongJae Park ASSERT_EQ(ret, 0); 511baa489faSSeongJae Park 512baa489faSSeongJae Park /* Simulate a device writing system memory. */ 513baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 514baa489faSSeongJae Park ASSERT_EQ(ret, 0); 515baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 516baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 517baa489faSSeongJae Park 518baa489faSSeongJae Park /* Check what the device wrote. */ 519baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 520baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 521baa489faSSeongJae Park 522baa489faSSeongJae Park hmm_buffer_free(buffer); 523baa489faSSeongJae Park } 524baa489faSSeongJae Park 525baa489faSSeongJae Park /* 526baa489faSSeongJae Park * Check that a device writing an anonymous private mapping 527baa489faSSeongJae Park * will copy-on-write if a child process inherits the mapping. 528baa489faSSeongJae Park */ 529baa489faSSeongJae Park TEST_F(hmm, anon_write_child) 530baa489faSSeongJae Park { 531baa489faSSeongJae Park struct hmm_buffer *buffer; 532baa489faSSeongJae Park unsigned long npages; 533baa489faSSeongJae Park unsigned long size; 534baa489faSSeongJae Park unsigned long i; 535baa489faSSeongJae Park int *ptr; 536baa489faSSeongJae Park pid_t pid; 537baa489faSSeongJae Park int child_fd; 538baa489faSSeongJae Park int ret; 539baa489faSSeongJae Park 540baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 541baa489faSSeongJae Park ASSERT_NE(npages, 0); 542baa489faSSeongJae Park size = npages << self->page_shift; 543baa489faSSeongJae Park 544baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 545baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 546baa489faSSeongJae Park 547baa489faSSeongJae Park buffer->fd = -1; 548baa489faSSeongJae Park buffer->size = size; 549baa489faSSeongJae Park buffer->mirror = malloc(size); 550baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 551baa489faSSeongJae Park 552baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 553baa489faSSeongJae Park PROT_READ | PROT_WRITE, 554baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 555baa489faSSeongJae Park buffer->fd, 0); 556baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 557baa489faSSeongJae Park 558baa489faSSeongJae Park /* Initialize buffer->ptr so we can tell if it is written. */ 559baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 560baa489faSSeongJae Park ptr[i] = i; 561baa489faSSeongJae Park 562baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 563baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 564baa489faSSeongJae Park ptr[i] = -i; 565baa489faSSeongJae Park 566baa489faSSeongJae Park pid = fork(); 567baa489faSSeongJae Park if (pid == -1) 568baa489faSSeongJae Park ASSERT_EQ(pid, 0); 569baa489faSSeongJae Park if (pid != 0) { 570baa489faSSeongJae Park waitpid(pid, &ret, 0); 571baa489faSSeongJae Park ASSERT_EQ(WIFEXITED(ret), 1); 572baa489faSSeongJae Park 573baa489faSSeongJae Park /* Check that the parent's buffer did not change. */ 574baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 575baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 576baa489faSSeongJae Park return; 577baa489faSSeongJae Park } 578baa489faSSeongJae Park 579baa489faSSeongJae Park /* Check that we see the parent's values. */ 580baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 581baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 582baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 583baa489faSSeongJae Park ASSERT_EQ(ptr[i], -i); 584baa489faSSeongJae Park 585baa489faSSeongJae Park /* The child process needs its own mirror to its own mm. */ 586baa489faSSeongJae Park child_fd = hmm_open(0); 587baa489faSSeongJae Park ASSERT_GE(child_fd, 0); 588baa489faSSeongJae Park 589baa489faSSeongJae Park /* Simulate a device writing system memory. */ 590baa489faSSeongJae Park ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages); 591baa489faSSeongJae Park ASSERT_EQ(ret, 0); 592baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 593baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 594baa489faSSeongJae Park 595baa489faSSeongJae Park /* Check what the device wrote. */ 596baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 597baa489faSSeongJae Park ASSERT_EQ(ptr[i], -i); 598baa489faSSeongJae Park 599baa489faSSeongJae Park close(child_fd); 600baa489faSSeongJae Park exit(0); 601baa489faSSeongJae Park } 602baa489faSSeongJae Park 603baa489faSSeongJae Park /* 604baa489faSSeongJae Park * Check that a device writing an anonymous shared mapping 605baa489faSSeongJae Park * will not copy-on-write if a child process inherits the mapping. 606baa489faSSeongJae Park */ 607baa489faSSeongJae Park TEST_F(hmm, anon_write_child_shared) 608baa489faSSeongJae Park { 609baa489faSSeongJae Park struct hmm_buffer *buffer; 610baa489faSSeongJae Park unsigned long npages; 611baa489faSSeongJae Park unsigned long size; 612baa489faSSeongJae Park unsigned long i; 613baa489faSSeongJae Park int *ptr; 614baa489faSSeongJae Park pid_t pid; 615baa489faSSeongJae Park int child_fd; 616baa489faSSeongJae Park int ret; 617baa489faSSeongJae Park 618baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 619baa489faSSeongJae Park ASSERT_NE(npages, 0); 620baa489faSSeongJae Park size = npages << self->page_shift; 621baa489faSSeongJae Park 622baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 623baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 624baa489faSSeongJae Park 625baa489faSSeongJae Park buffer->fd = -1; 626baa489faSSeongJae Park buffer->size = size; 627baa489faSSeongJae Park buffer->mirror = malloc(size); 628baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 629baa489faSSeongJae Park 630baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 631baa489faSSeongJae Park PROT_READ | PROT_WRITE, 632baa489faSSeongJae Park MAP_SHARED | MAP_ANONYMOUS, 633baa489faSSeongJae Park buffer->fd, 0); 634baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 635baa489faSSeongJae Park 636baa489faSSeongJae Park /* Initialize buffer->ptr so we can tell if it is written. */ 637baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 638baa489faSSeongJae Park ptr[i] = i; 639baa489faSSeongJae Park 640baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 641baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 642baa489faSSeongJae Park ptr[i] = -i; 643baa489faSSeongJae Park 644baa489faSSeongJae Park pid = fork(); 645baa489faSSeongJae Park if (pid == -1) 646baa489faSSeongJae Park ASSERT_EQ(pid, 0); 647baa489faSSeongJae Park if (pid != 0) { 648baa489faSSeongJae Park waitpid(pid, &ret, 0); 649baa489faSSeongJae Park ASSERT_EQ(WIFEXITED(ret), 1); 650baa489faSSeongJae Park 651baa489faSSeongJae Park /* Check that the parent's buffer did change. */ 652baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 653baa489faSSeongJae Park ASSERT_EQ(ptr[i], -i); 654baa489faSSeongJae Park return; 655baa489faSSeongJae Park } 656baa489faSSeongJae Park 657baa489faSSeongJae Park /* Check that we see the parent's values. */ 658baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 659baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 660baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 661baa489faSSeongJae Park ASSERT_EQ(ptr[i], -i); 662baa489faSSeongJae Park 663baa489faSSeongJae Park /* The child process needs its own mirror to its own mm. */ 664baa489faSSeongJae Park child_fd = hmm_open(0); 665baa489faSSeongJae Park ASSERT_GE(child_fd, 0); 666baa489faSSeongJae Park 667baa489faSSeongJae Park /* Simulate a device writing system memory. */ 668baa489faSSeongJae Park ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages); 669baa489faSSeongJae Park ASSERT_EQ(ret, 0); 670baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 671baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 672baa489faSSeongJae Park 673baa489faSSeongJae Park /* Check what the device wrote. */ 674baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 675baa489faSSeongJae Park ASSERT_EQ(ptr[i], -i); 676baa489faSSeongJae Park 677baa489faSSeongJae Park close(child_fd); 678baa489faSSeongJae Park exit(0); 679baa489faSSeongJae Park } 680baa489faSSeongJae Park 681baa489faSSeongJae Park /* 682baa489faSSeongJae Park * Write private anonymous huge page. 683baa489faSSeongJae Park */ 684baa489faSSeongJae Park TEST_F(hmm, anon_write_huge) 685baa489faSSeongJae Park { 686baa489faSSeongJae Park struct hmm_buffer *buffer; 687baa489faSSeongJae Park unsigned long npages; 688baa489faSSeongJae Park unsigned long size; 689baa489faSSeongJae Park unsigned long i; 690baa489faSSeongJae Park void *old_ptr; 691baa489faSSeongJae Park void *map; 692baa489faSSeongJae Park int *ptr; 693baa489faSSeongJae Park int ret; 694baa489faSSeongJae Park 695baa489faSSeongJae Park size = 2 * TWOMEG; 696baa489faSSeongJae Park 697baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 698baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 699baa489faSSeongJae Park 700baa489faSSeongJae Park buffer->fd = -1; 701baa489faSSeongJae Park buffer->size = size; 702baa489faSSeongJae Park buffer->mirror = malloc(size); 703baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 704baa489faSSeongJae Park 705baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 706baa489faSSeongJae Park PROT_READ | PROT_WRITE, 707baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 708baa489faSSeongJae Park buffer->fd, 0); 709baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 710baa489faSSeongJae Park 711baa489faSSeongJae Park size = TWOMEG; 712baa489faSSeongJae Park npages = size >> self->page_shift; 713baa489faSSeongJae Park map = (void *)ALIGN((uintptr_t)buffer->ptr, size); 714baa489faSSeongJae Park ret = madvise(map, size, MADV_HUGEPAGE); 715baa489faSSeongJae Park ASSERT_EQ(ret, 0); 716baa489faSSeongJae Park old_ptr = buffer->ptr; 717baa489faSSeongJae Park buffer->ptr = map; 718baa489faSSeongJae Park 719baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 720baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 721baa489faSSeongJae Park ptr[i] = i; 722baa489faSSeongJae Park 723baa489faSSeongJae Park /* Simulate a device writing system memory. */ 724baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 725baa489faSSeongJae Park ASSERT_EQ(ret, 0); 726baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 727baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 728baa489faSSeongJae Park 729baa489faSSeongJae Park /* Check what the device wrote. */ 730baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 731baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 732baa489faSSeongJae Park 733baa489faSSeongJae Park buffer->ptr = old_ptr; 734baa489faSSeongJae Park hmm_buffer_free(buffer); 735baa489faSSeongJae Park } 736baa489faSSeongJae Park 737baa489faSSeongJae Park /* 738baa489faSSeongJae Park * Read numeric data from raw and tagged kernel status files. Used to read 739baa489faSSeongJae Park * /proc and /sys data (without a tag) and from /proc/meminfo (with a tag). 740baa489faSSeongJae Park */ 741baa489faSSeongJae Park static long file_read_ulong(char *file, const char *tag) 742baa489faSSeongJae Park { 743baa489faSSeongJae Park int fd; 744baa489faSSeongJae Park char buf[2048]; 745baa489faSSeongJae Park int len; 746baa489faSSeongJae Park char *p, *q; 747baa489faSSeongJae Park long val; 748baa489faSSeongJae Park 749baa489faSSeongJae Park fd = open(file, O_RDONLY); 750baa489faSSeongJae Park if (fd < 0) { 751baa489faSSeongJae Park /* Error opening the file */ 752baa489faSSeongJae Park return -1; 753baa489faSSeongJae Park } 754baa489faSSeongJae Park 755baa489faSSeongJae Park len = read(fd, buf, sizeof(buf)); 756baa489faSSeongJae Park close(fd); 757baa489faSSeongJae Park if (len < 0) { 758baa489faSSeongJae Park /* Error in reading the file */ 759baa489faSSeongJae Park return -1; 760baa489faSSeongJae Park } 761baa489faSSeongJae Park if (len == sizeof(buf)) { 762baa489faSSeongJae Park /* Error file is too large */ 763baa489faSSeongJae Park return -1; 764baa489faSSeongJae Park } 765baa489faSSeongJae Park buf[len] = '\0'; 766baa489faSSeongJae Park 767baa489faSSeongJae Park /* Search for a tag if provided */ 768baa489faSSeongJae Park if (tag) { 769baa489faSSeongJae Park p = strstr(buf, tag); 770baa489faSSeongJae Park if (!p) 771baa489faSSeongJae Park return -1; /* looks like the line we want isn't there */ 772baa489faSSeongJae Park p += strlen(tag); 773baa489faSSeongJae Park } else 774baa489faSSeongJae Park p = buf; 775baa489faSSeongJae Park 776baa489faSSeongJae Park val = strtol(p, &q, 0); 777baa489faSSeongJae Park if (*q != ' ') { 778baa489faSSeongJae Park /* Error parsing the file */ 779baa489faSSeongJae Park return -1; 780baa489faSSeongJae Park } 781baa489faSSeongJae Park 782baa489faSSeongJae Park return val; 783baa489faSSeongJae Park } 784baa489faSSeongJae Park 785baa489faSSeongJae Park /* 786baa489faSSeongJae Park * Write huge TLBFS page. 787baa489faSSeongJae Park */ 788baa489faSSeongJae Park TEST_F(hmm, anon_write_hugetlbfs) 789baa489faSSeongJae Park { 790baa489faSSeongJae Park struct hmm_buffer *buffer; 791baa489faSSeongJae Park unsigned long npages; 792baa489faSSeongJae Park unsigned long size; 793baa489faSSeongJae Park unsigned long default_hsize; 794baa489faSSeongJae Park unsigned long i; 795baa489faSSeongJae Park int *ptr; 796baa489faSSeongJae Park int ret; 797baa489faSSeongJae Park 798baa489faSSeongJae Park default_hsize = file_read_ulong("/proc/meminfo", "Hugepagesize:"); 799baa489faSSeongJae Park if (default_hsize < 0 || default_hsize*1024 < default_hsize) 800baa489faSSeongJae Park SKIP(return, "Huge page size could not be determined"); 801baa489faSSeongJae Park default_hsize = default_hsize*1024; /* KB to B */ 802baa489faSSeongJae Park 803baa489faSSeongJae Park size = ALIGN(TWOMEG, default_hsize); 804baa489faSSeongJae Park npages = size >> self->page_shift; 805baa489faSSeongJae Park 806baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 807baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 808baa489faSSeongJae Park 809baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 810baa489faSSeongJae Park PROT_READ | PROT_WRITE, 811baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 812baa489faSSeongJae Park -1, 0); 813baa489faSSeongJae Park if (buffer->ptr == MAP_FAILED) { 814baa489faSSeongJae Park free(buffer); 815baa489faSSeongJae Park SKIP(return, "Huge page could not be allocated"); 816baa489faSSeongJae Park } 817baa489faSSeongJae Park 818baa489faSSeongJae Park buffer->fd = -1; 819baa489faSSeongJae Park buffer->size = size; 820baa489faSSeongJae Park buffer->mirror = malloc(size); 821baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 822baa489faSSeongJae Park 823baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 824baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 825baa489faSSeongJae Park ptr[i] = i; 826baa489faSSeongJae Park 827baa489faSSeongJae Park /* Simulate a device writing system memory. */ 828baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 829baa489faSSeongJae Park ASSERT_EQ(ret, 0); 830baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 831baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 832baa489faSSeongJae Park 833baa489faSSeongJae Park /* Check what the device wrote. */ 834baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 835baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 836baa489faSSeongJae Park 837baa489faSSeongJae Park munmap(buffer->ptr, buffer->size); 838baa489faSSeongJae Park buffer->ptr = NULL; 839baa489faSSeongJae Park hmm_buffer_free(buffer); 840baa489faSSeongJae Park } 841baa489faSSeongJae Park 842baa489faSSeongJae Park /* 843baa489faSSeongJae Park * Read mmap'ed file memory. 844baa489faSSeongJae Park */ 845baa489faSSeongJae Park TEST_F(hmm, file_read) 846baa489faSSeongJae Park { 847baa489faSSeongJae Park struct hmm_buffer *buffer; 848baa489faSSeongJae Park unsigned long npages; 849baa489faSSeongJae Park unsigned long size; 850baa489faSSeongJae Park unsigned long i; 851baa489faSSeongJae Park int *ptr; 852baa489faSSeongJae Park int ret; 853baa489faSSeongJae Park int fd; 854baa489faSSeongJae Park ssize_t len; 855baa489faSSeongJae Park 856baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 857baa489faSSeongJae Park ASSERT_NE(npages, 0); 858baa489faSSeongJae Park size = npages << self->page_shift; 859baa489faSSeongJae Park 860baa489faSSeongJae Park fd = hmm_create_file(size); 861baa489faSSeongJae Park ASSERT_GE(fd, 0); 862baa489faSSeongJae Park 863baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 864baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 865baa489faSSeongJae Park 866baa489faSSeongJae Park buffer->fd = fd; 867baa489faSSeongJae Park buffer->size = size; 868baa489faSSeongJae Park buffer->mirror = malloc(size); 869baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 870baa489faSSeongJae Park 871baa489faSSeongJae Park /* Write initial contents of the file. */ 872baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 873baa489faSSeongJae Park ptr[i] = i; 874baa489faSSeongJae Park len = pwrite(fd, buffer->mirror, size, 0); 875baa489faSSeongJae Park ASSERT_EQ(len, size); 876baa489faSSeongJae Park memset(buffer->mirror, 0, size); 877baa489faSSeongJae Park 878baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 879baa489faSSeongJae Park PROT_READ, 880baa489faSSeongJae Park MAP_SHARED, 881baa489faSSeongJae Park buffer->fd, 0); 882baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 883baa489faSSeongJae Park 884baa489faSSeongJae Park /* Simulate a device reading system memory. */ 885baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages); 886baa489faSSeongJae Park ASSERT_EQ(ret, 0); 887baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 888baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 889baa489faSSeongJae Park 890baa489faSSeongJae Park /* Check what the device read. */ 891baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 892baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 893baa489faSSeongJae Park 894baa489faSSeongJae Park hmm_buffer_free(buffer); 895baa489faSSeongJae Park } 896baa489faSSeongJae Park 897baa489faSSeongJae Park /* 898baa489faSSeongJae Park * Write mmap'ed file memory. 899baa489faSSeongJae Park */ 900baa489faSSeongJae Park TEST_F(hmm, file_write) 901baa489faSSeongJae Park { 902baa489faSSeongJae Park struct hmm_buffer *buffer; 903baa489faSSeongJae Park unsigned long npages; 904baa489faSSeongJae Park unsigned long size; 905baa489faSSeongJae Park unsigned long i; 906baa489faSSeongJae Park int *ptr; 907baa489faSSeongJae Park int ret; 908baa489faSSeongJae Park int fd; 909baa489faSSeongJae Park ssize_t len; 910baa489faSSeongJae Park 911baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 912baa489faSSeongJae Park ASSERT_NE(npages, 0); 913baa489faSSeongJae Park size = npages << self->page_shift; 914baa489faSSeongJae Park 915baa489faSSeongJae Park fd = hmm_create_file(size); 916baa489faSSeongJae Park ASSERT_GE(fd, 0); 917baa489faSSeongJae Park 918baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 919baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 920baa489faSSeongJae Park 921baa489faSSeongJae Park buffer->fd = fd; 922baa489faSSeongJae Park buffer->size = size; 923baa489faSSeongJae Park buffer->mirror = malloc(size); 924baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 925baa489faSSeongJae Park 926baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 927baa489faSSeongJae Park PROT_READ | PROT_WRITE, 928baa489faSSeongJae Park MAP_SHARED, 929baa489faSSeongJae Park buffer->fd, 0); 930baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 931baa489faSSeongJae Park 932baa489faSSeongJae Park /* Initialize data that the device will write to buffer->ptr. */ 933baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 934baa489faSSeongJae Park ptr[i] = i; 935baa489faSSeongJae Park 936baa489faSSeongJae Park /* Simulate a device writing system memory. */ 937baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 938baa489faSSeongJae Park ASSERT_EQ(ret, 0); 939baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 940baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 941baa489faSSeongJae Park 942baa489faSSeongJae Park /* Check what the device wrote. */ 943baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 944baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 945baa489faSSeongJae Park 946baa489faSSeongJae Park /* Check that the device also wrote the file. */ 947baa489faSSeongJae Park len = pread(fd, buffer->mirror, size, 0); 948baa489faSSeongJae Park ASSERT_EQ(len, size); 949baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 950baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 951baa489faSSeongJae Park 952baa489faSSeongJae Park hmm_buffer_free(buffer); 953baa489faSSeongJae Park } 954baa489faSSeongJae Park 955baa489faSSeongJae Park /* 956baa489faSSeongJae Park * Migrate anonymous memory to device private memory. 957baa489faSSeongJae Park */ 958baa489faSSeongJae Park TEST_F(hmm, migrate) 959baa489faSSeongJae Park { 960baa489faSSeongJae Park struct hmm_buffer *buffer; 961baa489faSSeongJae Park unsigned long npages; 962baa489faSSeongJae Park unsigned long size; 963baa489faSSeongJae Park unsigned long i; 964baa489faSSeongJae Park int *ptr; 965baa489faSSeongJae Park int ret; 966baa489faSSeongJae Park 967baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 968baa489faSSeongJae Park ASSERT_NE(npages, 0); 969baa489faSSeongJae Park size = npages << self->page_shift; 970baa489faSSeongJae Park 971baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 972baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 973baa489faSSeongJae Park 974baa489faSSeongJae Park buffer->fd = -1; 975baa489faSSeongJae Park buffer->size = size; 976baa489faSSeongJae Park buffer->mirror = malloc(size); 977baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 978baa489faSSeongJae Park 979baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 980baa489faSSeongJae Park PROT_READ | PROT_WRITE, 981baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 982baa489faSSeongJae Park buffer->fd, 0); 983baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 984baa489faSSeongJae Park 985baa489faSSeongJae Park /* Initialize buffer in system memory. */ 986baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 987baa489faSSeongJae Park ptr[i] = i; 988baa489faSSeongJae Park 989baa489faSSeongJae Park /* Migrate memory to device. */ 990baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 991baa489faSSeongJae Park ASSERT_EQ(ret, 0); 992baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 993baa489faSSeongJae Park 994baa489faSSeongJae Park /* Check what the device read. */ 995baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 996baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 997baa489faSSeongJae Park 998baa489faSSeongJae Park hmm_buffer_free(buffer); 999baa489faSSeongJae Park } 1000baa489faSSeongJae Park 1001baa489faSSeongJae Park /* 1002baa489faSSeongJae Park * Migrate anonymous memory to device private memory and fault some of it back 1003baa489faSSeongJae Park * to system memory, then try migrating the resulting mix of system and device 1004baa489faSSeongJae Park * private memory to the device. 1005baa489faSSeongJae Park */ 1006baa489faSSeongJae Park TEST_F(hmm, migrate_fault) 1007baa489faSSeongJae Park { 1008baa489faSSeongJae Park struct hmm_buffer *buffer; 1009baa489faSSeongJae Park unsigned long npages; 1010baa489faSSeongJae Park unsigned long size; 1011baa489faSSeongJae Park unsigned long i; 1012baa489faSSeongJae Park int *ptr; 1013baa489faSSeongJae Park int ret; 1014baa489faSSeongJae Park 1015baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1016baa489faSSeongJae Park ASSERT_NE(npages, 0); 1017baa489faSSeongJae Park size = npages << self->page_shift; 1018baa489faSSeongJae Park 1019baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1020baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1021baa489faSSeongJae Park 1022baa489faSSeongJae Park buffer->fd = -1; 1023baa489faSSeongJae Park buffer->size = size; 1024baa489faSSeongJae Park buffer->mirror = malloc(size); 1025baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1026baa489faSSeongJae Park 1027baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1028baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1029baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1030baa489faSSeongJae Park buffer->fd, 0); 1031baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1032baa489faSSeongJae Park 1033baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1034baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1035baa489faSSeongJae Park ptr[i] = i; 1036baa489faSSeongJae Park 1037baa489faSSeongJae Park /* Migrate memory to device. */ 1038baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 1039baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1040baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1041baa489faSSeongJae Park 1042baa489faSSeongJae Park /* Check what the device read. */ 1043baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1044baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1045baa489faSSeongJae Park 1046baa489faSSeongJae Park /* Fault half the pages back to system memory and check them. */ 1047baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i) 1048baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1049baa489faSSeongJae Park 1050baa489faSSeongJae Park /* Migrate memory to the device again. */ 1051baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 1052baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1053baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1054baa489faSSeongJae Park 1055baa489faSSeongJae Park /* Check what the device read. */ 1056baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1057baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1058baa489faSSeongJae Park 1059baa489faSSeongJae Park hmm_buffer_free(buffer); 1060baa489faSSeongJae Park } 1061baa489faSSeongJae Park 1062baa489faSSeongJae Park TEST_F(hmm, migrate_release) 1063baa489faSSeongJae Park { 1064baa489faSSeongJae Park struct hmm_buffer *buffer; 1065baa489faSSeongJae Park unsigned long npages; 1066baa489faSSeongJae Park unsigned long size; 1067baa489faSSeongJae Park unsigned long i; 1068baa489faSSeongJae Park int *ptr; 1069baa489faSSeongJae Park int ret; 1070baa489faSSeongJae Park 1071baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1072baa489faSSeongJae Park ASSERT_NE(npages, 0); 1073baa489faSSeongJae Park size = npages << self->page_shift; 1074baa489faSSeongJae Park 1075baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1076baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1077baa489faSSeongJae Park 1078baa489faSSeongJae Park buffer->fd = -1; 1079baa489faSSeongJae Park buffer->size = size; 1080baa489faSSeongJae Park buffer->mirror = malloc(size); 1081baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1082baa489faSSeongJae Park 1083baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, 1084baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, buffer->fd, 0); 1085baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1086baa489faSSeongJae Park 1087baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1088baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1089baa489faSSeongJae Park ptr[i] = i; 1090baa489faSSeongJae Park 1091baa489faSSeongJae Park /* Migrate memory to device. */ 1092baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 1093baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1094baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1095baa489faSSeongJae Park 1096baa489faSSeongJae Park /* Check what the device read. */ 1097baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1098baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1099baa489faSSeongJae Park 1100baa489faSSeongJae Park /* Release device memory. */ 1101baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_RELEASE, buffer, npages); 1102baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1103baa489faSSeongJae Park 1104baa489faSSeongJae Park /* Fault pages back to system memory and check them. */ 1105baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / (2 * sizeof(*ptr)); ++i) 1106baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1107baa489faSSeongJae Park 1108baa489faSSeongJae Park hmm_buffer_free(buffer); 1109baa489faSSeongJae Park } 1110baa489faSSeongJae Park 1111baa489faSSeongJae Park /* 1112baa489faSSeongJae Park * Migrate anonymous shared memory to device private memory. 1113baa489faSSeongJae Park */ 1114baa489faSSeongJae Park TEST_F(hmm, migrate_shared) 1115baa489faSSeongJae Park { 1116baa489faSSeongJae Park struct hmm_buffer *buffer; 1117baa489faSSeongJae Park unsigned long npages; 1118baa489faSSeongJae Park unsigned long size; 1119baa489faSSeongJae Park int ret; 1120baa489faSSeongJae Park 1121baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1122baa489faSSeongJae Park ASSERT_NE(npages, 0); 1123baa489faSSeongJae Park size = npages << self->page_shift; 1124baa489faSSeongJae Park 1125baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1126baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1127baa489faSSeongJae Park 1128baa489faSSeongJae Park buffer->fd = -1; 1129baa489faSSeongJae Park buffer->size = size; 1130baa489faSSeongJae Park buffer->mirror = malloc(size); 1131baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1132baa489faSSeongJae Park 1133baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1134baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1135baa489faSSeongJae Park MAP_SHARED | MAP_ANONYMOUS, 1136baa489faSSeongJae Park buffer->fd, 0); 1137baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1138baa489faSSeongJae Park 1139baa489faSSeongJae Park /* Migrate memory to device. */ 1140baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 1141baa489faSSeongJae Park ASSERT_EQ(ret, -ENOENT); 1142baa489faSSeongJae Park 1143baa489faSSeongJae Park hmm_buffer_free(buffer); 1144baa489faSSeongJae Park } 1145baa489faSSeongJae Park 1146baa489faSSeongJae Park /* 1147baa489faSSeongJae Park * Try to migrate various memory types to device private memory. 1148baa489faSSeongJae Park */ 1149baa489faSSeongJae Park TEST_F(hmm2, migrate_mixed) 1150baa489faSSeongJae Park { 1151baa489faSSeongJae Park struct hmm_buffer *buffer; 1152baa489faSSeongJae Park unsigned long npages; 1153baa489faSSeongJae Park unsigned long size; 1154baa489faSSeongJae Park int *ptr; 1155baa489faSSeongJae Park unsigned char *p; 1156baa489faSSeongJae Park int ret; 1157baa489faSSeongJae Park int val; 1158baa489faSSeongJae Park 1159baa489faSSeongJae Park npages = 6; 1160baa489faSSeongJae Park size = npages << self->page_shift; 1161baa489faSSeongJae Park 1162baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1163baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1164baa489faSSeongJae Park 1165baa489faSSeongJae Park buffer->fd = -1; 1166baa489faSSeongJae Park buffer->size = size; 1167baa489faSSeongJae Park buffer->mirror = malloc(size); 1168baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1169baa489faSSeongJae Park 1170baa489faSSeongJae Park /* Reserve a range of addresses. */ 1171baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1172baa489faSSeongJae Park PROT_NONE, 1173baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1174baa489faSSeongJae Park buffer->fd, 0); 1175baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1176baa489faSSeongJae Park p = buffer->ptr; 1177baa489faSSeongJae Park 1178baa489faSSeongJae Park /* Migrating a protected area should be an error. */ 1179baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd1, buffer, npages); 1180baa489faSSeongJae Park ASSERT_EQ(ret, -EINVAL); 1181baa489faSSeongJae Park 1182baa489faSSeongJae Park /* Punch a hole after the first page address. */ 1183baa489faSSeongJae Park ret = munmap(buffer->ptr + self->page_size, self->page_size); 1184baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1185baa489faSSeongJae Park 1186baa489faSSeongJae Park /* We expect an error if the vma doesn't cover the range. */ 1187baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 3); 1188baa489faSSeongJae Park ASSERT_EQ(ret, -EINVAL); 1189baa489faSSeongJae Park 1190baa489faSSeongJae Park /* Page 2 will be a read-only zero page. */ 1191baa489faSSeongJae Park ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size, 1192baa489faSSeongJae Park PROT_READ); 1193baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1194baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 2 * self->page_size); 1195baa489faSSeongJae Park val = *ptr + 3; 1196baa489faSSeongJae Park ASSERT_EQ(val, 3); 1197baa489faSSeongJae Park 1198baa489faSSeongJae Park /* Page 3 will be read-only. */ 1199baa489faSSeongJae Park ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 1200baa489faSSeongJae Park PROT_READ | PROT_WRITE); 1201baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1202baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 3 * self->page_size); 1203baa489faSSeongJae Park *ptr = val; 1204baa489faSSeongJae Park ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 1205baa489faSSeongJae Park PROT_READ); 1206baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1207baa489faSSeongJae Park 1208baa489faSSeongJae Park /* Page 4-5 will be read-write. */ 1209baa489faSSeongJae Park ret = mprotect(buffer->ptr + 4 * self->page_size, 2 * self->page_size, 1210baa489faSSeongJae Park PROT_READ | PROT_WRITE); 1211baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1212baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 4 * self->page_size); 1213baa489faSSeongJae Park *ptr = val; 1214baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 5 * self->page_size); 1215baa489faSSeongJae Park *ptr = val; 1216baa489faSSeongJae Park 1217baa489faSSeongJae Park /* Now try to migrate pages 2-5 to device 1. */ 1218baa489faSSeongJae Park buffer->ptr = p + 2 * self->page_size; 1219baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 4); 1220baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1221baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, 4); 1222baa489faSSeongJae Park 1223baa489faSSeongJae Park /* Page 5 won't be migrated to device 0 because it's on device 1. */ 1224baa489faSSeongJae Park buffer->ptr = p + 5 * self->page_size; 1225baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd0, buffer, 1); 1226baa489faSSeongJae Park ASSERT_EQ(ret, -ENOENT); 1227baa489faSSeongJae Park buffer->ptr = p; 1228baa489faSSeongJae Park 1229baa489faSSeongJae Park buffer->ptr = p; 1230baa489faSSeongJae Park hmm_buffer_free(buffer); 1231baa489faSSeongJae Park } 1232baa489faSSeongJae Park 1233baa489faSSeongJae Park /* 1234baa489faSSeongJae Park * Migrate anonymous memory to device memory and back to system memory 1235baa489faSSeongJae Park * multiple times. In case of private zone configuration, this is done 1236baa489faSSeongJae Park * through fault pages accessed by CPU. In case of coherent zone configuration, 1237baa489faSSeongJae Park * the pages from the device should be explicitly migrated back to system memory. 1238baa489faSSeongJae Park * The reason is Coherent device zone has coherent access by CPU, therefore 1239baa489faSSeongJae Park * it will not generate any page fault. 1240baa489faSSeongJae Park */ 1241baa489faSSeongJae Park TEST_F(hmm, migrate_multiple) 1242baa489faSSeongJae Park { 1243baa489faSSeongJae Park struct hmm_buffer *buffer; 1244baa489faSSeongJae Park unsigned long npages; 1245baa489faSSeongJae Park unsigned long size; 1246baa489faSSeongJae Park unsigned long i; 1247baa489faSSeongJae Park unsigned long c; 1248baa489faSSeongJae Park int *ptr; 1249baa489faSSeongJae Park int ret; 1250baa489faSSeongJae Park 1251baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1252baa489faSSeongJae Park ASSERT_NE(npages, 0); 1253baa489faSSeongJae Park size = npages << self->page_shift; 1254baa489faSSeongJae Park 1255baa489faSSeongJae Park for (c = 0; c < NTIMES; c++) { 1256baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1257baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1258baa489faSSeongJae Park 1259baa489faSSeongJae Park buffer->fd = -1; 1260baa489faSSeongJae Park buffer->size = size; 1261baa489faSSeongJae Park buffer->mirror = malloc(size); 1262baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1263baa489faSSeongJae Park 1264baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1265baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1266baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1267baa489faSSeongJae Park buffer->fd, 0); 1268baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1269baa489faSSeongJae Park 1270baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1271baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1272baa489faSSeongJae Park ptr[i] = i; 1273baa489faSSeongJae Park 1274baa489faSSeongJae Park /* Migrate memory to device. */ 1275baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 1276baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1277baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1278baa489faSSeongJae Park 1279baa489faSSeongJae Park /* Check what the device read. */ 1280baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1281baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1282baa489faSSeongJae Park 1283baa489faSSeongJae Park /* Migrate back to system memory and check them. */ 1284baa489faSSeongJae Park if (hmm_is_coherent_type(variant->device_number)) { 1285baa489faSSeongJae Park ret = hmm_migrate_dev_to_sys(self->fd, buffer, npages); 1286baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1287baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1288baa489faSSeongJae Park } 1289baa489faSSeongJae Park 1290baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1291baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1292baa489faSSeongJae Park 1293baa489faSSeongJae Park hmm_buffer_free(buffer); 1294baa489faSSeongJae Park } 1295baa489faSSeongJae Park } 1296baa489faSSeongJae Park 1297baa489faSSeongJae Park /* 1298baa489faSSeongJae Park * Read anonymous memory multiple times. 1299baa489faSSeongJae Park */ 1300baa489faSSeongJae Park TEST_F(hmm, anon_read_multiple) 1301baa489faSSeongJae Park { 1302baa489faSSeongJae Park struct hmm_buffer *buffer; 1303baa489faSSeongJae Park unsigned long npages; 1304baa489faSSeongJae Park unsigned long size; 1305baa489faSSeongJae Park unsigned long i; 1306baa489faSSeongJae Park unsigned long c; 1307baa489faSSeongJae Park int *ptr; 1308baa489faSSeongJae Park int ret; 1309baa489faSSeongJae Park 1310baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1311baa489faSSeongJae Park ASSERT_NE(npages, 0); 1312baa489faSSeongJae Park size = npages << self->page_shift; 1313baa489faSSeongJae Park 1314baa489faSSeongJae Park for (c = 0; c < NTIMES; c++) { 1315baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1316baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1317baa489faSSeongJae Park 1318baa489faSSeongJae Park buffer->fd = -1; 1319baa489faSSeongJae Park buffer->size = size; 1320baa489faSSeongJae Park buffer->mirror = malloc(size); 1321baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1322baa489faSSeongJae Park 1323baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1324baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1325baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1326baa489faSSeongJae Park buffer->fd, 0); 1327baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1328baa489faSSeongJae Park 1329baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1330baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1331baa489faSSeongJae Park ptr[i] = i + c; 1332baa489faSSeongJae Park 1333baa489faSSeongJae Park /* Simulate a device reading system memory. */ 1334baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1335baa489faSSeongJae Park npages); 1336baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1337baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1338baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 1339baa489faSSeongJae Park 1340baa489faSSeongJae Park /* Check what the device read. */ 1341baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1342baa489faSSeongJae Park ASSERT_EQ(ptr[i], i + c); 1343baa489faSSeongJae Park 1344baa489faSSeongJae Park hmm_buffer_free(buffer); 1345baa489faSSeongJae Park } 1346baa489faSSeongJae Park } 1347baa489faSSeongJae Park 1348baa489faSSeongJae Park void *unmap_buffer(void *p) 1349baa489faSSeongJae Park { 1350baa489faSSeongJae Park struct hmm_buffer *buffer = p; 1351baa489faSSeongJae Park 1352baa489faSSeongJae Park /* Delay for a bit and then unmap buffer while it is being read. */ 1353baa489faSSeongJae Park hmm_nanosleep(hmm_random() % 32000); 1354baa489faSSeongJae Park munmap(buffer->ptr + buffer->size / 2, buffer->size / 2); 1355baa489faSSeongJae Park buffer->ptr = NULL; 1356baa489faSSeongJae Park 1357baa489faSSeongJae Park return NULL; 1358baa489faSSeongJae Park } 1359baa489faSSeongJae Park 1360baa489faSSeongJae Park /* 1361baa489faSSeongJae Park * Try reading anonymous memory while it is being unmapped. 1362baa489faSSeongJae Park */ 1363baa489faSSeongJae Park TEST_F(hmm, anon_teardown) 1364baa489faSSeongJae Park { 1365baa489faSSeongJae Park unsigned long npages; 1366baa489faSSeongJae Park unsigned long size; 1367baa489faSSeongJae Park unsigned long c; 1368baa489faSSeongJae Park void *ret; 1369baa489faSSeongJae Park 1370baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1371baa489faSSeongJae Park ASSERT_NE(npages, 0); 1372baa489faSSeongJae Park size = npages << self->page_shift; 1373baa489faSSeongJae Park 1374baa489faSSeongJae Park for (c = 0; c < NTIMES; ++c) { 1375baa489faSSeongJae Park pthread_t thread; 1376baa489faSSeongJae Park struct hmm_buffer *buffer; 1377baa489faSSeongJae Park unsigned long i; 1378baa489faSSeongJae Park int *ptr; 1379baa489faSSeongJae Park int rc; 1380baa489faSSeongJae Park 1381baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1382baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1383baa489faSSeongJae Park 1384baa489faSSeongJae Park buffer->fd = -1; 1385baa489faSSeongJae Park buffer->size = size; 1386baa489faSSeongJae Park buffer->mirror = malloc(size); 1387baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1388baa489faSSeongJae Park 1389baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1390baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1391baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1392baa489faSSeongJae Park buffer->fd, 0); 1393baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1394baa489faSSeongJae Park 1395baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1396baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1397baa489faSSeongJae Park ptr[i] = i + c; 1398baa489faSSeongJae Park 1399baa489faSSeongJae Park rc = pthread_create(&thread, NULL, unmap_buffer, buffer); 1400baa489faSSeongJae Park ASSERT_EQ(rc, 0); 1401baa489faSSeongJae Park 1402baa489faSSeongJae Park /* Simulate a device reading system memory. */ 1403baa489faSSeongJae Park rc = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1404baa489faSSeongJae Park npages); 1405baa489faSSeongJae Park if (rc == 0) { 1406baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1407baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 1408baa489faSSeongJae Park 1409baa489faSSeongJae Park /* Check what the device read. */ 1410baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; 1411baa489faSSeongJae Park i < size / sizeof(*ptr); 1412baa489faSSeongJae Park ++i) 1413baa489faSSeongJae Park ASSERT_EQ(ptr[i], i + c); 1414baa489faSSeongJae Park } 1415baa489faSSeongJae Park 1416baa489faSSeongJae Park pthread_join(thread, &ret); 1417baa489faSSeongJae Park hmm_buffer_free(buffer); 1418baa489faSSeongJae Park } 1419baa489faSSeongJae Park } 1420baa489faSSeongJae Park 1421baa489faSSeongJae Park /* 1422baa489faSSeongJae Park * Test memory snapshot without faulting in pages accessed by the device. 1423baa489faSSeongJae Park */ 1424baa489faSSeongJae Park TEST_F(hmm, mixedmap) 1425baa489faSSeongJae Park { 1426baa489faSSeongJae Park struct hmm_buffer *buffer; 1427baa489faSSeongJae Park unsigned long npages; 1428baa489faSSeongJae Park unsigned long size; 1429baa489faSSeongJae Park unsigned char *m; 1430baa489faSSeongJae Park int ret; 1431baa489faSSeongJae Park 1432baa489faSSeongJae Park npages = 1; 1433baa489faSSeongJae Park size = npages << self->page_shift; 1434baa489faSSeongJae Park 1435baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1436baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1437baa489faSSeongJae Park 1438baa489faSSeongJae Park buffer->fd = -1; 1439baa489faSSeongJae Park buffer->size = size; 1440baa489faSSeongJae Park buffer->mirror = malloc(npages); 1441baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1442baa489faSSeongJae Park 1443baa489faSSeongJae Park 1444baa489faSSeongJae Park /* Reserve a range of addresses. */ 1445baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1446baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1447baa489faSSeongJae Park MAP_PRIVATE, 1448baa489faSSeongJae Park self->fd, 0); 1449baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1450baa489faSSeongJae Park 1451baa489faSSeongJae Park /* Simulate a device snapshotting CPU pagetables. */ 1452baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages); 1453baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1454baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1455baa489faSSeongJae Park 1456baa489faSSeongJae Park /* Check what the device saw. */ 1457baa489faSSeongJae Park m = buffer->mirror; 1458baa489faSSeongJae Park ASSERT_EQ(m[0], HMM_DMIRROR_PROT_READ); 1459baa489faSSeongJae Park 1460baa489faSSeongJae Park hmm_buffer_free(buffer); 1461baa489faSSeongJae Park } 1462baa489faSSeongJae Park 1463baa489faSSeongJae Park /* 1464baa489faSSeongJae Park * Test memory snapshot without faulting in pages accessed by the device. 1465baa489faSSeongJae Park */ 1466baa489faSSeongJae Park TEST_F(hmm2, snapshot) 1467baa489faSSeongJae Park { 1468baa489faSSeongJae Park struct hmm_buffer *buffer; 1469baa489faSSeongJae Park unsigned long npages; 1470baa489faSSeongJae Park unsigned long size; 1471baa489faSSeongJae Park int *ptr; 1472baa489faSSeongJae Park unsigned char *p; 1473baa489faSSeongJae Park unsigned char *m; 1474baa489faSSeongJae Park int ret; 1475baa489faSSeongJae Park int val; 1476baa489faSSeongJae Park 1477baa489faSSeongJae Park npages = 7; 1478baa489faSSeongJae Park size = npages << self->page_shift; 1479baa489faSSeongJae Park 1480baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1481baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1482baa489faSSeongJae Park 1483baa489faSSeongJae Park buffer->fd = -1; 1484baa489faSSeongJae Park buffer->size = size; 1485baa489faSSeongJae Park buffer->mirror = malloc(npages); 1486baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1487baa489faSSeongJae Park 1488baa489faSSeongJae Park /* Reserve a range of addresses. */ 1489baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1490baa489faSSeongJae Park PROT_NONE, 1491baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1492baa489faSSeongJae Park buffer->fd, 0); 1493baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1494baa489faSSeongJae Park p = buffer->ptr; 1495baa489faSSeongJae Park 1496baa489faSSeongJae Park /* Punch a hole after the first page address. */ 1497baa489faSSeongJae Park ret = munmap(buffer->ptr + self->page_size, self->page_size); 1498baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1499baa489faSSeongJae Park 1500baa489faSSeongJae Park /* Page 2 will be read-only zero page. */ 1501baa489faSSeongJae Park ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size, 1502baa489faSSeongJae Park PROT_READ); 1503baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1504baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 2 * self->page_size); 1505baa489faSSeongJae Park val = *ptr + 3; 1506baa489faSSeongJae Park ASSERT_EQ(val, 3); 1507baa489faSSeongJae Park 1508baa489faSSeongJae Park /* Page 3 will be read-only. */ 1509baa489faSSeongJae Park ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 1510baa489faSSeongJae Park PROT_READ | PROT_WRITE); 1511baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1512baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 3 * self->page_size); 1513baa489faSSeongJae Park *ptr = val; 1514baa489faSSeongJae Park ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size, 1515baa489faSSeongJae Park PROT_READ); 1516baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1517baa489faSSeongJae Park 1518baa489faSSeongJae Park /* Page 4-6 will be read-write. */ 1519baa489faSSeongJae Park ret = mprotect(buffer->ptr + 4 * self->page_size, 3 * self->page_size, 1520baa489faSSeongJae Park PROT_READ | PROT_WRITE); 1521baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1522baa489faSSeongJae Park ptr = (int *)(buffer->ptr + 4 * self->page_size); 1523baa489faSSeongJae Park *ptr = val; 1524baa489faSSeongJae Park 1525baa489faSSeongJae Park /* Page 5 will be migrated to device 0. */ 1526baa489faSSeongJae Park buffer->ptr = p + 5 * self->page_size; 1527baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd0, buffer, 1); 1528baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1529baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, 1); 1530baa489faSSeongJae Park 1531baa489faSSeongJae Park /* Page 6 will be migrated to device 1. */ 1532baa489faSSeongJae Park buffer->ptr = p + 6 * self->page_size; 1533baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd1, buffer, 1); 1534baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1535baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, 1); 1536baa489faSSeongJae Park 1537baa489faSSeongJae Park /* Simulate a device snapshotting CPU pagetables. */ 1538baa489faSSeongJae Park buffer->ptr = p; 1539baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_SNAPSHOT, buffer, npages); 1540baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1541baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1542baa489faSSeongJae Park 1543baa489faSSeongJae Park /* Check what the device saw. */ 1544baa489faSSeongJae Park m = buffer->mirror; 1545baa489faSSeongJae Park ASSERT_EQ(m[0], HMM_DMIRROR_PROT_ERROR); 1546baa489faSSeongJae Park ASSERT_EQ(m[1], HMM_DMIRROR_PROT_ERROR); 1547baa489faSSeongJae Park ASSERT_EQ(m[2], HMM_DMIRROR_PROT_ZERO | HMM_DMIRROR_PROT_READ); 1548baa489faSSeongJae Park ASSERT_EQ(m[3], HMM_DMIRROR_PROT_READ); 1549baa489faSSeongJae Park ASSERT_EQ(m[4], HMM_DMIRROR_PROT_WRITE); 1550baa489faSSeongJae Park if (!hmm_is_coherent_type(variant->device_number0)) { 1551baa489faSSeongJae Park ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL | 1552baa489faSSeongJae Park HMM_DMIRROR_PROT_WRITE); 1553baa489faSSeongJae Park ASSERT_EQ(m[6], HMM_DMIRROR_PROT_NONE); 1554baa489faSSeongJae Park } else { 1555baa489faSSeongJae Park ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | 1556baa489faSSeongJae Park HMM_DMIRROR_PROT_WRITE); 1557baa489faSSeongJae Park ASSERT_EQ(m[6], HMM_DMIRROR_PROT_DEV_COHERENT_REMOTE | 1558baa489faSSeongJae Park HMM_DMIRROR_PROT_WRITE); 1559baa489faSSeongJae Park } 1560baa489faSSeongJae Park 1561baa489faSSeongJae Park hmm_buffer_free(buffer); 1562baa489faSSeongJae Park } 1563baa489faSSeongJae Park 1564baa489faSSeongJae Park /* 1565baa489faSSeongJae Park * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that 1566baa489faSSeongJae Park * should be mapped by a large page table entry. 1567baa489faSSeongJae Park */ 1568baa489faSSeongJae Park TEST_F(hmm, compound) 1569baa489faSSeongJae Park { 1570baa489faSSeongJae Park struct hmm_buffer *buffer; 1571baa489faSSeongJae Park unsigned long npages; 1572baa489faSSeongJae Park unsigned long size; 1573baa489faSSeongJae Park unsigned long default_hsize; 1574baa489faSSeongJae Park int *ptr; 1575baa489faSSeongJae Park unsigned char *m; 1576baa489faSSeongJae Park int ret; 1577baa489faSSeongJae Park unsigned long i; 1578baa489faSSeongJae Park 1579baa489faSSeongJae Park /* Skip test if we can't allocate a hugetlbfs page. */ 1580baa489faSSeongJae Park 1581baa489faSSeongJae Park default_hsize = file_read_ulong("/proc/meminfo", "Hugepagesize:"); 1582baa489faSSeongJae Park if (default_hsize < 0 || default_hsize*1024 < default_hsize) 1583baa489faSSeongJae Park SKIP(return, "Huge page size could not be determined"); 1584baa489faSSeongJae Park default_hsize = default_hsize*1024; /* KB to B */ 1585baa489faSSeongJae Park 1586baa489faSSeongJae Park size = ALIGN(TWOMEG, default_hsize); 1587baa489faSSeongJae Park npages = size >> self->page_shift; 1588baa489faSSeongJae Park 1589baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1590baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1591baa489faSSeongJae Park 1592baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1593baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1594baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 1595baa489faSSeongJae Park -1, 0); 1596baa489faSSeongJae Park if (buffer->ptr == MAP_FAILED) { 1597baa489faSSeongJae Park free(buffer); 1598baa489faSSeongJae Park return; 1599baa489faSSeongJae Park } 1600baa489faSSeongJae Park 1601baa489faSSeongJae Park buffer->size = size; 1602baa489faSSeongJae Park buffer->mirror = malloc(npages); 1603baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1604baa489faSSeongJae Park 1605baa489faSSeongJae Park /* Initialize the pages the device will snapshot in buffer->ptr. */ 1606baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1607baa489faSSeongJae Park ptr[i] = i; 1608baa489faSSeongJae Park 1609baa489faSSeongJae Park /* Simulate a device snapshotting CPU pagetables. */ 1610baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages); 1611baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1612baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1613baa489faSSeongJae Park 1614baa489faSSeongJae Park /* Check what the device saw. */ 1615baa489faSSeongJae Park m = buffer->mirror; 1616baa489faSSeongJae Park for (i = 0; i < npages; ++i) 1617baa489faSSeongJae Park ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE | 1618baa489faSSeongJae Park HMM_DMIRROR_PROT_PMD); 1619baa489faSSeongJae Park 1620baa489faSSeongJae Park /* Make the region read-only. */ 1621baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_READ); 1622baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1623baa489faSSeongJae Park 1624baa489faSSeongJae Park /* Simulate a device snapshotting CPU pagetables. */ 1625baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages); 1626baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1627baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1628baa489faSSeongJae Park 1629baa489faSSeongJae Park /* Check what the device saw. */ 1630baa489faSSeongJae Park m = buffer->mirror; 1631baa489faSSeongJae Park for (i = 0; i < npages; ++i) 1632baa489faSSeongJae Park ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ | 1633baa489faSSeongJae Park HMM_DMIRROR_PROT_PMD); 1634baa489faSSeongJae Park 1635baa489faSSeongJae Park munmap(buffer->ptr, buffer->size); 1636baa489faSSeongJae Park buffer->ptr = NULL; 1637baa489faSSeongJae Park hmm_buffer_free(buffer); 1638baa489faSSeongJae Park } 1639baa489faSSeongJae Park 1640baa489faSSeongJae Park /* 1641baa489faSSeongJae Park * Test two devices reading the same memory (double mapped). 1642baa489faSSeongJae Park */ 1643baa489faSSeongJae Park TEST_F(hmm2, double_map) 1644baa489faSSeongJae Park { 1645baa489faSSeongJae Park struct hmm_buffer *buffer; 1646baa489faSSeongJae Park unsigned long npages; 1647baa489faSSeongJae Park unsigned long size; 1648baa489faSSeongJae Park unsigned long i; 1649baa489faSSeongJae Park int *ptr; 1650baa489faSSeongJae Park int ret; 1651baa489faSSeongJae Park 1652baa489faSSeongJae Park npages = 6; 1653baa489faSSeongJae Park size = npages << self->page_shift; 1654baa489faSSeongJae Park 1655baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1656baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1657baa489faSSeongJae Park 1658baa489faSSeongJae Park buffer->fd = -1; 1659baa489faSSeongJae Park buffer->size = size; 1660*87070a96SDonet Tom buffer->mirror = malloc(size); 1661baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1662baa489faSSeongJae Park 1663baa489faSSeongJae Park /* Reserve a range of addresses. */ 1664baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1665baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1666baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1667baa489faSSeongJae Park buffer->fd, 0); 1668baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1669baa489faSSeongJae Park 1670baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1671baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1672baa489faSSeongJae Park ptr[i] = i; 1673baa489faSSeongJae Park 1674baa489faSSeongJae Park /* Make region read-only. */ 1675baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_READ); 1676baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1677baa489faSSeongJae Park 1678baa489faSSeongJae Park /* Simulate device 0 reading system memory. */ 1679baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages); 1680baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1681baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1682baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 1683baa489faSSeongJae Park 1684baa489faSSeongJae Park /* Check what the device read. */ 1685baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1686baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1687baa489faSSeongJae Park 1688baa489faSSeongJae Park /* Simulate device 1 reading system memory. */ 1689baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_READ, buffer, npages); 1690baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1691baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1692baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 1693baa489faSSeongJae Park 1694baa489faSSeongJae Park /* Check what the device read. */ 1695baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1696baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1697baa489faSSeongJae Park 1698baa489faSSeongJae Park /* Migrate pages to device 1 and try to read from device 0. */ 1699baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd1, buffer, npages); 1700baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1701baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1702baa489faSSeongJae Park 1703baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages); 1704baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1705baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1706baa489faSSeongJae Park ASSERT_EQ(buffer->faults, 1); 1707baa489faSSeongJae Park 1708baa489faSSeongJae Park /* Check what device 0 read. */ 1709baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1710baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1711baa489faSSeongJae Park 1712baa489faSSeongJae Park hmm_buffer_free(buffer); 1713baa489faSSeongJae Park } 1714baa489faSSeongJae Park 1715baa489faSSeongJae Park /* 1716baa489faSSeongJae Park * Basic check of exclusive faulting. 1717baa489faSSeongJae Park */ 1718baa489faSSeongJae Park TEST_F(hmm, exclusive) 1719baa489faSSeongJae Park { 1720baa489faSSeongJae Park struct hmm_buffer *buffer; 1721baa489faSSeongJae Park unsigned long npages; 1722baa489faSSeongJae Park unsigned long size; 1723baa489faSSeongJae Park unsigned long i; 1724baa489faSSeongJae Park int *ptr; 1725baa489faSSeongJae Park int ret; 1726baa489faSSeongJae Park 1727baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1728baa489faSSeongJae Park ASSERT_NE(npages, 0); 1729baa489faSSeongJae Park size = npages << self->page_shift; 1730baa489faSSeongJae Park 1731baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1732baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1733baa489faSSeongJae Park 1734baa489faSSeongJae Park buffer->fd = -1; 1735baa489faSSeongJae Park buffer->size = size; 1736baa489faSSeongJae Park buffer->mirror = malloc(size); 1737baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1738baa489faSSeongJae Park 1739baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1740baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1741baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1742baa489faSSeongJae Park buffer->fd, 0); 1743baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1744baa489faSSeongJae Park 1745baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1746baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1747baa489faSSeongJae Park ptr[i] = i; 1748baa489faSSeongJae Park 1749baa489faSSeongJae Park /* Map memory exclusively for device access. */ 1750baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages); 1751baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1752baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1753baa489faSSeongJae Park 1754baa489faSSeongJae Park /* Check what the device read. */ 1755baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1756baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1757baa489faSSeongJae Park 1758baa489faSSeongJae Park /* Fault pages back to system memory and check them. */ 1759baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1760baa489faSSeongJae Park ASSERT_EQ(ptr[i]++, i); 1761baa489faSSeongJae Park 1762baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1763baa489faSSeongJae Park ASSERT_EQ(ptr[i], i+1); 1764baa489faSSeongJae Park 1765baa489faSSeongJae Park /* Check atomic access revoked */ 1766baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_CHECK_EXCLUSIVE, buffer, npages); 1767baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1768baa489faSSeongJae Park 1769baa489faSSeongJae Park hmm_buffer_free(buffer); 1770baa489faSSeongJae Park } 1771baa489faSSeongJae Park 1772baa489faSSeongJae Park TEST_F(hmm, exclusive_mprotect) 1773baa489faSSeongJae Park { 1774baa489faSSeongJae Park struct hmm_buffer *buffer; 1775baa489faSSeongJae Park unsigned long npages; 1776baa489faSSeongJae Park unsigned long size; 1777baa489faSSeongJae Park unsigned long i; 1778baa489faSSeongJae Park int *ptr; 1779baa489faSSeongJae Park int ret; 1780baa489faSSeongJae Park 1781baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1782baa489faSSeongJae Park ASSERT_NE(npages, 0); 1783baa489faSSeongJae Park size = npages << self->page_shift; 1784baa489faSSeongJae Park 1785baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1786baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1787baa489faSSeongJae Park 1788baa489faSSeongJae Park buffer->fd = -1; 1789baa489faSSeongJae Park buffer->size = size; 1790baa489faSSeongJae Park buffer->mirror = malloc(size); 1791baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1792baa489faSSeongJae Park 1793baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1794baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1795baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1796baa489faSSeongJae Park buffer->fd, 0); 1797baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1798baa489faSSeongJae Park 1799baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1800baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1801baa489faSSeongJae Park ptr[i] = i; 1802baa489faSSeongJae Park 1803baa489faSSeongJae Park /* Map memory exclusively for device access. */ 1804baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages); 1805baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1806baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1807baa489faSSeongJae Park 1808baa489faSSeongJae Park /* Check what the device read. */ 1809baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1810baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1811baa489faSSeongJae Park 1812baa489faSSeongJae Park ret = mprotect(buffer->ptr, size, PROT_READ); 1813baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1814baa489faSSeongJae Park 1815baa489faSSeongJae Park /* Simulate a device writing system memory. */ 1816baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages); 1817baa489faSSeongJae Park ASSERT_EQ(ret, -EPERM); 1818baa489faSSeongJae Park 1819baa489faSSeongJae Park hmm_buffer_free(buffer); 1820baa489faSSeongJae Park } 1821baa489faSSeongJae Park 1822baa489faSSeongJae Park /* 1823baa489faSSeongJae Park * Check copy-on-write works. 1824baa489faSSeongJae Park */ 1825baa489faSSeongJae Park TEST_F(hmm, exclusive_cow) 1826baa489faSSeongJae Park { 1827baa489faSSeongJae Park struct hmm_buffer *buffer; 1828baa489faSSeongJae Park unsigned long npages; 1829baa489faSSeongJae Park unsigned long size; 1830baa489faSSeongJae Park unsigned long i; 1831baa489faSSeongJae Park int *ptr; 1832baa489faSSeongJae Park int ret; 1833baa489faSSeongJae Park 1834baa489faSSeongJae Park npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; 1835baa489faSSeongJae Park ASSERT_NE(npages, 0); 1836baa489faSSeongJae Park size = npages << self->page_shift; 1837baa489faSSeongJae Park 1838baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1839baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1840baa489faSSeongJae Park 1841baa489faSSeongJae Park buffer->fd = -1; 1842baa489faSSeongJae Park buffer->size = size; 1843baa489faSSeongJae Park buffer->mirror = malloc(size); 1844baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1845baa489faSSeongJae Park 1846baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1847baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1848baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1849baa489faSSeongJae Park buffer->fd, 0); 1850baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1851baa489faSSeongJae Park 1852baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1853baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1854baa489faSSeongJae Park ptr[i] = i; 1855baa489faSSeongJae Park 1856baa489faSSeongJae Park /* Map memory exclusively for device access. */ 1857baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_EXCLUSIVE, buffer, npages); 1858baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1859baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1860baa489faSSeongJae Park 1861baa489faSSeongJae Park fork(); 1862baa489faSSeongJae Park 1863baa489faSSeongJae Park /* Fault pages back to system memory and check them. */ 1864baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1865baa489faSSeongJae Park ASSERT_EQ(ptr[i]++, i); 1866baa489faSSeongJae Park 1867baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1868baa489faSSeongJae Park ASSERT_EQ(ptr[i], i+1); 1869baa489faSSeongJae Park 1870baa489faSSeongJae Park hmm_buffer_free(buffer); 1871baa489faSSeongJae Park } 1872baa489faSSeongJae Park 1873baa489faSSeongJae Park static int gup_test_exec(int gup_fd, unsigned long addr, int cmd, 1874baa489faSSeongJae Park int npages, int size, int flags) 1875baa489faSSeongJae Park { 1876baa489faSSeongJae Park struct gup_test gup = { 1877baa489faSSeongJae Park .nr_pages_per_call = npages, 1878baa489faSSeongJae Park .addr = addr, 1879baa489faSSeongJae Park .gup_flags = FOLL_WRITE | flags, 1880baa489faSSeongJae Park .size = size, 1881baa489faSSeongJae Park }; 1882baa489faSSeongJae Park 1883baa489faSSeongJae Park if (ioctl(gup_fd, cmd, &gup)) { 1884baa489faSSeongJae Park perror("ioctl on error\n"); 1885baa489faSSeongJae Park return errno; 1886baa489faSSeongJae Park } 1887baa489faSSeongJae Park 1888baa489faSSeongJae Park return 0; 1889baa489faSSeongJae Park } 1890baa489faSSeongJae Park 1891baa489faSSeongJae Park /* 1892baa489faSSeongJae Park * Test get user device pages through gup_test. Setting PIN_LONGTERM flag. 1893baa489faSSeongJae Park * This should trigger a migration back to system memory for both, private 1894baa489faSSeongJae Park * and coherent type pages. 1895baa489faSSeongJae Park * This test makes use of gup_test module. Make sure GUP_TEST_CONFIG is added 1896baa489faSSeongJae Park * to your configuration before you run it. 1897baa489faSSeongJae Park */ 1898baa489faSSeongJae Park TEST_F(hmm, hmm_gup_test) 1899baa489faSSeongJae Park { 1900baa489faSSeongJae Park struct hmm_buffer *buffer; 1901baa489faSSeongJae Park int gup_fd; 1902baa489faSSeongJae Park unsigned long npages; 1903baa489faSSeongJae Park unsigned long size; 1904baa489faSSeongJae Park unsigned long i; 1905baa489faSSeongJae Park int *ptr; 1906baa489faSSeongJae Park int ret; 1907baa489faSSeongJae Park unsigned char *m; 1908baa489faSSeongJae Park 1909baa489faSSeongJae Park gup_fd = open("/sys/kernel/debug/gup_test", O_RDWR); 1910baa489faSSeongJae Park if (gup_fd == -1) 1911baa489faSSeongJae Park SKIP(return, "Skipping test, could not find gup_test driver"); 1912baa489faSSeongJae Park 1913baa489faSSeongJae Park npages = 4; 1914baa489faSSeongJae Park size = npages << self->page_shift; 1915baa489faSSeongJae Park 1916baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 1917baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 1918baa489faSSeongJae Park 1919baa489faSSeongJae Park buffer->fd = -1; 1920baa489faSSeongJae Park buffer->size = size; 1921baa489faSSeongJae Park buffer->mirror = malloc(size); 1922baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 1923baa489faSSeongJae Park 1924baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 1925baa489faSSeongJae Park PROT_READ | PROT_WRITE, 1926baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 1927baa489faSSeongJae Park buffer->fd, 0); 1928baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 1929baa489faSSeongJae Park 1930baa489faSSeongJae Park /* Initialize buffer in system memory. */ 1931baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1932baa489faSSeongJae Park ptr[i] = i; 1933baa489faSSeongJae Park 1934baa489faSSeongJae Park /* Migrate memory to device. */ 1935baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 1936baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1937baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1938baa489faSSeongJae Park /* Check what the device read. */ 1939baa489faSSeongJae Park for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) 1940baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1941baa489faSSeongJae Park 1942baa489faSSeongJae Park ASSERT_EQ(gup_test_exec(gup_fd, 1943baa489faSSeongJae Park (unsigned long)buffer->ptr, 1944baa489faSSeongJae Park GUP_BASIC_TEST, 1, self->page_size, 0), 0); 1945baa489faSSeongJae Park ASSERT_EQ(gup_test_exec(gup_fd, 1946baa489faSSeongJae Park (unsigned long)buffer->ptr + 1 * self->page_size, 1947baa489faSSeongJae Park GUP_FAST_BENCHMARK, 1, self->page_size, 0), 0); 1948baa489faSSeongJae Park ASSERT_EQ(gup_test_exec(gup_fd, 1949baa489faSSeongJae Park (unsigned long)buffer->ptr + 2 * self->page_size, 1950baa489faSSeongJae Park PIN_FAST_BENCHMARK, 1, self->page_size, FOLL_LONGTERM), 0); 1951baa489faSSeongJae Park ASSERT_EQ(gup_test_exec(gup_fd, 1952baa489faSSeongJae Park (unsigned long)buffer->ptr + 3 * self->page_size, 1953baa489faSSeongJae Park PIN_LONGTERM_BENCHMARK, 1, self->page_size, 0), 0); 1954baa489faSSeongJae Park 1955baa489faSSeongJae Park /* Take snapshot to CPU pagetables */ 1956baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages); 1957baa489faSSeongJae Park ASSERT_EQ(ret, 0); 1958baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 1959baa489faSSeongJae Park m = buffer->mirror; 1960baa489faSSeongJae Park if (hmm_is_coherent_type(variant->device_number)) { 1961baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[0]); 1962baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_DEV_COHERENT_LOCAL | HMM_DMIRROR_PROT_WRITE, m[1]); 1963baa489faSSeongJae Park } else { 1964baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[0]); 1965baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[1]); 1966baa489faSSeongJae Park } 1967baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[2]); 1968baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[3]); 1969baa489faSSeongJae Park /* 1970baa489faSSeongJae Park * Check again the content on the pages. Make sure there's no 1971baa489faSSeongJae Park * corrupted data. 1972baa489faSSeongJae Park */ 1973baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 1974baa489faSSeongJae Park ASSERT_EQ(ptr[i], i); 1975baa489faSSeongJae Park 1976baa489faSSeongJae Park close(gup_fd); 1977baa489faSSeongJae Park hmm_buffer_free(buffer); 1978baa489faSSeongJae Park } 1979baa489faSSeongJae Park 1980baa489faSSeongJae Park /* 1981baa489faSSeongJae Park * Test copy-on-write in device pages. 1982baa489faSSeongJae Park * In case of writing to COW private page(s), a page fault will migrate pages 1983baa489faSSeongJae Park * back to system memory first. Then, these pages will be duplicated. In case 1984baa489faSSeongJae Park * of COW device coherent type, pages are duplicated directly from device 1985baa489faSSeongJae Park * memory. 1986baa489faSSeongJae Park */ 1987baa489faSSeongJae Park TEST_F(hmm, hmm_cow_in_device) 1988baa489faSSeongJae Park { 1989baa489faSSeongJae Park struct hmm_buffer *buffer; 1990baa489faSSeongJae Park unsigned long npages; 1991baa489faSSeongJae Park unsigned long size; 1992baa489faSSeongJae Park unsigned long i; 1993baa489faSSeongJae Park int *ptr; 1994baa489faSSeongJae Park int ret; 1995baa489faSSeongJae Park unsigned char *m; 1996baa489faSSeongJae Park pid_t pid; 1997baa489faSSeongJae Park int status; 1998baa489faSSeongJae Park 1999baa489faSSeongJae Park npages = 4; 2000baa489faSSeongJae Park size = npages << self->page_shift; 2001baa489faSSeongJae Park 2002baa489faSSeongJae Park buffer = malloc(sizeof(*buffer)); 2003baa489faSSeongJae Park ASSERT_NE(buffer, NULL); 2004baa489faSSeongJae Park 2005baa489faSSeongJae Park buffer->fd = -1; 2006baa489faSSeongJae Park buffer->size = size; 2007baa489faSSeongJae Park buffer->mirror = malloc(size); 2008baa489faSSeongJae Park ASSERT_NE(buffer->mirror, NULL); 2009baa489faSSeongJae Park 2010baa489faSSeongJae Park buffer->ptr = mmap(NULL, size, 2011baa489faSSeongJae Park PROT_READ | PROT_WRITE, 2012baa489faSSeongJae Park MAP_PRIVATE | MAP_ANONYMOUS, 2013baa489faSSeongJae Park buffer->fd, 0); 2014baa489faSSeongJae Park ASSERT_NE(buffer->ptr, MAP_FAILED); 2015baa489faSSeongJae Park 2016baa489faSSeongJae Park /* Initialize buffer in system memory. */ 2017baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 2018baa489faSSeongJae Park ptr[i] = i; 2019baa489faSSeongJae Park 2020baa489faSSeongJae Park /* Migrate memory to device. */ 2021baa489faSSeongJae Park 2022baa489faSSeongJae Park ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); 2023baa489faSSeongJae Park ASSERT_EQ(ret, 0); 2024baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 2025baa489faSSeongJae Park 2026baa489faSSeongJae Park pid = fork(); 2027baa489faSSeongJae Park if (pid == -1) 2028baa489faSSeongJae Park ASSERT_EQ(pid, 0); 2029baa489faSSeongJae Park if (!pid) { 2030baa489faSSeongJae Park /* Child process waitd for SIGTERM from the parent. */ 2031baa489faSSeongJae Park while (1) { 2032baa489faSSeongJae Park } 2033baa489faSSeongJae Park perror("Should not reach this\n"); 2034baa489faSSeongJae Park exit(0); 2035baa489faSSeongJae Park } 2036baa489faSSeongJae Park /* Parent process writes to COW pages(s) and gets a 2037baa489faSSeongJae Park * new copy in system. In case of device private pages, 2038baa489faSSeongJae Park * this write causes a migration to system mem first. 2039baa489faSSeongJae Park */ 2040baa489faSSeongJae Park for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) 2041baa489faSSeongJae Park ptr[i] = i; 2042baa489faSSeongJae Park 2043baa489faSSeongJae Park /* Terminate child and wait */ 2044baa489faSSeongJae Park EXPECT_EQ(0, kill(pid, SIGTERM)); 2045baa489faSSeongJae Park EXPECT_EQ(pid, waitpid(pid, &status, 0)); 2046baa489faSSeongJae Park EXPECT_NE(0, WIFSIGNALED(status)); 2047baa489faSSeongJae Park EXPECT_EQ(SIGTERM, WTERMSIG(status)); 2048baa489faSSeongJae Park 2049baa489faSSeongJae Park /* Take snapshot to CPU pagetables */ 2050baa489faSSeongJae Park ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_SNAPSHOT, buffer, npages); 2051baa489faSSeongJae Park ASSERT_EQ(ret, 0); 2052baa489faSSeongJae Park ASSERT_EQ(buffer->cpages, npages); 2053baa489faSSeongJae Park m = buffer->mirror; 2054baa489faSSeongJae Park for (i = 0; i < npages; i++) 2055baa489faSSeongJae Park ASSERT_EQ(HMM_DMIRROR_PROT_WRITE, m[i]); 2056baa489faSSeongJae Park 2057baa489faSSeongJae Park hmm_buffer_free(buffer); 2058baa489faSSeongJae Park } 2059baa489faSSeongJae Park TEST_HARNESS_MAIN 2060