13
13
#include <stdio.h>
14
14
#include <stdlib.h>
15
15
#include <time.h>
16
- #include <poll.h>
17
16
#include <pthread.h>
18
17
#include <linux/userfaultfd.h>
19
18
#include <sys/syscall.h>
@@ -77,8 +76,20 @@ static int handle_uffd_page_request(int uffd_mode, int uffd,
77
76
copy .mode = 0 ;
78
77
79
78
r = ioctl (uffd , UFFDIO_COPY , & copy );
80
- if (r == -1 ) {
81
- pr_info ("Failed UFFDIO_COPY in 0x%lx from thread %d with errno: %d\n" ,
79
+ /*
80
+ * With multiple vCPU threads fault on a single page and there are
81
+ * multiple readers for the UFFD, at least one of the UFFDIO_COPYs
82
+ * will fail with EEXIST: handle that case without signaling an
83
+ * error.
84
+ *
85
+ * Note that this also suppress any EEXISTs occurring from,
86
+ * e.g., the first UFFDIO_COPY/CONTINUEs on a page. That never
87
+ * happens here, but a realistic VMM might potentially maintain
88
+ * some external state to correctly surface EEXISTs to userspace
89
+ * (or prevent duplicate COPY/CONTINUEs in the first place).
90
+ */
91
+ if (r == -1 && errno != EEXIST ) {
92
+ pr_info ("Failed UFFDIO_COPY in 0x%lx from thread %d, errno = %d\n" ,
82
93
addr , tid , errno );
83
94
return r ;
84
95
}
@@ -89,8 +100,20 @@ static int handle_uffd_page_request(int uffd_mode, int uffd,
89
100
cont .range .len = demand_paging_size ;
90
101
91
102
r = ioctl (uffd , UFFDIO_CONTINUE , & cont );
92
- if (r == -1 ) {
93
- pr_info ("Failed UFFDIO_CONTINUE in 0x%lx from thread %d with errno: %d\n" ,
103
+ /*
104
+ * With multiple vCPU threads fault on a single page and there are
105
+ * multiple readers for the UFFD, at least one of the UFFDIO_COPYs
106
+ * will fail with EEXIST: handle that case without signaling an
107
+ * error.
108
+ *
109
+ * Note that this also suppress any EEXISTs occurring from,
110
+ * e.g., the first UFFDIO_COPY/CONTINUEs on a page. That never
111
+ * happens here, but a realistic VMM might potentially maintain
112
+ * some external state to correctly surface EEXISTs to userspace
113
+ * (or prevent duplicate COPY/CONTINUEs in the first place).
114
+ */
115
+ if (r == -1 && errno != EEXIST ) {
116
+ pr_info ("Failed UFFDIO_CONTINUE in 0x%lx, thread %d, errno = %d\n" ,
94
117
addr , tid , errno );
95
118
return r ;
96
119
}
@@ -110,7 +133,9 @@ static int handle_uffd_page_request(int uffd_mode, int uffd,
110
133
111
134
struct test_params {
112
135
int uffd_mode ;
136
+ bool single_uffd ;
113
137
useconds_t uffd_delay ;
138
+ int readers_per_uffd ;
114
139
enum vm_mem_backing_src_type src_type ;
115
140
bool partition_vcpu_memory_access ;
116
141
};
@@ -131,10 +156,12 @@ static void run_test(enum vm_guest_mode mode, void *arg)
131
156
struct memstress_vcpu_args * vcpu_args ;
132
157
struct test_params * p = arg ;
133
158
struct uffd_desc * * uffd_descs = NULL ;
159
+ uint64_t uffd_region_size ;
134
160
struct timespec start ;
135
161
struct timespec ts_diff ;
162
+ double vcpu_paging_rate ;
136
163
struct kvm_vm * vm ;
137
- int i ;
164
+ int i , num_uffds = 0 ;
138
165
139
166
vm = memstress_create_vm (mode , nr_vcpus , guest_percpu_mem_size , 1 ,
140
167
p -> src_type , p -> partition_vcpu_memory_access );
@@ -147,17 +174,22 @@ static void run_test(enum vm_guest_mode mode, void *arg)
147
174
memset (guest_data_prototype , 0xAB , demand_paging_size );
148
175
149
176
if (p -> uffd_mode == UFFDIO_REGISTER_MODE_MINOR ) {
150
- for (i = 0 ; i < nr_vcpus ; i ++ ) {
177
+ num_uffds = p -> single_uffd ? 1 : nr_vcpus ;
178
+ for (i = 0 ; i < num_uffds ; i ++ ) {
151
179
vcpu_args = & memstress_args .vcpu_args [i ];
152
180
prefault_mem (addr_gpa2alias (vm , vcpu_args -> gpa ),
153
181
vcpu_args -> pages * memstress_args .guest_page_size );
154
182
}
155
183
}
156
184
157
185
if (p -> uffd_mode ) {
158
- uffd_descs = malloc (nr_vcpus * sizeof (struct uffd_desc * ));
186
+ num_uffds = p -> single_uffd ? 1 : nr_vcpus ;
187
+ uffd_region_size = nr_vcpus * guest_percpu_mem_size / num_uffds ;
188
+
189
+ uffd_descs = malloc (num_uffds * sizeof (struct uffd_desc * ));
159
190
TEST_ASSERT (uffd_descs , "Memory allocation failed" );
160
- for (i = 0 ; i < nr_vcpus ; i ++ ) {
191
+ for (i = 0 ; i < num_uffds ; i ++ ) {
192
+ struct memstress_vcpu_args * vcpu_args ;
161
193
void * vcpu_hva ;
162
194
163
195
vcpu_args = & memstress_args .vcpu_args [i ];
@@ -170,7 +202,8 @@ static void run_test(enum vm_guest_mode mode, void *arg)
170
202
*/
171
203
uffd_descs [i ] = uffd_setup_demand_paging (
172
204
p -> uffd_mode , p -> uffd_delay , vcpu_hva ,
173
- vcpu_args -> pages * memstress_args .guest_page_size ,
205
+ uffd_region_size ,
206
+ p -> readers_per_uffd ,
174
207
& handle_uffd_page_request );
175
208
}
176
209
}
@@ -187,15 +220,19 @@ static void run_test(enum vm_guest_mode mode, void *arg)
187
220
188
221
if (p -> uffd_mode ) {
189
222
/* Tell the user fault fd handler threads to quit */
190
- for (i = 0 ; i < nr_vcpus ; i ++ )
223
+ for (i = 0 ; i < num_uffds ; i ++ )
191
224
uffd_stop_demand_paging (uffd_descs [i ]);
192
225
}
193
226
194
- pr_info ("Total guest execution time: %ld.%.9lds\n" ,
227
+ pr_info ("Total guest execution time:\t %ld.%.9lds\n" ,
195
228
ts_diff .tv_sec , ts_diff .tv_nsec );
196
- pr_info ("Overall demand paging rate: %f pgs/sec\n" ,
197
- memstress_args .vcpu_args [0 ].pages * nr_vcpus /
198
- ((double )ts_diff .tv_sec + (double )ts_diff .tv_nsec / NSEC_PER_SEC ));
229
+
230
+ vcpu_paging_rate = memstress_args .vcpu_args [0 ].pages /
231
+ ((double )ts_diff .tv_sec + (double )ts_diff .tv_nsec / NSEC_PER_SEC );
232
+ pr_info ("Per-vcpu demand paging rate:\t%f pgs/sec/vcpu\n" ,
233
+ vcpu_paging_rate );
234
+ pr_info ("Overall demand paging rate:\t%f pgs/sec\n" ,
235
+ vcpu_paging_rate * nr_vcpus );
199
236
200
237
memstress_destroy_vm (vm );
201
238
@@ -207,15 +244,20 @@ static void run_test(enum vm_guest_mode mode, void *arg)
207
244
static void help (char * name )
208
245
{
209
246
puts ("" );
210
- printf ("usage: %s [-h] [-m vm_mode] [-u uffd_mode] [-d uffd_delay_usec]\n"
211
- " [-b memory] [-s type] [-v vcpus] [-c cpu_list] [-o]\n" , name );
247
+ printf ("usage: %s [-h] [-m vm_mode] [-u uffd_mode] [-a]\n"
248
+ " [-d uffd_delay_usec] [-r readers_per_uffd] [-b memory]\n"
249
+ " [-s type] [-v vcpus] [-c cpu_list] [-o]\n" , name );
212
250
guest_modes_help ();
213
251
printf (" -u: use userfaultfd to handle vCPU page faults. Mode is a\n"
214
252
" UFFD registration mode: 'MISSING' or 'MINOR'.\n" );
215
253
kvm_print_vcpu_pinning_help ();
254
+ printf (" -a: Use a single userfaultfd for all of guest memory, instead of\n"
255
+ " creating one for each region paged by a unique vCPU\n"
256
+ " Set implicitly with -o, and no effect without -u.\n" );
216
257
printf (" -d: add a delay in usec to the User Fault\n"
217
258
" FD handler to simulate demand paging\n"
218
259
" overheads. Ignored without -u.\n" );
260
+ printf (" -r: Set the number of reader threads per uffd.\n" );
219
261
printf (" -b: specify the size of the memory region which should be\n"
220
262
" demand paged by each vCPU. e.g. 10M or 3G.\n"
221
263
" Default: 1G\n" );
@@ -234,12 +276,14 @@ int main(int argc, char *argv[])
234
276
struct test_params p = {
235
277
.src_type = DEFAULT_VM_MEM_SRC ,
236
278
.partition_vcpu_memory_access = true,
279
+ .readers_per_uffd = 1 ,
280
+ .single_uffd = false,
237
281
};
238
282
int opt ;
239
283
240
284
guest_modes_append_default ();
241
285
242
- while ((opt = getopt (argc , argv , "hm :u:d:b:s:v:c:o " )) != -1 ) {
286
+ while ((opt = getopt (argc , argv , "ahom :u:d:b:s:v:c:r: " )) != -1 ) {
243
287
switch (opt ) {
244
288
case 'm' :
245
289
guest_modes_cmdline (optarg );
@@ -251,6 +295,9 @@ int main(int argc, char *argv[])
251
295
p .uffd_mode = UFFDIO_REGISTER_MODE_MINOR ;
252
296
TEST_ASSERT (p .uffd_mode , "UFFD mode must be 'MISSING' or 'MINOR'." );
253
297
break ;
298
+ case 'a' :
299
+ p .single_uffd = true;
300
+ break ;
254
301
case 'd' :
255
302
p .uffd_delay = strtoul (optarg , NULL , 0 );
256
303
TEST_ASSERT (p .uffd_delay >= 0 , "A negative UFFD delay is not supported." );
@@ -271,6 +318,13 @@ int main(int argc, char *argv[])
271
318
break ;
272
319
case 'o' :
273
320
p .partition_vcpu_memory_access = false;
321
+ p .single_uffd = true;
322
+ break ;
323
+ case 'r' :
324
+ p .readers_per_uffd = atoi (optarg );
325
+ TEST_ASSERT (p .readers_per_uffd >= 1 ,
326
+ "Invalid number of readers per uffd %d: must be >=1" ,
327
+ p .readers_per_uffd );
274
328
break ;
275
329
case 'h' :
276
330
default :
0 commit comments