@@ -35,18 +35,27 @@ static uint64_t *guest_test_memory = (uint64_t *)TEST_GVA;
35
35
#define PREPARE_FN_NR 10
36
36
#define CHECK_FN_NR 10
37
37
38
+ static struct event_cnt {
39
+ int uffd_faults ;
40
+ /* uffd_faults is incremented from multiple threads. */
41
+ pthread_mutex_t uffd_faults_mutex ;
42
+ } events ;
43
+
38
44
struct test_desc {
39
45
const char * name ;
40
46
uint64_t mem_mark_cmd ;
41
47
/* Skip the test if any prepare function returns false */
42
48
bool (* guest_prepare [PREPARE_FN_NR ])(void );
43
49
void (* guest_test )(void );
44
50
void (* guest_test_check [CHECK_FN_NR ])(void );
51
+ uffd_handler_t uffd_pt_handler ;
52
+ uffd_handler_t uffd_data_handler ;
45
53
void (* dabt_handler )(struct ex_regs * regs );
46
54
void (* iabt_handler )(struct ex_regs * regs );
47
55
uint32_t pt_memslot_flags ;
48
56
uint32_t data_memslot_flags ;
49
57
bool skip ;
58
+ struct event_cnt expected_events ;
50
59
};
51
60
52
61
struct test_params {
@@ -263,7 +272,110 @@ static void no_iabt_handler(struct ex_regs *regs)
263
272
GUEST_ASSERT_1 (false, regs -> pc );
264
273
}
265
274
275
+ static struct uffd_args {
276
+ char * copy ;
277
+ void * hva ;
278
+ uint64_t paging_size ;
279
+ } pt_args , data_args ;
280
+
266
281
/* Returns true to continue the test, and false if it should be skipped. */
282
+ static int uffd_generic_handler (int uffd_mode , int uffd , struct uffd_msg * msg ,
283
+ struct uffd_args * args , bool expect_write )
284
+ {
285
+ uint64_t addr = msg -> arg .pagefault .address ;
286
+ uint64_t flags = msg -> arg .pagefault .flags ;
287
+ struct uffdio_copy copy ;
288
+ int ret ;
289
+
290
+ TEST_ASSERT (uffd_mode == UFFDIO_REGISTER_MODE_MISSING ,
291
+ "The only expected UFFD mode is MISSING" );
292
+ ASSERT_EQ (!!(flags & UFFD_PAGEFAULT_FLAG_WRITE ), expect_write );
293
+ ASSERT_EQ (addr , (uint64_t )args -> hva );
294
+
295
+ pr_debug ("uffd fault: addr=%p write=%d\n" ,
296
+ (void * )addr , !!(flags & UFFD_PAGEFAULT_FLAG_WRITE ));
297
+
298
+ copy .src = (uint64_t )args -> copy ;
299
+ copy .dst = addr ;
300
+ copy .len = args -> paging_size ;
301
+ copy .mode = 0 ;
302
+
303
+ ret = ioctl (uffd , UFFDIO_COPY , & copy );
304
+ if (ret == -1 ) {
305
+ pr_info ("Failed UFFDIO_COPY in 0x%lx with errno: %d\n" ,
306
+ addr , errno );
307
+ return ret ;
308
+ }
309
+
310
+ pthread_mutex_lock (& events .uffd_faults_mutex );
311
+ events .uffd_faults += 1 ;
312
+ pthread_mutex_unlock (& events .uffd_faults_mutex );
313
+ return 0 ;
314
+ }
315
+
316
+ static int uffd_pt_write_handler (int mode , int uffd , struct uffd_msg * msg )
317
+ {
318
+ return uffd_generic_handler (mode , uffd , msg , & pt_args , true);
319
+ }
320
+
321
+ static int uffd_data_write_handler (int mode , int uffd , struct uffd_msg * msg )
322
+ {
323
+ return uffd_generic_handler (mode , uffd , msg , & data_args , true);
324
+ }
325
+
326
+ static int uffd_data_read_handler (int mode , int uffd , struct uffd_msg * msg )
327
+ {
328
+ return uffd_generic_handler (mode , uffd , msg , & data_args , false);
329
+ }
330
+
331
+ static void setup_uffd_args (struct userspace_mem_region * region ,
332
+ struct uffd_args * args )
333
+ {
334
+ args -> hva = (void * )region -> region .userspace_addr ;
335
+ args -> paging_size = region -> region .memory_size ;
336
+
337
+ args -> copy = malloc (args -> paging_size );
338
+ TEST_ASSERT (args -> copy , "Failed to allocate data copy." );
339
+ memcpy (args -> copy , args -> hva , args -> paging_size );
340
+ }
341
+
342
+ static void setup_uffd (struct kvm_vm * vm , struct test_params * p ,
343
+ struct uffd_desc * * pt_uffd , struct uffd_desc * * data_uffd )
344
+ {
345
+ struct test_desc * test = p -> test_desc ;
346
+ int uffd_mode = UFFDIO_REGISTER_MODE_MISSING ;
347
+
348
+ setup_uffd_args (vm_get_mem_region (vm , MEM_REGION_PT ), & pt_args );
349
+ setup_uffd_args (vm_get_mem_region (vm , MEM_REGION_TEST_DATA ), & data_args );
350
+
351
+ * pt_uffd = NULL ;
352
+ if (test -> uffd_pt_handler )
353
+ * pt_uffd = uffd_setup_demand_paging (uffd_mode , 0 ,
354
+ pt_args .hva ,
355
+ pt_args .paging_size ,
356
+ test -> uffd_pt_handler );
357
+
358
+ * data_uffd = NULL ;
359
+ if (test -> uffd_data_handler )
360
+ * data_uffd = uffd_setup_demand_paging (uffd_mode , 0 ,
361
+ data_args .hva ,
362
+ data_args .paging_size ,
363
+ test -> uffd_data_handler );
364
+ }
365
+
366
+ static void free_uffd (struct test_desc * test , struct uffd_desc * pt_uffd ,
367
+ struct uffd_desc * data_uffd )
368
+ {
369
+ if (test -> uffd_pt_handler )
370
+ uffd_stop_demand_paging (pt_uffd );
371
+ if (test -> uffd_data_handler )
372
+ uffd_stop_demand_paging (data_uffd );
373
+
374
+ free (pt_args .copy );
375
+ free (data_args .copy );
376
+ }
377
+
378
+ /* Returns false if the test should be skipped. */
267
379
static bool punch_hole_in_backing_store (struct kvm_vm * vm ,
268
380
struct userspace_mem_region * region )
269
381
{
@@ -404,6 +516,11 @@ static void setup_memslots(struct kvm_vm *vm, struct test_params *p)
404
516
vm -> memslots [MEM_REGION_TEST_DATA ] = TEST_DATA_MEMSLOT ;
405
517
}
406
518
519
+ static void check_event_counts (struct test_desc * test )
520
+ {
521
+ ASSERT_EQ (test -> expected_events .uffd_faults , events .uffd_faults );
522
+ }
523
+
407
524
static void print_test_banner (enum vm_guest_mode mode , struct test_params * p )
408
525
{
409
526
struct test_desc * test = p -> test_desc ;
@@ -414,6 +531,11 @@ static void print_test_banner(enum vm_guest_mode mode, struct test_params *p)
414
531
vm_mem_backing_src_alias (p -> src_type )-> name );
415
532
}
416
533
534
+ static void reset_event_counts (void )
535
+ {
536
+ memset (& events , 0 , sizeof (events ));
537
+ }
538
+
417
539
/*
418
540
* This function either succeeds, skips the test (after setting test->skip), or
419
541
* fails with a TEST_FAIL that aborts all tests.
@@ -453,6 +575,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
453
575
struct test_desc * test = p -> test_desc ;
454
576
struct kvm_vm * vm ;
455
577
struct kvm_vcpu * vcpu ;
578
+ struct uffd_desc * pt_uffd , * data_uffd ;
456
579
457
580
print_test_banner (mode , p );
458
581
@@ -465,14 +588,31 @@ static void run_test(enum vm_guest_mode mode, void *arg)
465
588
466
589
ucall_init (vm , NULL );
467
590
591
+ reset_event_counts ();
592
+
593
+ /*
594
+ * Set some code in the data memslot for the guest to execute (only
595
+ * applicable to the EXEC tests). This has to be done before
596
+ * setup_uffd() as that function copies the memslot data for the uffd
597
+ * handler.
598
+ */
468
599
load_exec_code_for_test (vm );
600
+ setup_uffd (vm , p , & pt_uffd , & data_uffd );
469
601
setup_abort_handlers (vm , vcpu , test );
470
602
vcpu_args_set (vcpu , 1 , test );
471
603
472
604
vcpu_run_loop (vm , vcpu , test );
473
605
474
606
ucall_uninit (vm );
475
607
kvm_vm_free (vm );
608
+ free_uffd (test , pt_uffd , data_uffd );
609
+
610
+ /*
611
+ * Make sure we check the events after the uffd threads have exited,
612
+ * which means they updated their respective event counters.
613
+ */
614
+ if (!test -> skip )
615
+ check_event_counts (test );
476
616
}
477
617
478
618
static void help (char * name )
@@ -488,6 +628,7 @@ static void help(char *name)
488
628
#define SNAME (s ) #s
489
629
#define SCAT2 (a , b ) SNAME(a ## _ ## b)
490
630
#define SCAT3 (a , b , c ) SCAT2(a, SCAT2(b, c))
631
+ #define SCAT4 (a , b , c , d ) SCAT2(a, SCAT3(b, c, d))
491
632
492
633
#define _CHECK (_test ) _CHECK_##_test
493
634
#define _PREPARE (_test ) _PREPARE_##_test
@@ -515,6 +656,21 @@ static void help(char *name)
515
656
.mem_mark_cmd = _mark_cmd, \
516
657
.guest_test = _access, \
517
658
.guest_test_check = { _CHECK(_with_af) }, \
659
+ .expected_events = { 0 }, \
660
+ }
661
+
662
+ #define TEST_UFFD (_access , _with_af , _mark_cmd , \
663
+ _uffd_data_handler , _uffd_pt_handler , _uffd_faults ) \
664
+ { \
665
+ .name = SCAT4(uffd, _access, _with_af, #_mark_cmd), \
666
+ .guest_prepare = { _PREPARE(_with_af), \
667
+ _PREPARE(_access) }, \
668
+ .guest_test = _access, \
669
+ .mem_mark_cmd = _mark_cmd, \
670
+ .guest_test_check = { _CHECK(_with_af) }, \
671
+ .uffd_data_handler = _uffd_data_handler, \
672
+ .uffd_pt_handler = _uffd_pt_handler, \
673
+ .expected_events = { .uffd_faults = _uffd_faults, }, \
518
674
}
519
675
520
676
static struct test_desc tests [] = {
@@ -545,6 +701,37 @@ static struct test_desc tests[] = {
545
701
TEST_ACCESS (guest_at , no_af , CMD_HOLE_DATA ),
546
702
TEST_ACCESS (guest_dc_zva , no_af , CMD_HOLE_DATA ),
547
703
704
+ /*
705
+ * Punch holes in the data and PT backing stores and mark them for
706
+ * userfaultfd handling. This should result in 2 faults: the access
707
+ * on the data backing store, and its respective S1 page table walk
708
+ * (S1PTW).
709
+ */
710
+ TEST_UFFD (guest_read64 , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
711
+ uffd_data_read_handler , uffd_pt_write_handler , 2 ),
712
+ /* no_af should also lead to a PT write. */
713
+ TEST_UFFD (guest_read64 , no_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
714
+ uffd_data_read_handler , uffd_pt_write_handler , 2 ),
715
+ /* Note how that cas invokes the read handler. */
716
+ TEST_UFFD (guest_cas , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
717
+ uffd_data_read_handler , uffd_pt_write_handler , 2 ),
718
+ /*
719
+ * Can't test guest_at with_af as it's IMPDEF whether the AF is set.
720
+ * The S1PTW fault should still be marked as a write.
721
+ */
722
+ TEST_UFFD (guest_at , no_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
723
+ uffd_data_read_handler , uffd_pt_write_handler , 1 ),
724
+ TEST_UFFD (guest_ld_preidx , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
725
+ uffd_data_read_handler , uffd_pt_write_handler , 2 ),
726
+ TEST_UFFD (guest_write64 , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
727
+ uffd_data_write_handler , uffd_pt_write_handler , 2 ),
728
+ TEST_UFFD (guest_dc_zva , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
729
+ uffd_data_write_handler , uffd_pt_write_handler , 2 ),
730
+ TEST_UFFD (guest_st_preidx , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
731
+ uffd_data_write_handler , uffd_pt_write_handler , 2 ),
732
+ TEST_UFFD (guest_exec , with_af , CMD_HOLE_DATA | CMD_HOLE_PT ,
733
+ uffd_data_read_handler , uffd_pt_write_handler , 2 ),
734
+
548
735
{ 0 }
549
736
};
550
737
0 commit comments