2020-01-23 10:04:27 -08:00
// SPDX-License-Identifier: GPL-2.0
/*
* KVM demand paging test
* Adapted from dirty_log_test . c
*
* Copyright ( C ) 2018 , Red Hat , Inc .
* Copyright ( C ) 2019 , Google , Inc .
*/
2020-12-18 15:17:33 +01:00
# define _GNU_SOURCE /* for pipe2 */
2020-01-23 10:04:27 -08:00
# include <stdio.h>
# include <stdlib.h>
# include <time.h>
2020-02-20 18:09:12 +01:00
# include <poll.h>
2020-01-23 10:04:27 -08:00
# include <pthread.h>
2020-02-20 18:09:12 +01:00
# include <linux/userfaultfd.h>
2020-12-18 15:17:32 +01:00
# include <sys/syscall.h>
2020-01-23 10:04:27 -08:00
2020-12-18 15:17:32 +01:00
# include "kvm_util.h"
2020-10-27 16:37:29 -07:00
# include "test_util.h"
2020-12-18 15:17:32 +01:00
# include "perf_test_util.h"
# include "guest_modes.h"
2020-01-23 10:04:27 -08:00
2020-02-20 18:09:12 +01:00
# ifdef __NR_userfaultfd
2020-01-23 10:04:27 -08:00
2020-01-23 10:04:34 -08:00
# ifdef PRINT_PER_PAGE_UPDATES
2020-02-14 15:59:16 +01:00
# define PER_PAGE_DEBUG(...) printf(__VA_ARGS__)
2020-01-23 10:04:34 -08:00
# else
2020-02-14 15:59:16 +01:00
# define PER_PAGE_DEBUG(...) _no_printf(__VA_ARGS__)
2020-01-23 10:04:34 -08:00
# endif
2020-01-23 10:04:33 -08:00
# ifdef PRINT_PER_VCPU_UPDATES
2020-02-14 15:59:16 +01:00
# define PER_VCPU_DEBUG(...) printf(__VA_ARGS__)
2020-01-23 10:04:33 -08:00
# else
2020-02-14 15:59:16 +01:00
# define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__)
2020-01-23 10:04:33 -08:00
# endif
2020-02-20 18:09:12 +01:00
static char * guest_data_prototype ;
2020-01-23 10:04:27 -08:00
static void * vcpu_worker ( void * data )
{
int ret ;
2020-10-27 16:37:29 -07:00
struct vcpu_args * vcpu_args = ( struct vcpu_args * ) data ;
int vcpu_id = vcpu_args - > vcpu_id ;
struct kvm_vm * vm = perf_test_args . vm ;
2020-01-23 10:04:27 -08:00
struct kvm_run * run ;
2020-10-27 16:37:31 -07:00
struct timespec start ;
struct timespec ts_diff ;
2020-01-23 10:04:27 -08:00
2020-01-23 10:04:33 -08:00
vcpu_args_set ( vm , vcpu_id , 1 , vcpu_id ) ;
2020-01-23 10:04:31 -08:00
run = vcpu_state ( vm , vcpu_id ) ;
2020-01-23 10:04:27 -08:00
2020-01-23 10:04:34 -08:00
clock_gettime ( CLOCK_MONOTONIC , & start ) ;
2020-01-23 10:04:27 -08:00
/* Let the guest access its memory */
2020-01-23 10:04:31 -08:00
ret = _vcpu_run ( vm , vcpu_id ) ;
2020-01-23 10:04:27 -08:00
TEST_ASSERT ( ret = = 0 , " vcpu_run failed: %d \n " , ret ) ;
2020-01-23 10:04:31 -08:00
if ( get_ucall ( vm , vcpu_id , NULL ) ! = UCALL_SYNC ) {
2020-01-23 10:04:27 -08:00
TEST_ASSERT ( false ,
" Invalid guest sync status: exit_reason=%s \n " ,
exit_reason_str ( run - > exit_reason ) ) ;
}
2020-10-27 16:37:31 -07:00
ts_diff = timespec_diff_now ( start ) ;
2020-03-16 18:37:03 +01:00
PER_VCPU_DEBUG ( " vCPU %d execution time: %ld.%.9lds \n " , vcpu_id ,
ts_diff . tv_sec , ts_diff . tv_nsec ) ;
2020-01-23 10:04:34 -08:00
2020-01-23 10:04:27 -08:00
return NULL ;
}
2020-02-20 18:09:12 +01:00
static int handle_uffd_page_request ( int uffd , uint64_t addr )
{
pid_t tid ;
2020-01-23 10:04:34 -08:00
struct timespec start ;
2020-10-27 16:37:31 -07:00
struct timespec ts_diff ;
2020-02-20 18:09:12 +01:00
struct uffdio_copy copy ;
int r ;
tid = syscall ( __NR_gettid ) ;
copy . src = ( uint64_t ) guest_data_prototype ;
copy . dst = addr ;
2020-10-27 16:37:29 -07:00
copy . len = perf_test_args . host_page_size ;
2020-02-20 18:09:12 +01:00
copy . mode = 0 ;
2020-01-23 10:04:34 -08:00
clock_gettime ( CLOCK_MONOTONIC , & start ) ;
2020-02-20 18:09:12 +01:00
r = ioctl ( uffd , UFFDIO_COPY , & copy ) ;
if ( r = = - 1 ) {
2020-02-14 15:59:16 +01:00
pr_info ( " Failed Paged in 0x%lx from thread %d with errno: %d \n " ,
addr , tid , errno ) ;
2020-02-20 18:09:12 +01:00
return r ;
}
2020-10-27 16:37:31 -07:00
ts_diff = timespec_diff_now ( start ) ;
2020-01-23 10:04:34 -08:00
2020-03-16 18:37:03 +01:00
PER_PAGE_DEBUG ( " UFFDIO_COPY %d \t %ld ns \n " , tid ,
2020-10-27 16:37:31 -07:00
timespec_to_ns ( ts_diff ) ) ;
2020-01-23 10:04:34 -08:00
PER_PAGE_DEBUG ( " Paged in %ld bytes at 0x%lx from thread %d \n " ,
2020-10-27 16:37:29 -07:00
perf_test_args . host_page_size , addr , tid ) ;
2020-01-23 10:04:34 -08:00
2020-02-20 18:09:12 +01:00
return 0 ;
}
bool quit_uffd_thread ;
struct uffd_handler_args {
int uffd ;
int pipefd ;
2020-02-20 18:09:59 +01:00
useconds_t delay ;
2020-02-20 18:09:12 +01:00
} ;
static void * uffd_handler_thread_fn ( void * arg )
{
struct uffd_handler_args * uffd_args = ( struct uffd_handler_args * ) arg ;
int uffd = uffd_args - > uffd ;
int pipefd = uffd_args - > pipefd ;
2020-02-20 18:09:59 +01:00
useconds_t delay = uffd_args - > delay ;
2020-02-20 18:09:12 +01:00
int64_t pages = 0 ;
2020-10-27 16:37:31 -07:00
struct timespec start ;
struct timespec ts_diff ;
2020-02-20 18:09:12 +01:00
2020-01-23 10:04:34 -08:00
clock_gettime ( CLOCK_MONOTONIC , & start ) ;
2020-02-20 18:09:12 +01:00
while ( ! quit_uffd_thread ) {
struct uffd_msg msg ;
struct pollfd pollfd [ 2 ] ;
char tmp_chr ;
int r ;
uint64_t addr ;
pollfd [ 0 ] . fd = uffd ;
pollfd [ 0 ] . events = POLLIN ;
pollfd [ 1 ] . fd = pipefd ;
pollfd [ 1 ] . events = POLLIN ;
r = poll ( pollfd , 2 , - 1 ) ;
switch ( r ) {
case - 1 :
2020-02-14 15:59:16 +01:00
pr_info ( " poll err " ) ;
2020-02-20 18:09:12 +01:00
continue ;
case 0 :
continue ;
case 1 :
break ;
default :
2020-02-14 15:59:16 +01:00
pr_info ( " Polling uffd returned %d " , r ) ;
2020-02-20 18:09:12 +01:00
return NULL ;
}
if ( pollfd [ 0 ] . revents & POLLERR ) {
2020-02-14 15:59:16 +01:00
pr_info ( " uffd revents has POLLERR " ) ;
2020-02-20 18:09:12 +01:00
return NULL ;
}
if ( pollfd [ 1 ] . revents & POLLIN ) {
r = read ( pollfd [ 1 ] . fd , & tmp_chr , 1 ) ;
TEST_ASSERT ( r = = 1 ,
" Error reading pipefd in UFFD thread \n " ) ;
return NULL ;
}
if ( ! pollfd [ 0 ] . revents & POLLIN )
continue ;
r = read ( uffd , & msg , sizeof ( msg ) ) ;
if ( r = = - 1 ) {
if ( errno = = EAGAIN )
continue ;
2020-02-14 15:59:16 +01:00
pr_info ( " Read of uffd gor errno %d " , errno ) ;
2020-02-20 18:09:12 +01:00
return NULL ;
}
if ( r ! = sizeof ( msg ) ) {
2020-02-14 15:59:16 +01:00
pr_info ( " Read on uffd returned unexpected size: %d bytes " , r ) ;
2020-02-20 18:09:12 +01:00
return NULL ;
}
if ( ! ( msg . event & UFFD_EVENT_PAGEFAULT ) )
continue ;
2020-02-20 18:09:59 +01:00
if ( delay )
usleep ( delay ) ;
2020-02-20 18:09:12 +01:00
addr = msg . arg . pagefault . address ;
r = handle_uffd_page_request ( uffd , addr ) ;
if ( r < 0 )
return NULL ;
pages + + ;
}
2020-10-27 16:37:31 -07:00
ts_diff = timespec_diff_now ( start ) ;
2020-03-16 18:37:03 +01:00
PER_VCPU_DEBUG ( " userfaulted %ld pages over %ld.%.9lds. (%f/sec) \n " ,
pages , ts_diff . tv_sec , ts_diff . tv_nsec ,
pages / ( ( double ) ts_diff . tv_sec + ( double ) ts_diff . tv_nsec / 100000000.0 ) ) ;
2020-01-23 10:04:34 -08:00
2020-02-20 18:09:12 +01:00
return NULL ;
}
static int setup_demand_paging ( struct kvm_vm * vm ,
2020-02-20 18:09:59 +01:00
pthread_t * uffd_handler_thread , int pipefd ,
2020-01-23 10:04:33 -08:00
useconds_t uffd_delay ,
struct uffd_handler_args * uffd_args ,
void * hva , uint64_t len )
2020-02-20 18:09:12 +01:00
{
int uffd ;
struct uffdio_api uffdio_api ;
struct uffdio_register uffdio_register ;
uffd = syscall ( __NR_userfaultfd , O_CLOEXEC | O_NONBLOCK ) ;
if ( uffd = = - 1 ) {
2020-02-14 15:59:16 +01:00
pr_info ( " uffd creation failed \n " ) ;
2020-02-20 18:09:12 +01:00
return - 1 ;
}
uffdio_api . api = UFFD_API ;
uffdio_api . features = 0 ;
if ( ioctl ( uffd , UFFDIO_API , & uffdio_api ) = = - 1 ) {
2020-02-14 15:59:16 +01:00
pr_info ( " ioctl uffdio_api failed \n " ) ;
2020-02-20 18:09:12 +01:00
return - 1 ;
}
2020-01-23 10:04:33 -08:00
uffdio_register . range . start = ( uint64_t ) hva ;
uffdio_register . range . len = len ;
2020-02-20 18:09:12 +01:00
uffdio_register . mode = UFFDIO_REGISTER_MODE_MISSING ;
if ( ioctl ( uffd , UFFDIO_REGISTER , & uffdio_register ) = = - 1 ) {
2020-02-14 15:59:16 +01:00
pr_info ( " ioctl uffdio_register failed \n " ) ;
2020-02-20 18:09:12 +01:00
return - 1 ;
}
if ( ( uffdio_register . ioctls & UFFD_API_RANGE_IOCTLS ) ! =
UFFD_API_RANGE_IOCTLS ) {
2020-02-14 15:59:16 +01:00
pr_info ( " unexpected userfaultfd ioctl set \n " ) ;
2020-02-20 18:09:12 +01:00
return - 1 ;
}
2020-01-23 10:04:33 -08:00
uffd_args - > uffd = uffd ;
uffd_args - > pipefd = pipefd ;
uffd_args - > delay = uffd_delay ;
2020-02-20 18:09:12 +01:00
pthread_create ( uffd_handler_thread , NULL , uffd_handler_thread_fn ,
2020-01-23 10:04:33 -08:00
uffd_args ) ;
PER_VCPU_DEBUG ( " Created uffd thread for HVA range [%p, %p) \n " ,
hva , hva + len ) ;
2020-02-20 18:09:12 +01:00
return 0 ;
}
2020-12-18 15:17:32 +01:00
struct test_params {
bool use_uffd ;
useconds_t uffd_delay ;
} ;
static void run_test ( enum vm_guest_mode mode , void * arg )
2020-01-23 10:04:27 -08:00
{
2020-12-18 15:17:32 +01:00
struct test_params * p = arg ;
2020-01-23 10:04:33 -08:00
pthread_t * vcpu_threads ;
pthread_t * uffd_handler_threads = NULL ;
struct uffd_handler_args * uffd_args = NULL ;
2020-10-27 16:37:31 -07:00
struct timespec start ;
struct timespec ts_diff ;
2020-01-23 10:04:33 -08:00
int * pipefds = NULL ;
2020-01-23 10:04:27 -08:00
struct kvm_vm * vm ;
2020-01-23 10:04:33 -08:00
int vcpu_id ;
2020-02-20 18:09:12 +01:00
int r ;
2020-01-23 10:04:27 -08:00
2020-11-04 22:23:53 +01:00
vm = create_vm ( mode , nr_vcpus , guest_percpu_mem_size ) ;
2020-01-23 10:04:27 -08:00
2020-10-27 16:37:32 -07:00
perf_test_args . wr_fract = 1 ;
2020-10-27 16:37:29 -07:00
guest_data_prototype = malloc ( perf_test_args . host_page_size ) ;
2020-01-23 10:04:33 -08:00
TEST_ASSERT ( guest_data_prototype ,
" Failed to allocate buffer for guest data pattern " ) ;
2020-10-27 16:37:29 -07:00
memset ( guest_data_prototype , 0xAB , perf_test_args . host_page_size ) ;
2020-01-23 10:04:33 -08:00
2020-11-04 22:23:53 +01:00
vcpu_threads = malloc ( nr_vcpus * sizeof ( * vcpu_threads ) ) ;
2020-01-23 10:04:33 -08:00
TEST_ASSERT ( vcpu_threads , " Memory allocation failed " ) ;
2020-01-23 10:04:27 -08:00
2020-11-04 22:23:53 +01:00
add_vcpus ( vm , nr_vcpus , guest_percpu_mem_size ) ;
2020-10-27 16:37:29 -07:00
2020-12-18 15:17:32 +01:00
if ( p - > use_uffd ) {
2020-01-23 10:04:33 -08:00
uffd_handler_threads =
2020-11-04 22:23:53 +01:00
malloc ( nr_vcpus * sizeof ( * uffd_handler_threads ) ) ;
2020-01-23 10:04:33 -08:00
TEST_ASSERT ( uffd_handler_threads , " Memory allocation failed " ) ;
2020-02-20 18:09:12 +01:00
2020-11-04 22:23:53 +01:00
uffd_args = malloc ( nr_vcpus * sizeof ( * uffd_args ) ) ;
2020-01-23 10:04:33 -08:00
TEST_ASSERT ( uffd_args , " Memory allocation failed " ) ;
2020-11-04 22:23:53 +01:00
pipefds = malloc ( sizeof ( int ) * nr_vcpus * 2 ) ;
2020-01-23 10:04:33 -08:00
TEST_ASSERT ( pipefds , " Unable to allocate memory for pipefd " ) ;
2020-11-04 22:23:53 +01:00
for ( vcpu_id = 0 ; vcpu_id < nr_vcpus ; vcpu_id + + ) {
2020-10-27 16:37:29 -07:00
vm_paddr_t vcpu_gpa ;
void * vcpu_hva ;
2020-01-23 10:04:33 -08:00
2020-11-04 22:23:52 +01:00
vcpu_gpa = guest_test_phys_mem + ( vcpu_id * guest_percpu_mem_size ) ;
2020-10-27 16:37:29 -07:00
PER_VCPU_DEBUG ( " Added VCPU %d with test mem gpa [%lx, %lx) \n " ,
2020-11-04 22:23:52 +01:00
vcpu_id , vcpu_gpa , vcpu_gpa + guest_percpu_mem_size ) ;
2020-01-23 10:04:33 -08:00
2020-10-27 16:37:29 -07:00
/* Cache the HVA pointer of the region */
vcpu_hva = addr_gpa2hva ( vm , vcpu_gpa ) ;
2020-01-23 10:04:33 -08:00
/*
* Set up user fault fd to handle demand paging
* requests .
*/
r = pipe2 ( & pipefds [ vcpu_id * 2 ] ,
O_CLOEXEC | O_NONBLOCK ) ;
TEST_ASSERT ( ! r , " Failed to set up pipefd " ) ;
r = setup_demand_paging ( vm ,
& uffd_handler_threads [ vcpu_id ] ,
pipefds [ vcpu_id * 2 ] ,
2020-12-18 15:17:32 +01:00
p - > uffd_delay , & uffd_args [ vcpu_id ] ,
2020-11-04 22:23:52 +01:00
vcpu_hva , guest_percpu_mem_size ) ;
2020-01-23 10:04:33 -08:00
if ( r < 0 )
exit ( - r ) ;
}
}
2020-01-23 10:04:27 -08:00
/* Export the shared variables to the guest */
2020-10-27 16:37:29 -07:00
sync_global_to_guest ( vm , perf_test_args ) ;
2020-01-23 10:04:27 -08:00
2020-02-14 15:59:16 +01:00
pr_info ( " Finished creating vCPUs and starting uffd threads \n " ) ;
2020-01-23 10:04:33 -08:00
2020-01-23 10:04:34 -08:00
clock_gettime ( CLOCK_MONOTONIC , & start ) ;
2020-11-04 22:23:53 +01:00
for ( vcpu_id = 0 ; vcpu_id < nr_vcpus ; vcpu_id + + ) {
2020-01-23 10:04:33 -08:00
pthread_create ( & vcpu_threads [ vcpu_id ] , NULL , vcpu_worker ,
2020-10-27 16:37:29 -07:00
& perf_test_args . vcpu_args [ vcpu_id ] ) ;
2020-01-23 10:04:33 -08:00
}
2020-02-14 15:59:16 +01:00
pr_info ( " Started all vCPUs \n " ) ;
2020-01-23 10:04:33 -08:00
/* Wait for the vcpu threads to quit */
2020-11-04 22:23:53 +01:00
for ( vcpu_id = 0 ; vcpu_id < nr_vcpus ; vcpu_id + + ) {
2020-01-23 10:04:33 -08:00
pthread_join ( vcpu_threads [ vcpu_id ] , NULL ) ;
PER_VCPU_DEBUG ( " Joined thread for vCPU %d \n " , vcpu_id ) ;
}
2020-10-27 16:37:31 -07:00
ts_diff = timespec_diff_now ( start ) ;
2020-01-23 10:04:27 -08:00
2020-10-27 16:37:31 -07:00
pr_info ( " All vCPU threads joined \n " ) ;
2020-01-23 10:04:34 -08:00
2020-12-18 15:17:32 +01:00
if ( p - > use_uffd ) {
2020-02-20 18:09:12 +01:00
char c ;
2020-01-23 10:04:33 -08:00
/* Tell the user fault fd handler threads to quit */
2020-11-04 22:23:53 +01:00
for ( vcpu_id = 0 ; vcpu_id < nr_vcpus ; vcpu_id + + ) {
2020-01-23 10:04:33 -08:00
r = write ( pipefds [ vcpu_id * 2 + 1 ] , & c , 1 ) ;
TEST_ASSERT ( r = = 1 , " Unable to write to pipefd " ) ;
2020-02-20 18:09:12 +01:00
2020-01-23 10:04:33 -08:00
pthread_join ( uffd_handler_threads [ vcpu_id ] , NULL ) ;
}
2020-02-20 18:09:12 +01:00
}
2020-03-16 18:37:03 +01:00
pr_info ( " Total guest execution time: %ld.%.9lds \n " ,
ts_diff . tv_sec , ts_diff . tv_nsec ) ;
2020-02-14 15:59:16 +01:00
pr_info ( " Overall demand paging rate: %f pgs/sec \n " ,
2020-11-04 22:23:53 +01:00
perf_test_args . vcpu_args [ 0 ] . pages * nr_vcpus /
2020-10-27 16:37:29 -07:00
( ( double ) ts_diff . tv_sec + ( double ) ts_diff . tv_nsec / 100000000.0 ) ) ;
2020-01-23 10:04:34 -08:00
2020-01-23 10:04:27 -08:00
ucall_uninit ( vm ) ;
kvm_vm_free ( vm ) ;
2020-02-20 18:09:12 +01:00
free ( guest_data_prototype ) ;
2020-01-23 10:04:33 -08:00
free ( vcpu_threads ) ;
2020-12-18 15:17:32 +01:00
if ( p - > use_uffd ) {
2020-01-23 10:04:33 -08:00
free ( uffd_handler_threads ) ;
free ( uffd_args ) ;
free ( pipefds ) ;
}
2020-01-23 10:04:27 -08:00
}
static void help ( char * name )
{
puts ( " " ) ;
2020-01-23 10:04:30 -08:00
printf ( " usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec] \n "
2020-01-23 10:04:33 -08:00
" [-b memory] [-v vcpus] \n " , name ) ;
2020-12-18 15:17:32 +01:00
guest_modes_help ( ) ;
2020-02-20 18:09:59 +01:00
printf ( " -u: use User Fault FD to handle vCPU page \n "
" faults. \n " ) ;
printf ( " -d: add a delay in usec to the User Fault \n "
" FD handler to simulate demand paging \n "
" overheads. Ignored without -u. \n " ) ;
2020-01-23 10:04:30 -08:00
printf ( " -b: specify the size of the memory region which should be \n "
2020-01-23 10:04:33 -08:00
" demand paged by each vCPU. e.g. 10M or 3G. \n "
" Default: 1G \n " ) ;
printf ( " -v: specify the number of vCPUs to run. \n " ) ;
2020-01-23 10:04:27 -08:00
puts ( " " ) ;
exit ( 0 ) ;
}
int main ( int argc , char * argv [ ] )
{
2020-11-04 22:23:53 +01:00
int max_vcpus = kvm_check_cap ( KVM_CAP_MAX_VCPUS ) ;
2020-12-18 15:17:32 +01:00
struct test_params p = { } ;
int opt ;
guest_modes_append_default ( ) ;
2020-01-23 10:04:27 -08:00
2020-01-23 10:04:33 -08:00
while ( ( opt = getopt ( argc , argv , " hm:ud:b:v: " ) ) ! = - 1 ) {
2020-01-23 10:04:27 -08:00
switch ( opt ) {
case ' m ' :
2020-12-18 15:17:32 +01:00
guest_modes_cmdline ( optarg ) ;
2020-01-23 10:04:27 -08:00
break ;
2020-02-20 18:09:12 +01:00
case ' u ' :
2020-12-18 15:17:32 +01:00
p . use_uffd = true ;
2020-02-20 18:09:12 +01:00
break ;
2020-02-20 18:09:59 +01:00
case ' d ' :
2020-12-18 15:17:32 +01:00
p . uffd_delay = strtoul ( optarg , NULL , 0 ) ;
TEST_ASSERT ( p . uffd_delay > = 0 , " A negative UFFD delay is not supported. " ) ;
2020-02-20 18:09:59 +01:00
break ;
2020-01-23 10:04:30 -08:00
case ' b ' :
2020-11-04 22:23:52 +01:00
guest_percpu_mem_size = parse_size ( optarg ) ;
2020-01-23 10:04:33 -08:00
break ;
case ' v ' :
2020-11-04 22:23:53 +01:00
nr_vcpus = atoi ( optarg ) ;
TEST_ASSERT ( nr_vcpus > 0 & & nr_vcpus < = max_vcpus ,
" Invalid number of vcpus, must be between 1 and %d " , max_vcpus ) ;
2020-01-23 10:04:30 -08:00
break ;
2020-01-23 10:04:27 -08:00
case ' h ' :
default :
help ( argv [ 0 ] ) ;
break ;
}
}
2020-12-18 15:17:32 +01:00
for_each_guest_mode ( run_test , & p ) ;
2020-01-23 10:04:27 -08:00
return 0 ;
}
2020-02-20 18:09:12 +01:00
# else /* __NR_userfaultfd */
# warning "missing __NR_userfaultfd definition"
int main ( void )
{
2020-03-10 10:15:56 +01:00
print_skip ( " __NR_userfaultfd must be present for userfaultfd test " ) ;
return KSFT_SKIP ;
2020-02-20 18:09:12 +01:00
}
# endif /* __NR_userfaultfd */