@@ -73,33 +73,48 @@ static void *vcpu_worker(void *data)
73
73
return NULL ;
74
74
}
75
75
76
- static int handle_uffd_page_request (int uffd , uint64_t addr )
76
+ static int handle_uffd_page_request (int uffd_mode , int uffd , uint64_t addr )
77
77
{
78
- pid_t tid ;
78
+ pid_t tid = syscall ( __NR_gettid ) ;
79
79
struct timespec start ;
80
80
struct timespec ts_diff ;
81
- struct uffdio_copy copy ;
82
81
int r ;
83
82
84
- tid = syscall ( __NR_gettid );
83
+ clock_gettime ( CLOCK_MONOTONIC , & start );
85
84
86
- copy .src = (uint64_t )guest_data_prototype ;
87
- copy .dst = addr ;
88
- copy .len = demand_paging_size ;
89
- copy .mode = 0 ;
85
+ if (uffd_mode == UFFDIO_REGISTER_MODE_MISSING ) {
86
+ struct uffdio_copy copy ;
90
87
91
- clock_gettime (CLOCK_MONOTONIC , & start );
88
+ copy .src = (uint64_t )guest_data_prototype ;
89
+ copy .dst = addr ;
90
+ copy .len = demand_paging_size ;
91
+ copy .mode = 0 ;
92
+
93
+ r = ioctl (uffd , UFFDIO_COPY , & copy );
94
+ if (r == -1 ) {
95
+ pr_info ("Failed UFFDIO_COPY in 0x%lx from thread %d with errno: %d\n" ,
96
+ addr , tid , errno );
97
+ return r ;
98
+ }
99
+ } else if (uffd_mode == UFFDIO_REGISTER_MODE_MINOR ) {
100
+ struct uffdio_continue cont = {0 };
101
+
102
+ cont .range .start = addr ;
103
+ cont .range .len = demand_paging_size ;
92
104
93
- r = ioctl (uffd , UFFDIO_COPY , & copy );
94
- if (r == -1 ) {
95
- pr_info ("Failed Paged in 0x%lx from thread %d with errno: %d\n" ,
96
- addr , tid , errno );
97
- return r ;
105
+ r = ioctl (uffd , UFFDIO_CONTINUE , & cont );
106
+ if (r == -1 ) {
107
+ pr_info ("Failed UFFDIO_CONTINUE in 0x%lx from thread %d with errno: %d\n" ,
108
+ addr , tid , errno );
109
+ return r ;
110
+ }
111
+ } else {
112
+ TEST_FAIL ("Invalid uffd mode %d" , uffd_mode );
98
113
}
99
114
100
115
ts_diff = timespec_elapsed (start );
101
116
102
- PER_PAGE_DEBUG ("UFFDIO_COPY %d \t%ld ns\n" , tid ,
117
+ PER_PAGE_DEBUG ("UFFD page-in %d \t%ld ns\n" , tid ,
103
118
timespec_to_ns (ts_diff ));
104
119
PER_PAGE_DEBUG ("Paged in %ld bytes at 0x%lx from thread %d\n" ,
105
120
demand_paging_size , addr , tid );
@@ -110,6 +125,7 @@ static int handle_uffd_page_request(int uffd, uint64_t addr)
110
125
bool quit_uffd_thread ;
111
126
112
127
struct uffd_handler_args {
128
+ int uffd_mode ;
113
129
int uffd ;
114
130
int pipefd ;
115
131
useconds_t delay ;
@@ -186,7 +202,7 @@ static void *uffd_handler_thread_fn(void *arg)
186
202
if (delay )
187
203
usleep (delay );
188
204
addr = msg .arg .pagefault .address ;
189
- r = handle_uffd_page_request (uffd , addr );
205
+ r = handle_uffd_page_request (uffd_args -> uffd_mode , uffd , addr );
190
206
if (r < 0 )
191
207
return NULL ;
192
208
pages ++ ;
@@ -202,13 +218,32 @@ static void *uffd_handler_thread_fn(void *arg)
202
218
203
219
static void setup_demand_paging (struct kvm_vm * vm ,
204
220
pthread_t * uffd_handler_thread , int pipefd ,
205
- useconds_t uffd_delay ,
221
+ int uffd_mode , useconds_t uffd_delay ,
206
222
struct uffd_handler_args * uffd_args ,
207
- void * hva , uint64_t len )
223
+ void * hva , void * alias , uint64_t len )
208
224
{
225
+ bool is_minor = (uffd_mode == UFFDIO_REGISTER_MODE_MINOR );
209
226
int uffd ;
210
227
struct uffdio_api uffdio_api ;
211
228
struct uffdio_register uffdio_register ;
229
+ uint64_t expected_ioctls = ((uint64_t ) 1 ) << _UFFDIO_COPY ;
230
+
231
+ PER_PAGE_DEBUG ("Userfaultfd %s mode, faults resolved with %s\n" ,
232
+ is_minor ? "MINOR" : "MISSING" ,
233
+ is_minor ? "UFFDIO_CONINUE" : "UFFDIO_COPY" );
234
+
235
+ /* In order to get minor faults, prefault via the alias. */
236
+ if (is_minor ) {
237
+ size_t p ;
238
+
239
+ expected_ioctls = ((uint64_t ) 1 ) << _UFFDIO_CONTINUE ;
240
+
241
+ TEST_ASSERT (alias != NULL , "Alias required for minor faults" );
242
+ for (p = 0 ; p < (len / demand_paging_size ); ++ p ) {
243
+ memcpy (alias + (p * demand_paging_size ),
244
+ guest_data_prototype , demand_paging_size );
245
+ }
246
+ }
212
247
213
248
uffd = syscall (__NR_userfaultfd , O_CLOEXEC | O_NONBLOCK );
214
249
TEST_ASSERT (uffd >= 0 , "uffd creation failed, errno: %d" , errno );
@@ -221,12 +256,13 @@ static void setup_demand_paging(struct kvm_vm *vm,
221
256
222
257
uffdio_register .range .start = (uint64_t )hva ;
223
258
uffdio_register .range .len = len ;
224
- uffdio_register .mode = UFFDIO_REGISTER_MODE_MISSING ;
259
+ uffdio_register .mode = uffd_mode ;
225
260
TEST_ASSERT (ioctl (uffd , UFFDIO_REGISTER , & uffdio_register ) != -1 ,
226
261
"ioctl UFFDIO_REGISTER failed" );
227
- TEST_ASSERT ((uffdio_register .ioctls & UFFD_API_RANGE_IOCTLS ) ==
228
- UFFD_API_RANGE_IOCTLS , "unexpected userfaultfd ioctl set " );
262
+ TEST_ASSERT ((uffdio_register .ioctls & expected_ioctls ) ==
263
+ expected_ioctls , "missing userfaultfd ioctls " );
229
264
265
+ uffd_args -> uffd_mode = uffd_mode ;
230
266
uffd_args -> uffd = uffd ;
231
267
uffd_args -> pipefd = pipefd ;
232
268
uffd_args -> delay = uffd_delay ;
@@ -238,7 +274,7 @@ static void setup_demand_paging(struct kvm_vm *vm,
238
274
}
239
275
240
276
struct test_params {
241
- bool use_uffd ;
277
+ int uffd_mode ;
242
278
useconds_t uffd_delay ;
243
279
enum vm_mem_backing_src_type src_type ;
244
280
bool partition_vcpu_memory_access ;
@@ -275,7 +311,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
275
311
perf_test_setup_vcpus (vm , nr_vcpus , guest_percpu_mem_size ,
276
312
p -> partition_vcpu_memory_access );
277
313
278
- if (p -> use_uffd ) {
314
+ if (p -> uffd_mode ) {
279
315
uffd_handler_threads =
280
316
malloc (nr_vcpus * sizeof (* uffd_handler_threads ));
281
317
TEST_ASSERT (uffd_handler_threads , "Memory allocation failed" );
@@ -289,6 +325,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
289
325
for (vcpu_id = 0 ; vcpu_id < nr_vcpus ; vcpu_id ++ ) {
290
326
vm_paddr_t vcpu_gpa ;
291
327
void * vcpu_hva ;
328
+ void * vcpu_alias ;
292
329
uint64_t vcpu_mem_size ;
293
330
294
331
@@ -303,8 +340,9 @@ static void run_test(enum vm_guest_mode mode, void *arg)
303
340
PER_VCPU_DEBUG ("Added VCPU %d with test mem gpa [%lx, %lx)\n" ,
304
341
vcpu_id , vcpu_gpa , vcpu_gpa + vcpu_mem_size );
305
342
306
- /* Cache the HVA pointer of the region */
343
+ /* Cache the host addresses of the region */
307
344
vcpu_hva = addr_gpa2hva (vm , vcpu_gpa );
345
+ vcpu_alias = addr_gpa2alias (vm , vcpu_gpa );
308
346
309
347
/*
310
348
* Set up user fault fd to handle demand paging
@@ -315,8 +353,9 @@ static void run_test(enum vm_guest_mode mode, void *arg)
315
353
TEST_ASSERT (!r , "Failed to set up pipefd" );
316
354
317
355
setup_demand_paging (vm , & uffd_handler_threads [vcpu_id ],
318
- pipefds [vcpu_id * 2 ], p -> uffd_delay ,
319
- & uffd_args [vcpu_id ], vcpu_hva ,
356
+ pipefds [vcpu_id * 2 ], p -> uffd_mode ,
357
+ p -> uffd_delay , & uffd_args [vcpu_id ],
358
+ vcpu_hva , vcpu_alias ,
320
359
vcpu_mem_size );
321
360
}
322
361
}
@@ -345,7 +384,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
345
384
346
385
pr_info ("All vCPU threads joined\n" );
347
386
348
- if (p -> use_uffd ) {
387
+ if (p -> uffd_mode ) {
349
388
char c ;
350
389
351
390
/* Tell the user fault fd handler threads to quit */
@@ -367,7 +406,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
367
406
368
407
free (guest_data_prototype );
369
408
free (vcpu_threads );
370
- if (p -> use_uffd ) {
409
+ if (p -> uffd_mode ) {
371
410
free (uffd_handler_threads );
372
411
free (uffd_args );
373
412
free (pipefds );
@@ -377,11 +416,11 @@ static void run_test(enum vm_guest_mode mode, void *arg)
377
416
static void help (char * name )
378
417
{
379
418
puts ("" );
380
- printf ("usage: %s [-h] [-m mode ] [-u] [-d uffd_delay_usec]\n"
419
+ printf ("usage: %s [-h] [-m vm_mode ] [-u uffd_mode ] [-d uffd_delay_usec]\n"
381
420
" [-b memory] [-t type] [-v vcpus] [-o]\n" , name );
382
421
guest_modes_help ();
383
- printf (" -u: use User Fault FD to handle vCPU page\n"
384
- " faults .\n" );
422
+ printf (" -u: use userfaultfd to handle vCPU page faults. Mode is a \n"
423
+ " UFFD registration mode: 'MISSING' or 'MINOR' .\n" );
385
424
printf (" -d: add a delay in usec to the User Fault\n"
386
425
" FD handler to simulate demand paging\n"
387
426
" overheads. Ignored without -u.\n" );
@@ -408,13 +447,17 @@ int main(int argc, char *argv[])
408
447
409
448
guest_modes_append_default ();
410
449
411
- while ((opt = getopt (argc , argv , "hm:ud :b:t:v:o" )) != -1 ) {
450
+ while ((opt = getopt (argc , argv , "hm:u:d :b:t:v:o" )) != -1 ) {
412
451
switch (opt ) {
413
452
case 'm' :
414
453
guest_modes_cmdline (optarg );
415
454
break ;
416
455
case 'u' :
417
- p .use_uffd = true;
456
+ if (!strcmp ("MISSING" , optarg ))
457
+ p .uffd_mode = UFFDIO_REGISTER_MODE_MISSING ;
458
+ else if (!strcmp ("MINOR" , optarg ))
459
+ p .uffd_mode = UFFDIO_REGISTER_MODE_MINOR ;
460
+ TEST_ASSERT (p .uffd_mode , "UFFD mode must be 'MISSING' or 'MINOR'." );
418
461
break ;
419
462
case 'd' :
420
463
p .uffd_delay = strtoul (optarg , NULL , 0 );
@@ -441,6 +484,9 @@ int main(int argc, char *argv[])
441
484
}
442
485
}
443
486
487
+ TEST_ASSERT (p .uffd_mode != UFFDIO_REGISTER_MODE_MINOR || p .src_type == VM_MEM_SRC_SHMEM ,
488
+ "userfaultfd MINOR mode requires shared memory; pick a different -t" );
489
+
444
490
for_each_guest_mode (run_test , & p );
445
491
446
492
return 0 ;
0 commit comments