From: "David Hildenbrand (Arm)" <david@kernel.org>
To: Konstantin Khorenko <khorenko@virtuozzo.com>,
Andrew Morton <akpm@linux-foundation.org>,
Zi Yan <ziy@nvidia.com>, Lorenzo Stoakes <ljs@kernel.org>,
Alistair Popple <apopple@nvidia.com>,
Dan Williams <djbw@kernel.org>
Cc: linux-mm@kvack.org, nvdimm@lists.linux.dev,
linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 2/2] selftests/mm: test fork of a device DAX PUD
Date: Thu, 5 Nov 2026 20:30:14 +0100 [thread overview]
Message-ID: <eb5e8a1c-a654-4d68-92b1-db0128fa8752@kernel.org> (raw)
In-Reply-To: <20261105-vstor-150143-copy_huge_pud-folio-refcount-v2-2-2828fe5a51ed@virtuozzo.com>
On 11/5/26 18:00, Konstantin Khorenko wrote:
> Fork a process that maps a device DAX instance with a PUD, in a VMA
> whose page tables fork copies because it has a guard region. The child
> must account the copied PUD in its RssFile, which a new rss_file() in
> vm_util reads as rss_anon() reads RssAnon.
>
> Without a reference, an rmap and a counter for the copied PUD, the
> child's RssFile misses the PUD, and its exit leaves the child's file
> counter negative: "Bad rss-counter state".
>
> It runs only on a device named in argv[1] or $FORK_DAX_PUD_DEVICE: on
> a kernel without the fix it leaves the device folio's mapcount below
> zero.
>
> Signed-off-by: Konstantin Khorenko <khorenko@virtuozzo.com>
> ---
> MAINTAINERS | 1 +
> tools/testing/selftests/mm/Makefile | 1 +
> tools/testing/selftests/mm/fork_dax_pud.c | 114 ++++++++++++++++++++++++++++++
> tools/testing/selftests/mm/run_vmtests.sh | 1 +
> tools/testing/selftests/mm/vm_util.c | 21 ++++++
> tools/testing/selftests/mm/vm_util.h | 1 +
> 6 files changed, 139 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d63d8b2152773..247e16f3276f4 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17931,6 +17931,7 @@ F: mm/huge_memory.c
> F: mm/khugepaged.c
> F: mm/mm_slot.h
> F: tools/mm/thpmaps
> +F: tools/testing/selftests/mm/fork_dax_pud.c
> F: tools/testing/selftests/mm/khugepaged.c
> F: tools/testing/selftests/mm/split_huge_page_test.c
> F: tools/testing/selftests/mm/transhuge-stress.c
> diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
> index 0ea72e9979347..130500f5f6e0a 100644
> --- a/tools/testing/selftests/mm/Makefile
> +++ b/tools/testing/selftests/mm/Makefile
> @@ -108,6 +108,7 @@ TEST_GEN_FILES += folio_split_race_test
> TEST_GEN_FILES += folio_order_check
> TEST_GEN_FILES += khugepaged_sync_check
> TEST_GEN_FILES += khugepaged_race
> +TEST_GEN_FILES += fork_dax_pud
> TEST_GEN_FILES += soft-dirty
>
> ifeq ($(ARCH),x86_64)
> diff --git a/tools/testing/selftests/mm/fork_dax_pud.c b/tools/testing/selftests/mm/fork_dax_pud.c
> new file mode 100644
> index 0000000000000..0d19c30b1dc6f
> --- /dev/null
> +++ b/tools/testing/selftests/mm/fork_dax_pud.c
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * fork() of a process that maps a device DAX instance with a PUD, in a VMA
> + * whose page tables fork copies because it has a guard region. The child
> + * must account the copied PUD in its RssFile.
> + *
> + * Runs only on a device named in argv[1] or $FORK_DAX_PUD_DEVICE that
> + * nothing else maps, with 1 GiB alignment and at least 2 GiB. To make one
> + * on pmem emulated in RAM, with CONFIG_X86_PMEM_LEGACY and
> + * CONFIG_DEV_DAX_PMEM:
> + *
> + * boot with memmap=3G!4G
> + * ndctl create-namespace -f -e namespace0.0 -m devdax -a 1G
> + * FORK_DAX_PUD_DEVICE=/dev/dax0.0 ./fork_dax_pud
> + */
> +#define _GNU_SOURCE
> +#include <asm-generic/mman.h> /* Force the import of the tools version. */
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <setjmp.h>
> +#include <signal.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +#include <sys/wait.h>
> +
> +#include "kselftest.h"
> +#include "vm_util.h"
> +
> +#define SZ_1G (1UL << 30)
> +#define DAX_SYSFS "/sys/bus/dax/devices"
> +
> +static sigjmp_buf sigbus_jmp;
> +
> +static void sigbus_handler(int sig)
> +{
> + siglongjmp(sigbus_jmp, 1);
> +}
> +
> +static bool dax_attr(const char *dev, const char *attr, unsigned long *val)
> +{
> + char path[PATH_MAX];
> +
> + snprintf(path, sizeof(path), DAX_SYSFS "/%s/%s", dev, attr);
> + return !read_sysfs(path, val);
> +}
> +
> +static bool dax_dev_ok(const char *path)
> +{
> + const char *name = strrchr(path, '/');
> + unsigned long align, size;
> +
> + name = name ? name + 1 : path;
> + return dax_attr(name, "align", &align) && align == SZ_1G &&
> + dax_attr(name, "size", &size) && size >= 2 * SZ_1G;
> +}
> +
> +int main(int argc, char **argv)
> +{
> + const char *path = argc > 1 ? argv[1] : getenv("FORK_DAX_PUD_DEVICE");
> + char *area, *mem;
> + int fd, status;
> + pid_t pid;
> +
> + ksft_print_header();
> + if (!path)
> + ksft_exit_skip("no devdax device given\n");
> + if (!dax_dev_ok(path))
> + ksft_exit_skip("%s: needs 1 GiB alignment and 2 GiB\n", path);
> +
> + fd = open(path, O_RDONLY);
> + if (fd < 0)
> + ksft_exit_skip("%s: %s\n", path, strerror(errno));
> + area = mmap(NULL, 3 * SZ_1G, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> + if (area == MAP_FAILED)
> + ksft_exit_fail_perror("mmap");
> + mem = (char *)(((unsigned long)area + SZ_1G - 1) & ~(SZ_1G - 1));
> + mem = mmap(mem, 2 * SZ_1G, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
> + if (mem == MAP_FAILED)
> + ksft_exit_skip("mmap %s: %s\n", path, strerror(errno));
> +
> + /* A PUD in the first GiB, a guard page at the end of the second. */
> + signal(SIGBUS, sigbus_handler);
> + if (sigsetjmp(sigbus_jmp, 1))
> + ksft_exit_skip("%s cannot be mapped with a PUD\n", path);
> + FORCE_READ(mem[0]);
> + signal(SIGBUS, SIG_DFL);
> + if (madvise(mem + 2 * SZ_1G - getpagesize(), getpagesize(),
> + MADV_GUARD_INSTALL))
> + ksft_exit_skip("MADV_GUARD_INSTALL: %s\n", strerror(errno));
> +
> + ksft_set_plan(1);
> + fflush(stdout);
> + pid = fork();
> + if (pid < 0)
> + ksft_exit_fail_perror("fork");
> + if (!pid)
> + _exit(rss_file() < (SZ_1G >> 10) ? 10 : 0);
> + if (waitpid(pid, &status, 0) != pid)
> + ksft_exit_fail_perror("waitpid");
> +
> + if (!WIFEXITED(status) || WEXITSTATUS(status))
> + ksft_test_result_fail("child does not account the copied PUD\n");
> + else
> + ksft_test_result_pass("devdax PUD copied by fork\n");
> +
> + munmap(area, 3 * SZ_1G);
> + close(fd);
> + ksft_finished();
> +}
I prefer to not merge this. It requires an actual dax device, so it's not very
applicable on CI systems, and we don't want selftests to take parameters in one
form or the other.
--
Cheers,
David
prev parent reply other threads:[~2026-11-05 19:30 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 17:00 [PATCH v2 0/2] mm/huge_memory: take a reference on a device DAX PUD copied by fork Konstantin Khorenko
2026-11-05 17:00 ` [PATCH v2 1/2] mm/huge_memory: take a reference on a folio " Konstantin Khorenko
2026-11-05 19:28 ` David Hildenbrand (Arm)
2026-11-05 20:19 ` Zi Yan
2026-11-05 20:31 ` David Hildenbrand (Arm)
2026-11-05 17:00 ` [PATCH v2 2/2] selftests/mm: test fork of a device DAX PUD Konstantin Khorenko
2026-11-05 19:30 ` David Hildenbrand (Arm) [this message]
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=eb5e8a1c-a654-4d68-92b1-db0128fa8752@kernel.org \
--to=david@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=apopple@nvidia.com \
--cc=djbw@kernel.org \
--cc=khorenko@virtuozzo.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=nvdimm@lists.linux.dev \
--cc=ziy@nvidia.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®