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.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>
This commit is contained in:
parent
73c1ea939b
commit
c3315502c9
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user