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
FIXTURE(hmm)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
FIXTURE_VARIANT(hmm)75baa489faSSeongJae Park FIXTURE_VARIANT(hmm)
76baa489faSSeongJae Park {
77baa489faSSeongJae Park int device_number;
78baa489faSSeongJae Park };
79baa489faSSeongJae Park
FIXTURE_VARIANT_ADD(hmm,hmm_device_private)80baa489faSSeongJae Park FIXTURE_VARIANT_ADD(hmm, hmm_device_private)
81baa489faSSeongJae Park {
82baa489faSSeongJae Park .device_number = HMM_PRIVATE_DEVICE_ONE,
83baa489faSSeongJae Park };
84baa489faSSeongJae Park
FIXTURE_VARIANT_ADD(hmm,hmm_device_coherent)85baa489faSSeongJae Park FIXTURE_VARIANT_ADD(hmm, hmm_device_coherent)
86baa489faSSeongJae Park {
87baa489faSSeongJae Park .device_number = HMM_COHERENCE_DEVICE_ONE,
88baa489faSSeongJae Park };
89baa489faSSeongJae Park
FIXTURE(hmm2)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
FIXTURE_VARIANT(hmm2)98baa489faSSeongJae Park FIXTURE_VARIANT(hmm2)
99baa489faSSeongJae Park {
100baa489faSSeongJae Park int device_number0;
101baa489faSSeongJae Park int device_number1;
102baa489faSSeongJae Park };
103baa489faSSeongJae Park
FIXTURE_VARIANT_ADD(hmm2,hmm2_device_private)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
FIXTURE_VARIANT_ADD(hmm2,hmm2_device_coherent)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
hmm_open(int unit)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
hmm_is_coherent_type(int dev_num)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
FIXTURE_SETUP(hmm)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
FIXTURE_SETUP(hmm2)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
FIXTURE_TEARDOWN(hmm)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
FIXTURE_TEARDOWN(hmm2)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
hmm_dmirror_cmd(int fd,unsigned long request,struct hmm_buffer * buffer,unsigned long npages)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
hmm_buffer_free(struct hmm_buffer * buffer)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 */
hmm_create_file(unsigned long size)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 */
hmm_random(void)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
hmm_nanosleep(unsigned int n)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
hmm_migrate_sys_to_dev(int fd,struct hmm_buffer * buffer,unsigned long npages)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
hmm_migrate_dev_to_sys(int fd,struct hmm_buffer * buffer,unsigned long npages)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 */
TEST_F(hmm,open_close)285baa489faSSeongJae Park TEST_F(hmm, open_close)
286baa489faSSeongJae Park {
287baa489faSSeongJae Park }
288baa489faSSeongJae Park
289baa489faSSeongJae Park /*
290baa489faSSeongJae Park * Read private anonymous memory.
291baa489faSSeongJae Park */
TEST_F(hmm,anon_read)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 */
TEST_F(hmm,anon_read_prot)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 */
TEST_F(hmm,anon_write)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 */
TEST_F(hmm,anon_write_prot)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 */
TEST_F(hmm,anon_write_child)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 */
TEST_F(hmm,anon_write_child_shared)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 */
TEST_F(hmm,anon_write_huge)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 */
file_read_ulong(char * file,const char * tag)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 */
TEST_F(hmm,anon_write_hugetlbfs)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 */
TEST_F(hmm,file_read)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 */
TEST_F(hmm,file_write)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 */
TEST_F(hmm,migrate)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 */
TEST_F(hmm,migrate_fault)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
TEST_F(hmm,migrate_release)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 */
TEST_F(hmm,migrate_shared)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 */
TEST_F(hmm2,migrate_mixed)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 */
TEST_F(hmm,migrate_multiple)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 */
TEST_F(hmm,anon_read_multiple)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
unmap_buffer(void * p)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 */
TEST_F(hmm,anon_teardown)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 */
TEST_F(hmm,mixedmap)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 */
TEST_F(hmm2,snapshot)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 */
TEST_F(hmm,compound)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 */
TEST_F(hmm2,double_map)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 */
TEST_F(hmm,exclusive)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
TEST_F(hmm,exclusive_mprotect)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 */
TEST_F(hmm,exclusive_cow)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
gup_test_exec(int gup_fd,unsigned long addr,int cmd,int npages,int size,int flags)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 */
TEST_F(hmm,hmm_gup_test)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 */
TEST_F(hmm,hmm_cow_in_device)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