From: Brendan Jackman <jackmanb@google.com>
To: Ujwal Kundur <ujwal.kundur@gmail.com>,
<akpm@linux-foundation.org>, <peterx@redhat.com>,
<shuah@kernel.org>
Cc: <linux-kernel@vger.kernel.org>, <linux-kselftest@vger.kernel.org>,
<linux-mm@kvack.org>
Subject: Re: [PATCH v4 1/1] selftests/mm/uffd: Refactor non-composite global vars into struct
Date: Tue, 10 Jun 2025 11:32:40 +0000 [thread overview]
Message-ID: <DAITJKYRQMFD.OLOUWS7UPGVD@google.com> (raw)
In-Reply-To: <20250531074625.478-1-ujwal.kundur@gmail.com>
On Sat May 31, 2025 at 7:46 AM UTC, Ujwal Kundur wrote:
> Refactor macros and non-composite global variable definitions into a
> struct that is defined at the start of a test and is passed around
> instead of relying on global vars.
>
> Renders the implementation / use of test_uffdio_copy_eexist
> vestigial and will be addressed in a later patch.
>
> Signed-off-by: Ujwal Kundur <ujwal.kundur@gmail.com>
I applied this to f09079bd04a92 and tested it using this janky script:
https://github.com/bjackman/linux/blob/github-base/.github/scripts/run_local.sh
With this file modified to un-comment userfaultfd:
https://github.com/bjackman/linux/blob/github-base/.github/scripts/test.guest.sh
And I hit this:
# -------------------------
# running ./uffd-unit-tests
# -------------------------
# Testing UFFDIO_API (with syscall)... done
# Testing UFFDIO_API (with /dev/userfaultfd)... done
# Testing register-ioctls on anon... ERROR: munmap (errno=22, @uffd-common.c:277)
# done
# [FAIL]
not ok 6 uffd-unit-tests # exit=1
It reproduced on a second run but I haven't dug any further at all.
> ---
> Changes since v3:
> - more formatting fixes
Thanks, code LGTM except for the below...
> static void sigalrm(int sig)
> {
> if (sig != SIGALRM)
> abort();
> - test_uffdio_copy_eexist = true;
> + /* TODO: Remove or fix this test */
> + // gopts->test_uffdio_copy_eexist = true;
Er I still think we need to clean this up before merging.
I am still being very lazy here and not researching this properly, but I
would suggest that we just leave this SIGALRM logic in place (as I said
before I don't mean I _like_ that logic, I just mean we should not
change it as part of an otherwise-straightforward refactoring series).
Since signal handlers are truly global I would say we should just keep
the global variable for communicating between main() and the handler,
e.g:
/*
* A SIGALRM handler will set the test_uffdio_copy_eexist field of this struct
* to true every ALARM_INTERVAL_SECS.
*/
uffd_global_test_opts_t *sigalrm_gopts;
static void sigalrm(int sig)
{
if (sig != SIGALRM)
abort();
sigalrm_->test_uffdio_copy_eexist = true;
alarm(ALARM_INTERVAL_SECS);
}
int main(int argc, char **argv)
{
unsigned long nr_cpus;
size_t bytes;
uffd_global_test_opts_t *gopts =
(uffd_global_test_opts_t *) malloc(sizeof(uffd_global_test_opts_t));
if (argc < 4)
usage();
sigalrm_gopts = gopts;
if (signal(SIGALRM, sigalrm) == SIG_ERR)
err("failed to arm SIGALRM");
alarm(ALARM_INTERVAL_SECS);
On the surface this has somewhat walked back on the "get rid of global
variables" goal but actually it's still a massive improvement, the
global is only in scope in the places where it's actually needed so it
doesn't create a maintenance mess, and note in particular there' no
longer any references to it from a header file. Commentary can be used
to discourage people from expanding its scope.
Then in a later series it could be removed by one of the methods you
mentioned in:
https://lore.kernel.org/all/CALkFLLK19Uqr2veWCn79cbLLgde5f+otf9Qx0xSPGdhdnekGrw@mail.gmail.com/
> alarm(ALARM_INTERVAL_SECS);
> }
>
> @@ -438,6 +451,9 @@ int main(int argc, char **argv)
> unsigned long nr_cpus;
> size_t bytes;
>
> + uffd_global_test_opts_t *gopts =
> + (uffd_global_test_opts_t *) malloc(sizeof(uffd_global_test_opts_t));
> +
> if (argc < 4)
> usage();
>
> @@ -445,11 +461,11 @@ int main(int argc, char **argv)
> err("failed to arm SIGALRM");
> alarm(ALARM_INTERVAL_SECS);
>
> - parse_test_type_arg(argv[1]);
> + parse_test_type_arg(gopts, argv[1]);
> bytes = atol(argv[2]) * 1024 * 1024;
>
> - if (test_type == TEST_HUGETLB &&
> - get_free_hugepages() < bytes / page_size) {
> + if (gopts->test_type == TEST_HUGETLB &&
> + get_free_hugepages() < bytes / gopts->page_size) {
> printf("skip: Skipping userfaultfd... not enough hugepages\n");
> return KSFT_SKIP;
> }
> @@ -459,15 +475,15 @@ int main(int argc, char **argv)
> /* Don't let calculation below go to zero. */
> ksft_print_msg("_SC_NPROCESSORS_ONLN (%lu) too large, capping nr_threads to 32\n",
> nr_cpus);
> - nr_parallel = 32;
> + gopts->nr_parallel = 32;
> } else {
> - nr_parallel = nr_cpus;
> + gopts->nr_parallel = nr_cpus;
> }
>
> - nr_pages_per_cpu = bytes / page_size / nr_parallel;
> - if (!nr_pages_per_cpu) {
> + gopts->nr_pages_per_cpu = bytes / gopts->page_size / gopts->nr_parallel;
> + if (!gopts->nr_pages_per_cpu) {
> _err("pages_per_cpu = 0, cannot test (%lu / %lu / %lu)",
> - bytes, page_size, nr_parallel);
> + bytes, gopts->page_size, gopts->nr_parallel);
> usage();
> }
>
> @@ -476,11 +492,11 @@ int main(int argc, char **argv)
> _err("invalid bounces");
> usage();
> }
> - nr_pages = nr_pages_per_cpu * nr_parallel;
> + gopts->nr_pages = gopts->nr_pages_per_cpu * gopts->nr_parallel;
>
> printf("nr_pages: %lu, nr_pages_per_cpu: %lu\n",
> - nr_pages, nr_pages_per_cpu);
> - return userfaultfd_stress();
> + gopts->nr_pages, gopts->nr_pages_per_cpu);
> + return userfaultfd_stress(gopts);
> }
>
> #else /* __NR_userfaultfd */
> diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c
> index c73fd5d455c8..bed96f41c578 100644
> --- a/tools/testing/selftests/mm/uffd-unit-tests.c
> +++ b/tools/testing/selftests/mm/uffd-unit-tests.c
> @@ -76,7 +76,7 @@ struct uffd_test_args {
> typedef struct uffd_test_args uffd_test_args_t;
>
> /* Returns: UFFD_TEST_* */
> -typedef void (*uffd_test_fn)(uffd_test_args_t *);
> +typedef void (*uffd_test_fn)(uffd_global_test_opts_t *, uffd_test_args_t *);
>
> typedef struct {
> const char *name;
> @@ -181,33 +181,6 @@ static int test_uffd_api(bool use_dev)
> return 1;
> }
>
> -/*
> - * This function initializes the global variables. TODO: remove global
> - * vars and then remove this.
> - */
> -static int
> -uffd_setup_environment(uffd_test_args_t *args, uffd_test_case_t *test,
> - mem_type_t *mem_type, const char **errmsg)
> -{
> - map_shared = mem_type->shared;
> - uffd_test_ops = mem_type->mem_ops;
> - uffd_test_case_ops = test->test_case_ops;
> -
> - if (mem_type->mem_flag & (MEM_HUGETLB_PRIVATE | MEM_HUGETLB))
> - page_size = default_huge_page_size();
> - else
> - page_size = psize();
> -
> - /* Ensure we have at least 2 pages */
> - nr_pages = MAX(UFFD_TEST_MEM_SIZE, page_size * 2) / page_size;
> - /* TODO: remove this global var.. it's so ugly */
> - nr_parallel = 1;
> -
> - /* Initialize test arguments */
> - args->mem_type = mem_type;
> -
> - return uffd_test_ctx_init(test->uffd_feature_required, errmsg);
> -}
>
> static bool uffd_feature_supported(uffd_test_case_t *test)
> {
> @@ -237,7 +210,8 @@ static int pagemap_open(void)
> } while (0)
>
> typedef struct {
> - int parent_uffd, child_uffd;
> + uffd_global_test_opts_t *gopts;
> + int child_uffd;
> } fork_event_args;
>
> static void *fork_event_consumer(void *data)
> @@ -245,10 +219,10 @@ static void *fork_event_consumer(void *data)
> fork_event_args *args = data;
> struct uffd_msg msg = { 0 };
>
> - ready_for_fork = true;
> + args->gopts->ready_for_fork = true;
>
> /* Read until a full msg received */
> - while (uffd_read_msg(args->parent_uffd, &msg));
> + while (uffd_read_msg(args->gopts, &msg));
>
> if (msg.event != UFFD_EVENT_FORK)
> err("wrong message: %u\n", msg.event);
> @@ -304,9 +278,9 @@ static void unpin_pages(pin_args *args)
> args->pinned = false;
> }
>
> -static int pagemap_test_fork(int uffd, bool with_event, bool test_pin)
> +static int pagemap_test_fork(uffd_global_test_opts_t *gopts, bool with_event, bool test_pin)
> {
> - fork_event_args args = { .parent_uffd = uffd, .child_uffd = -1 };
> + fork_event_args args = { .gopts = gopts, .child_uffd = -1 };
> pthread_t thread;
> pid_t child;
> uint64_t value;
> @@ -314,10 +288,10 @@ static int pagemap_test_fork(int uffd, bool with_event, bool test_pin)
>
> /* Prepare a thread to resolve EVENT_FORK */
> if (with_event) {
> - ready_for_fork = false;
> + gopts->ready_for_fork = false;
> if (pthread_create(&thread, NULL, fork_event_consumer, &args))
> err("pthread_create()");
> - while (!ready_for_fork)
> + while (!gopts->ready_for_fork)
> ; /* Wait for the poll_thread to start executing before forking */
> }
>
> @@ -328,14 +302,14 @@ static int pagemap_test_fork(int uffd, bool with_event, bool test_pin)
>
> fd = pagemap_open();
>
> - if (test_pin && pin_pages(&args, area_dst, page_size))
> + if (test_pin && pin_pages(&args, gopts->area_dst, gopts->page_size))
> /*
> * Normally when reach here we have pinned in
> * previous tests, so shouldn't fail anymore
> */
> err("pin page failed in child");
>
> - value = pagemap_get_entry(fd, area_dst);
> + value = pagemap_get_entry(fd, gopts->area_dst);
> /*
> * After fork(), we should handle uffd-wp bit differently:
> *
> @@ -361,70 +335,70 @@ static int pagemap_test_fork(int uffd, bool with_event, bool test_pin)
> return result;
> }
>
> -static void uffd_wp_unpopulated_test(uffd_test_args_t *args)
> +static void uffd_wp_unpopulated_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> uint64_t value;
> int pagemap_fd;
>
> - if (uffd_register(uffd, area_dst, nr_pages * page_size,
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size,
> false, true, false))
> err("register failed");
>
> pagemap_fd = pagemap_open();
>
> /* Test applying pte marker to anon unpopulated */
> - wp_range(uffd, (uint64_t)area_dst, page_size, true);
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, true);
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, true);
>
> /* Test unprotect on anon pte marker */
> - wp_range(uffd, (uint64_t)area_dst, page_size, false);
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, false);
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, false);
>
> /* Test zap on anon marker */
> - wp_range(uffd, (uint64_t)area_dst, page_size, true);
> - if (madvise(area_dst, page_size, MADV_DONTNEED))
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, true);
> + if (madvise(gopts->area_dst, gopts->page_size, MADV_DONTNEED))
> err("madvise(MADV_DONTNEED) failed");
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, false);
>
> /* Test fault in after marker removed */
> - *area_dst = 1;
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + *gopts->area_dst = 1;
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, false);
> /* Drop it to make pte none again */
> - if (madvise(area_dst, page_size, MADV_DONTNEED))
> + if (madvise(gopts->area_dst, gopts->page_size, MADV_DONTNEED))
> err("madvise(MADV_DONTNEED) failed");
>
> /* Test read-zero-page upon pte marker */
> - wp_range(uffd, (uint64_t)area_dst, page_size, true);
> - *(volatile char *)area_dst;
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, true);
> + *(volatile char *)gopts->area_dst;
> /* Drop it to make pte none again */
> - if (madvise(area_dst, page_size, MADV_DONTNEED))
> + if (madvise(gopts->area_dst, gopts->page_size, MADV_DONTNEED))
> err("madvise(MADV_DONTNEED) failed");
>
> uffd_test_pass();
> }
>
> -static void uffd_wp_fork_test_common(uffd_test_args_t *args,
> +static void uffd_wp_fork_test_common(uffd_global_test_opts_t *gopts, uffd_test_args_t *args,
> bool with_event)
> {
> int pagemap_fd;
> uint64_t value;
>
> - if (uffd_register(uffd, area_dst, nr_pages * page_size,
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size,
> false, true, false))
> err("register failed");
>
> pagemap_fd = pagemap_open();
>
> /* Touch the page */
> - *area_dst = 1;
> - wp_range(uffd, (uint64_t)area_dst, page_size, true);
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + *gopts->area_dst = 1;
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, true);
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, true);
> - if (pagemap_test_fork(uffd, with_event, false)) {
> + if (pagemap_test_fork(gopts, with_event, false)) {
> uffd_test_fail("Detected %s uffd-wp bit in child in present pte",
> with_event ? "missing" : "stall");
> goto out;
> @@ -442,79 +416,80 @@ static void uffd_wp_fork_test_common(uffd_test_args_t *args,
> * to expose pte markers.
> */
> if (args->mem_type->shared) {
> - if (madvise(area_dst, page_size, MADV_DONTNEED))
> + if (madvise(gopts->area_dst, gopts->page_size, MADV_DONTNEED))
> err("MADV_DONTNEED");
> } else {
> /*
> * NOTE: ignore retval because private-hugetlb doesn't yet
> * support swapping, so it could fail.
> */
> - madvise(area_dst, page_size, MADV_PAGEOUT);
> + madvise(gopts->area_dst, gopts->page_size, MADV_PAGEOUT);
> }
>
> /* Uffd-wp should persist even swapped out */
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, true);
> - if (pagemap_test_fork(uffd, with_event, false)) {
> + if (pagemap_test_fork(gopts, with_event, false)) {
> uffd_test_fail("Detected %s uffd-wp bit in child in zapped pte",
> with_event ? "missing" : "stall");
> goto out;
> }
>
> /* Unprotect; this tests swap pte modifications */
> - wp_range(uffd, (uint64_t)area_dst, page_size, false);
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, false);
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, false);
>
> /* Fault in the page from disk */
> - *area_dst = 2;
> - value = pagemap_get_entry(pagemap_fd, area_dst);
> + *gopts->area_dst = 2;
> + value = pagemap_get_entry(pagemap_fd, gopts->area_dst);
> pagemap_check_wp(value, false);
> uffd_test_pass();
> out:
> - if (uffd_unregister(uffd, area_dst, nr_pages * page_size))
> + if (uffd_unregister(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size))
> err("unregister failed");
> close(pagemap_fd);
> }
>
> -static void uffd_wp_fork_test(uffd_test_args_t *args)
> +static void uffd_wp_fork_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_wp_fork_test_common(args, false);
> + uffd_wp_fork_test_common(gopts, args, false);
> }
>
> -static void uffd_wp_fork_with_event_test(uffd_test_args_t *args)
> +static void uffd_wp_fork_with_event_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_wp_fork_test_common(args, true);
> + uffd_wp_fork_test_common(gopts, args, true);
> }
>
> -static void uffd_wp_fork_pin_test_common(uffd_test_args_t *args,
> +static void uffd_wp_fork_pin_test_common(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *args,
> bool with_event)
> {
> int pagemap_fd;
> pin_args pin_args = {};
>
> - if (uffd_register(uffd, area_dst, page_size, false, true, false))
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->page_size, false, true, false))
> err("register failed");
>
> pagemap_fd = pagemap_open();
>
> /* Touch the page */
> - *area_dst = 1;
> - wp_range(uffd, (uint64_t)area_dst, page_size, true);
> + *gopts->area_dst = 1;
> + wp_range(gopts->uffd, (uint64_t)gopts->area_dst, gopts->page_size, true);
>
> /*
> * 1. First pin, then fork(). This tests fork() special path when
> * doing early CoW if the page is private.
> */
> - if (pin_pages(&pin_args, area_dst, page_size)) {
> + if (pin_pages(&pin_args, gopts->area_dst, gopts->page_size)) {
> uffd_test_skip("Possibly CONFIG_GUP_TEST missing "
> "or unprivileged");
> close(pagemap_fd);
> - uffd_unregister(uffd, area_dst, page_size);
> + uffd_unregister(gopts->uffd, gopts->area_dst, gopts->page_size);
> return;
> }
>
> - if (pagemap_test_fork(uffd, with_event, false)) {
> + if (pagemap_test_fork(gopts, with_event, false)) {
> uffd_test_fail("Detected %s uffd-wp bit in early CoW of fork()",
> with_event ? "missing" : "stall");
> unpin_pages(&pin_args);
> @@ -527,49 +502,50 @@ static void uffd_wp_fork_pin_test_common(uffd_test_args_t *args,
> * 2. First fork(), then pin (in the child, where test_pin==true).
> * This tests COR, aka, page unsharing on private memories.
> */
> - if (pagemap_test_fork(uffd, with_event, true)) {
> + if (pagemap_test_fork(gopts, with_event, true)) {
> uffd_test_fail("Detected %s uffd-wp bit when RO pin",
> with_event ? "missing" : "stall");
> goto out;
> }
> uffd_test_pass();
> out:
> - if (uffd_unregister(uffd, area_dst, page_size))
> + if (uffd_unregister(gopts->uffd, gopts->area_dst, gopts->page_size))
> err("register failed");
> close(pagemap_fd);
> }
>
> -static void uffd_wp_fork_pin_test(uffd_test_args_t *args)
> +static void uffd_wp_fork_pin_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_wp_fork_pin_test_common(args, false);
> + uffd_wp_fork_pin_test_common(gopts, args, false);
> }
>
> -static void uffd_wp_fork_pin_with_event_test(uffd_test_args_t *args)
> +static void uffd_wp_fork_pin_with_event_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_wp_fork_pin_test_common(args, true);
> + uffd_wp_fork_pin_test_common(gopts, args, true);
> }
>
> -static void check_memory_contents(char *p)
> +static void check_memory_contents(uffd_global_test_opts_t *gopts, char *p)
> {
> unsigned long i, j;
> uint8_t expected_byte;
>
> - for (i = 0; i < nr_pages; ++i) {
> + for (i = 0; i < gopts->nr_pages; ++i) {
> expected_byte = ~((uint8_t)(i % ((uint8_t)-1)));
> - for (j = 0; j < page_size; j++) {
> - uint8_t v = *(uint8_t *)(p + (i * page_size) + j);
> + for (j = 0; j < gopts->page_size; j++) {
> + uint8_t v = *(uint8_t *)(p + (i * gopts->page_size) + j);
> if (v != expected_byte)
> err("unexpected page contents");
> }
> }
> }
>
> -static void uffd_minor_test_common(bool test_collapse, bool test_wp)
> +static void uffd_minor_test_common(uffd_global_test_opts_t *gopts, bool test_collapse, bool test_wp)
> {
> unsigned long p;
> pthread_t uffd_mon;
> char c;
> struct uffd_args args = { 0 };
> + args.gopts = gopts;
>
> /*
> * NOTE: MADV_COLLAPSE is not yet compatible with WP, so testing
> @@ -577,7 +553,7 @@ static void uffd_minor_test_common(bool test_collapse, bool test_wp)
> */
> assert(!(test_collapse && test_wp));
>
> - if (uffd_register(uffd, area_dst_alias, nr_pages * page_size,
> + if (uffd_register(gopts->uffd, gopts->area_dst_alias, gopts->nr_pages * gopts->page_size,
> /* NOTE! MADV_COLLAPSE may not work with uffd-wp */
> false, test_wp, true))
> err("register failure");
> @@ -586,9 +562,9 @@ static void uffd_minor_test_common(bool test_collapse, bool test_wp)
> * After registering with UFFD, populate the non-UFFD-registered side of
> * the shared mapping. This should *not* trigger any UFFD minor faults.
> */
> - for (p = 0; p < nr_pages; ++p)
> - memset(area_dst + (p * page_size), p % ((uint8_t)-1),
> - page_size);
> + for (p = 0; p < gopts->nr_pages; ++p)
> + memset(gopts->area_dst + (p * gopts->page_size), p % ((uint8_t)-1),
> + gopts->page_size);
>
> args.apply_wp = test_wp;
> if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
> @@ -600,50 +576,51 @@ static void uffd_minor_test_common(bool test_collapse, bool test_wp)
> * fault. uffd_poll_thread will resolve the fault by bit-flipping the
> * page's contents, and then issuing a CONTINUE ioctl.
> */
> - check_memory_contents(area_dst_alias);
> + check_memory_contents(gopts, gopts->area_dst_alias);
>
> - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c))
> + if (write(gopts->pipefd[1], &c, sizeof(c)) != sizeof(c))
> err("pipe write");
> if (pthread_join(uffd_mon, NULL))
> err("join() failed");
>
> if (test_collapse) {
> - if (madvise(area_dst_alias, nr_pages * page_size,
> + if (madvise(gopts->area_dst_alias, gopts->nr_pages * gopts->page_size,
> MADV_COLLAPSE)) {
> /* It's fine to fail for this one... */
> uffd_test_skip("MADV_COLLAPSE failed");
> return;
> }
>
> - uffd_test_ops->check_pmd_mapping(area_dst,
> - nr_pages * page_size /
> + uffd_test_ops->check_pmd_mapping(gopts,
> + gopts->area_dst,
> + gopts->nr_pages * gopts->page_size /
> read_pmd_pagesize());
> /*
> * This won't cause uffd-fault - it purely just makes sure there
> * was no corruption.
> */
> - check_memory_contents(area_dst_alias);
> + check_memory_contents(gopts, gopts->area_dst_alias);
> }
>
> - if (args.missing_faults != 0 || args.minor_faults != nr_pages)
> + if (args.missing_faults != 0 || args.minor_faults != gopts->nr_pages)
> uffd_test_fail("stats check error");
> else
> uffd_test_pass();
> }
>
> -void uffd_minor_test(uffd_test_args_t *args)
> +void uffd_minor_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_minor_test_common(false, false);
> + uffd_minor_test_common(gopts, false, false);
> }
>
> -void uffd_minor_wp_test(uffd_test_args_t *args)
> +void uffd_minor_wp_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_minor_test_common(false, true);
> + uffd_minor_test_common(gopts, false, true);
> }
>
> -void uffd_minor_collapse_test(uffd_test_args_t *args)
> +void uffd_minor_collapse_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_minor_test_common(true, false);
> + uffd_minor_test_common(gopts, true, false);
> }
>
> static sigjmp_buf jbuf, *sigbuf;
> @@ -678,7 +655,7 @@ static void sighndl(int sig, siginfo_t *siginfo, void *ptr)
> * This also tests UFFD_FEATURE_EVENT_FORK event along with the signal
> * feature. Using monitor thread, verify no userfault events are generated.
> */
> -static int faulting_process(int signal_test, bool wp)
> +static int faulting_process(uffd_global_test_opts_t *gopts, int signal_test, bool wp)
> {
> unsigned long nr, i;
> unsigned long long count;
> @@ -687,7 +664,7 @@ static int faulting_process(int signal_test, bool wp)
> struct sigaction act;
> volatile unsigned long signalled = 0;
>
> - split_nr_pages = (nr_pages + 1) / 2;
> + split_nr_pages = (gopts->nr_pages + 1) / 2;
>
> if (signal_test) {
> sigbuf = &jbuf;
> @@ -701,7 +678,7 @@ static int faulting_process(int signal_test, bool wp)
>
> for (nr = 0; nr < split_nr_pages; nr++) {
> volatile int steps = 1;
> - unsigned long offset = nr * page_size;
> + unsigned long offset = nr * gopts->page_size;
>
> if (signal_test) {
> if (sigsetjmp(*sigbuf, 1) != 0) {
> @@ -713,15 +690,15 @@ static int faulting_process(int signal_test, bool wp)
> if (steps == 1) {
> /* This is a MISSING request */
> steps++;
> - if (copy_page(uffd, offset, wp))
> + if (copy_page(gopts, offset, wp))
> signalled++;
> } else {
> /* This is a WP request */
> assert(steps == 2);
> - wp_range(uffd,
> - (__u64)area_dst +
> + wp_range(gopts->uffd,
> + (__u64)gopts->area_dst +
> offset,
> - page_size, false);
> + gopts->page_size, false);
> }
> } else {
> signalled++;
> @@ -730,51 +707,53 @@ static int faulting_process(int signal_test, bool wp)
> }
> }
>
> - count = *area_count(area_dst, nr);
> - if (count != count_verify[nr])
> + count = *area_count(gopts->area_dst, nr, gopts);
> + if (count != gopts->count_verify[nr])
> err("nr %lu memory corruption %llu %llu\n",
> - nr, count, count_verify[nr]);
> + nr, count, gopts->count_verify[nr]);
> /*
> * Trigger write protection if there is by writing
> * the same value back.
> */
> - *area_count(area_dst, nr) = count;
> + *area_count(gopts->area_dst, nr, gopts) = count;
> }
>
> if (signal_test)
> return signalled != split_nr_pages;
>
> - area_dst = mremap(area_dst, nr_pages * page_size, nr_pages * page_size,
> - MREMAP_MAYMOVE | MREMAP_FIXED, area_src);
> - if (area_dst == MAP_FAILED)
> + gopts->area_dst = mremap(gopts->area_dst, gopts->nr_pages * gopts->page_size,
> + gopts->nr_pages * gopts->page_size,
> + MREMAP_MAYMOVE | MREMAP_FIXED,
> + gopts->area_src);
> + if (gopts->area_dst == MAP_FAILED)
> err("mremap");
> /* Reset area_src since we just clobbered it */
> - area_src = NULL;
> + gopts->area_src = NULL;
>
> - for (; nr < nr_pages; nr++) {
> - count = *area_count(area_dst, nr);
> - if (count != count_verify[nr]) {
> + for (; nr < gopts->nr_pages; nr++) {
> + count = *area_count(gopts->area_dst, nr, gopts);
> + if (count != gopts->count_verify[nr]) {
> err("nr %lu memory corruption %llu %llu\n",
> - nr, count, count_verify[nr]);
> + nr, count, gopts->count_verify[nr]);
> }
> /*
> * Trigger write protection if there is by writing
> * the same value back.
> */
> - *area_count(area_dst, nr) = count;
> + *area_count(gopts->area_dst, nr, gopts) = count;
> }
>
> - uffd_test_ops->release_pages(area_dst);
> + uffd_test_ops->release_pages(gopts, gopts->area_dst);
>
> - for (nr = 0; nr < nr_pages; nr++)
> - for (i = 0; i < page_size; i++)
> - if (*(area_dst + nr * page_size + i) != 0)
> + for (nr = 0; nr < gopts->nr_pages; nr++)
> + for (i = 0; i < gopts->page_size; i++)
> + if (*(gopts->area_dst + nr * gopts->page_size + i) != 0)
> err("page %lu offset %lu is not zero", nr, i);
>
> return 0;
> }
>
> -static void uffd_sigbus_test_common(bool wp)
> +static void uffd_sigbus_test_common(uffd_global_test_opts_t *gopts, bool wp)
> {
> unsigned long userfaults;
> pthread_t uffd_mon;
> @@ -782,25 +761,26 @@ static void uffd_sigbus_test_common(bool wp)
> int err;
> char c;
> struct uffd_args args = { 0 };
> + args.gopts = gopts;
>
> - ready_for_fork = false;
> + gopts->ready_for_fork = false;
>
> - fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK);
> + fcntl(gopts->uffd, F_SETFL, gopts->uffd_flags | O_NONBLOCK);
>
> - if (uffd_register(uffd, area_dst, nr_pages * page_size,
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size,
> true, wp, false))
> err("register failure");
>
> - if (faulting_process(1, wp))
> + if (faulting_process(gopts, 1, wp))
> err("faulting process failed");
>
> - uffd_test_ops->release_pages(area_dst);
> + uffd_test_ops->release_pages(gopts, gopts->area_dst);
>
> args.apply_wp = wp;
> if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
> err("uffd_poll_thread create");
>
> - while (!ready_for_fork)
> + while (!gopts->ready_for_fork)
> ; /* Wait for the poll_thread to start executing before forking */
>
> pid = fork();
> @@ -808,12 +788,12 @@ static void uffd_sigbus_test_common(bool wp)
> err("fork");
>
> if (!pid)
> - exit(faulting_process(2, wp));
> + exit(faulting_process(gopts, 2, wp));
>
> waitpid(pid, &err, 0);
> if (err)
> err("faulting process failed");
> - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c))
> + if (write(gopts->pipefd[1], &c, sizeof(c)) != sizeof(c))
> err("pipe write");
> if (pthread_join(uffd_mon, (void **)&userfaults))
> err("pthread_join()");
> @@ -824,28 +804,29 @@ static void uffd_sigbus_test_common(bool wp)
> uffd_test_pass();
> }
>
> -static void uffd_sigbus_test(uffd_test_args_t *args)
> +static void uffd_sigbus_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_sigbus_test_common(false);
> + uffd_sigbus_test_common(gopts, false);
> }
>
> -static void uffd_sigbus_wp_test(uffd_test_args_t *args)
> +static void uffd_sigbus_wp_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_sigbus_test_common(true);
> + uffd_sigbus_test_common(gopts, true);
> }
>
> -static void uffd_events_test_common(bool wp)
> +static void uffd_events_test_common(uffd_global_test_opts_t *gopts, bool wp)
> {
> pthread_t uffd_mon;
> pid_t pid;
> int err;
> char c;
> struct uffd_args args = { 0 };
> + args.gopts = gopts;
>
> - ready_for_fork = false;
> + gopts->ready_for_fork = false;
>
> - fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK);
> - if (uffd_register(uffd, area_dst, nr_pages * page_size,
> + fcntl(gopts->uffd, F_SETFL, gopts->uffd_flags | O_NONBLOCK);
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size,
> true, wp, false))
> err("register failure");
>
> @@ -853,7 +834,7 @@ static void uffd_events_test_common(bool wp)
> if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
> err("uffd_poll_thread create");
>
> - while (!ready_for_fork)
> + while (!gopts->ready_for_fork)
> ; /* Wait for the poll_thread to start executing before forking */
>
> pid = fork();
> @@ -861,39 +842,39 @@ static void uffd_events_test_common(bool wp)
> err("fork");
>
> if (!pid)
> - exit(faulting_process(0, wp));
> + exit(faulting_process(gopts, 0, wp));
>
> waitpid(pid, &err, 0);
> if (err)
> err("faulting process failed");
> - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c))
> + if (write(gopts->pipefd[1], &c, sizeof(c)) != sizeof(c))
> err("pipe write");
> if (pthread_join(uffd_mon, NULL))
> err("pthread_join()");
>
> - if (args.missing_faults != nr_pages)
> + if (args.missing_faults != gopts->nr_pages)
> uffd_test_fail("Fault counts wrong");
> else
> uffd_test_pass();
> }
>
> -static void uffd_events_test(uffd_test_args_t *args)
> +static void uffd_events_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_events_test_common(false);
> + uffd_events_test_common(gopts, false);
> }
>
> -static void uffd_events_wp_test(uffd_test_args_t *args)
> +static void uffd_events_wp_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> - uffd_events_test_common(true);
> + uffd_events_test_common(gopts, true);
> }
>
> -static void retry_uffdio_zeropage(int ufd,
> +static void retry_uffdio_zeropage(uffd_global_test_opts_t *gopts,
> struct uffdio_zeropage *uffdio_zeropage)
> {
> - uffd_test_ops->alias_mapping(&uffdio_zeropage->range.start,
> + uffd_test_ops->alias_mapping(gopts, &uffdio_zeropage->range.start,
> uffdio_zeropage->range.len,
> 0);
> - if (ioctl(ufd, UFFDIO_ZEROPAGE, uffdio_zeropage)) {
> + if (ioctl(gopts->uffd, UFFDIO_ZEROPAGE, uffdio_zeropage)) {
> if (uffdio_zeropage->zeropage != -EEXIST)
> err("UFFDIO_ZEROPAGE error: %"PRId64,
> (int64_t)uffdio_zeropage->zeropage);
> @@ -903,16 +884,16 @@ static void retry_uffdio_zeropage(int ufd,
> }
> }
>
> -static bool do_uffdio_zeropage(int ufd, bool has_zeropage)
> +static bool do_uffdio_zeropage(uffd_global_test_opts_t *gopts, 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.range.start = (unsigned long) gopts->area_dst;
> + uffdio_zeropage.range.len = gopts->page_size;
> uffdio_zeropage.mode = 0;
> - ret = ioctl(ufd, UFFDIO_ZEROPAGE, &uffdio_zeropage);
> + ret = ioctl(gopts->uffd, UFFDIO_ZEROPAGE, &uffdio_zeropage);
> res = uffdio_zeropage.zeropage;
> if (ret) {
> /* real retval in ufdio_zeropage.zeropage */
> @@ -921,10 +902,10 @@ static bool do_uffdio_zeropage(int ufd, bool has_zeropage)
> else if (res != -EINVAL)
> err("UFFDIO_ZEROPAGE not -EINVAL");
> } else if (has_zeropage) {
> - if (res != page_size)
> + if (res != gopts->page_size)
> err("UFFDIO_ZEROPAGE unexpected size");
> else
> - retry_uffdio_zeropage(ufd, &uffdio_zeropage);
> + retry_uffdio_zeropage(gopts, &uffdio_zeropage);
> return true;
> } else
> err("UFFDIO_ZEROPAGE succeeded");
> @@ -950,25 +931,29 @@ uffd_register_detect_zeropage(int uffd, void *addr, uint64_t len)
> }
>
> /* exercise UFFDIO_ZEROPAGE */
> -static void uffd_zeropage_test(uffd_test_args_t *args)
> +static void uffd_zeropage_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> bool has_zeropage;
> int i;
>
> - has_zeropage = uffd_register_detect_zeropage(uffd, area_dst, page_size);
> - if (area_dst_alias)
> + has_zeropage = uffd_register_detect_zeropage(gopts->uffd,
> + gopts->area_dst,
> + gopts->page_size);
> + if (gopts->area_dst_alias)
> /* Ignore the retval; we already have it */
> - uffd_register_detect_zeropage(uffd, area_dst_alias, page_size);
> + uffd_register_detect_zeropage(gopts->uffd, gopts->area_dst_alias, gopts->page_size);
>
> - if (do_uffdio_zeropage(uffd, has_zeropage))
> - for (i = 0; i < page_size; i++)
> - if (area_dst[i] != 0)
> + if (do_uffdio_zeropage(gopts, has_zeropage))
> + for (i = 0; i < gopts->page_size; i++)
> + if (gopts->area_dst[i] != 0)
> err("data non-zero at offset %d\n", i);
>
> - if (uffd_unregister(uffd, area_dst, page_size))
> + if (uffd_unregister(gopts->uffd, gopts->area_dst, gopts->page_size))
> err("unregister");
>
> - if (area_dst_alias && uffd_unregister(uffd, area_dst_alias, page_size))
> + if (gopts->area_dst_alias && uffd_unregister(gopts->uffd,
> + gopts->area_dst_alias,
> + gopts->page_size))
> err("unregister");
>
> uffd_test_pass();
> @@ -987,26 +972,27 @@ static void uffd_register_poison(int uffd, void *addr, uint64_t len)
> err("registered area doesn't support COPY and POISON ioctls");
> }
>
> -static void do_uffdio_poison(int uffd, unsigned long offset)
> +static void do_uffdio_poison(uffd_global_test_opts_t *gopts, unsigned long offset)
> {
> struct uffdio_poison uffdio_poison = { 0 };
> int ret;
> __s64 res;
>
> - uffdio_poison.range.start = (unsigned long) area_dst + offset;
> - uffdio_poison.range.len = page_size;
> + uffdio_poison.range.start = (unsigned long) gopts->area_dst + offset;
> + uffdio_poison.range.len = gopts->page_size;
> uffdio_poison.mode = 0;
> - ret = ioctl(uffd, UFFDIO_POISON, &uffdio_poison);
> + ret = ioctl(gopts->uffd, UFFDIO_POISON, &uffdio_poison);
> res = uffdio_poison.updated;
>
> if (ret)
> err("UFFDIO_POISON error: %"PRId64, (int64_t)res);
> - else if (res != page_size)
> + else if (res != gopts->page_size)
> err("UFFDIO_POISON unexpected size: %"PRId64, (int64_t)res);
> }
>
> -static void uffd_poison_handle_fault(
> - struct uffd_msg *msg, struct uffd_args *args)
> +static void uffd_poison_handle_fault(uffd_global_test_opts_t *gopts,
> + struct uffd_msg *msg,
> + struct uffd_args *args)
> {
> unsigned long offset;
>
> @@ -1017,17 +1003,17 @@ static void uffd_poison_handle_fault(
> (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_MINOR))
> err("unexpected fault type %llu", msg->arg.pagefault.flags);
>
> - offset = (char *)(unsigned long)msg->arg.pagefault.address - area_dst;
> - offset &= ~(page_size-1);
> + offset = (char *)(unsigned long)msg->arg.pagefault.address - gopts->area_dst;
> + offset &= ~(gopts->page_size-1);
>
> /* Odd pages -> copy zeroed page; even pages -> poison. */
> - if (offset & page_size)
> - copy_page(uffd, offset, false);
> + if (offset & gopts->page_size)
> + copy_page(gopts, offset, false);
> else
> - do_uffdio_poison(uffd, offset);
> + do_uffdio_poison(gopts, offset);
> }
>
> -static void uffd_poison_test(uffd_test_args_t *targs)
> +static void uffd_poison_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *targs)
> {
> pthread_t uffd_mon;
> char c;
> @@ -1036,10 +1022,12 @@ static void uffd_poison_test(uffd_test_args_t *targs)
> unsigned long nr_sigbus = 0;
> unsigned long nr;
>
> - fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK);
> + args.gopts = gopts;
> +
> + fcntl(gopts->uffd, F_SETFL, gopts->uffd_flags | O_NONBLOCK);
>
> - uffd_register_poison(uffd, area_dst, nr_pages * page_size);
> - memset(area_src, 0, nr_pages * page_size);
> + uffd_register_poison(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size);
> + memset(gopts->area_src, 0, gopts->nr_pages * gopts->page_size);
>
> args.handle_fault = uffd_poison_handle_fault;
> if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
> @@ -1051,9 +1039,9 @@ static void uffd_poison_test(uffd_test_args_t *targs)
> if (sigaction(SIGBUS, &act, 0))
> err("sigaction");
>
> - for (nr = 0; nr < nr_pages; ++nr) {
> - unsigned long offset = nr * page_size;
> - const char *bytes = (const char *) area_dst + offset;
> + for (nr = 0; nr < gopts->nr_pages; ++nr) {
> + unsigned long offset = nr * gopts->page_size;
> + const char *bytes = (const char *) gopts->area_dst + offset;
> const char *i;
>
> if (sigsetjmp(*sigbuf, 1)) {
> @@ -1066,27 +1054,29 @@ static void uffd_poison_test(uffd_test_args_t *targs)
> continue;
> }
>
> - for (i = bytes; i < bytes + page_size; ++i) {
> + for (i = bytes; i < bytes + gopts->page_size; ++i) {
> if (*i)
> err("nonzero byte in area_dst (%p) at %p: %u",
> - area_dst, i, *i);
> + gopts->area_dst, i, *i);
> }
> }
>
> - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c))
> + if (write(gopts->pipefd[1], &c, sizeof(c)) != sizeof(c))
> err("pipe write");
> if (pthread_join(uffd_mon, NULL))
> err("pthread_join()");
>
> - if (nr_sigbus != nr_pages / 2)
> + if (nr_sigbus != gopts->nr_pages / 2)
> err("expected to receive %lu SIGBUS, actually received %lu",
> - nr_pages / 2, nr_sigbus);
> + gopts->nr_pages / 2, nr_sigbus);
>
> uffd_test_pass();
> }
>
> static void
> -uffd_move_handle_fault_common(struct uffd_msg *msg, struct uffd_args *args,
> +uffd_move_handle_fault_common(uffd_global_test_opts_t *gopts,
> + struct uffd_msg *msg,
> + struct uffd_args *args,
> unsigned long len)
> {
> unsigned long offset;
> @@ -1098,28 +1088,32 @@ uffd_move_handle_fault_common(struct uffd_msg *msg, struct uffd_args *args,
> (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_MINOR | UFFD_PAGEFAULT_FLAG_WRITE))
> err("unexpected fault type %llu", msg->arg.pagefault.flags);
>
> - offset = (char *)(unsigned long)msg->arg.pagefault.address - area_dst;
> + offset = (char *)(unsigned long)msg->arg.pagefault.address - gopts->area_dst;
> offset &= ~(len-1);
>
> - if (move_page(uffd, offset, len))
> + if (move_page(gopts, offset, len))
> args->missing_faults++;
> }
>
> -static void uffd_move_handle_fault(struct uffd_msg *msg,
> +static void uffd_move_handle_fault(uffd_global_test_opts_t *gopts, struct uffd_msg *msg,
> struct uffd_args *args)
> {
> - uffd_move_handle_fault_common(msg, args, page_size);
> + uffd_move_handle_fault_common(gopts, msg, args, gopts->page_size);
> }
>
> -static void uffd_move_pmd_handle_fault(struct uffd_msg *msg,
> +static void uffd_move_pmd_handle_fault(uffd_global_test_opts_t *gopts, struct uffd_msg *msg,
> struct uffd_args *args)
> {
> - uffd_move_handle_fault_common(msg, args, read_pmd_pagesize());
> + uffd_move_handle_fault_common(gopts, msg, args, read_pmd_pagesize());
> }
>
> static void
> -uffd_move_test_common(uffd_test_args_t *targs, unsigned long chunk_size,
> - void (*handle_fault)(struct uffd_msg *msg, struct uffd_args *args))
> +uffd_move_test_common(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *targs,
> + unsigned long chunk_size,
> + void (*handle_fault)(struct uffd_global_test_opts *gopts,
> + struct uffd_msg *msg, struct uffd_args *args)
> +)
> {
> unsigned long nr;
> pthread_t uffd_mon;
> @@ -1131,11 +1125,13 @@ uffd_move_test_common(uffd_test_args_t *targs, unsigned long chunk_size,
> unsigned long src_offs = 0;
> unsigned long dst_offs = 0;
>
> + args.gopts = gopts;
> +
> /* Prevent source pages from being mapped more than once */
> - if (madvise(area_src, nr_pages * page_size, MADV_DONTFORK))
> + if (madvise(gopts->area_src, gopts->nr_pages * gopts->page_size, MADV_DONTFORK))
> err("madvise(MADV_DONTFORK) failure");
>
> - if (uffd_register(uffd, area_dst, nr_pages * page_size,
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size,
> true, false, false))
> err("register failure");
>
> @@ -1143,22 +1139,22 @@ uffd_move_test_common(uffd_test_args_t *targs, unsigned long chunk_size,
> if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
> err("uffd_poll_thread create");
>
> - step_size = chunk_size / page_size;
> - step_count = nr_pages / step_size;
> + step_size = chunk_size / gopts->page_size;
> + step_count = gopts->nr_pages / step_size;
>
> - if (chunk_size > page_size) {
> - char *aligned_src = ALIGN_UP(area_src, chunk_size);
> - char *aligned_dst = ALIGN_UP(area_dst, chunk_size);
> + if (chunk_size > gopts->page_size) {
> + char *aligned_src = ALIGN_UP(gopts->area_src, chunk_size);
> + char *aligned_dst = ALIGN_UP(gopts->area_dst, chunk_size);
>
> - if (aligned_src != area_src || aligned_dst != area_dst) {
> - src_offs = (aligned_src - area_src) / page_size;
> - dst_offs = (aligned_dst - area_dst) / page_size;
> + if (aligned_src != gopts->area_src || aligned_dst != gopts->area_dst) {
> + src_offs = (aligned_src - gopts->area_src) / gopts->page_size;
> + dst_offs = (aligned_dst - gopts->area_dst) / gopts->page_size;
> step_count--;
> }
> - orig_area_src = area_src;
> - orig_area_dst = area_dst;
> - area_src = aligned_src;
> - area_dst = aligned_dst;
> + orig_area_src = gopts->area_src;
> + orig_area_dst = gopts->area_dst;
> + gopts->area_src = aligned_src;
> + gopts->area_dst = aligned_dst;
> }
>
> /*
> @@ -1172,34 +1168,34 @@ uffd_move_test_common(uffd_test_args_t *targs, unsigned long chunk_size,
>
> /* Check area_src content */
> for (i = 0; i < step_size; i++) {
> - count = *area_count(area_src, nr + i);
> - if (count != count_verify[src_offs + nr + i])
> + count = *area_count(gopts->area_src, nr + i, gopts);
> + if (count != gopts->count_verify[src_offs + nr + i])
> err("nr %lu source memory invalid %llu %llu\n",
> - nr + i, count, count_verify[src_offs + nr + i]);
> + nr + i, count, gopts->count_verify[src_offs + nr + i]);
> }
>
> /* Faulting into area_dst should move the page or the huge page */
> for (i = 0; i < step_size; i++) {
> - count = *area_count(area_dst, nr + i);
> - if (count != count_verify[dst_offs + nr + i])
> + count = *area_count(gopts->area_dst, nr + i, gopts);
> + if (count != gopts->count_verify[dst_offs + nr + i])
> err("nr %lu memory corruption %llu %llu\n",
> - nr, count, count_verify[dst_offs + nr + i]);
> + nr, count, gopts->count_verify[dst_offs + nr + i]);
> }
>
> /* Re-check area_src content which should be empty */
> for (i = 0; i < step_size; i++) {
> - count = *area_count(area_src, nr + i);
> + count = *area_count(gopts->area_src, nr + i, gopts);
> if (count != 0)
> err("nr %lu move failed %llu %llu\n",
> - nr, count, count_verify[src_offs + nr + i]);
> + nr, count, gopts->count_verify[src_offs + nr + i]);
> }
> }
> - if (chunk_size > page_size) {
> - area_src = orig_area_src;
> - area_dst = orig_area_dst;
> + if (chunk_size > gopts->page_size) {
> + gopts->area_src = orig_area_src;
> + gopts->area_dst = orig_area_dst;
> }
>
> - if (write(pipefd[1], &c, sizeof(c)) != sizeof(c))
> + if (write(gopts->pipefd[1], &c, sizeof(c)) != sizeof(c))
> err("pipe write");
> if (pthread_join(uffd_mon, NULL))
> err("join() failed");
> @@ -1210,24 +1206,24 @@ uffd_move_test_common(uffd_test_args_t *targs, unsigned long chunk_size,
> uffd_test_pass();
> }
>
> -static void uffd_move_test(uffd_test_args_t *targs)
> +static void uffd_move_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *targs)
> {
> - uffd_move_test_common(targs, page_size, uffd_move_handle_fault);
> + uffd_move_test_common(gopts, targs, gopts->page_size, uffd_move_handle_fault);
> }
>
> -static void uffd_move_pmd_test(uffd_test_args_t *targs)
> +static void uffd_move_pmd_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *targs)
> {
> - if (madvise(area_dst, nr_pages * page_size, MADV_HUGEPAGE))
> + if (madvise(gopts->area_dst, gopts->nr_pages * gopts->page_size, MADV_HUGEPAGE))
> err("madvise(MADV_HUGEPAGE) failure");
> - uffd_move_test_common(targs, read_pmd_pagesize(),
> + uffd_move_test_common(gopts, targs, read_pmd_pagesize(),
> uffd_move_pmd_handle_fault);
> }
>
> -static void uffd_move_pmd_split_test(uffd_test_args_t *targs)
> +static void uffd_move_pmd_split_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *targs)
> {
> - if (madvise(area_dst, nr_pages * page_size, MADV_NOHUGEPAGE))
> + if (madvise(gopts->area_dst, gopts->nr_pages * gopts->page_size, MADV_NOHUGEPAGE))
> err("madvise(MADV_NOHUGEPAGE) failure");
> - uffd_move_test_common(targs, read_pmd_pagesize(),
> + uffd_move_test_common(gopts, targs, read_pmd_pagesize(),
> uffd_move_pmd_handle_fault);
> }
>
> @@ -1287,6 +1283,11 @@ typedef enum {
> THR_STATE_UNINTERRUPTIBLE,
> } thread_state;
>
> +typedef struct {
> + uffd_global_test_opts_t *gopts;
> + volatile pid_t *pid;
> +} mmap_changing_thread_args;
> +
> static void sleep_short(void)
> {
> usleep(1000);
> @@ -1329,7 +1330,9 @@ static void thread_state_until(pid_t tid, thread_state state)
>
> static void *uffd_mmap_changing_thread(void *opaque)
> {
> - volatile pid_t *pid = opaque;
> + mmap_changing_thread_args *args = opaque;
> + uffd_global_test_opts_t *gopts = args->gopts;
> + volatile pid_t *pid = args->pid;
> int ret;
>
> /* Unfortunately, it's only fetch-able from the thread itself.. */
> @@ -1337,21 +1340,21 @@ static void *uffd_mmap_changing_thread(void *opaque)
> *pid = syscall(SYS_gettid);
>
> /* Inject an event, this will hang solid until the event read */
> - ret = madvise(area_dst, page_size, MADV_REMOVE);
> + ret = madvise(gopts->area_dst, gopts->page_size, MADV_REMOVE);
> if (ret)
> err("madvise(MADV_REMOVE) failed");
>
> return NULL;
> }
>
> -static void uffd_consume_message(int fd)
> +static void uffd_consume_message(uffd_global_test_opts_t *gopts)
> {
> struct uffd_msg msg = { 0 };
>
> - while (uffd_read_msg(fd, &msg));
> + while (uffd_read_msg(gopts, &msg));
> }
>
> -static void uffd_mmap_changing_test(uffd_test_args_t *targs)
> +static void uffd_mmap_changing_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *targs)
> {
> /*
> * This stores the real PID (which can be different from how tid is
> @@ -1360,13 +1363,14 @@ static void uffd_mmap_changing_test(uffd_test_args_t *targs)
> pid_t pid = 0;
> pthread_t tid;
> int ret;
> + mmap_changing_thread_args args = { gopts, &pid };
>
> - if (uffd_register(uffd, area_dst, nr_pages * page_size,
> + if (uffd_register(gopts->uffd, gopts->area_dst, gopts->nr_pages * gopts->page_size,
> true, false, false))
> err("uffd_register() failed");
>
> /* Create a thread to generate the racy event */
> - ret = pthread_create(&tid, NULL, uffd_mmap_changing_thread, &pid);
> + ret = pthread_create(&tid, NULL, uffd_mmap_changing_thread, &args);
> if (ret)
> err("pthread_create() failed");
>
> @@ -1380,26 +1384,26 @@ static void uffd_mmap_changing_test(uffd_test_args_t *targs)
> /* Wait until the thread hangs at REMOVE event */
> thread_state_until(pid, THR_STATE_UNINTERRUPTIBLE);
>
> - if (!uffdio_mmap_changing_test_copy(uffd))
> + if (!uffdio_mmap_changing_test_copy(gopts->uffd))
> return;
>
> - if (!uffdio_mmap_changing_test_zeropage(uffd))
> + if (!uffdio_mmap_changing_test_zeropage(gopts->uffd))
> return;
>
> - if (!uffdio_mmap_changing_test_move(uffd))
> + if (!uffdio_mmap_changing_test_move(gopts->uffd))
> return;
>
> - if (!uffdio_mmap_changing_test_poison(uffd))
> + if (!uffdio_mmap_changing_test_poison(gopts->uffd))
> return;
>
> - if (!uffdio_mmap_changing_test_continue(uffd))
> + if (!uffdio_mmap_changing_test_continue(gopts->uffd))
> return;
>
> /*
> * All succeeded above! Recycle everything. Start by reading the
> * event so as to kick the thread roll again..
> */
> - uffd_consume_message(uffd);
> + uffd_consume_message(gopts);
>
> ret = pthread_join(tid, NULL);
> assert(ret == 0);
> @@ -1407,10 +1411,10 @@ static void uffd_mmap_changing_test(uffd_test_args_t *targs)
> uffd_test_pass();
> }
>
> -static int prevent_hugepages(const char **errmsg)
> +static int prevent_hugepages(uffd_global_test_opts_t *gopts, const char **errmsg)
> {
> /* This should be done before source area is populated */
> - if (madvise(area_src, nr_pages * page_size, MADV_NOHUGEPAGE)) {
> + if (madvise(gopts->area_src, gopts->nr_pages * gopts->page_size, MADV_NOHUGEPAGE)) {
> /* Ignore only if CONFIG_TRANSPARENT_HUGEPAGE=n */
> if (errno != EINVAL) {
> if (errmsg)
> @@ -1421,10 +1425,10 @@ static int prevent_hugepages(const char **errmsg)
> return 0;
> }
>
> -static int request_hugepages(const char **errmsg)
> +static int request_hugepages(uffd_global_test_opts_t *gopts, const char **errmsg)
> {
> /* This should be done before source area is populated */
> - if (madvise(area_src, nr_pages * page_size, MADV_HUGEPAGE)) {
> + if (madvise(gopts->area_src, gopts->nr_pages * gopts->page_size, MADV_HUGEPAGE)) {
> if (errmsg) {
> *errmsg = (errno == EINVAL) ?
> "CONFIG_TRANSPARENT_HUGEPAGE is not set" :
> @@ -1448,13 +1452,17 @@ struct uffd_test_case_ops uffd_move_test_pmd_case_ops = {
> * Note that _UFFDIO_ZEROPAGE is tested separately in the zeropage test.
> */
> static void
> -do_register_ioctls_test(uffd_test_args_t *args, bool miss, bool wp, bool minor)
> +do_register_ioctls_test(uffd_global_test_opts_t *gopts,
> + uffd_test_args_t *args,
> + bool miss,
> + bool wp,
> + bool minor)
> {
> uint64_t ioctls = 0, expected = BIT_ULL(_UFFDIO_WAKE);
> mem_type_t *mem_type = args->mem_type;
> int ret;
>
> - ret = uffd_register_with_ioctls(uffd, area_dst, page_size,
> + ret = uffd_register_with_ioctls(gopts->uffd, gopts->area_dst, gopts->page_size,
> miss, wp, minor, &ioctls);
>
> /*
> @@ -1485,18 +1493,18 @@ do_register_ioctls_test(uffd_test_args_t *args, bool miss, bool wp, bool minor)
> "(miss=%d, wp=%d, minor=%d): expected=0x%"PRIx64", "
> "returned=0x%"PRIx64, miss, wp, minor, expected, ioctls);
>
> - if (uffd_unregister(uffd, area_dst, page_size))
> + if (uffd_unregister(gopts->uffd, gopts->area_dst, gopts->page_size))
> err("unregister");
> }
>
> -static void uffd_register_ioctls_test(uffd_test_args_t *args)
> +static void uffd_register_ioctls_test(uffd_global_test_opts_t *gopts, uffd_test_args_t *args)
> {
> int miss, wp, minor;
>
> for (miss = 0; miss <= 1; miss++)
> for (wp = 0; wp <= 1; wp++)
> for (minor = 0; minor <= 1; minor++)
> - do_register_ioctls_test(args, miss, wp, minor);
> + do_register_ioctls_test(gopts, args, miss, wp, minor);
>
> uffd_test_pass();
> }
> @@ -1734,6 +1742,28 @@ int main(int argc, char *argv[])
> }
> for (j = 0; j < n_mems; j++) {
> mem_type = &mem_types[j];
> +
> + /* Initialize global test options */
> + uffd_global_test_opts_t gopts;
> +
> + gopts.map_shared = mem_type->shared;
> + uffd_test_ops = mem_type->mem_ops;
> + uffd_test_case_ops = test->test_case_ops;
> +
> + if (mem_type->mem_flag & (MEM_HUGETLB_PRIVATE | MEM_HUGETLB))
> + gopts.page_size = default_huge_page_size();
> + else
> + gopts.page_size = psize();
> +
> + /* Ensure we have at least 2 pages */
> + gopts.nr_pages = MAX(UFFD_TEST_MEM_SIZE, gopts.page_size * 2)
> + / gopts.page_size;
> +
> + gopts.nr_parallel = 1;
> +
> + /* Initialize test arguments */
> + args.mem_type = mem_type;
> +
> if (!(test->mem_targets & mem_type->mem_flag))
> continue;
>
> @@ -1748,13 +1778,12 @@ int main(int argc, char *argv[])
> uffd_test_skip("feature missing");
> continue;
> }
> - if (uffd_setup_environment(&args, test, mem_type,
> - &errmsg)) {
> + if (uffd_test_ctx_init(&gopts, test->uffd_feature_required, &errmsg)) {
> uffd_test_skip(errmsg);
> continue;
> }
> - test->uffd_fn(&args);
> - uffd_test_ctx_clear();
> + test->uffd_fn(&gopts, &args);
> + uffd_test_ctx_clear(&gopts);
> }
> }
>
> diff --git a/tools/testing/selftests/mm/uffd-wp-mremap.c b/tools/testing/selftests/mm/uffd-wp-mremap.c
> index c2ba7d46c7b4..c286b1015f32 100644
> --- a/tools/testing/selftests/mm/uffd-wp-mremap.c
> +++ b/tools/testing/selftests/mm/uffd-wp-mremap.c
> @@ -157,7 +157,11 @@ static bool range_is_swapped(void *addr, size_t size)
> return true;
> }
>
> -static void test_one_folio(size_t size, bool private, bool swapout, bool hugetlb)
> +static void test_one_folio(uffd_global_test_opts_t *gopts,
> + size_t size,
> + bool private,
> + bool swapout,
> + bool hugetlb)
> {
> struct uffdio_writeprotect wp_prms;
> uint64_t features = 0;
> @@ -181,21 +185,21 @@ static void test_one_folio(size_t size, bool private, bool swapout, bool hugetlb
> }
>
> /* Register range for uffd-wp. */
> - if (userfaultfd_open(&features)) {
> + if (userfaultfd_open(gopts, &features)) {
> if (errno == ENOENT)
> ksft_test_result_skip("userfaultfd not available\n");
> else
> ksft_test_result_fail("userfaultfd_open() failed\n");
> goto out;
> }
> - if (uffd_register(uffd, mem, size, false, true, false)) {
> + if (uffd_register(gopts->uffd, mem, size, false, true, false)) {
> ksft_test_result_fail("uffd_register() failed\n");
> goto out;
> }
> wp_prms.mode = UFFDIO_WRITEPROTECT_MODE_WP;
> wp_prms.range.start = (uintptr_t)mem;
> wp_prms.range.len = size;
> - if (ioctl(uffd, UFFDIO_WRITEPROTECT, &wp_prms)) {
> + if (ioctl(gopts->uffd, UFFDIO_WRITEPROTECT, &wp_prms)) {
> ksft_test_result_fail("ioctl(UFFDIO_WRITEPROTECT) failed\n");
> goto out;
> }
> @@ -242,9 +246,9 @@ static void test_one_folio(size_t size, bool private, bool swapout, bool hugetlb
> out:
> if (mem)
> munmap(mem, size);
> - if (uffd >= 0) {
> - close(uffd);
> - uffd = -1;
> + if (gopts->uffd >= 0) {
> + close(gopts->uffd);
> + gopts->uffd = -1;
> }
> }
>
> @@ -336,6 +340,7 @@ static const struct testcase testcases[] = {
>
> int main(int argc, char **argv)
> {
> + uffd_global_test_opts_t gopts;
> struct thp_settings settings;
> int i, j, plan = 0;
>
> @@ -367,8 +372,8 @@ int main(int argc, char **argv)
> const struct testcase *tc = &testcases[i];
>
> for (j = 0; j < *tc->nr_sizes; j++)
> - test_one_folio(tc->sizes[j], tc->private, tc->swapout,
> - tc->hugetlb);
> + test_one_folio(&gopts, tc->sizes[j], tc->private,
> + tc->swapout, tc->hugetlb);
> }
>
> /* If THP is supported, restore original THP settings. */
next prev parent reply other threads:[~2025-06-10 11:32 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-01 16:38 [PATCH 0/4] selftests/mm/uffd: refactor global variables Ujwal Kundur
2025-05-01 16:38 ` [PATCH 1/4] selftests/mm/uffd: Refactor non-composite global vars into struct Ujwal Kundur
2025-05-02 12:16 ` Brendan Jackman
2025-05-02 12:28 ` Brendan Jackman
2025-05-03 18:16 ` Ujwal Kundur
2025-05-04 2:25 ` Andrew Morton
2025-05-01 16:38 ` [PATCH 2/4] selftests/mm/uffd: Swap global vars with global test options Ujwal Kundur
2025-05-01 16:38 ` [PATCH 3/4] selftests/mm/uffd: Swap global variables with global test opts Ujwal Kundur
2025-05-01 16:38 ` [PATCH 4/4] " Ujwal Kundur
2025-05-02 12:18 ` [PATCH 0/4] selftests/mm/uffd: refactor global variables Brendan Jackman
2025-05-04 9:48 ` [PATCH v2 1/1] selftests/mm/uffd: Refactor non-composite global vars into struct Ujwal Kundur
2025-05-06 0:57 ` Andrew Morton
2025-05-10 16:03 ` [PATCH v3 " Ujwal Kundur
2025-05-13 12:12 ` Brendan Jackman
2025-05-19 13:50 ` Ujwal Kundur
2025-05-19 21:40 ` Andrew Morton
2025-05-20 9:16 ` Brendan Jackman
2025-05-25 19:19 ` Ujwal Kundur
2025-05-26 9:08 ` Brendan Jackman
2025-05-30 7:45 ` Ujwal Kundur
2025-05-31 7:46 ` [PATCH v4 " Ujwal Kundur
2025-06-10 6:57 ` Ujwal Kundur
2025-06-10 11:32 ` Brendan Jackman [this message]
2025-06-16 6:38 ` Ujwal Kundur
2025-06-16 10:04 ` [PATCH v5 " Ujwal Kundur
2025-06-17 0:26 ` Andrew Morton
2025-06-17 15:52 ` Peter Xu
2025-06-17 17:22 ` Peter Xu
2025-06-18 10:00 ` Brendan Jackman
2025-06-26 5:22 ` Ujwal Kundur
2025-06-26 14:12 ` Peter Xu
2025-06-30 11:25 ` Ujwal Kundur
2025-07-02 15:20 ` [PATCH v6 " Ujwal Kundur
2025-07-04 16:20 ` Peter Xu
2025-07-10 5:07 ` Ujwal Kundur
2025-08-06 15:03 ` Ujwal Kundur
2025-08-07 16:45 ` Peter Xu
2025-08-13 11:33 ` Brendan Jackman
2025-08-16 14:12 ` Ujwal Kundur
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DAITJKYRQMFD.OLOUWS7UPGVD@google.com \
--to=jackmanb@google.com \
--cc=akpm@linux-foundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=peterx@redhat.com \
--cc=shuah@kernel.org \
--cc=ujwal.kundur@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.