aboutsummaryrefslogtreecommitdiff
path: root/testcases/kernel
diff options
context:
space:
mode:
authorChristian Amann <camann@suse.com>2019-03-19 09:07:32 +0100
committerPetr Vorel <pvorel@suse.cz>2019-03-25 13:59:56 +0100
commitb3621af0481f091763b3614fdaa1f12a9a108d93 (patch)
tree37985accecd2dccdecc9249c1b1b53b7f7c810ba /testcases/kernel
parentd07b77d578d72ea0645e6d6f6c1bc5b64b099025 (diff)
syscalls: Add userfaultfd testcase
This tests the userfaultfd syscall to handle pagefault events. It does so by registering a userfaultfd object to the address of a memory page. In a second thread it handles the event and writes data in the monitored memory page to indicate success. Signed-off-by: Christian Amann <camann@suse.com> Signed-off-by: Petr Vorel <pvorel@suse.cz>
Diffstat (limited to 'testcases/kernel')
-rw-r--r--testcases/kernel/syscalls/userfaultfd/.gitignore1
-rw-r--r--testcases/kernel/syscalls/userfaultfd/Makefile14
-rw-r--r--testcases/kernel/syscalls/userfaultfd/userfaultfd01.c122
3 files changed, 137 insertions, 0 deletions
diff --git a/testcases/kernel/syscalls/userfaultfd/.gitignore b/testcases/kernel/syscalls/userfaultfd/.gitignore
new file mode 100644
index 000000000..d819a2a7c
--- /dev/null
+++ b/testcases/kernel/syscalls/userfaultfd/.gitignore
@@ -0,0 +1 @@
+/userfaultfd01
diff --git a/testcases/kernel/syscalls/userfaultfd/Makefile b/testcases/kernel/syscalls/userfaultfd/Makefile
new file mode 100644
index 000000000..31ddc4a42
--- /dev/null
+++ b/testcases/kernel/syscalls/userfaultfd/Makefile
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (c) 2019 SUSE LLC
+#
+# Author: Christian Amann <camann@suse.com>
+#
+
+top_srcdir ?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
+
+userfaultfd01: CFLAGS += -pthread
diff --git a/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c b/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c
new file mode 100644
index 000000000..a5e142209
--- /dev/null
+++ b/testcases/kernel/syscalls/userfaultfd/userfaultfd01.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 SUSE LLC
+ * Author: Christian Amann <camann@suse.com>
+ *
+ * Test userfaultfd
+ *
+ * Force a pagefault event and handle it using userfaultfd
+ * from a different thread
+ */
+
+#include "config.h"
+#include "tst_test.h"
+
+#ifdef HAVE_LINUX_USERFAULTFD_H
+#include <linux/userfaultfd.h>
+#include <poll.h>
+
+#include "tst_safe_macros.h"
+#include "tst_safe_pthread.h"
+#include "lapi/syscalls.h"
+
+static int page_size;
+static char *page;
+static void *copy_page;
+static int uffd;
+
+static int sys_userfaultfd(int flags)
+{
+ return tst_syscall(__NR_userfaultfd, flags);
+}
+
+static void set_pages(void)
+{
+ page_size = sysconf(_SC_PAGE_SIZE);
+ page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+}
+
+static void handle_thread(void)
+{
+ static struct uffd_msg msg;
+ struct uffdio_copy uffdio_copy;
+
+ struct pollfd pollfd;
+ int nready;
+
+ pollfd.fd = uffd;
+ pollfd.events = POLLIN;
+ nready = poll(&pollfd, 1, -1);
+ if (nready == -1)
+ tst_brk(TBROK | TERRNO,
+ "Error on poll");
+
+ SAFE_READ(1, uffd, &msg, sizeof(msg));
+
+ if (msg.event != UFFD_EVENT_PAGEFAULT)
+ tst_brk(TBROK | TERRNO,
+ "Received unexpected UFFD_EVENT");
+
+ memset(copy_page, 'X', page_size);
+
+ uffdio_copy.src = (unsigned long) copy_page;
+
+ uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address
+ & ~(page_size - 1);
+ uffdio_copy.len = page_size;
+ uffdio_copy.mode = 0;
+ uffdio_copy.copy = 0;
+ SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
+
+ close(uffd);
+}
+
+static void run(void)
+{
+ pthread_t thr;
+ struct uffdio_api uffdio_api;
+ struct uffdio_register uffdio_register;
+
+ set_pages();
+
+ uffd = sys_userfaultfd(O_CLOEXEC | O_NONBLOCK);
+
+ if (uffd == -1)
+ tst_brk(TBROK | TERRNO,
+ "Could not create userfault file descriptor");
+
+ uffdio_api.api = UFFD_API;
+ uffdio_api.features = 0;
+ SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
+
+ uffdio_register.range.start = (unsigned long) page;
+ uffdio_register.range.len = page_size;
+ uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+
+ SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
+
+ SAFE_PTHREAD_CREATE(&thr, NULL,
+ (void * (*)(void *)) handle_thread, NULL);
+
+ char c = page[0xf];
+
+ if (c == 'X')
+ tst_res(TPASS, "Pagefault handled!");
+ else
+ tst_res(TFAIL, "Pagefault not handled!");
+
+ SAFE_PTHREAD_JOIN(thr, NULL);
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .min_kver = "4.3",
+ .timeout = 20
+};
+
+#else
+ TST_TEST_TCONF("This system does not provide userfaultfd support");
+#endif