@@ -227,25 +227,65 @@ static int pagemap_open(void)
227
227
err("pagemap uffd-wp bit error: 0x%"PRIx64, value); \
228
228
} while (0)
229
229
230
- static int pagemap_test_fork (bool present )
230
+ typedef struct {
231
+ int parent_uffd , child_uffd ;
232
+ } fork_event_args ;
233
+
234
+ static void * fork_event_consumer (void * data )
231
235
{
232
- pid_t child = fork ();
236
+ fork_event_args * args = data ;
237
+ struct uffd_msg msg = { 0 };
238
+
239
+ /* Read until a full msg received */
240
+ while (uffd_read_msg (args -> parent_uffd , & msg ));
241
+
242
+ if (msg .event != UFFD_EVENT_FORK )
243
+ err ("wrong message: %u\n" , msg .event );
244
+
245
+ /* Just to be properly freed later */
246
+ args -> child_uffd = msg .arg .fork .ufd ;
247
+ return NULL ;
248
+ }
249
+
250
+ static int pagemap_test_fork (int uffd , bool with_event )
251
+ {
252
+ fork_event_args args = { .parent_uffd = uffd , .child_uffd = -1 };
253
+ pthread_t thread ;
254
+ pid_t child ;
233
255
uint64_t value ;
234
256
int fd , result ;
235
257
258
+ /* Prepare a thread to resolve EVENT_FORK */
259
+ if (with_event ) {
260
+ if (pthread_create (& thread , NULL , fork_event_consumer , & args ))
261
+ err ("pthread_create()" );
262
+ }
263
+
264
+ child = fork ();
236
265
if (!child ) {
237
266
/* Open the pagemap fd of the child itself */
238
267
fd = pagemap_open ();
239
268
value = pagemap_get_entry (fd , area_dst );
240
269
/*
241
- * After fork() uffd-wp bit should be gone as long as we're
242
- * without UFFD_FEATURE_EVENT_FORK
270
+ * After fork(), we should handle uffd-wp bit differently:
271
+ *
272
+ * (1) when with EVENT_FORK, it should persist
273
+ * (2) when without EVENT_FORK, it should be dropped
243
274
*/
244
- pagemap_check_wp (value , false );
275
+ pagemap_check_wp (value , with_event );
245
276
/* Succeed */
246
277
exit (0 );
247
278
}
248
279
waitpid (child , & result , 0 );
280
+
281
+ if (with_event ) {
282
+ if (pthread_join (thread , NULL ))
283
+ err ("pthread_join()" );
284
+ if (args .child_uffd < 0 )
285
+ err ("Didn't receive child uffd" );
286
+ close (args .child_uffd );
287
+ }
288
+
249
289
return result ;
250
290
}
251
291
@@ -295,7 +335,8 @@ static void uffd_wp_unpopulated_test(uffd_test_args_t *args)
295
335
uffd_test_pass ();
296
336
}
297
337
298
- static void uffd_pagemap_test (uffd_test_args_t * args )
338
+ static void uffd_wp_fork_test_common (uffd_test_args_t * args ,
339
+ bool with_event )
299
340
{
300
341
int pagemap_fd ;
301
342
uint64_t value ;
@@ -311,23 +352,42 @@ static void uffd_pagemap_test(uffd_test_args_t *args)
311
352
wp_range (uffd , (uint64_t )area_dst , page_size , true);
312
353
value = pagemap_get_entry (pagemap_fd , area_dst );
313
354
pagemap_check_wp (value , true);
314
- /* Make sure uffd-wp bit dropped when fork */
315
- if (pagemap_test_fork (true))
316
- err ("Detected stall uffd-wp bit in child" );
317
-
318
- /* Exclusive required or PAGEOUT won't work */
319
- if (!(value & PM_MMAP_EXCLUSIVE ))
320
- err ("multiple mapping detected: 0x%" PRIx64 , value );
355
+ if (pagemap_test_fork (uffd , with_event )) {
356
+ uffd_test_fail ("Detected %s uffd-wp bit in child in present pte" ,
357
+ with_event ? "missing" : "stall" );
358
+ goto out ;
359
+ }
321
360
322
- if (madvise (area_dst , page_size , MADV_PAGEOUT ))
323
- err ("madvise(MADV_PAGEOUT) failed" );
361
+ /*
362
+ * This is an attempt for zapping the pgtable so as to test the
363
+ * markers.
364
+ *
365
+ * For private mappings, PAGEOUT will only work on exclusive ptes
366
+ * (PM_MMAP_EXCLUSIVE) which we should satisfy.
367
+ *
368
+ * For shared, PAGEOUT may not work. Use DONTNEED instead which
369
+ * plays a similar role of zapping (rather than freeing the page)
370
+ * to expose pte markers.
371
+ */
372
+ if (args -> mem_type -> shared ) {
373
+ if (madvise (area_dst , page_size , MADV_DONTNEED ))
374
+ err ("MADV_DONTNEED" );
375
+ } else {
376
+ /*
377
+ * NOTE: ignore retval because private-hugetlb doesn't yet
378
+ * support swapping, so it could fail.
379
+ */
380
+ madvise (area_dst , page_size , MADV_PAGEOUT );
381
+ }
324
382
325
383
/* Uffd-wp should persist even swapped out */
326
384
value = pagemap_get_entry (pagemap_fd , area_dst );
327
385
pagemap_check_wp (value , true);
328
- /* Make sure uffd-wp bit dropped when fork */
329
- if (pagemap_test_fork (false))
330
- err ("Detected stall uffd-wp bit in child" );
386
+ if (pagemap_test_fork (uffd , with_event )) {
387
+ uffd_test_fail ("Detected %s uffd-wp bit in child in zapped pte" ,
388
+ with_event ? "missing" : "stall" );
389
+ goto out ;
390
+ }
331
391
332
392
/* Unprotect; this tests swap pte modifications */
333
393
wp_range (uffd , (uint64_t )area_dst , page_size , false);
@@ -338,9 +398,21 @@ static void uffd_pagemap_test(uffd_test_args_t *args)
338
398
* area_dst = 2 ;
339
399
value = pagemap_get_entry (pagemap_fd , area_dst );
340
400
pagemap_check_wp (value , false);
341
-
342
- close (pagemap_fd );
343
401
uffd_test_pass ();
402
+ out :
403
+ if (uffd_unregister (uffd , area_dst , nr_pages * page_size ))
404
+ err ("unregister failed" );
405
+ close (pagemap_fd );
406
+ }
407
+
408
+ static void uffd_wp_fork_test (uffd_test_args_t * args )
409
+ {
410
+ uffd_wp_fork_test_common (args , false);
411
+ }
412
+
413
+ static void uffd_wp_fork_with_event_test (uffd_test_args_t * args )
414
+ {
415
+ uffd_wp_fork_test_common (args , true);
344
416
}
345
417
346
418
static void check_memory_contents (char * p )
@@ -836,10 +908,20 @@ uffd_test_case_t uffd_tests[] = {
836
908
.uffd_feature_required = 0 ,
837
909
},
838
910
{
839
- .name = "pagemap" ,
840
- .uffd_fn = uffd_pagemap_test ,
841
- .mem_targets = MEM_ANON ,
842
- .uffd_feature_required = UFFD_FEATURE_PAGEFAULT_FLAG_WP ,
911
+ .name = "wp-fork" ,
912
+ .uffd_fn = uffd_wp_fork_test ,
913
+ .mem_targets = MEM_ALL ,
914
+ .uffd_feature_required = UFFD_FEATURE_PAGEFAULT_FLAG_WP |
915
+ UFFD_FEATURE_WP_HUGETLBFS_SHMEM ,
916
+ },
917
+ {
918
+ .name = "wp-fork-with-event" ,
919
+ .uffd_fn = uffd_wp_fork_with_event_test ,
920
+ .mem_targets = MEM_ALL ,
921
+ .uffd_feature_required = UFFD_FEATURE_PAGEFAULT_FLAG_WP |
922
+ UFFD_FEATURE_WP_HUGETLBFS_SHMEM |
923
+ /* when set, child process should inherit uffd-wp bits */
924
+ UFFD_FEATURE_EVENT_FORK ,
843
925
},
844
926
{
845
927
.name = "wp-unpopulated" ,
0 commit comments