diff options
author | Christian Amann <camann@suse.com> | 2019-03-19 09:07:32 +0100 |
---|---|---|
committer | Petr Vorel <pvorel@suse.cz> | 2019-03-25 13:59:56 +0100 |
commit | b3621af0481f091763b3614fdaa1f12a9a108d93 (patch) | |
tree | 37985accecd2dccdecc9249c1b1b53b7f7c810ba /testcases/kernel | |
parent | d07b77d578d72ea0645e6d6f6c1bc5b64b099025 (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/.gitignore | 1 | ||||
-rw-r--r-- | testcases/kernel/syscalls/userfaultfd/Makefile | 14 | ||||
-rw-r--r-- | testcases/kernel/syscalls/userfaultfd/userfaultfd01.c | 122 |
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 |