1 // SPDX-License-Identifier: GPL-2.0
4 * Adapted from demand_paging_test.c
6 * Copyright (C) 2018, Red Hat, Inc.
7 * Copyright (C) 2019-2022 Google LLC
15 #include <linux/userfaultfd.h>
16 #include <sys/epoll.h>
17 #include <sys/syscall.h>
20 #include "test_util.h"
21 #include "memstress.h"
22 #include "userfaultfd_util.h"
24 #ifdef __NR_userfaultfd
26 static void *uffd_handler_thread_fn(void *arg
)
28 struct uffd_reader_args
*reader_args
= (struct uffd_reader_args
*)arg
;
29 int uffd
= reader_args
->uffd
;
31 struct timespec start
;
32 struct timespec ts_diff
;
33 struct epoll_event evt
;
36 epollfd
= epoll_create(1);
37 TEST_ASSERT(epollfd
>= 0, "Failed to create epollfd.");
39 evt
.events
= EPOLLIN
| EPOLLEXCLUSIVE
;
41 TEST_ASSERT(!epoll_ctl(epollfd
, EPOLL_CTL_ADD
, uffd
, &evt
),
42 "Failed to add uffd to epollfd");
46 TEST_ASSERT(!epoll_ctl(epollfd
, EPOLL_CTL_ADD
, reader_args
->pipe
, &evt
),
47 "Failed to add pipe to epollfd");
49 clock_gettime(CLOCK_MONOTONIC
, &start
);
54 r
= epoll_wait(epollfd
, &evt
, 1, -1);
56 "Unexpected number of events (%d) from epoll, errno = %d",
59 if (evt
.data
.u32
== 1) {
62 TEST_ASSERT(!(evt
.events
& (EPOLLERR
| EPOLLHUP
)),
63 "Reader thread received EPOLLERR or EPOLLHUP on pipe.");
64 r
= read(reader_args
->pipe
, &tmp_chr
, 1);
66 "Error reading pipefd in uffd reader thread");
70 TEST_ASSERT(!(evt
.events
& (EPOLLERR
| EPOLLHUP
)),
71 "Reader thread received EPOLLERR or EPOLLHUP on uffd.");
73 r
= read(uffd
, &msg
, sizeof(msg
));
75 TEST_ASSERT(errno
== EAGAIN
,
76 "Error reading from UFFD: errno = %d", errno
);
80 TEST_ASSERT(r
== sizeof(msg
),
81 "Read on uffd returned unexpected number of bytes (%d)", r
);
83 if (!(msg
.event
& UFFD_EVENT_PAGEFAULT
))
86 if (reader_args
->delay
)
87 usleep(reader_args
->delay
);
88 r
= reader_args
->handler(reader_args
->uffd_mode
, uffd
, &msg
);
90 "Reader thread handler fn returned negative value %d", r
);
94 ts_diff
= timespec_elapsed(start
);
95 PER_VCPU_DEBUG("userfaulted %ld pages over %ld.%.9lds. (%f/sec)\n",
96 pages
, ts_diff
.tv_sec
, ts_diff
.tv_nsec
,
97 pages
/ ((double)ts_diff
.tv_sec
+ (double)ts_diff
.tv_nsec
/ NSEC_PER_SEC
));
102 struct uffd_desc
*uffd_setup_demand_paging(int uffd_mode
, useconds_t delay
,
103 void *hva
, uint64_t len
,
104 uint64_t num_readers
,
105 uffd_handler_t handler
)
107 struct uffd_desc
*uffd_desc
;
108 bool is_minor
= (uffd_mode
== UFFDIO_REGISTER_MODE_MINOR
);
110 struct uffdio_api uffdio_api
;
111 struct uffdio_register uffdio_register
;
112 uint64_t expected_ioctls
= ((uint64_t) 1) << _UFFDIO_COPY
;
115 PER_PAGE_DEBUG("Userfaultfd %s mode, faults resolved with %s\n",
116 is_minor
? "MINOR" : "MISSING",
117 is_minor
? "UFFDIO_CONINUE" : "UFFDIO_COPY");
119 uffd_desc
= malloc(sizeof(struct uffd_desc
));
120 TEST_ASSERT(uffd_desc
, "Failed to malloc uffd descriptor");
122 uffd_desc
->pipefds
= calloc(sizeof(int), num_readers
);
123 TEST_ASSERT(uffd_desc
->pipefds
, "Failed to alloc pipes");
125 uffd_desc
->readers
= calloc(sizeof(pthread_t
), num_readers
);
126 TEST_ASSERT(uffd_desc
->readers
, "Failed to alloc reader threads");
128 uffd_desc
->reader_args
= calloc(sizeof(struct uffd_reader_args
), num_readers
);
129 TEST_ASSERT(uffd_desc
->reader_args
, "Failed to alloc reader_args");
131 uffd_desc
->num_readers
= num_readers
;
133 /* In order to get minor faults, prefault via the alias. */
135 expected_ioctls
= ((uint64_t) 1) << _UFFDIO_CONTINUE
;
137 uffd
= syscall(__NR_userfaultfd
, O_CLOEXEC
| O_NONBLOCK
);
138 TEST_ASSERT(uffd
>= 0, "uffd creation failed, errno: %d", errno
);
140 uffdio_api
.api
= UFFD_API
;
141 uffdio_api
.features
= 0;
142 TEST_ASSERT(ioctl(uffd
, UFFDIO_API
, &uffdio_api
) != -1,
143 "ioctl UFFDIO_API failed: %" PRIu64
,
144 (uint64_t)uffdio_api
.api
);
146 uffdio_register
.range
.start
= (uint64_t)hva
;
147 uffdio_register
.range
.len
= len
;
148 uffdio_register
.mode
= uffd_mode
;
149 TEST_ASSERT(ioctl(uffd
, UFFDIO_REGISTER
, &uffdio_register
) != -1,
150 "ioctl UFFDIO_REGISTER failed");
151 TEST_ASSERT((uffdio_register
.ioctls
& expected_ioctls
) ==
152 expected_ioctls
, "missing userfaultfd ioctls");
154 uffd_desc
->uffd
= uffd
;
155 for (i
= 0; i
< uffd_desc
->num_readers
; ++i
) {
158 ret
= pipe2((int *) &pipes
, O_CLOEXEC
| O_NONBLOCK
);
159 TEST_ASSERT(!ret
, "Failed to set up pipefd %i for uffd_desc %p",
162 uffd_desc
->pipefds
[i
] = pipes
[1];
164 uffd_desc
->reader_args
[i
].uffd_mode
= uffd_mode
;
165 uffd_desc
->reader_args
[i
].uffd
= uffd
;
166 uffd_desc
->reader_args
[i
].delay
= delay
;
167 uffd_desc
->reader_args
[i
].handler
= handler
;
168 uffd_desc
->reader_args
[i
].pipe
= pipes
[0];
170 pthread_create(&uffd_desc
->readers
[i
], NULL
, uffd_handler_thread_fn
,
171 &uffd_desc
->reader_args
[i
]);
173 PER_VCPU_DEBUG("Created uffd thread %i for HVA range [%p, %p)\n",
180 void uffd_stop_demand_paging(struct uffd_desc
*uffd
)
185 for (i
= 0; i
< uffd
->num_readers
; ++i
)
186 TEST_ASSERT(write(uffd
->pipefds
[i
], &c
, 1) == 1,
187 "Unable to write to pipefd %i for uffd_desc %p", i
, uffd
);
189 for (i
= 0; i
< uffd
->num_readers
; ++i
)
190 TEST_ASSERT(!pthread_join(uffd
->readers
[i
], NULL
),
191 "Pthread_join failed on reader %i for uffd_desc %p", i
, uffd
);
195 for (i
= 0; i
< uffd
->num_readers
; ++i
) {
196 close(uffd
->pipefds
[i
]);
197 close(uffd
->reader_args
[i
].pipe
);
202 free(uffd
->reader_args
);
206 #endif /* __NR_userfaultfd */