Commit c3315502 authored by Peter Xu's avatar Peter Xu Committed by Andrew Morton

selftests/mm: move zeropage test into uffd unit tests

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.comSigned-off-by: default avatarPeter 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: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 73c1ea93
......@@ -109,15 +109,6 @@ static inline uint64_t uffd_minor_feature(void)
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)
......
......@@ -660,7 +660,100 @@ 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,
......
......@@ -198,8 +198,9 @@ unsigned long default_huge_page_size(void)
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, uint64_t len,
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 };
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment