162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Test handling of code that might set PTE/PMD dirty in read-only VMAs. 462306a36Sopenharmony_ci * Setting a PTE/PMD dirty must not accidentally set the PTE/PMD writable. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 2023, Red Hat, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author(s): David Hildenbrand <david@redhat.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <fcntl.h> 1162306a36Sopenharmony_ci#include <signal.h> 1262306a36Sopenharmony_ci#include <unistd.h> 1362306a36Sopenharmony_ci#include <string.h> 1462306a36Sopenharmony_ci#include <errno.h> 1562306a36Sopenharmony_ci#include <stdlib.h> 1662306a36Sopenharmony_ci#include <stdbool.h> 1762306a36Sopenharmony_ci#include <stdint.h> 1862306a36Sopenharmony_ci#include <sys/mman.h> 1962306a36Sopenharmony_ci#include <setjmp.h> 2062306a36Sopenharmony_ci#include <sys/syscall.h> 2162306a36Sopenharmony_ci#include <sys/ioctl.h> 2262306a36Sopenharmony_ci#include <linux/userfaultfd.h> 2362306a36Sopenharmony_ci#include <linux/mempolicy.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "../kselftest.h" 2662306a36Sopenharmony_ci#include "vm_util.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic size_t pagesize; 2962306a36Sopenharmony_cistatic size_t thpsize; 3062306a36Sopenharmony_cistatic int mem_fd; 3162306a36Sopenharmony_cistatic int pagemap_fd; 3262306a36Sopenharmony_cistatic sigjmp_buf env; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void signal_handler(int sig) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci if (sig == SIGSEGV) 3762306a36Sopenharmony_ci siglongjmp(env, 1); 3862306a36Sopenharmony_ci siglongjmp(env, 2); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void do_test_write_sigsegv(char *mem) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci char orig = *mem; 4462306a36Sopenharmony_ci int ret; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (signal(SIGSEGV, signal_handler) == SIG_ERR) { 4762306a36Sopenharmony_ci ksft_test_result_fail("signal() failed\n"); 4862306a36Sopenharmony_ci return; 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ret = sigsetjmp(env, 1); 5262306a36Sopenharmony_ci if (!ret) 5362306a36Sopenharmony_ci *mem = orig + 1; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (signal(SIGSEGV, SIG_DFL) == SIG_ERR) 5662306a36Sopenharmony_ci ksft_test_result_fail("signal() failed\n"); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ksft_test_result(ret == 1 && *mem == orig, 5962306a36Sopenharmony_ci "SIGSEGV generated, page not modified\n"); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic char *mmap_thp_range(int prot, char **_mmap_mem, size_t *_mmap_size) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci const size_t mmap_size = 2 * thpsize; 6562306a36Sopenharmony_ci char *mem, *mmap_mem; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci mmap_mem = mmap(NULL, mmap_size, prot, MAP_PRIVATE|MAP_ANON, 6862306a36Sopenharmony_ci -1, 0); 6962306a36Sopenharmony_ci if (mmap_mem == MAP_FAILED) { 7062306a36Sopenharmony_ci ksft_test_result_fail("mmap() failed\n"); 7162306a36Sopenharmony_ci return MAP_FAILED; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci mem = (char *)(((uintptr_t)mmap_mem + thpsize) & ~(thpsize - 1)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (madvise(mem, thpsize, MADV_HUGEPAGE)) { 7662306a36Sopenharmony_ci ksft_test_result_skip("MADV_HUGEPAGE failed\n"); 7762306a36Sopenharmony_ci munmap(mmap_mem, mmap_size); 7862306a36Sopenharmony_ci return MAP_FAILED; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci *_mmap_mem = mmap_mem; 8262306a36Sopenharmony_ci *_mmap_size = mmap_size; 8362306a36Sopenharmony_ci return mem; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void test_ptrace_write(void) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci char data = 1; 8962306a36Sopenharmony_ci char *mem; 9062306a36Sopenharmony_ci int ret; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ksft_print_msg("[INFO] PTRACE write access\n"); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci mem = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0); 9562306a36Sopenharmony_ci if (mem == MAP_FAILED) { 9662306a36Sopenharmony_ci ksft_test_result_fail("mmap() failed\n"); 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Fault in the shared zeropage. */ 10162306a36Sopenharmony_ci if (*mem != 0) { 10262306a36Sopenharmony_ci ksft_test_result_fail("Memory not zero\n"); 10362306a36Sopenharmony_ci goto munmap; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* 10762306a36Sopenharmony_ci * Unshare the page (populating a fresh anon page that might be set 10862306a36Sopenharmony_ci * dirty in the PTE) in the read-only VMA using ptrace (FOLL_FORCE). 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci lseek(mem_fd, (uintptr_t) mem, SEEK_SET); 11162306a36Sopenharmony_ci ret = write(mem_fd, &data, 1); 11262306a36Sopenharmony_ci if (ret != 1 || *mem != data) { 11362306a36Sopenharmony_ci ksft_test_result_fail("write() failed\n"); 11462306a36Sopenharmony_ci goto munmap; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci do_test_write_sigsegv(mem); 11862306a36Sopenharmony_cimunmap: 11962306a36Sopenharmony_ci munmap(mem, pagesize); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void test_ptrace_write_thp(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci char *mem, *mmap_mem; 12562306a36Sopenharmony_ci size_t mmap_size; 12662306a36Sopenharmony_ci char data = 1; 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ksft_print_msg("[INFO] PTRACE write access to THP\n"); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci mem = mmap_thp_range(PROT_READ, &mmap_mem, &mmap_size); 13262306a36Sopenharmony_ci if (mem == MAP_FAILED) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * Write to the first subpage in the read-only VMA using 13762306a36Sopenharmony_ci * ptrace(FOLL_FORCE), eventually placing a fresh THP that is marked 13862306a36Sopenharmony_ci * dirty in the PMD. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci lseek(mem_fd, (uintptr_t) mem, SEEK_SET); 14162306a36Sopenharmony_ci ret = write(mem_fd, &data, 1); 14262306a36Sopenharmony_ci if (ret != 1 || *mem != data) { 14362306a36Sopenharmony_ci ksft_test_result_fail("write() failed\n"); 14462306a36Sopenharmony_ci goto munmap; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* MM populated a THP if we got the last subpage populated as well. */ 14862306a36Sopenharmony_ci if (!pagemap_is_populated(pagemap_fd, mem + thpsize - pagesize)) { 14962306a36Sopenharmony_ci ksft_test_result_skip("Did not get a THP populated\n"); 15062306a36Sopenharmony_ci goto munmap; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci do_test_write_sigsegv(mem); 15462306a36Sopenharmony_cimunmap: 15562306a36Sopenharmony_ci munmap(mmap_mem, mmap_size); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void test_page_migration(void) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci char *mem; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ksft_print_msg("[INFO] Page migration\n"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci mem = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, 16562306a36Sopenharmony_ci -1, 0); 16662306a36Sopenharmony_ci if (mem == MAP_FAILED) { 16762306a36Sopenharmony_ci ksft_test_result_fail("mmap() failed\n"); 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Populate a fresh page and dirty it. */ 17262306a36Sopenharmony_ci memset(mem, 1, pagesize); 17362306a36Sopenharmony_ci if (mprotect(mem, pagesize, PROT_READ)) { 17462306a36Sopenharmony_ci ksft_test_result_fail("mprotect() failed\n"); 17562306a36Sopenharmony_ci goto munmap; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Trigger page migration. Might not be available or fail. */ 17962306a36Sopenharmony_ci if (syscall(__NR_mbind, mem, pagesize, MPOL_LOCAL, NULL, 0x7fful, 18062306a36Sopenharmony_ci MPOL_MF_MOVE)) { 18162306a36Sopenharmony_ci ksft_test_result_skip("mbind() failed\n"); 18262306a36Sopenharmony_ci goto munmap; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci do_test_write_sigsegv(mem); 18662306a36Sopenharmony_cimunmap: 18762306a36Sopenharmony_ci munmap(mem, pagesize); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void test_page_migration_thp(void) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci char *mem, *mmap_mem; 19362306a36Sopenharmony_ci size_t mmap_size; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ksft_print_msg("[INFO] Page migration of THP\n"); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mem = mmap_thp_range(PROT_READ|PROT_WRITE, &mmap_mem, &mmap_size); 19862306a36Sopenharmony_ci if (mem == MAP_FAILED) 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Write to the first page, which might populate a fresh anon THP 20362306a36Sopenharmony_ci * and dirty it. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci memset(mem, 1, pagesize); 20662306a36Sopenharmony_ci if (mprotect(mem, thpsize, PROT_READ)) { 20762306a36Sopenharmony_ci ksft_test_result_fail("mprotect() failed\n"); 20862306a36Sopenharmony_ci goto munmap; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* MM populated a THP if we got the last subpage populated as well. */ 21262306a36Sopenharmony_ci if (!pagemap_is_populated(pagemap_fd, mem + thpsize - pagesize)) { 21362306a36Sopenharmony_ci ksft_test_result_skip("Did not get a THP populated\n"); 21462306a36Sopenharmony_ci goto munmap; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Trigger page migration. Might not be available or fail. */ 21862306a36Sopenharmony_ci if (syscall(__NR_mbind, mem, thpsize, MPOL_LOCAL, NULL, 0x7fful, 21962306a36Sopenharmony_ci MPOL_MF_MOVE)) { 22062306a36Sopenharmony_ci ksft_test_result_skip("mbind() failed\n"); 22162306a36Sopenharmony_ci goto munmap; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci do_test_write_sigsegv(mem); 22562306a36Sopenharmony_cimunmap: 22662306a36Sopenharmony_ci munmap(mmap_mem, mmap_size); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void test_pte_mapped_thp(void) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci char *mem, *mmap_mem; 23262306a36Sopenharmony_ci size_t mmap_size; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ksft_print_msg("[INFO] PTE-mapping a THP\n"); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci mem = mmap_thp_range(PROT_READ|PROT_WRITE, &mmap_mem, &mmap_size); 23762306a36Sopenharmony_ci if (mem == MAP_FAILED) 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* 24162306a36Sopenharmony_ci * Write to the first page, which might populate a fresh anon THP 24262306a36Sopenharmony_ci * and dirty it. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci memset(mem, 1, pagesize); 24562306a36Sopenharmony_ci if (mprotect(mem, thpsize, PROT_READ)) { 24662306a36Sopenharmony_ci ksft_test_result_fail("mprotect() failed\n"); 24762306a36Sopenharmony_ci goto munmap; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* MM populated a THP if we got the last subpage populated as well. */ 25162306a36Sopenharmony_ci if (!pagemap_is_populated(pagemap_fd, mem + thpsize - pagesize)) { 25262306a36Sopenharmony_ci ksft_test_result_skip("Did not get a THP populated\n"); 25362306a36Sopenharmony_ci goto munmap; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Trigger PTE-mapping the THP by mprotect'ing the last subpage. */ 25762306a36Sopenharmony_ci if (mprotect(mem + thpsize - pagesize, pagesize, 25862306a36Sopenharmony_ci PROT_READ|PROT_WRITE)) { 25962306a36Sopenharmony_ci ksft_test_result_fail("mprotect() failed\n"); 26062306a36Sopenharmony_ci goto munmap; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci do_test_write_sigsegv(mem); 26462306a36Sopenharmony_cimunmap: 26562306a36Sopenharmony_ci munmap(mmap_mem, mmap_size); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci#ifdef __NR_userfaultfd 26962306a36Sopenharmony_cistatic void test_uffdio_copy(void) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct uffdio_register uffdio_register; 27262306a36Sopenharmony_ci struct uffdio_copy uffdio_copy; 27362306a36Sopenharmony_ci struct uffdio_api uffdio_api; 27462306a36Sopenharmony_ci char *dst, *src; 27562306a36Sopenharmony_ci int uffd; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ksft_print_msg("[INFO] UFFDIO_COPY\n"); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci src = malloc(pagesize); 28062306a36Sopenharmony_ci memset(src, 1, pagesize); 28162306a36Sopenharmony_ci dst = mmap(NULL, pagesize, PROT_READ, MAP_PRIVATE|MAP_ANON, -1, 0); 28262306a36Sopenharmony_ci if (dst == MAP_FAILED) { 28362306a36Sopenharmony_ci ksft_test_result_fail("mmap() failed\n"); 28462306a36Sopenharmony_ci return; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); 28862306a36Sopenharmony_ci if (uffd < 0) { 28962306a36Sopenharmony_ci ksft_test_result_skip("__NR_userfaultfd failed\n"); 29062306a36Sopenharmony_ci goto munmap; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci uffdio_api.api = UFFD_API; 29462306a36Sopenharmony_ci uffdio_api.features = 0; 29562306a36Sopenharmony_ci if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) { 29662306a36Sopenharmony_ci ksft_test_result_fail("UFFDIO_API failed\n"); 29762306a36Sopenharmony_ci goto close_uffd; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci uffdio_register.range.start = (unsigned long) dst; 30162306a36Sopenharmony_ci uffdio_register.range.len = pagesize; 30262306a36Sopenharmony_ci uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; 30362306a36Sopenharmony_ci if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) { 30462306a36Sopenharmony_ci ksft_test_result_fail("UFFDIO_REGISTER failed\n"); 30562306a36Sopenharmony_ci goto close_uffd; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Place a page in a read-only VMA, which might set the PTE dirty. */ 30962306a36Sopenharmony_ci uffdio_copy.dst = (unsigned long) dst; 31062306a36Sopenharmony_ci uffdio_copy.src = (unsigned long) src; 31162306a36Sopenharmony_ci uffdio_copy.len = pagesize; 31262306a36Sopenharmony_ci uffdio_copy.mode = 0; 31362306a36Sopenharmony_ci if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy)) { 31462306a36Sopenharmony_ci ksft_test_result_fail("UFFDIO_COPY failed\n"); 31562306a36Sopenharmony_ci goto close_uffd; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci do_test_write_sigsegv(dst); 31962306a36Sopenharmony_ciclose_uffd: 32062306a36Sopenharmony_ci close(uffd); 32162306a36Sopenharmony_cimunmap: 32262306a36Sopenharmony_ci munmap(dst, pagesize); 32362306a36Sopenharmony_ci free(src); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci#endif /* __NR_userfaultfd */ 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciint main(void) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int err, tests = 2; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci pagesize = getpagesize(); 33262306a36Sopenharmony_ci thpsize = read_pmd_pagesize(); 33362306a36Sopenharmony_ci if (thpsize) { 33462306a36Sopenharmony_ci ksft_print_msg("[INFO] detected THP size: %zu KiB\n", 33562306a36Sopenharmony_ci thpsize / 1024); 33662306a36Sopenharmony_ci tests += 3; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci#ifdef __NR_userfaultfd 33962306a36Sopenharmony_ci tests += 1; 34062306a36Sopenharmony_ci#endif /* __NR_userfaultfd */ 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ksft_print_header(); 34362306a36Sopenharmony_ci ksft_set_plan(tests); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci mem_fd = open("/proc/self/mem", O_RDWR); 34662306a36Sopenharmony_ci if (mem_fd < 0) 34762306a36Sopenharmony_ci ksft_exit_fail_msg("opening /proc/self/mem failed\n"); 34862306a36Sopenharmony_ci pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 34962306a36Sopenharmony_ci if (pagemap_fd < 0) 35062306a36Sopenharmony_ci ksft_exit_fail_msg("opening /proc/self/pagemap failed\n"); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* 35362306a36Sopenharmony_ci * On some ptrace(FOLL_FORCE) write access via /proc/self/mem in 35462306a36Sopenharmony_ci * read-only VMAs, the kernel may set the PTE/PMD dirty. 35562306a36Sopenharmony_ci */ 35662306a36Sopenharmony_ci test_ptrace_write(); 35762306a36Sopenharmony_ci if (thpsize) 35862306a36Sopenharmony_ci test_ptrace_write_thp(); 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * On page migration, the kernel may set the PTE/PMD dirty when 36162306a36Sopenharmony_ci * remapping the page. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci test_page_migration(); 36462306a36Sopenharmony_ci if (thpsize) 36562306a36Sopenharmony_ci test_page_migration_thp(); 36662306a36Sopenharmony_ci /* PTE-mapping a THP might propagate the dirty PMD bit to the PTEs. */ 36762306a36Sopenharmony_ci if (thpsize) 36862306a36Sopenharmony_ci test_pte_mapped_thp(); 36962306a36Sopenharmony_ci /* Placing a fresh page via userfaultfd may set the PTE dirty. */ 37062306a36Sopenharmony_ci#ifdef __NR_userfaultfd 37162306a36Sopenharmony_ci test_uffdio_copy(); 37262306a36Sopenharmony_ci#endif /* __NR_userfaultfd */ 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci err = ksft_get_fail_cnt(); 37562306a36Sopenharmony_ci if (err) 37662306a36Sopenharmony_ci ksft_exit_fail_msg("%d out of %d tests failed\n", 37762306a36Sopenharmony_ci err, ksft_test_num()); 37862306a36Sopenharmony_ci return ksft_exit_pass(); 37962306a36Sopenharmony_ci} 380