Skip to content

Commit 16530be

Browse files
ricardobranco777pevik
authored andcommitted
userfaultfd: Add test using /dev/userfaultfd instead of syscall
Signed-off-by: Ricardo Branco <rbranco@suse.de> Reviewed-by: Petr Vorel <pvorel@suse.cz> Reviewed-by: Cyril Hrubis <chrubis@suse.cz> Link: #1280
1 parent 1623b6d commit 16530be

5 files changed

Lines changed: 131 additions & 0 deletions

File tree

include/lapi/userfaultfd.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ struct uffdio_zeropage {
160160
#define UFFD_USER_MODE_ONLY 1
161161
#endif /* UFFD_USER_MODE_ONLY */
162162

163+
#ifndef USERFAULTFD_IOC
164+
#define USERFAULTFD_IOC 0xAA
165+
#endif /* USERFAULTFD_IOC */
166+
#ifndef USERFAULTFD_IOC_NEW
167+
#define USERFAULTFD_IOC_NEW _IO(USERFAULTFD_IOC, 0x00)
168+
#endif /* USERFAULTFD_IOC_NEW */
163169

164170
/* UFFD_PAGEFAULT_FLAG_MINOR and UFFDIO_CONTINUE were added in v5.13 */
165171
#ifndef UFFD_PAGEFAULT_FLAG_MINOR

runtest/syscalls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,7 @@ umount2_02 umount2_02
17711771

17721772
userfaultfd01 userfaultfd01
17731773
userfaultfd02 userfaultfd02
1774+
userfaultfd03 userfaultfd03
17741775

17751776
ustat01 ustat01
17761777
ustat02 ustat02
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/userfaultfd01
22
/userfaultfd02
3+
/userfaultfd03

testcases/kernel/syscalls/userfaultfd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ include $(top_srcdir)/include/mk/generic_leaf_target.mk
1313

1414
userfaultfd01: CFLAGS += -pthread
1515
userfaultfd02: CFLAGS += -pthread
16+
userfaultfd03: CFLAGS += -pthread
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (c) 2025 SUSE LLC
4+
* Author: Christian Amann <camann@suse.com>
5+
* Author: Ricardo Branco <rbranco@suse.com>
6+
*/
7+
8+
/*\
9+
* Force a pagefault event and handle it using :manpage:`userfaultfd(2)`
10+
* from a different thread using /dev/userfaultfd instead of syscall,
11+
* using USERFAULTFD_IOC_NEW ioctl to create the uffd & UFFDIO_COPY.
12+
*/
13+
14+
#include "config.h"
15+
#include <poll.h>
16+
#include "tst_test.h"
17+
#include "tst_safe_macros.h"
18+
#include "tst_safe_pthread.h"
19+
#include "lapi/userfaultfd.h"
20+
21+
static int page_size;
22+
static char *page;
23+
static void *copy_page;
24+
static int uffd;
25+
26+
static int open_userfaultfd(int flags)
27+
{
28+
int fd, fd2;
29+
30+
fd = SAFE_OPEN("/dev/userfaultfd", O_RDWR);
31+
32+
fd2 = SAFE_IOCTL(fd, USERFAULTFD_IOC_NEW, flags);
33+
34+
SAFE_CLOSE(fd);
35+
36+
return fd2;
37+
}
38+
39+
static void set_pages(void)
40+
{
41+
page_size = sysconf(_SC_PAGE_SIZE);
42+
page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
43+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
44+
copy_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
45+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
46+
}
47+
48+
static void reset_pages(void)
49+
{
50+
SAFE_MUNMAP(page, page_size);
51+
SAFE_MUNMAP(copy_page, page_size);
52+
}
53+
54+
static void *handle_thread(void)
55+
{
56+
static struct uffd_msg msg;
57+
struct uffdio_copy uffdio_copy = {};
58+
59+
struct pollfd pollfd;
60+
int nready;
61+
62+
pollfd.fd = uffd;
63+
pollfd.events = POLLIN;
64+
nready = poll(&pollfd, 1, -1);
65+
if (nready == -1)
66+
tst_brk(TBROK | TERRNO, "Error on poll");
67+
68+
SAFE_READ(1, uffd, &msg, sizeof(msg));
69+
70+
if (msg.event != UFFD_EVENT_PAGEFAULT)
71+
tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT %d", msg.event);
72+
73+
memset(copy_page, 'X', page_size);
74+
75+
uffdio_copy.src = (unsigned long) copy_page;
76+
77+
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address
78+
& ~(page_size - 1);
79+
uffdio_copy.len = page_size;
80+
SAFE_IOCTL(uffd, UFFDIO_COPY, &uffdio_copy);
81+
82+
close(uffd);
83+
return NULL;
84+
}
85+
86+
static void run(void)
87+
{
88+
pthread_t thr;
89+
struct uffdio_api uffdio_api = {};
90+
struct uffdio_register uffdio_register;
91+
92+
set_pages();
93+
94+
uffd = open_userfaultfd(O_CLOEXEC | O_NONBLOCK);
95+
96+
uffdio_api.api = UFFD_API;
97+
SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
98+
99+
uffdio_register.range.start = (unsigned long) page;
100+
uffdio_register.range.len = page_size;
101+
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
102+
103+
SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
104+
105+
SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL);
106+
107+
char c = page[0xf];
108+
109+
if (c == 'X')
110+
tst_res(TPASS, "Pagefault handled via /dev/userfaultfd");
111+
else
112+
tst_res(TFAIL, "Pagefault not handled via /dev/userfaultfd");
113+
114+
SAFE_PTHREAD_JOIN(thr, NULL);
115+
reset_pages();
116+
}
117+
118+
static struct tst_test test = {
119+
.needs_root = 1,
120+
.test_all = run,
121+
.min_kver = "5.11",
122+
};

0 commit comments

Comments
 (0)