Skip to content

Commit 1623b6d

Browse files
ricardobranco777pevik
authored andcommitted
userfaultfd: Add test using UFFDIO_MOVE
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 df188cd commit 1623b6d

6 files changed

Lines changed: 126 additions & 0 deletions

File tree

configure.ac

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,8 @@ AC_CHECK_TYPES([struct sockaddr_vm],,,[
277277
#include <linux/vm_sockets.h>
278278
])
279279

280+
AC_CHECK_TYPES([struct uffdio_move],,,[#include <linux/userfaultfd.h>])
281+
280282
# Tools knobs
281283

282284
# Bash

include/lapi/userfaultfd.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,30 @@ struct uffdio_continue {
189189
#define UFFD_FEATURE_MINOR_SHMEM (1<<10)
190190
#endif /* UFFD_FEATURE_MINOR_SHMEM */
191191

192+
#ifndef HAVE_STRUCT_UFFDIO_MOVE
193+
#define _UFFDIO_MOVE (0x05)
194+
#define UFFDIO_MOVE _IOWR(UFFDIO, _UFFDIO_MOVE, \
195+
struct uffdio_move)
196+
197+
struct uffdio_move {
198+
__u64 dst;
199+
__u64 src;
200+
__u64 len;
201+
/*
202+
* Especially if used to atomically remove memory from the
203+
* address space the wake on the dst range is not needed.
204+
*/
205+
#define UFFDIO_MOVE_MODE_DONTWAKE ((__u64)1<<0)
206+
#define UFFDIO_MOVE_MODE_ALLOW_SRC_HOLES ((__u64)1<<1)
207+
__u64 mode;
208+
/*
209+
* "move" is written by the ioctl and must be at the end: the
210+
* copy_from_user will not read the last 8 bytes.
211+
*/
212+
__s64 move;
213+
};
214+
#endif /* HAVE_STRUCT_UFFDIO_MOVE */
215+
192216
#define SAFE_USERFAULTFD(flags, retry) \
193217
safe_userfaultfd(__FILE__, __LINE__, (flags), (retry))
194218

runtest/syscalls

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

17721772
userfaultfd01 userfaultfd01
1773+
userfaultfd02 userfaultfd02
17731774

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

testcases/kernel/syscalls/userfaultfd/Makefile

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

1414
userfaultfd01: CFLAGS += -pthread
15+
userfaultfd02: CFLAGS += -pthread
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 UFFDIO_MOVE.
11+
*/
12+
13+
#include "config.h"
14+
#include "tst_test.h"
15+
#include "tst_safe_macros.h"
16+
#include "tst_safe_pthread.h"
17+
#include "lapi/userfaultfd.h"
18+
19+
static int page_size;
20+
static char *page;
21+
static void *move_page;
22+
static int uffd;
23+
24+
static void set_pages(void)
25+
{
26+
page_size = sysconf(_SC_PAGE_SIZE);
27+
page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
28+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
29+
move_page = SAFE_MMAP(NULL, page_size, PROT_READ | PROT_WRITE,
30+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
31+
}
32+
33+
static void reset_pages(void)
34+
{
35+
SAFE_MUNMAP(page, page_size);
36+
SAFE_MUNMAP(move_page, page_size);
37+
}
38+
39+
static void *handle_thread(void)
40+
{
41+
static struct uffd_msg msg;
42+
struct uffdio_move uffdio_move = {};
43+
44+
SAFE_READ(1, uffd, &msg, sizeof(msg));
45+
46+
if (msg.event != UFFD_EVENT_PAGEFAULT)
47+
tst_brk(TBROK | TERRNO, "Received unexpected UFFD_EVENT %d", msg.event);
48+
49+
memset(move_page, 'X', page_size);
50+
51+
uffdio_move.src = (unsigned long) move_page;
52+
53+
uffdio_move.dst = (unsigned long) msg.arg.pagefault.address
54+
& ~(page_size - 1);
55+
uffdio_move.len = page_size;
56+
SAFE_IOCTL(uffd, UFFDIO_MOVE, &uffdio_move);
57+
58+
close(uffd);
59+
return NULL;
60+
}
61+
62+
static void run(void)
63+
{
64+
pthread_t thr;
65+
struct uffdio_api uffdio_api = {};
66+
struct uffdio_register uffdio_register;
67+
68+
set_pages();
69+
70+
uffd = SAFE_USERFAULTFD(O_CLOEXEC, false);
71+
72+
uffdio_api.api = UFFD_API;
73+
SAFE_IOCTL(uffd, UFFDIO_API, &uffdio_api);
74+
75+
uffdio_register.range.start = (unsigned long) page;
76+
uffdio_register.range.len = page_size;
77+
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
78+
79+
SAFE_IOCTL(uffd, UFFDIO_REGISTER, &uffdio_register);
80+
81+
SAFE_PTHREAD_CREATE(&thr, NULL, (void *) handle_thread, NULL);
82+
83+
char c = page[0xf];
84+
85+
if (c == 'X')
86+
tst_res(TPASS, "Pagefault handled via UFFDIO_MOVE");
87+
else
88+
tst_res(TFAIL, "Pagefault not handled via UFFDIO_MOVE");
89+
90+
SAFE_PTHREAD_JOIN(thr, NULL);
91+
reset_pages();
92+
}
93+
94+
static struct tst_test test = {
95+
.test_all = run,
96+
.min_kver = "6.8",
97+
};

0 commit comments

Comments
 (0)