Commit ad0ce23e authored by Axel Rasmussen's avatar Axel Rasmussen Committed by Linus Torvalds

userfaultfd/selftests: fix calculation of expected ioctls

Today, we assert that the ioctls the kernel reports as supported for a
registration match a precomputed list.  We decide which ioctls are
supported by examining the memory type.  Then, in several locations we
"fix up" this list by adding or removing things this initial decision
got wrong.

What ioctls the kernel reports is actually a function of several things:
- The memory type
- Kernel feature support (e.g., no writeprotect on aarch64)
- The registration type (e.g., CONTINUE only supported for MINOR mode)

So, we can't fully compute this at the start, in set_test_type.  It
varies per test, depending on what registration mode(s) those tests use.

Instead, introduce a new function which computes the correct list.  This
centralizes the add/remove of ioctls depending on these function inputs
in one place, so we don't have to repeat ourselves in various tests.

Not only is the resulting code a bit shorter, but it fixes a real bug in
the existing code: previously, we would incorrectly require the
writeprotect ioctl to be present on aarch64, where it isn't actually
supported.

Link: https://lkml.kernel.org/r/20210930212309.4001967-4-axelrasmussen@google.comSigned-off-by: default avatarAxel Rasmussen <axelrasmussen@google.com>
Reviewed-by: default avatarPeter Xu <peterx@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1042a53d
...@@ -308,37 +308,24 @@ static void shmem_alias_mapping(__u64 *start, size_t len, unsigned long offset) ...@@ -308,37 +308,24 @@ static void shmem_alias_mapping(__u64 *start, size_t len, unsigned long offset)
} }
struct uffd_test_ops { struct uffd_test_ops {
unsigned long expected_ioctls;
void (*allocate_area)(void **alloc_area); void (*allocate_area)(void **alloc_area);
void (*release_pages)(char *rel_area); void (*release_pages)(char *rel_area);
void (*alias_mapping)(__u64 *start, size_t len, unsigned long offset); void (*alias_mapping)(__u64 *start, size_t len, unsigned long offset);
}; };
#define SHMEM_EXPECTED_IOCTLS ((1 << _UFFDIO_WAKE) | \
(1 << _UFFDIO_COPY) | \
(1 << _UFFDIO_ZEROPAGE))
#define ANON_EXPECTED_IOCTLS ((1 << _UFFDIO_WAKE) | \
(1 << _UFFDIO_COPY) | \
(1 << _UFFDIO_ZEROPAGE) | \
(1 << _UFFDIO_WRITEPROTECT))
static struct uffd_test_ops anon_uffd_test_ops = { static struct uffd_test_ops anon_uffd_test_ops = {
.expected_ioctls = ANON_EXPECTED_IOCTLS,
.allocate_area = anon_allocate_area, .allocate_area = anon_allocate_area,
.release_pages = anon_release_pages, .release_pages = anon_release_pages,
.alias_mapping = noop_alias_mapping, .alias_mapping = noop_alias_mapping,
}; };
static struct uffd_test_ops shmem_uffd_test_ops = { static struct uffd_test_ops shmem_uffd_test_ops = {
.expected_ioctls = SHMEM_EXPECTED_IOCTLS,
.allocate_area = shmem_allocate_area, .allocate_area = shmem_allocate_area,
.release_pages = shmem_release_pages, .release_pages = shmem_release_pages,
.alias_mapping = shmem_alias_mapping, .alias_mapping = shmem_alias_mapping,
}; };
static struct uffd_test_ops hugetlb_uffd_test_ops = { static struct uffd_test_ops hugetlb_uffd_test_ops = {
.expected_ioctls = UFFD_API_RANGE_IOCTLS_BASIC & ~(1 << _UFFDIO_CONTINUE),
.allocate_area = hugetlb_allocate_area, .allocate_area = hugetlb_allocate_area,
.release_pages = hugetlb_release_pages, .release_pages = hugetlb_release_pages,
.alias_mapping = hugetlb_alias_mapping, .alias_mapping = hugetlb_alias_mapping,
...@@ -356,6 +343,33 @@ static inline uint64_t uffd_minor_feature(void) ...@@ -356,6 +343,33 @@ static inline uint64_t uffd_minor_feature(void)
return 0; return 0;
} }
static uint64_t get_expected_ioctls(uint64_t mode)
{
uint64_t ioctls = UFFD_API_RANGE_IOCTLS;
if (test_type == TEST_HUGETLB)
ioctls &= ~(1 << _UFFDIO_ZEROPAGE);
if (!((mode & UFFDIO_REGISTER_MODE_WP) && test_uffdio_wp))
ioctls &= ~(1 << _UFFDIO_WRITEPROTECT);
if (!((mode & UFFDIO_REGISTER_MODE_MINOR) && test_uffdio_minor))
ioctls &= ~(1 << _UFFDIO_CONTINUE);
return ioctls;
}
static void assert_expected_ioctls_present(uint64_t mode, uint64_t ioctls)
{
uint64_t expected = get_expected_ioctls(mode);
uint64_t actual = ioctls & expected;
if (actual != expected) {
err("missing ioctl(s): expected %"PRIx64" actual: %"PRIx64,
expected, actual);
}
}
static void userfaultfd_open(uint64_t *features) static void userfaultfd_open(uint64_t *features)
{ {
struct uffdio_api uffdio_api; struct uffdio_api uffdio_api;
...@@ -1017,11 +1031,9 @@ static int __uffdio_zeropage(int ufd, unsigned long offset, bool retry) ...@@ -1017,11 +1031,9 @@ static int __uffdio_zeropage(int ufd, unsigned long offset, bool retry)
{ {
struct uffdio_zeropage uffdio_zeropage; struct uffdio_zeropage uffdio_zeropage;
int ret; int ret;
unsigned long has_zeropage; bool has_zeropage = get_expected_ioctls(0) & (1 << _UFFDIO_ZEROPAGE);
__s64 res; __s64 res;
has_zeropage = uffd_test_ops->expected_ioctls & (1 << _UFFDIO_ZEROPAGE);
if (offset >= nr_pages * page_size) if (offset >= nr_pages * page_size)
err("unexpected offset %lu", offset); err("unexpected offset %lu", offset);
uffdio_zeropage.range.start = (unsigned long) area_dst + offset; uffdio_zeropage.range.start = (unsigned long) area_dst + offset;
...@@ -1061,7 +1073,6 @@ static int uffdio_zeropage(int ufd, unsigned long offset) ...@@ -1061,7 +1073,6 @@ static int uffdio_zeropage(int ufd, unsigned long offset)
static int userfaultfd_zeropage_test(void) static int userfaultfd_zeropage_test(void)
{ {
struct uffdio_register uffdio_register; struct uffdio_register uffdio_register;
unsigned long expected_ioctls;
printf("testing UFFDIO_ZEROPAGE: "); printf("testing UFFDIO_ZEROPAGE: ");
fflush(stdout); fflush(stdout);
...@@ -1076,9 +1087,8 @@ static int userfaultfd_zeropage_test(void) ...@@ -1076,9 +1087,8 @@ static int userfaultfd_zeropage_test(void)
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
err("register failure"); err("register failure");
expected_ioctls = uffd_test_ops->expected_ioctls; assert_expected_ioctls_present(
if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) uffdio_register.mode, uffdio_register.ioctls);
err("unexpected missing ioctl for anon memory");
if (uffdio_zeropage(uffd, 0)) if (uffdio_zeropage(uffd, 0))
if (my_bcmp(area_dst, zeropage, page_size)) if (my_bcmp(area_dst, zeropage, page_size))
...@@ -1091,7 +1101,6 @@ static int userfaultfd_zeropage_test(void) ...@@ -1091,7 +1101,6 @@ static int userfaultfd_zeropage_test(void)
static int userfaultfd_events_test(void) static int userfaultfd_events_test(void)
{ {
struct uffdio_register uffdio_register; struct uffdio_register uffdio_register;
unsigned long expected_ioctls;
pthread_t uffd_mon; pthread_t uffd_mon;
int err, features; int err, features;
pid_t pid; pid_t pid;
...@@ -1115,9 +1124,8 @@ static int userfaultfd_events_test(void) ...@@ -1115,9 +1124,8 @@ static int userfaultfd_events_test(void)
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
err("register failure"); err("register failure");
expected_ioctls = uffd_test_ops->expected_ioctls; assert_expected_ioctls_present(
if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) uffdio_register.mode, uffdio_register.ioctls);
err("unexpected missing ioctl for anon memory");
if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats)) if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, &stats))
err("uffd_poll_thread create"); err("uffd_poll_thread create");
...@@ -1145,7 +1153,6 @@ static int userfaultfd_events_test(void) ...@@ -1145,7 +1153,6 @@ static int userfaultfd_events_test(void)
static int userfaultfd_sig_test(void) static int userfaultfd_sig_test(void)
{ {
struct uffdio_register uffdio_register; struct uffdio_register uffdio_register;
unsigned long expected_ioctls;
unsigned long userfaults; unsigned long userfaults;
pthread_t uffd_mon; pthread_t uffd_mon;
int err, features; int err, features;
...@@ -1169,9 +1176,8 @@ static int userfaultfd_sig_test(void) ...@@ -1169,9 +1176,8 @@ static int userfaultfd_sig_test(void)
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
err("register failure"); err("register failure");
expected_ioctls = uffd_test_ops->expected_ioctls; assert_expected_ioctls_present(
if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls) uffdio_register.mode, uffdio_register.ioctls);
err("unexpected missing ioctl for anon memory");
if (faulting_process(1)) if (faulting_process(1))
err("faulting process failed"); err("faulting process failed");
...@@ -1206,7 +1212,6 @@ static int userfaultfd_sig_test(void) ...@@ -1206,7 +1212,6 @@ static int userfaultfd_sig_test(void)
static int userfaultfd_minor_test(void) static int userfaultfd_minor_test(void)
{ {
struct uffdio_register uffdio_register; struct uffdio_register uffdio_register;
unsigned long expected_ioctls;
unsigned long p; unsigned long p;
pthread_t uffd_mon; pthread_t uffd_mon;
uint8_t expected_byte; uint8_t expected_byte;
...@@ -1228,10 +1233,8 @@ static int userfaultfd_minor_test(void) ...@@ -1228,10 +1233,8 @@ static int userfaultfd_minor_test(void)
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
err("register failure"); err("register failure");
expected_ioctls = uffd_test_ops->expected_ioctls; assert_expected_ioctls_present(
expected_ioctls |= 1 << _UFFDIO_CONTINUE; uffdio_register.mode, uffdio_register.ioctls);
if ((uffdio_register.ioctls & expected_ioctls) != expected_ioctls)
err("unexpected missing ioctl(s)");
/* /*
* After registering with UFFD, populate the non-UFFD-registered side of * After registering with UFFD, populate the non-UFFD-registered side of
...@@ -1428,8 +1431,6 @@ static int userfaultfd_stress(void) ...@@ -1428,8 +1431,6 @@ static int userfaultfd_stress(void)
pthread_attr_setstacksize(&attr, 16*1024*1024); pthread_attr_setstacksize(&attr, 16*1024*1024);
while (bounces--) { while (bounces--) {
unsigned long expected_ioctls;
printf("bounces: %d, mode:", bounces); printf("bounces: %d, mode:", bounces);
if (bounces & BOUNCE_RANDOM) if (bounces & BOUNCE_RANDOM)
printf(" rnd"); printf(" rnd");
...@@ -1457,10 +1458,8 @@ static int userfaultfd_stress(void) ...@@ -1457,10 +1458,8 @@ static int userfaultfd_stress(void)
uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP; uffdio_register.mode |= UFFDIO_REGISTER_MODE_WP;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register))
err("register failure"); err("register failure");
expected_ioctls = uffd_test_ops->expected_ioctls; assert_expected_ioctls_present(
if ((uffdio_register.ioctls & expected_ioctls) != uffdio_register.mode, uffdio_register.ioctls);
expected_ioctls)
err("unexpected missing ioctl for anon memory");
if (area_dst_alias) { if (area_dst_alias) {
uffdio_register.range.start = (unsigned long) uffdio_register.range.start = (unsigned long)
......
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