| From: Peter Xu <peterx@redhat.com> |
| Subject: selftests/mm: move zeropage test into uffd unit tests |
| Date: Wed, 12 Apr 2023 12:44:04 -0400 |
| |
| Simplifies it a bit along the way, e.g., drop the never used offset field |
| (which was always the 1st page so offset=0). |
| |
| Introduce uffd_register_with_ioctls() out of uffd_register() to detect |
| uffdio_register.ioctls got returned. Check that automatically when testing |
| UFFDIO_ZEROPAGE on different types of memory (and kernel). |
| |
| Link: https://lkml.kernel.org/r/20230412164404.328815-1-peterx@redhat.com |
| Signed-off-by: Peter Xu <peterx@redhat.com> |
| Cc: Axel Rasmussen <axelrasmussen@google.com> |
| Cc: David Hildenbrand <david@redhat.com> |
| Cc: Dmitry Safonov <0x7f454c46@gmail.com> |
| Cc: Mike Kravetz <mike.kravetz@oracle.com> |
| Cc: Mike Rapoport (IBM) <rppt@kernel.org> |
| Cc: Zach O'Keefe <zokeefe@google.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| tools/testing/selftests/mm/uffd-stress.c | 94 ----------------- |
| tools/testing/selftests/mm/uffd-unit-tests.c | 93 ++++++++++++++++ |
| tools/testing/selftests/mm/vm_util.c | 14 ++ |
| tools/testing/selftests/mm/vm_util.h | 2 |
| 4 files changed, 108 insertions(+), 95 deletions(-) |
| |
| --- a/tools/testing/selftests/mm/uffd-stress.c~selftests-mm-move-zeropage-test-into-uffd-unit-tests |
| +++ a/tools/testing/selftests/mm/uffd-stress.c |
| @@ -109,15 +109,6 @@ static inline uint64_t uffd_minor_featur |
| return 0; |
| } |
| |
| -static int my_bcmp(char *str1, char *str2, size_t n) |
| -{ |
| - unsigned long i; |
| - for (i = 0; i < n; i++) |
| - if (str1[i] != str2[i]) |
| - return 1; |
| - return 0; |
| -} |
| - |
| static void *locking_thread(void *arg) |
| { |
| unsigned long cpu = (unsigned long) arg; |
| @@ -273,89 +264,6 @@ static int stress(struct uffd_args *args |
| return 0; |
| } |
| |
| -static void retry_uffdio_zeropage(int ufd, |
| - struct uffdio_zeropage *uffdio_zeropage, |
| - unsigned long offset) |
| -{ |
| - uffd_test_ops->alias_mapping(&uffdio_zeropage->range.start, |
| - uffdio_zeropage->range.len, |
| - offset); |
| - if (ioctl(ufd, UFFDIO_ZEROPAGE, uffdio_zeropage)) { |
| - if (uffdio_zeropage->zeropage != -EEXIST) |
| - err("UFFDIO_ZEROPAGE error: %"PRId64, |
| - (int64_t)uffdio_zeropage->zeropage); |
| - } else { |
| - err("UFFDIO_ZEROPAGE error: %"PRId64, |
| - (int64_t)uffdio_zeropage->zeropage); |
| - } |
| -} |
| - |
| -static int __uffdio_zeropage(int ufd, unsigned long offset) |
| -{ |
| - struct uffdio_zeropage uffdio_zeropage; |
| - int ret; |
| - bool has_zeropage = !(test_type == TEST_HUGETLB); |
| - __s64 res; |
| - |
| - if (offset >= nr_pages * page_size) |
| - err("unexpected offset %lu", offset); |
| - uffdio_zeropage.range.start = (unsigned long) area_dst + offset; |
| - uffdio_zeropage.range.len = page_size; |
| - uffdio_zeropage.mode = 0; |
| - ret = ioctl(ufd, UFFDIO_ZEROPAGE, &uffdio_zeropage); |
| - res = uffdio_zeropage.zeropage; |
| - if (ret) { |
| - /* real retval in ufdio_zeropage.zeropage */ |
| - if (has_zeropage) |
| - err("UFFDIO_ZEROPAGE error: %"PRId64, (int64_t)res); |
| - else if (res != -EINVAL) |
| - err("UFFDIO_ZEROPAGE not -EINVAL"); |
| - } else if (has_zeropage) { |
| - if (res != page_size) { |
| - err("UFFDIO_ZEROPAGE unexpected size"); |
| - } else { |
| - retry_uffdio_zeropage(ufd, &uffdio_zeropage, |
| - offset); |
| - return 1; |
| - } |
| - } else |
| - err("UFFDIO_ZEROPAGE succeeded"); |
| - |
| - return 0; |
| -} |
| - |
| -static int uffdio_zeropage(int ufd, unsigned long offset) |
| -{ |
| - return __uffdio_zeropage(ufd, offset); |
| -} |
| - |
| -/* exercise UFFDIO_ZEROPAGE */ |
| -static int userfaultfd_zeropage_test(void) |
| -{ |
| - printf("testing UFFDIO_ZEROPAGE: "); |
| - fflush(stdout); |
| - |
| - uffd_test_ctx_init(0); |
| - |
| - if (uffd_register(uffd, area_dst, nr_pages * page_size, |
| - true, test_uffdio_wp, false)) |
| - err("register failure"); |
| - |
| - if (area_dst_alias) { |
| - /* Needed this to test zeropage-retry on shared memory */ |
| - if (uffd_register(uffd, area_dst_alias, nr_pages * page_size, |
| - true, test_uffdio_wp, false)) |
| - err("register failure"); |
| - } |
| - |
| - if (uffdio_zeropage(uffd, 0)) |
| - if (my_bcmp(area_dst, zeropage, page_size)) |
| - err("zeropage is not zero"); |
| - |
| - printf("done.\n"); |
| - return 0; |
| -} |
| - |
| static int userfaultfd_stress(void) |
| { |
| void *area; |
| @@ -467,7 +375,7 @@ static int userfaultfd_stress(void) |
| uffd_stats_report(args, nr_cpus); |
| } |
| |
| - return userfaultfd_zeropage_test(); |
| + return 0; |
| } |
| |
| static void set_test_type(const char *type) |
| --- a/tools/testing/selftests/mm/uffd-unit-tests.c~selftests-mm-move-zeropage-test-into-uffd-unit-tests |
| +++ a/tools/testing/selftests/mm/uffd-unit-tests.c |
| @@ -660,8 +660,101 @@ static void uffd_events_wp_test(void) |
| uffd_events_test_common(true); |
| } |
| |
| +static void retry_uffdio_zeropage(int ufd, |
| + struct uffdio_zeropage *uffdio_zeropage) |
| +{ |
| + uffd_test_ops->alias_mapping(&uffdio_zeropage->range.start, |
| + uffdio_zeropage->range.len, |
| + 0); |
| + if (ioctl(ufd, UFFDIO_ZEROPAGE, uffdio_zeropage)) { |
| + if (uffdio_zeropage->zeropage != -EEXIST) |
| + err("UFFDIO_ZEROPAGE error: %"PRId64, |
| + (int64_t)uffdio_zeropage->zeropage); |
| + } else { |
| + err("UFFDIO_ZEROPAGE error: %"PRId64, |
| + (int64_t)uffdio_zeropage->zeropage); |
| + } |
| +} |
| + |
| +static bool do_uffdio_zeropage(int ufd, bool has_zeropage) |
| +{ |
| + struct uffdio_zeropage uffdio_zeropage = { 0 }; |
| + int ret; |
| + __s64 res; |
| + |
| + uffdio_zeropage.range.start = (unsigned long) area_dst; |
| + uffdio_zeropage.range.len = page_size; |
| + uffdio_zeropage.mode = 0; |
| + ret = ioctl(ufd, UFFDIO_ZEROPAGE, &uffdio_zeropage); |
| + res = uffdio_zeropage.zeropage; |
| + if (ret) { |
| + /* real retval in ufdio_zeropage.zeropage */ |
| + if (has_zeropage) |
| + err("UFFDIO_ZEROPAGE error: %"PRId64, (int64_t)res); |
| + else if (res != -EINVAL) |
| + err("UFFDIO_ZEROPAGE not -EINVAL"); |
| + } else if (has_zeropage) { |
| + if (res != page_size) |
| + err("UFFDIO_ZEROPAGE unexpected size"); |
| + else |
| + retry_uffdio_zeropage(ufd, &uffdio_zeropage); |
| + return true; |
| + } else |
| + err("UFFDIO_ZEROPAGE succeeded"); |
| + |
| + return false; |
| +} |
| + |
| +/* |
| + * Registers a range with MISSING mode only for zeropage test. Return true |
| + * if UFFDIO_ZEROPAGE supported, false otherwise. Can't use uffd_register() |
| + * because we want to detect .ioctls along the way. |
| + */ |
| +static bool |
| +uffd_register_detect_zeropage(int uffd, void *addr, uint64_t len) |
| +{ |
| + uint64_t ioctls = 0; |
| + |
| + if (uffd_register_with_ioctls(uffd, addr, len, true, |
| + false, false, &ioctls)) |
| + err("zeropage register fail"); |
| + |
| + return ioctls & (1 << _UFFDIO_ZEROPAGE); |
| +} |
| + |
| +/* exercise UFFDIO_ZEROPAGE */ |
| +static void uffd_zeropage_test(void) |
| +{ |
| + bool has_zeropage; |
| + int i; |
| + |
| + has_zeropage = uffd_register_detect_zeropage(uffd, area_dst, page_size); |
| + if (area_dst_alias) |
| + /* Ignore the retval; we already have it */ |
| + uffd_register_detect_zeropage(uffd, area_dst_alias, page_size); |
| + |
| + if (do_uffdio_zeropage(uffd, has_zeropage)) |
| + for (i = 0; i < page_size; i++) |
| + if (area_dst[i] != 0) |
| + err("data non-zero at offset %d\n", i); |
| + |
| + if (uffd_unregister(uffd, area_dst, page_size)) |
| + err("unregister"); |
| + |
| + if (area_dst_alias && uffd_unregister(uffd, area_dst_alias, page_size)) |
| + err("unregister"); |
| + |
| + uffd_test_pass(); |
| +} |
| + |
| uffd_test_case_t uffd_tests[] = { |
| { |
| + .name = "zeropage", |
| + .uffd_fn = uffd_zeropage_test, |
| + .mem_targets = MEM_ALL, |
| + .uffd_feature_required = 0, |
| + }, |
| + { |
| .name = "pagemap", |
| .uffd_fn = uffd_pagemap_test, |
| .mem_targets = MEM_ANON, |
| --- a/tools/testing/selftests/mm/vm_util.c~selftests-mm-move-zeropage-test-into-uffd-unit-tests |
| +++ a/tools/testing/selftests/mm/vm_util.c |
| @@ -198,8 +198,9 @@ unsigned long default_huge_page_size(voi |
| return hps; |
| } |
| |
| -int uffd_register(int uffd, void *addr, uint64_t len, |
| - bool miss, bool wp, bool minor) |
| +/* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */ |
| +int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len, |
| + bool miss, bool wp, bool minor, uint64_t *ioctls) |
| { |
| struct uffdio_register uffdio_register = { 0 }; |
| uint64_t mode = 0; |
| @@ -218,10 +219,19 @@ int uffd_register(int uffd, void *addr, |
| |
| if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) |
| ret = -errno; |
| + else if (ioctls) |
| + *ioctls = uffdio_register.ioctls; |
| |
| return ret; |
| } |
| |
| +int uffd_register(int uffd, void *addr, uint64_t len, |
| + bool miss, bool wp, bool minor) |
| +{ |
| + return uffd_register_with_ioctls(uffd, addr, len, |
| + miss, wp, minor, NULL); |
| +} |
| + |
| int uffd_unregister(int uffd, void *addr, uint64_t len) |
| { |
| struct uffdio_range range = { .start = (uintptr_t)addr, .len = len }; |
| --- a/tools/testing/selftests/mm/vm_util.h~selftests-mm-move-zeropage-test-into-uffd-unit-tests |
| +++ a/tools/testing/selftests/mm/vm_util.h |
| @@ -52,6 +52,8 @@ int uffd_open_dev(unsigned int flags); |
| int uffd_open_sys(unsigned int flags); |
| int uffd_open(unsigned int flags); |
| int uffd_get_features(uint64_t *features); |
| +int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len, |
| + bool miss, bool wp, bool minor, uint64_t *ioctls); |
| |
| /* |
| * On ppc64 this will only work with radix 2M hugepage size |
| _ |