2020-03-21 00:32:48

by Ralph Campbell

[permalink] [raw]
Subject: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

This series adds basic self tests for HMM and are intended for Jason
Gunthorpe's rdma tree which has a number of HMM patches applied.

Changes v7 -> v8:
Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
"Small hmm_range_fault() cleanups".
Applied a number of changes from Jason's comments.

Changes v6 -> v7:
Rebased to linux-5.6.0-rc6
Reverted back to just using mmu_interval_notifier_insert() and making
this series only introduce HMM self tests.

Changes v5 -> v6:
Rebased to linux-5.5.0-rc6
Refactored mmu interval notifier patches
Converted nouveau to use the new mmu interval notifier API

Changes v4 -> v5:
Added mmu interval notifier insert/remove/update callable from the
invalidate() callback
Updated HMM tests to use the new core interval notifier API

Changes v1 -> v4:
https://lore.kernel.org/linux-mm/[email protected]

Ralph Campbell (3):
mm/hmm/test: add selftest driver for HMM
mm/hmm/test: add selftests for HMM
MAINTAINERS: add HMM selftests

MAINTAINERS | 3 +
include/uapi/linux/test_hmm.h | 59 ++
lib/Kconfig.debug | 12 +
lib/Makefile | 1 +
lib/test_hmm.c | 1177 +++++++++++++++++++++
tools/testing/selftests/vm/.gitignore | 1 +
tools/testing/selftests/vm/Makefile | 3 +
tools/testing/selftests/vm/config | 2 +
tools/testing/selftests/vm/hmm-tests.c | 1353 ++++++++++++++++++++++++
tools/testing/selftests/vm/run_vmtests | 16 +
tools/testing/selftests/vm/test_hmm.sh | 97 ++
11 files changed, 2724 insertions(+)
create mode 100644 include/uapi/linux/test_hmm.h
create mode 100644 lib/test_hmm.c
create mode 100644 tools/testing/selftests/vm/hmm-tests.c
create mode 100755 tools/testing/selftests/vm/test_hmm.sh

--
2.20.1


2020-03-21 00:33:09

by Ralph Campbell

[permalink] [raw]
Subject: [PATCH v8 2/3] mm/hmm/test: add selftests for HMM

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

Signed-off-by: Ralph Campbell <[email protected]>
---
tools/testing/selftests/vm/.gitignore | 1 +
tools/testing/selftests/vm/Makefile | 3 +
tools/testing/selftests/vm/config | 2 +
tools/testing/selftests/vm/hmm-tests.c | 1353 ++++++++++++++++++++++++
tools/testing/selftests/vm/run_vmtests | 16 +
tools/testing/selftests/vm/test_hmm.sh | 97 ++
6 files changed, 1472 insertions(+)
create mode 100644 tools/testing/selftests/vm/hmm-tests.c
create mode 100755 tools/testing/selftests/vm/test_hmm.sh

diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore
index 31b3c98b6d34..3054565b3f07 100644
--- a/tools/testing/selftests/vm/.gitignore
+++ b/tools/testing/selftests/vm/.gitignore
@@ -14,3 +14,4 @@ virtual_address_range
gup_benchmark
va_128TBswitch
map_fixed_noreplace
+hmm-tests
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile
index 7f9a8a8c31da..3fadab99d991 100644
--- a/tools/testing/selftests/vm/Makefile
+++ b/tools/testing/selftests/vm/Makefile
@@ -7,6 +7,7 @@ CFLAGS = -Wall -I ../../../../usr/include $(EXTRA_CFLAGS)
LDLIBS = -lrt
TEST_GEN_FILES = compaction_test
TEST_GEN_FILES += gup_benchmark
+TEST_GEN_FILES += hmm-tests
TEST_GEN_FILES += hugepage-mmap
TEST_GEN_FILES += hugepage-shm
TEST_GEN_FILES += map_hugetlb
@@ -31,6 +32,8 @@ TEST_FILES := test_vmalloc.sh
KSFT_KHDR_INSTALL := 1
include ../lib.mk

+$(OUTPUT)/hmm-tests: LDLIBS += -lhugetlbfs -lpthread
+
$(OUTPUT)/userfaultfd: LDLIBS += -lpthread

$(OUTPUT)/mlock-random-test: LDLIBS += -lcap
diff --git a/tools/testing/selftests/vm/config b/tools/testing/selftests/vm/config
index 93b90a9b1eeb..6b64d61ec455 100644
--- a/tools/testing/selftests/vm/config
+++ b/tools/testing/selftests/vm/config
@@ -1,3 +1,5 @@
CONFIG_SYSVIPC=y
CONFIG_USERFAULTFD=y
CONFIG_TEST_VMALLOC=m
+CONFIG_DEVICE_PRIVATE=y
+CONFIG_TEST_HMM=y
diff --git a/tools/testing/selftests/vm/hmm-tests.c b/tools/testing/selftests/vm/hmm-tests.c
new file mode 100644
index 000000000000..033a12c7ab5b
--- /dev/null
+++ b/tools/testing/selftests/vm/hmm-tests.c
@@ -0,0 +1,1353 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HMM stands for Heterogeneous Memory Management, it is a helper layer inside
+ * the linux kernel to help device drivers mirror a process address space in
+ * the device. This allows the device to use the same address space which
+ * makes communication and data exchange a lot easier.
+ *
+ * This framework's sole purpose is to exercise various code paths inside
+ * the kernel to make sure that HMM performs as expected and to flush out any
+ * bugs.
+ */
+
+#include "../kselftest_harness.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <strings.h>
+#include <time.h>
+#include <pthread.h>
+#include <hugetlbfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <linux/test_hmm.h>
+
+struct hmm_buffer {
+ void *ptr;
+ void *mirror;
+ unsigned long size;
+ int fd;
+ uint64_t cpages;
+ uint64_t faults;
+};
+
+#define TWOMEG (1 << 21)
+#define HMM_BUFFER_SIZE (1024 << 12)
+#define HMM_PATH_MAX 64
+#define NTIMES 256
+
+#define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
+
+FIXTURE(hmm)
+{
+ int fd;
+ unsigned int page_size;
+ unsigned int page_shift;
+};
+
+FIXTURE(hmm2)
+{
+ int fd0;
+ int fd1;
+ unsigned int page_size;
+ unsigned int page_shift;
+};
+
+static int hmm_open(int unit)
+{
+ char pathname[HMM_PATH_MAX];
+ int fd;
+
+ snprintf(pathname, sizeof(pathname), "/dev/hmm_dmirror%d", unit);
+ fd = open(pathname, O_RDWR, 0);
+ if (fd < 0)
+ fprintf(stderr, "could not open hmm dmirror driver (%s)\n",
+ pathname);
+ return fd;
+}
+
+FIXTURE_SETUP(hmm)
+{
+ self->page_size = sysconf(_SC_PAGE_SIZE);
+ self->page_shift = ffs(self->page_size) - 1;
+
+ self->fd = hmm_open(0);
+ ASSERT_GE(self->fd, 0);
+}
+
+FIXTURE_SETUP(hmm2)
+{
+ self->page_size = sysconf(_SC_PAGE_SIZE);
+ self->page_shift = ffs(self->page_size) - 1;
+
+ self->fd0 = hmm_open(0);
+ ASSERT_GE(self->fd0, 0);
+ self->fd1 = hmm_open(1);
+ ASSERT_GE(self->fd1, 0);
+}
+
+FIXTURE_TEARDOWN(hmm)
+{
+ int ret = close(self->fd);
+
+ ASSERT_EQ(ret, 0);
+ self->fd = -1;
+}
+
+FIXTURE_TEARDOWN(hmm2)
+{
+ int ret = close(self->fd0);
+
+ ASSERT_EQ(ret, 0);
+ self->fd0 = -1;
+
+ ret = close(self->fd1);
+ ASSERT_EQ(ret, 0);
+ self->fd1 = -1;
+}
+
+static int hmm_dmirror_cmd(int fd,
+ unsigned long request,
+ struct hmm_buffer *buffer,
+ unsigned long npages)
+{
+ struct hmm_dmirror_cmd cmd;
+ int ret;
+
+ /* Simulate a device reading system memory. */
+ cmd.addr = (__u64)buffer->ptr;
+ cmd.ptr = (__u64)buffer->mirror;
+ cmd.npages = npages;
+
+ for (;;) {
+ ret = ioctl(fd, request, &cmd);
+ if (ret == 0)
+ break;
+ if (errno == EINTR)
+ continue;
+ return -errno;
+ }
+ buffer->cpages = cmd.cpages;
+ buffer->faults = cmd.faults;
+
+ return 0;
+}
+
+static void hmm_buffer_free(struct hmm_buffer *buffer)
+{
+ if (buffer == NULL)
+ return;
+
+ if (buffer->ptr)
+ munmap(buffer->ptr, buffer->size);
+ free(buffer->mirror);
+ free(buffer);
+}
+
+/*
+ * Create a temporary file that will be deleted on close.
+ */
+static int hmm_create_file(unsigned long size)
+{
+ char path[HMM_PATH_MAX];
+ int fd;
+
+ strcpy(path, "/tmp");
+ fd = open(path, O_TMPFILE | O_EXCL | O_RDWR, 0600);
+ if (fd >= 0) {
+ int r;
+
+ do {
+ r = ftruncate(fd, size);
+ } while (r == -1 && errno == EINTR);
+ if (!r)
+ return fd;
+ close(fd);
+ }
+ return -1;
+}
+
+/*
+ * Return a random unsigned number.
+ */
+static unsigned int hmm_random(void)
+{
+ static int fd = -1;
+ unsigned int r;
+
+ if (fd < 0) {
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s:%d failed to open /dev/urandom\n",
+ __FILE__, __LINE__);
+ return ~0U;
+ }
+ }
+ read(fd, &r, sizeof(r));
+ return r;
+}
+
+static void hmm_nanosleep(unsigned int n)
+{
+ struct timespec t;
+
+ t.tv_sec = 0;
+ t.tv_nsec = n;
+ nanosleep(&t, NULL);
+}
+
+/*
+ * Simple NULL test of device open/close.
+ */
+TEST_F(hmm, open_close)
+{
+}
+
+/*
+ * Read private anonymous memory.
+ */
+TEST_F(hmm, anon_read)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+ int val;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /*
+ * Initialize buffer in system memory but leave the first two pages
+ * zero (pte_none and pfn_zero).
+ */
+ i = 2 * self->page_size / sizeof(*ptr);
+ for (ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Set buffer permission to read-only. */
+ ret = mprotect(buffer->ptr, size, PROT_READ);
+ ASSERT_EQ(ret, 0);
+
+ /* Populate the CPU page table with a special zero page. */
+ val = *(int *)(buffer->ptr + self->page_size);
+ ASSERT_EQ(val, 0);
+
+ /* Simulate a device reading system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device read. */
+ ptr = buffer->mirror;
+ for (i = 0; i < 2 * self->page_size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], 0);
+ for (; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Read private anonymous memory which has been protected with
+ * mprotect() PROT_NONE.
+ */
+TEST_F(hmm, anon_read_prot)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Initialize mirror buffer so we can verify it isn't written. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = -i;
+
+ /* Protect buffer from reading. */
+ ret = mprotect(buffer->ptr, size, PROT_NONE);
+ ASSERT_EQ(ret, 0);
+
+ /* Simulate a device reading system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages);
+ ASSERT_EQ(ret, -EFAULT);
+
+ /* Allow CPU to read the buffer so we can check it. */
+ ret = mprotect(buffer->ptr, size, PROT_READ);
+ ASSERT_EQ(ret, 0);
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], -i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Write private anonymous memory.
+ */
+TEST_F(hmm, anon_write)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Write private anonymous memory which has been protected with
+ * mprotect() PROT_READ.
+ */
+TEST_F(hmm, anon_write_prot)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Simulate a device reading a zero page of memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, 1);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, 1);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, -EPERM);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], 0);
+
+ /* Now allow writing and see that the zero page is replaced. */
+ ret = mprotect(buffer->ptr, size, PROT_WRITE | PROT_READ);
+ ASSERT_EQ(ret, 0);
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Check that a device writing an anonymous private mapping
+ * will copy-on-write if a child process inherits the mapping.
+ */
+TEST_F(hmm, anon_write_child)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ pid_t pid;
+ int child_fd;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer->ptr so we can tell if it is written. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = -i;
+
+ pid = fork();
+ if (pid == -1)
+ ASSERT_EQ(pid, 0);
+ if (pid != 0) {
+ waitpid(pid, &ret, 0);
+ ASSERT_EQ(WIFEXITED(ret), 1);
+
+ /* Check that the parent's buffer did not change. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+ return;
+ }
+
+ /* Check that we see the parent's values. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], -i);
+
+ /* The child process needs its own mirror to its own mm. */
+ child_fd = hmm_open(0);
+ ASSERT_GE(child_fd, 0);
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], -i);
+
+ close(child_fd);
+ exit(0);
+}
+
+/*
+ * Check that a device writing an anonymous shared mapping
+ * will not copy-on-write if a child process inherits the mapping.
+ */
+TEST_F(hmm, anon_write_child_shared)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ pid_t pid;
+ int child_fd;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer->ptr so we can tell if it is written. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = -i;
+
+ pid = fork();
+ if (pid == -1)
+ ASSERT_EQ(pid, 0);
+ if (pid != 0) {
+ waitpid(pid, &ret, 0);
+ ASSERT_EQ(WIFEXITED(ret), 1);
+
+ /* Check that the parent's buffer did change. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], -i);
+ return;
+ }
+
+ /* Check that we see the parent's values. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], -i);
+
+ /* The child process needs its own mirror to its own mm. */
+ child_fd = hmm_open(0);
+ ASSERT_GE(child_fd, 0);
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(child_fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], -i);
+
+ close(child_fd);
+ exit(0);
+}
+
+/*
+ * Write private anonymous huge page.
+ */
+TEST_F(hmm, anon_write_huge)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ void *old_ptr;
+ void *map;
+ int *ptr;
+ int ret;
+
+ size = 2 * TWOMEG;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ size = TWOMEG;
+ npages = size >> self->page_shift;
+ map = (void *)ALIGN((uintptr_t)buffer->ptr, size);
+ ret = madvise(map, size, MADV_HUGEPAGE);
+ ASSERT_EQ(ret, 0);
+ old_ptr = buffer->ptr;
+ buffer->ptr = map;
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ buffer->ptr = old_ptr;
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Write huge TLBFS page.
+ */
+TEST_F(hmm, anon_write_hugetlbfs)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+ long pagesizes[4];
+ int n, idx;
+
+ /* Skip test if we can't allocate a hugetlbfs page. */
+
+ n = gethugepagesizes(pagesizes, 4);
+ if (n <= 0)
+ return;
+ for (idx = 0; --n > 0; ) {
+ if (pagesizes[n] < pagesizes[idx])
+ idx = n;
+ }
+ size = ALIGN(TWOMEG, pagesizes[idx]);
+ npages = size >> self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->ptr = get_hugepage_region(size, GHR_STRICT);
+ if (buffer->ptr == NULL) {
+ free(buffer);
+ return;
+ }
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ free_hugepage_region(buffer->ptr);
+ buffer->ptr = NULL;
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Read mmap'ed file memory.
+ */
+TEST_F(hmm, file_read)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+ int fd;
+ ssize_t len;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ fd = hmm_create_file(size);
+ ASSERT_GE(fd, 0);
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = fd;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ /* Write initial contents of the file. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+ len = pwrite(fd, buffer->mirror, size, 0);
+ ASSERT_EQ(len, size);
+ memset(buffer->mirror, 0, size);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ,
+ MAP_SHARED,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Simulate a device reading system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Write mmap'ed file memory.
+ */
+TEST_F(hmm, file_write)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+ int fd;
+ ssize_t len;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ fd = hmm_create_file(size);
+ ASSERT_GE(fd, 0);
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = fd;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize data that the device will write to buffer->ptr. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Simulate a device writing system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_WRITE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device wrote. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Check that the device also wrote the file. */
+ len = pread(fd, buffer->mirror, size, 0);
+ ASSERT_EQ(len, size);
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Migrate anonymous memory to device private memory.
+ */
+TEST_F(hmm, migrate)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Migrate memory to device. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Migrate anonymous memory to device private memory and fault it back to system
+ * memory.
+ */
+TEST_F(hmm, migrate_fault)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Migrate memory to device. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Fault pages back to system memory and check them. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Try to migrate various memory types to device private memory.
+ */
+TEST_F(hmm2, migrate_mixed)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ int *ptr;
+ unsigned char *p;
+ int ret;
+ int val;
+
+ npages = 6;
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ /* Reserve a range of addresses. */
+ buffer->ptr = mmap(NULL, size,
+ PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+ p = buffer->ptr;
+
+ /* Now try to migrate everything to device 1. */
+ ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, 6);
+
+ /* Punch a hole after the first page address. */
+ ret = munmap(buffer->ptr + self->page_size, self->page_size);
+ ASSERT_EQ(ret, 0);
+
+ /* We expect an error if the vma doesn't cover the range. */
+ ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 3);
+ ASSERT_EQ(ret, -EINVAL);
+
+ /* Page 2 will be a read-only zero page. */
+ ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size,
+ PROT_READ);
+ ASSERT_EQ(ret, 0);
+ ptr = (int *)(buffer->ptr + 2 * self->page_size);
+ val = *ptr + 3;
+ ASSERT_EQ(val, 3);
+
+ /* Page 3 will be read-only. */
+ ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
+ PROT_READ | PROT_WRITE);
+ ASSERT_EQ(ret, 0);
+ ptr = (int *)(buffer->ptr + 3 * self->page_size);
+ *ptr = val;
+ ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
+ PROT_READ);
+ ASSERT_EQ(ret, 0);
+
+ /* Page 4 will be read-write. */
+ ret = mprotect(buffer->ptr + 4 * self->page_size, self->page_size,
+ PROT_READ | PROT_WRITE);
+ ASSERT_EQ(ret, 0);
+ ptr = (int *)(buffer->ptr + 4 * self->page_size);
+ *ptr = val;
+
+ /* Page 5 won't be migrated to device 0 because it's on device 1. */
+ buffer->ptr = p + 5 * self->page_size;
+ ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_MIGRATE, buffer, 1);
+ ASSERT_EQ(ret, -ENOENT);
+ buffer->ptr = p;
+
+ /* Now try to migrate pages 2-3 to device 1. */
+ buffer->ptr = p + 2 * self->page_size;
+ ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 2);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, 2);
+ buffer->ptr = p;
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Migrate anonymous memory to device private memory and fault it back to system
+ * memory multiple times.
+ */
+TEST_F(hmm, migrate_multiple)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ unsigned long c;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ for (c = 0; c < NTIMES; c++) {
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Migrate memory to device. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_MIGRATE, buffer,
+ npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Fault pages back to system memory and check them. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ hmm_buffer_free(buffer);
+ }
+}
+
+/*
+ * Read anonymous memory multiple times.
+ */
+TEST_F(hmm, anon_read_multiple)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ unsigned long c;
+ int *ptr;
+ int ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ for (c = 0; c < NTIMES; c++) {
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i + c;
+
+ /* Simulate a device reading system memory. */
+ ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer,
+ npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i + c);
+
+ hmm_buffer_free(buffer);
+ }
+}
+
+void *unmap_buffer(void *p)
+{
+ struct hmm_buffer *buffer = p;
+
+ /* Delay for a bit and then unmap buffer while it is being read. */
+ hmm_nanosleep(hmm_random() % 32000);
+ munmap(buffer->ptr + buffer->size / 2, buffer->size / 2);
+ buffer->ptr = NULL;
+
+ return NULL;
+}
+
+/*
+ * Try reading anonymous memory while it is being unmapped.
+ */
+TEST_F(hmm, anon_teardown)
+{
+ unsigned long npages;
+ unsigned long size;
+ unsigned long c;
+ void *ret;
+
+ npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
+ ASSERT_NE(npages, 0);
+ size = npages << self->page_shift;
+
+ for (c = 0; c < NTIMES; ++c) {
+ pthread_t thread;
+ struct hmm_buffer *buffer;
+ unsigned long i;
+ int *ptr;
+ int rc;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(size);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i + c;
+
+ rc = pthread_create(&thread, NULL, unmap_buffer, buffer);
+ ASSERT_EQ(rc, 0);
+
+ /* Simulate a device reading system memory. */
+ rc = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ, buffer,
+ npages);
+ if (rc == 0) {
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror;
+ i < size / sizeof(*ptr);
+ ++i)
+ ASSERT_EQ(ptr[i], i + c);
+ }
+
+ pthread_join(thread, &ret);
+ hmm_buffer_free(buffer);
+ }
+}
+
+/*
+ * Test memory snapshot without faulting in pages accessed by the device.
+ */
+TEST_F(hmm2, snapshot)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ int *ptr;
+ unsigned char *p;
+ unsigned char *m;
+ int ret;
+ int val;
+
+ npages = 7;
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(npages);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ /* Reserve a range of addresses. */
+ buffer->ptr = mmap(NULL, size,
+ PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+ p = buffer->ptr;
+
+ /* Punch a hole after the first page address. */
+ ret = munmap(buffer->ptr + self->page_size, self->page_size);
+ ASSERT_EQ(ret, 0);
+
+ /* Page 2 will be read-only zero page. */
+ ret = mprotect(buffer->ptr + 2 * self->page_size, self->page_size,
+ PROT_READ);
+ ASSERT_EQ(ret, 0);
+ ptr = (int *)(buffer->ptr + 2 * self->page_size);
+ val = *ptr + 3;
+ ASSERT_EQ(val, 3);
+
+ /* Page 3 will be read-only. */
+ ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
+ PROT_READ | PROT_WRITE);
+ ASSERT_EQ(ret, 0);
+ ptr = (int *)(buffer->ptr + 3 * self->page_size);
+ *ptr = val;
+ ret = mprotect(buffer->ptr + 3 * self->page_size, self->page_size,
+ PROT_READ);
+ ASSERT_EQ(ret, 0);
+
+ /* Page 4-6 will be read-write. */
+ ret = mprotect(buffer->ptr + 4 * self->page_size, 3 * self->page_size,
+ PROT_READ | PROT_WRITE);
+ ASSERT_EQ(ret, 0);
+ ptr = (int *)(buffer->ptr + 4 * self->page_size);
+ *ptr = val;
+
+ /* Page 5 will be migrated to device 0. */
+ buffer->ptr = p + 5 * self->page_size;
+ ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_MIGRATE, buffer, 1);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, 1);
+
+ /* Page 6 will be migrated to device 1. */
+ buffer->ptr = p + 6 * self->page_size;
+ ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_MIGRATE, buffer, 1);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, 1);
+
+ /* Simulate a device snapshotting CPU pagetables. */
+ buffer->ptr = p;
+ ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_SNAPSHOT, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+
+ /* Check what the device saw. */
+ m = buffer->mirror;
+ ASSERT_EQ(m[0], HMM_DMIRROR_PROT_ERROR);
+ ASSERT_EQ(m[1], HMM_DMIRROR_PROT_NONE);
+ ASSERT_EQ(m[2], HMM_DMIRROR_PROT_ZERO | HMM_DMIRROR_PROT_READ);
+ ASSERT_EQ(m[3], HMM_DMIRROR_PROT_READ);
+ ASSERT_EQ(m[4], HMM_DMIRROR_PROT_WRITE);
+ ASSERT_EQ(m[5], HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL |
+ HMM_DMIRROR_PROT_WRITE);
+ ASSERT_EQ(m[6], HMM_DMIRROR_PROT_NONE);
+
+ hmm_buffer_free(buffer);
+}
+
+/*
+ * Test two devices reading the same memory (double mapped).
+ */
+TEST_F(hmm2, double_map)
+{
+ struct hmm_buffer *buffer;
+ unsigned long npages;
+ unsigned long size;
+ unsigned long i;
+ int *ptr;
+ int ret;
+
+ npages = 6;
+ size = npages << self->page_shift;
+
+ buffer = malloc(sizeof(*buffer));
+ ASSERT_NE(buffer, NULL);
+
+ buffer->fd = -1;
+ buffer->size = size;
+ buffer->mirror = malloc(npages);
+ ASSERT_NE(buffer->mirror, NULL);
+
+ /* Reserve a range of addresses. */
+ buffer->ptr = mmap(NULL, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ buffer->fd, 0);
+ ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+ /* Initialize buffer in system memory. */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ptr[i] = i;
+
+ /* Make region read-only. */
+ ret = mprotect(buffer->ptr, size, PROT_READ);
+ ASSERT_EQ(ret, 0);
+
+ /* Simulate device 0 reading system memory. */
+ ret = hmm_dmirror_cmd(self->fd0, HMM_DMIRROR_READ, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Simulate device 1 reading system memory. */
+ ret = hmm_dmirror_cmd(self->fd1, HMM_DMIRROR_READ, buffer, npages);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(buffer->cpages, npages);
+ ASSERT_EQ(buffer->faults, 1);
+
+ /* Check what the device read. */
+ for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i);
+
+ /* Punch a hole after the first page address. */
+ ret = munmap(buffer->ptr + self->page_size, self->page_size);
+ ASSERT_EQ(ret, 0);
+
+ hmm_buffer_free(buffer);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests
index f33714843198..5e82b2574d54 100755
--- a/tools/testing/selftests/vm/run_vmtests
+++ b/tools/testing/selftests/vm/run_vmtests
@@ -270,4 +270,20 @@ else
exitcode=1
fi

+echo "------------------------------------"
+echo "running HMM smoke test"
+echo "------------------------------------"
+./test_hmm.sh smoke
+ret_val=$?
+
+if [ $ret_val -eq 0 ]; then
+ echo "[PASS]"
+elif [ $ret_val -eq $ksft_skip ]; then
+ echo "[SKIP]"
+ exitcode=$ksft_skip
+else
+ echo "[FAIL]"
+ exitcode=1
+fi
+
exit $exitcode
diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
new file mode 100755
index 000000000000..461e4a99a362
--- /dev/null
+++ b/tools/testing/selftests/vm/test_hmm.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2018 Uladzislau Rezki (Sony) <[email protected]>
+#
+# This is a test script for the kernel test driver to analyse vmalloc
+# allocator. Therefore it is just a kernel module loader. You can specify
+# and pass different parameters in order to:
+# a) analyse performance of vmalloc allocations;
+# b) stressing and stability check of vmalloc subsystem.
+
+TEST_NAME="test_hmm"
+DRIVER="test_hmm"
+
+# 1 if fails
+exitcode=1
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+check_test_requirements()
+{
+ uid=$(id -u)
+ if [ $uid -ne 0 ]; then
+ echo "$0: Must be run as root"
+ exit $ksft_skip
+ fi
+
+ if ! which modprobe > /dev/null 2>&1; then
+ echo "$0: You need modprobe installed"
+ exit $ksft_skip
+ fi
+
+ if ! modinfo $DRIVER > /dev/null 2>&1; then
+ echo "$0: You must have the following enabled in your kernel:"
+ echo "CONFIG_TEST_HMM=m"
+ exit $ksft_skip
+ fi
+}
+
+load_driver()
+{
+ modprobe $DRIVER > /dev/null 2>&1
+ if [ $? == 0 ]; then
+ major=$(awk "\$2==\"HMM_DMIRROR\" {print \$1}" /proc/devices)
+ mknod /dev/hmm_dmirror0 c $major 0
+ mknod /dev/hmm_dmirror1 c $major 1
+ fi
+}
+
+unload_driver()
+{
+ modprobe -r $DRIVER > /dev/null 2>&1
+ rm -f /dev/hmm_dmirror?
+}
+
+run_smoke()
+{
+ echo "Running smoke test. Note, this test provides basic coverage."
+
+ load_driver
+ ./hmm-tests
+ unload_driver
+}
+
+usage()
+{
+ echo -n "Usage: $0"
+ echo
+ echo "Example usage:"
+ echo
+ echo "# Shows help message"
+ echo "./${TEST_NAME}.sh"
+ echo
+ echo "# Smoke testing"
+ echo "./${TEST_NAME}.sh smoke"
+ echo
+ exit 0
+}
+
+function run_test()
+{
+ if [ $# -eq 0 ]; then
+ usage
+ else
+ if [ "$1" = "smoke" ]; then
+ run_smoke
+ else
+ usage
+ fi
+ fi
+}
+
+check_test_requirements
+run_test $@
+
+exit 0
--
2.20.1

2020-03-21 09:02:02

by Leon Romanovsky

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> This series adds basic self tests for HMM and are intended for Jason
> Gunthorpe's rdma tree which has a number of HMM patches applied.
>
> Changes v7 -> v8:
> Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
> "Small hmm_range_fault() cleanups".
> Applied a number of changes from Jason's comments.
>
> Changes v6 -> v7:
> Rebased to linux-5.6.0-rc6
> Reverted back to just using mmu_interval_notifier_insert() and making
> this series only introduce HMM self tests.
>
> Changes v5 -> v6:
> Rebased to linux-5.5.0-rc6
> Refactored mmu interval notifier patches
> Converted nouveau to use the new mmu interval notifier API
>
> Changes v4 -> v5:
> Added mmu interval notifier insert/remove/update callable from the
> invalidate() callback
> Updated HMM tests to use the new core interval notifier API
>
> Changes v1 -> v4:
> https://lore.kernel.org/linux-mm/[email protected]
>
> Ralph Campbell (3):
> mm/hmm/test: add selftest driver for HMM
> mm/hmm/test: add selftests for HMM
> MAINTAINERS: add HMM selftests
>
> MAINTAINERS | 3 +
> include/uapi/linux/test_hmm.h | 59 ++

Isn't UAPI folder supposed to be for user-visible interfaces that follow
the rule of non-breaking user space and not for selftests?

Thanks

2020-03-21 14:44:27

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> This series adds basic self tests for HMM and are intended for Jason
> Gunthorpe's rdma tree which has a number of HMM patches applied.

We are at v8 of this series and noboy from the selftests land has
commented, can someone help?

Jason

2020-03-21 17:29:27

by Ralph Campbell

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM


On 3/21/20 2:00 AM, Leon Romanovsky wrote:
> On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
>> This series adds basic self tests for HMM and are intended for Jason
>> Gunthorpe's rdma tree which has a number of HMM patches applied.
>>
>> Changes v7 -> v8:
>> Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
>> "Small hmm_range_fault() cleanups".
>> Applied a number of changes from Jason's comments.
>>
>> Changes v6 -> v7:
>> Rebased to linux-5.6.0-rc6
>> Reverted back to just using mmu_interval_notifier_insert() and making
>> this series only introduce HMM self tests.
>>
>> Changes v5 -> v6:
>> Rebased to linux-5.5.0-rc6
>> Refactored mmu interval notifier patches
>> Converted nouveau to use the new mmu interval notifier API
>>
>> Changes v4 -> v5:
>> Added mmu interval notifier insert/remove/update callable from the
>> invalidate() callback
>> Updated HMM tests to use the new core interval notifier API
>>
>> Changes v1 -> v4:
>> https://lore.kernel.org/linux-mm/[email protected]
>>
>> Ralph Campbell (3):
>> mm/hmm/test: add selftest driver for HMM
>> mm/hmm/test: add selftests for HMM
>> MAINTAINERS: add HMM selftests
>>
>> MAINTAINERS | 3 +
>> include/uapi/linux/test_hmm.h | 59 ++
>
> Isn't UAPI folder supposed to be for user-visible interfaces that follow
> the rule of non-breaking user space and not for selftests?
>
> Thanks
>

Most of the other kernel module tests seem to invoke the test as part of the
module load/init. I'm open to moving it if there is a more appropriate location.

2020-03-21 21:56:06

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Sat, Mar 21, 2020 at 10:27:46AM -0700, Ralph Campbell wrote:
>
> On 3/21/20 2:00 AM, Leon Romanovsky wrote:
> > On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> > > This series adds basic self tests for HMM and are intended for Jason
> > > Gunthorpe's rdma tree which has a number of HMM patches applied.
> > >
> > > Changes v7 -> v8:
> > > Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
> > > "Small hmm_range_fault() cleanups".
> > > Applied a number of changes from Jason's comments.
> > >
> > > Changes v6 -> v7:
> > > Rebased to linux-5.6.0-rc6
> > > Reverted back to just using mmu_interval_notifier_insert() and making
> > > this series only introduce HMM self tests.
> > >
> > > Changes v5 -> v6:
> > > Rebased to linux-5.5.0-rc6
> > > Refactored mmu interval notifier patches
> > > Converted nouveau to use the new mmu interval notifier API
> > >
> > > Changes v4 -> v5:
> > > Added mmu interval notifier insert/remove/update callable from the
> > > invalidate() callback
> > > Updated HMM tests to use the new core interval notifier API
> > >
> > > Changes v1 -> v4:
> > > https://lore.kernel.org/linux-mm/[email protected]
> > >
> > > Ralph Campbell (3):
> > > mm/hmm/test: add selftest driver for HMM
> > > mm/hmm/test: add selftests for HMM
> > > MAINTAINERS: add HMM selftests
> > >
> > > MAINTAINERS | 3 +
> > > include/uapi/linux/test_hmm.h | 59 ++
> >
> > Isn't UAPI folder supposed to be for user-visible interfaces that follow
> > the rule of non-breaking user space and not for selftests?
> >
> > Thanks
> >
>
> Most of the other kernel module tests seem to invoke the test as part of the
> module load/init. I'm open to moving it if there is a more appropriate location.

Is it even possible to create a user mm_struct and put crazy things in
it soley from a kernel module?

Jason

2020-03-22 08:11:19

by Leon Romanovsky

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Sat, Mar 21, 2020 at 06:55:05PM -0300, Jason Gunthorpe wrote:
> On Sat, Mar 21, 2020 at 10:27:46AM -0700, Ralph Campbell wrote:
> >
> > On 3/21/20 2:00 AM, Leon Romanovsky wrote:
> > > On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> > > > This series adds basic self tests for HMM and are intended for Jason
> > > > Gunthorpe's rdma tree which has a number of HMM patches applied.
> > > >
> > > > Changes v7 -> v8:
> > > > Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
> > > > "Small hmm_range_fault() cleanups".
> > > > Applied a number of changes from Jason's comments.
> > > >
> > > > Changes v6 -> v7:
> > > > Rebased to linux-5.6.0-rc6
> > > > Reverted back to just using mmu_interval_notifier_insert() and making
> > > > this series only introduce HMM self tests.
> > > >
> > > > Changes v5 -> v6:
> > > > Rebased to linux-5.5.0-rc6
> > > > Refactored mmu interval notifier patches
> > > > Converted nouveau to use the new mmu interval notifier API
> > > >
> > > > Changes v4 -> v5:
> > > > Added mmu interval notifier insert/remove/update callable from the
> > > > invalidate() callback
> > > > Updated HMM tests to use the new core interval notifier API
> > > >
> > > > Changes v1 -> v4:
> > > > https://lore.kernel.org/linux-mm/[email protected]
> > > >
> > > > Ralph Campbell (3):
> > > > mm/hmm/test: add selftest driver for HMM
> > > > mm/hmm/test: add selftests for HMM
> > > > MAINTAINERS: add HMM selftests
> > > >
> > > > MAINTAINERS | 3 +
> > > > include/uapi/linux/test_hmm.h | 59 ++
> > >
> > > Isn't UAPI folder supposed to be for user-visible interfaces that follow
> > > the rule of non-breaking user space and not for selftests?
> > >
> > > Thanks
> > >
> >
> > Most of the other kernel module tests seem to invoke the test as part of the
> > module load/init. I'm open to moving it if there is a more appropriate location.
>
> Is it even possible to create a user mm_struct and put crazy things in
> it soley from a kernel module?

I didn't look very closely of what Ralph did in his patchsets, but from
what I know, if you want in-kernel interface, you use in-kernel module,
if you want to test user visible uapi, you write application. You don't
create new UAPI just to test something in the kernel.

Can kunit help here?
https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html

Thanks

>
> Jason
>

2020-03-23 18:22:36

by Ralph Campbell

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM


On 3/22/20 1:10 AM, Leon Romanovsky wrote:
> On Sat, Mar 21, 2020 at 06:55:05PM -0300, Jason Gunthorpe wrote:
>> On Sat, Mar 21, 2020 at 10:27:46AM -0700, Ralph Campbell wrote:
>>>
>>> On 3/21/20 2:00 AM, Leon Romanovsky wrote:
>>>> On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
>>>>> This series adds basic self tests for HMM and are intended for Jason
>>>>> Gunthorpe's rdma tree which has a number of HMM patches applied.
>>>>>
>>>>> Changes v7 -> v8:
>>>>> Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
>>>>> "Small hmm_range_fault() cleanups".
>>>>> Applied a number of changes from Jason's comments.
>>>>>
>>>>> Changes v6 -> v7:
>>>>> Rebased to linux-5.6.0-rc6
>>>>> Reverted back to just using mmu_interval_notifier_insert() and making
>>>>> this series only introduce HMM self tests.
>>>>>
>>>>> Changes v5 -> v6:
>>>>> Rebased to linux-5.5.0-rc6
>>>>> Refactored mmu interval notifier patches
>>>>> Converted nouveau to use the new mmu interval notifier API
>>>>>
>>>>> Changes v4 -> v5:
>>>>> Added mmu interval notifier insert/remove/update callable from the
>>>>> invalidate() callback
>>>>> Updated HMM tests to use the new core interval notifier API
>>>>>
>>>>> Changes v1 -> v4:
>>>>> https://lore.kernel.org/linux-mm/[email protected]
>>>>>
>>>>> Ralph Campbell (3):
>>>>> mm/hmm/test: add selftest driver for HMM
>>>>> mm/hmm/test: add selftests for HMM
>>>>> MAINTAINERS: add HMM selftests
>>>>>
>>>>> MAINTAINERS | 3 +
>>>>> include/uapi/linux/test_hmm.h | 59 ++
>>>>
>>>> Isn't UAPI folder supposed to be for user-visible interfaces that follow
>>>> the rule of non-breaking user space and not for selftests?
>>>>
>>>> Thanks
>>>>
>>>
>>> Most of the other kernel module tests seem to invoke the test as part of the
>>> module load/init. I'm open to moving it if there is a more appropriate location.
>>
>> Is it even possible to create a user mm_struct and put crazy things in
>> it soley from a kernel module?
>
> I didn't look very closely of what Ralph did in his patchsets, but from
> what I know, if you want in-kernel interface, you use in-kernel module,
> if you want to test user visible uapi, you write application. You don't
> create new UAPI just to test something in the kernel.
>
> Can kunit help here?
> https://www.kernel.org/doc/html/latest/dev-tools/kunit/index.html
>
> Thanks
>
>>
>> Jason

The tests are intended to cover hmm_range_fault() and the migrate_vma_setup(),
migrate_vma_pages(), and migrate_vma_finalize() kernel functions that a device
driver can call to initialize hardware that has its own page tables.
An example is a GPU where the code on the GPU sees the same address space as
code running on the host CPU. This means the test has to have a user process
to create a user process address space and a device driver to simulate some
real device driver. The UAPI is for the user level test program to tell the
kernel module test driver what to do and return results.
The complexity is all around maintaining coherent copies of the user process
page tables while hardware and CPUs are accessing the same physical addresses.
The pages are not pinned as with most I/O so system activity like pagein/pageout,
LRU page reclaim, compaction, and the process calling functions like
mmap(), mprotect(), madvise(), fork(), etc. all update the CPU and device page
tables and would be very hard to duplicate in a kernel level only KUNIT style
of test.

2020-03-23 18:27:14

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Sun, Mar 22, 2020 at 10:10:38AM +0200, Leon Romanovsky wrote:
> On Sat, Mar 21, 2020 at 06:55:05PM -0300, Jason Gunthorpe wrote:
> > On Sat, Mar 21, 2020 at 10:27:46AM -0700, Ralph Campbell wrote:
> > >
> > > On 3/21/20 2:00 AM, Leon Romanovsky wrote:
> > > > On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> > > > > This series adds basic self tests for HMM and are intended for Jason
> > > > > Gunthorpe's rdma tree which has a number of HMM patches applied.
> > > > >
> > > > > Changes v7 -> v8:
> > > > > Rebased to Jason's rdma/hmm tree, plus Jason's 6 patch series
> > > > > "Small hmm_range_fault() cleanups".
> > > > > Applied a number of changes from Jason's comments.
> > > > >
> > > > > Changes v6 -> v7:
> > > > > Rebased to linux-5.6.0-rc6
> > > > > Reverted back to just using mmu_interval_notifier_insert() and making
> > > > > this series only introduce HMM self tests.
> > > > >
> > > > > Changes v5 -> v6:
> > > > > Rebased to linux-5.5.0-rc6
> > > > > Refactored mmu interval notifier patches
> > > > > Converted nouveau to use the new mmu interval notifier API
> > > > >
> > > > > Changes v4 -> v5:
> > > > > Added mmu interval notifier insert/remove/update callable from the
> > > > > invalidate() callback
> > > > > Updated HMM tests to use the new core interval notifier API
> > > > >
> > > > > Changes v1 -> v4:
> > > > > https://lore.kernel.org/linux-mm/[email protected]
> > > > >
> > > > > Ralph Campbell (3):
> > > > > mm/hmm/test: add selftest driver for HMM
> > > > > mm/hmm/test: add selftests for HMM
> > > > > MAINTAINERS: add HMM selftests
> > > > >
> > > > > MAINTAINERS | 3 +
> > > > > include/uapi/linux/test_hmm.h | 59 ++
> > > >
> > > > Isn't UAPI folder supposed to be for user-visible interfaces that follow
> > > > the rule of non-breaking user space and not for selftests?
> > > >
> > > > Thanks
> > > >
> > >
> > > Most of the other kernel module tests seem to invoke the test as part of the
> > > module load/init. I'm open to moving it if there is a more appropriate location.
> >
> > Is it even possible to create a user mm_struct and put crazy things in
> > it soley from a kernel module?
>
> I didn't look very closely of what Ralph did in his patchsets, but from
> what I know, if you want in-kernel interface, you use in-kernel module,
> if you want to test user visible uapi, you write application. You don't
> create new UAPI just to test something in the kernel.

That works fine as long as the in-kernel interfaces don't require user
created objects like mm_struct and vmas, which is the case here.

So there must be some special uAPI between the kerne/user to make it
work.

Jason

2020-04-16 00:12:31

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> This series adds basic self tests for HMM and are intended for Jason
> Gunthorpe's rdma tree which has a number of HMM patches applied.

Here are some hunks I noticed while testing this:

--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2201,7 +2201,8 @@ config TEST_MEMINIT

config TEST_HMM
tristate "Test HMM (Heterogeneous Memory Management)"
- depends on DEVICE_PRIVATE
+ depends on TRANSPARENT_HUGEPAGE
+ select DEVICE_PRIVATE
select HMM_MIRROR
select MMU_NOTIFIER
help

It fails testing if TRANSPARENT_HUGEPAGE is not on

@@ -1097,6 +1071,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
spin_lock_init(&mdevice->lock);

cdev_init(&mdevice->cdevice, &dmirror_fops);
+ mdevice->cdevice.owner = THIS_MODULE;
ret = cdev_add(&mdevice->cdevice, dev, 1);
if (ret)
return ret;

The use of cdev without a struct device is super weird, but it still
needs this

diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
index 461e4a99a362cf..0647b525a62564 100755
--- a/tools/testing/selftests/vm/test_hmm.sh
+++ b/tools/testing/selftests/vm/test_hmm.sh
@@ -59,7 +59,7 @@ run_smoke()
echo "Running smoke test. Note, this test provides basic coverage."

load_driver
- ./hmm-tests
+ $(dirname "${BASH_SOURCE[0]}")/hmm-tests
unload_driver
}

Make it runnably reliably

Jason

2020-04-16 00:28:03

by Ralph Campbell

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM


On 4/15/20 7:41 AM, Jason Gunthorpe wrote:
> On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
>> This series adds basic self tests for HMM and are intended for Jason
>> Gunthorpe's rdma tree which has a number of HMM patches applied.
>
> Here are some hunks I noticed while testing this:
>
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -2201,7 +2201,8 @@ config TEST_MEMINIT
>
> config TEST_HMM
> tristate "Test HMM (Heterogeneous Memory Management)"
> - depends on DEVICE_PRIVATE
> + depends on TRANSPARENT_HUGEPAGE
> + select DEVICE_PRIVATE
> select HMM_MIRROR
> select MMU_NOTIFIER
> help
>
> It fails testing if TRANSPARENT_HUGEPAGE is not on
>
> @@ -1097,6 +1071,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
> spin_lock_init(&mdevice->lock);
>
> cdev_init(&mdevice->cdevice, &dmirror_fops);
> + mdevice->cdevice.owner = THIS_MODULE;
> ret = cdev_add(&mdevice->cdevice, dev, 1);
> if (ret)
> return ret;
>
> The use of cdev without a struct device is super weird, but it still
> needs this
>
> diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
> index 461e4a99a362cf..0647b525a62564 100755
> --- a/tools/testing/selftests/vm/test_hmm.sh
> +++ b/tools/testing/selftests/vm/test_hmm.sh
> @@ -59,7 +59,7 @@ run_smoke()
> echo "Running smoke test. Note, this test provides basic coverage."
>
> load_driver
> - ./hmm-tests
> + $(dirname "${BASH_SOURCE[0]}")/hmm-tests
> unload_driver
> }
>
> Make it runnably reliably
>
> Jason

Thanks for the fixes. I'll apply these and send a v9.
I will also add missing calls to release_mem_region() to free the reserved device private
addresses.

2020-04-16 01:02:26

by Jason Gunthorpe

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Wed, Apr 15, 2020 at 10:29:52PM +0300, Leon Romanovsky wrote:
> On Wed, Apr 15, 2020 at 10:28:23AM -0700, Ralph Campbell wrote:
> >
> > On 4/15/20 7:41 AM, Jason Gunthorpe wrote:
> > > On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> > > > This series adds basic self tests for HMM and are intended for Jason
> > > > Gunthorpe's rdma tree which has a number of HMM patches applied.
> > >
> > > Here are some hunks I noticed while testing this:
> > >
> > > +++ b/lib/Kconfig.debug
> > > @@ -2201,7 +2201,8 @@ config TEST_MEMINIT
> > > config TEST_HMM
> > > tristate "Test HMM (Heterogeneous Memory Management)"
> > > - depends on DEVICE_PRIVATE
> > > + depends on TRANSPARENT_HUGEPAGE
> > > + select DEVICE_PRIVATE
> > > select HMM_MIRROR
> > > select MMU_NOTIFIER
> > > help
> > >
> > > It fails testing if TRANSPARENT_HUGEPAGE is not on
> > >
> > > @@ -1097,6 +1071,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
> > > spin_lock_init(&mdevice->lock);
> > > cdev_init(&mdevice->cdevice, &dmirror_fops);
> > > + mdevice->cdevice.owner = THIS_MODULE;
> > > ret = cdev_add(&mdevice->cdevice, dev, 1);
> > > if (ret)
> > > return ret;
> > >
> > > The use of cdev without a struct device is super weird, but it still
> > > needs this
> > >
> > > diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
> > > index 461e4a99a362cf..0647b525a62564 100755
> > > +++ b/tools/testing/selftests/vm/test_hmm.sh
> > > @@ -59,7 +59,7 @@ run_smoke()
> > > echo "Running smoke test. Note, this test provides basic coverage."
> > > load_driver
> > > - ./hmm-tests
> > > + $(dirname "${BASH_SOURCE[0]}")/hmm-tests
> > > unload_driver
> > > }
> > >
> > > Make it runnably reliably
> > >
> > > Jason
> >
> > Thanks for the fixes. I'll apply these and send a v9.
> > I will also add missing calls to release_mem_region() to free the reserved device private
> > addresses.
>
> If you decide to ignore my request to avoid addition of special header
> file to UAPI, at least don't copy and install that file without some
> special CONFIG option (TEST_HMM ???) requested by the users. It also
> will be good to get Acked-by on this change from HMM people.
>
> However, I still think that include/uapi/linux/test_hmm.h opens
> pandora box of having UAPI files without real promise to keep it
> backward compatible.

It would be nice if we could put the header outside the uapi
directory and outside the install machinery

Maybe for now hackery some relative include like
#include "../../../lib/hmm_test_uapi.h"

?

I don't see any sane way to avoid the dedicate module and special
ioctl though.

Jason

2020-04-16 01:02:51

by Ralph Campbell

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM


On 4/15/20 12:29 PM, Leon Romanovsky wrote:
> On Wed, Apr 15, 2020 at 10:28:23AM -0700, Ralph Campbell wrote:
>>
>> On 4/15/20 7:41 AM, Jason Gunthorpe wrote:
>>> On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
>>>> This series adds basic self tests for HMM and are intended for Jason
>>>> Gunthorpe's rdma tree which has a number of HMM patches applied.
>>>
>>> Here are some hunks I noticed while testing this:
>>>
>>> --- a/lib/Kconfig.debug
>>> +++ b/lib/Kconfig.debug
>>> @@ -2201,7 +2201,8 @@ config TEST_MEMINIT
>>> config TEST_HMM
>>> tristate "Test HMM (Heterogeneous Memory Management)"
>>> - depends on DEVICE_PRIVATE
>>> + depends on TRANSPARENT_HUGEPAGE
>>> + select DEVICE_PRIVATE
>>> select HMM_MIRROR
>>> select MMU_NOTIFIER
>>> help
>>>
>>> It fails testing if TRANSPARENT_HUGEPAGE is not on
>>>
>>> @@ -1097,6 +1071,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
>>> spin_lock_init(&mdevice->lock);
>>> cdev_init(&mdevice->cdevice, &dmirror_fops);
>>> + mdevice->cdevice.owner = THIS_MODULE;
>>> ret = cdev_add(&mdevice->cdevice, dev, 1);
>>> if (ret)
>>> return ret;
>>>
>>> The use of cdev without a struct device is super weird, but it still
>>> needs this
>>>
>>> diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
>>> index 461e4a99a362cf..0647b525a62564 100755
>>> --- a/tools/testing/selftests/vm/test_hmm.sh
>>> +++ b/tools/testing/selftests/vm/test_hmm.sh
>>> @@ -59,7 +59,7 @@ run_smoke()
>>> echo "Running smoke test. Note, this test provides basic coverage."
>>> load_driver
>>> - ./hmm-tests
>>> + $(dirname "${BASH_SOURCE[0]}")/hmm-tests
>>> unload_driver
>>> }
>>>
>>> Make it runnably reliably
>>>
>>> Jason
>>
>> Thanks for the fixes. I'll apply these and send a v9.
>> I will also add missing calls to release_mem_region() to free the reserved device private
>> addresses.
>
> If you decide to ignore my request to avoid addition of special header
> file to UAPI, at least don't copy and install that file without some
> special CONFIG option (TEST_HMM ???) requested by the users. It also
> will be good to get Acked-by on this change from HMM people.
>
> However, I still think that include/uapi/linux/test_hmm.h opens
> pandora box of having UAPI files without real promise to keep it
> backward compatible.
>
> Thanks

I think that is a valid point. I would expect the test<->driver UAPI to track the kernel
version since the sources are "released" together. I suppose a version number could be
included in the request structure to handle mismatch driver and test program but that
may be overkill.
Are you suggesting that include/linux/test_hmm.h is a better location?

2020-04-16 01:03:59

by Leon Romanovsky

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Wed, Apr 15, 2020 at 10:28:23AM -0700, Ralph Campbell wrote:
>
> On 4/15/20 7:41 AM, Jason Gunthorpe wrote:
> > On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> > > This series adds basic self tests for HMM and are intended for Jason
> > > Gunthorpe's rdma tree which has a number of HMM patches applied.
> >
> > Here are some hunks I noticed while testing this:
> >
> > --- a/lib/Kconfig.debug
> > +++ b/lib/Kconfig.debug
> > @@ -2201,7 +2201,8 @@ config TEST_MEMINIT
> > config TEST_HMM
> > tristate "Test HMM (Heterogeneous Memory Management)"
> > - depends on DEVICE_PRIVATE
> > + depends on TRANSPARENT_HUGEPAGE
> > + select DEVICE_PRIVATE
> > select HMM_MIRROR
> > select MMU_NOTIFIER
> > help
> >
> > It fails testing if TRANSPARENT_HUGEPAGE is not on
> >
> > @@ -1097,6 +1071,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
> > spin_lock_init(&mdevice->lock);
> > cdev_init(&mdevice->cdevice, &dmirror_fops);
> > + mdevice->cdevice.owner = THIS_MODULE;
> > ret = cdev_add(&mdevice->cdevice, dev, 1);
> > if (ret)
> > return ret;
> >
> > The use of cdev without a struct device is super weird, but it still
> > needs this
> >
> > diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
> > index 461e4a99a362cf..0647b525a62564 100755
> > --- a/tools/testing/selftests/vm/test_hmm.sh
> > +++ b/tools/testing/selftests/vm/test_hmm.sh
> > @@ -59,7 +59,7 @@ run_smoke()
> > echo "Running smoke test. Note, this test provides basic coverage."
> > load_driver
> > - ./hmm-tests
> > + $(dirname "${BASH_SOURCE[0]}")/hmm-tests
> > unload_driver
> > }
> >
> > Make it runnably reliably
> >
> > Jason
>
> Thanks for the fixes. I'll apply these and send a v9.
> I will also add missing calls to release_mem_region() to free the reserved device private
> addresses.

If you decide to ignore my request to avoid addition of special header
file to UAPI, at least don't copy and install that file without some
special CONFIG option (TEST_HMM ???) requested by the users. It also
will be good to get Acked-by on this change from HMM people.

However, I still think that include/uapi/linux/test_hmm.h opens
pandora box of having UAPI files without real promise to keep it
backward compatible.

Thanks

>

2020-04-16 01:05:21

by Leon Romanovsky

[permalink] [raw]
Subject: Re: [PATCH v8 0/3] mm/hmm/test: add self tests for HMM

On Wed, Apr 15, 2020 at 12:39:45PM -0700, Ralph Campbell wrote:
>
> On 4/15/20 12:29 PM, Leon Romanovsky wrote:
> > On Wed, Apr 15, 2020 at 10:28:23AM -0700, Ralph Campbell wrote:
> > >
> > > On 4/15/20 7:41 AM, Jason Gunthorpe wrote:
> > > > On Fri, Mar 20, 2020 at 05:31:05PM -0700, Ralph Campbell wrote:
> > > > > This series adds basic self tests for HMM and are intended for Jason
> > > > > Gunthorpe's rdma tree which has a number of HMM patches applied.
> > > >
> > > > Here are some hunks I noticed while testing this:
> > > >
> > > > --- a/lib/Kconfig.debug
> > > > +++ b/lib/Kconfig.debug
> > > > @@ -2201,7 +2201,8 @@ config TEST_MEMINIT
> > > > config TEST_HMM
> > > > tristate "Test HMM (Heterogeneous Memory Management)"
> > > > - depends on DEVICE_PRIVATE
> > > > + depends on TRANSPARENT_HUGEPAGE
> > > > + select DEVICE_PRIVATE
> > > > select HMM_MIRROR
> > > > select MMU_NOTIFIER
> > > > help
> > > >
> > > > It fails testing if TRANSPARENT_HUGEPAGE is not on
> > > >
> > > > @@ -1097,6 +1071,7 @@ static int dmirror_device_init(struct dmirror_device *mdevice, int id)
> > > > spin_lock_init(&mdevice->lock);
> > > > cdev_init(&mdevice->cdevice, &dmirror_fops);
> > > > + mdevice->cdevice.owner = THIS_MODULE;
> > > > ret = cdev_add(&mdevice->cdevice, dev, 1);
> > > > if (ret)
> > > > return ret;
> > > >
> > > > The use of cdev without a struct device is super weird, but it still
> > > > needs this
> > > >
> > > > diff --git a/tools/testing/selftests/vm/test_hmm.sh b/tools/testing/selftests/vm/test_hmm.sh
> > > > index 461e4a99a362cf..0647b525a62564 100755
> > > > --- a/tools/testing/selftests/vm/test_hmm.sh
> > > > +++ b/tools/testing/selftests/vm/test_hmm.sh
> > > > @@ -59,7 +59,7 @@ run_smoke()
> > > > echo "Running smoke test. Note, this test provides basic coverage."
> > > > load_driver
> > > > - ./hmm-tests
> > > > + $(dirname "${BASH_SOURCE[0]}")/hmm-tests
> > > > unload_driver
> > > > }
> > > >
> > > > Make it runnably reliably
> > > >
> > > > Jason
> > >
> > > Thanks for the fixes. I'll apply these and send a v9.
> > > I will also add missing calls to release_mem_region() to free the reserved device private
> > > addresses.
> >
> > If you decide to ignore my request to avoid addition of special header
> > file to UAPI, at least don't copy and install that file without some
> > special CONFIG option (TEST_HMM ???) requested by the users. It also
> > will be good to get Acked-by on this change from HMM people.
> >
> > However, I still think that include/uapi/linux/test_hmm.h opens
> > pandora box of having UAPI files without real promise to keep it
> > backward compatible.
> >
> > Thanks
>
> I think that is a valid point. I would expect the test<->driver UAPI to track the kernel
> version since the sources are "released" together. I suppose a version number could be
> included in the request structure to handle mismatch driver and test program but that
> may be overkill.

Yes, it is really overkill.

> Are you suggesting that include/linux/test_hmm.h is a better location?

It is one of options, another option maybe similar to that is done in
scripts/mod/modpost.c [1], where C file is generated on the fly.

[1] https://lore.kernel.org/netdev/[email protected]