Commit 1042a53d authored by Axel Rasmussen's avatar Axel Rasmussen Committed by Linus Torvalds

userfaultfd/selftests: fix feature support detection

Before any tests are run, in set_test_type, we decide what feature(s) we
are going to be testing, based upon our command line arguments.
However, the supported features are not just a function of the memory
type being used, so this is broken.

For instance, consider writeprotect support.  It is "normally" supported
for anonymous memory, but furthermore it requires that the kernel has
CONFIG_HAVE_ARCH_USERFAULTFD_WP.  So, it is *not* supported at all on
aarch64, for example.

So, this fixes this by querying the kernel for the set of features it
supports in set_test_type, by opening a userfaultfd and issuing a
UFFDIO_API ioctl.  Based upon the reported features, we toggle what
tests are enabled.

Link: https://lkml.kernel.org/r/20210930212309.4001967-3-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 1c10e674
...@@ -346,6 +346,16 @@ static struct uffd_test_ops hugetlb_uffd_test_ops = { ...@@ -346,6 +346,16 @@ static struct uffd_test_ops hugetlb_uffd_test_ops = {
static struct uffd_test_ops *uffd_test_ops; static struct uffd_test_ops *uffd_test_ops;
static inline uint64_t uffd_minor_feature(void)
{
if (test_type == TEST_HUGETLB && map_shared)
return UFFD_FEATURE_MINOR_HUGETLBFS;
else if (test_type == TEST_SHMEM)
return UFFD_FEATURE_MINOR_SHMEM;
else
return 0;
}
static void userfaultfd_open(uint64_t *features) static void userfaultfd_open(uint64_t *features)
{ {
struct uffdio_api uffdio_api; struct uffdio_api uffdio_api;
...@@ -406,7 +416,7 @@ static void uffd_test_ctx_clear(void) ...@@ -406,7 +416,7 @@ static void uffd_test_ctx_clear(void)
munmap_area((void **)&area_dst_alias); munmap_area((void **)&area_dst_alias);
} }
static void uffd_test_ctx_init_ext(uint64_t *features) static void uffd_test_ctx_init(uint64_t features)
{ {
unsigned long nr, cpu; unsigned long nr, cpu;
...@@ -415,7 +425,7 @@ static void uffd_test_ctx_init_ext(uint64_t *features) ...@@ -415,7 +425,7 @@ static void uffd_test_ctx_init_ext(uint64_t *features)
uffd_test_ops->allocate_area((void **)&area_src); uffd_test_ops->allocate_area((void **)&area_src);
uffd_test_ops->allocate_area((void **)&area_dst); uffd_test_ops->allocate_area((void **)&area_dst);
userfaultfd_open(features); userfaultfd_open(&features);
count_verify = malloc(nr_pages * sizeof(unsigned long long)); count_verify = malloc(nr_pages * sizeof(unsigned long long));
if (!count_verify) if (!count_verify)
...@@ -463,11 +473,6 @@ static void uffd_test_ctx_init_ext(uint64_t *features) ...@@ -463,11 +473,6 @@ static void uffd_test_ctx_init_ext(uint64_t *features)
err("pipe"); err("pipe");
} }
static inline void uffd_test_ctx_init(uint64_t features)
{
uffd_test_ctx_init_ext(&features);
}
static int my_bcmp(char *str1, char *str2, size_t n) static int my_bcmp(char *str1, char *str2, size_t n)
{ {
unsigned long i; unsigned long i;
...@@ -1208,7 +1213,6 @@ static int userfaultfd_minor_test(void) ...@@ -1208,7 +1213,6 @@ static int userfaultfd_minor_test(void)
void *expected_page; void *expected_page;
char c; char c;
struct uffd_stats stats = { 0 }; struct uffd_stats stats = { 0 };
uint64_t req_features, features_out;
if (!test_uffdio_minor) if (!test_uffdio_minor)
return 0; return 0;
...@@ -1216,21 +1220,7 @@ static int userfaultfd_minor_test(void) ...@@ -1216,21 +1220,7 @@ static int userfaultfd_minor_test(void)
printf("testing minor faults: "); printf("testing minor faults: ");
fflush(stdout); fflush(stdout);
if (test_type == TEST_HUGETLB) uffd_test_ctx_init(uffd_minor_feature());
req_features = UFFD_FEATURE_MINOR_HUGETLBFS;
else if (test_type == TEST_SHMEM)
req_features = UFFD_FEATURE_MINOR_SHMEM;
else
return 1;
features_out = req_features;
uffd_test_ctx_init_ext(&features_out);
/* If kernel reports required features aren't supported, skip test. */
if ((features_out & req_features) != req_features) {
printf("skipping test due to lack of feature support\n");
fflush(stdout);
return 0;
}
uffdio_register.range.start = (unsigned long)area_dst_alias; uffdio_register.range.start = (unsigned long)area_dst_alias;
uffdio_register.range.len = nr_pages * page_size; uffdio_register.range.len = nr_pages * page_size;
...@@ -1591,6 +1581,8 @@ unsigned long default_huge_page_size(void) ...@@ -1591,6 +1581,8 @@ unsigned long default_huge_page_size(void)
static void set_test_type(const char *type) static void set_test_type(const char *type)
{ {
uint64_t features = UFFD_API_FEATURES;
if (!strcmp(type, "anon")) { if (!strcmp(type, "anon")) {
test_type = TEST_ANON; test_type = TEST_ANON;
uffd_test_ops = &anon_uffd_test_ops; uffd_test_ops = &anon_uffd_test_ops;
...@@ -1624,6 +1616,22 @@ static void set_test_type(const char *type) ...@@ -1624,6 +1616,22 @@ static void set_test_type(const char *type)
if ((unsigned long) area_count(NULL, 0) + sizeof(unsigned long long) * 2 if ((unsigned long) area_count(NULL, 0) + sizeof(unsigned long long) * 2
> page_size) > page_size)
err("Impossible to run this test"); err("Impossible to run this test");
/*
* Whether we can test certain features depends not just on test type,
* but also on whether or not this particular kernel supports the
* feature.
*/
userfaultfd_open(&features);
test_uffdio_wp = test_uffdio_wp &&
(features & UFFD_FEATURE_PAGEFAULT_FLAG_WP);
test_uffdio_minor = test_uffdio_minor &&
(features & uffd_minor_feature());
close(uffd);
uffd = -1;
} }
static void sigalrm(int sig) static void sigalrm(int sig)
......
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