From: James Houghton <jthoughton@google.com>
To: akpm@linux-foundation.org
Cc: baohua@kernel.org, baolin.wang@linux.alibaba.com,
david@kernel.org, dev.jain@arm.com, hughd@google.com,
jthoughton@google.com, kas@kernel.org, lance.yang@linux.dev,
liam@infradead.org, linux-kernel@vger.kernel.org,
linux-mm@kvack.org, ljs@kernel.org, nico.pache@linux.dev,
ryan.roberts@arm.com, shy828301@gmail.com,
stable@vger.kernel.org, usama.arif@linux.dev, ziy@nvidia.com,
zokeefe@google.com
Subject: Re: [PATCH v2] mm/khugepaged: Don't install PMDs in uffd-minor-registered VMAs
Date: Sat, 29 Aug 2026 02:50:33 +0000 [thread overview]
Message-ID: <20260829025034.2500920-1-jthoughton@google.com> (raw)
In-Reply-To: <20260828183801.04fd2d2a6af837cb45b60e0f@linux-foundation.org>
On Fri, Aug 28, 2026 at 6:38 PM Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Fri, 28 Aug 2026 18:18:11 -0700 James Houghton <jthoughton@google.com> wrote:
> > Ah, just to be clear, I linked a repro on v1. Here[1] is that repro.
> > Sorry, slipped my mind to also include it in the notes for this v2.
> >
> > [1] https://gist.github.com/48ca/d399bf534158e80241fb4937ef1ff664
>
> Cool thanks, I'll slap this into the changelog.
>
> # ./a.out
> TAP version 13
> 1..1
> madvise(MADV_COLLAPSE) succeeded
> ioctl(UFFDIO_CONTINUE) failed: File exists (errno=17)
> not ok 1 - memfd userfaultfd MINOR continue test
>
>
> We do have a ton of uffd selftesting code in there. Adding this case
> to uffd-unit-tests.c would presumably be pretty simple. We don't
> really have a policy on adding a test-case after fixing something -
> it's common practice but why? We already fixed it!
Good idea. The below patch should work; I'll send it properly on Monday.
diff --git a/tools/testing/selftests/mm/uffd-unit-tests.c b/tools/testing/selftests/mm/uffd-unit-tests.c
index ef9b3956bdcf..227bcd6aef9d 100644
--- a/tools/testing/selftests/mm/uffd-unit-tests.c
+++ b/tools/testing/selftests/mm/uffd-unit-tests.c
@@ -518,19 +518,34 @@ static void uffd_wp_fork_pin_with_event_test(uffd_global_test_opts_t *gopts, uff
uffd_wp_fork_pin_test_common(gopts, args, true);
}
-static void check_memory_contents(uffd_global_test_opts_t *gopts, char *p)
+static int __check_memory_contents(unsigned long offset,
+ unsigned long nr_pages,
+ uffd_global_test_opts_t *gopts,
+ char *p)
{
unsigned long i, j;
uint8_t expected_byte;
- for (i = 0; i < gopts->nr_pages; ++i) {
+ if (nr_pages + offset < nr_pages)
+ err("overflow in memory check");
+ if (nr_pages + offset > gopts->nr_pages)
+ err("out of bounds memory check");
+
+ for (i = offset; i < nr_pages; ++i) {
expected_byte = ~((uint8_t)(i % ((uint8_t)-1)));
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");
+ return 1;
}
}
+
+ return 0;
+}
+
+static int check_memory_contents(uffd_global_test_opts_t *gopts, char *p)
+{
+ return __check_memory_contents(0, gopts->nr_pages, gopts, p);
}
static void uffd_minor_test_common(uffd_global_test_opts_t *gopts, bool test_collapse, bool test_wp)
@@ -539,6 +554,8 @@ static void uffd_minor_test_common(uffd_global_test_opts_t *gopts, bool test_col
pthread_t uffd_mon;
char c = '\0';
struct uffd_args args = { 0 };
+ unsigned long checked = 0;
+ bool bad_contents;
args.gopts = gopts;
/*
@@ -564,19 +581,51 @@ static void uffd_minor_test_common(uffd_global_test_opts_t *gopts, bool test_col
if (pthread_create(&uffd_mon, NULL, uffd_poll_thread, &args))
err("uffd_poll_thread create");
+ if (test_collapse) {
+ /*
+ * Read just a single page and try collapsing. The collapse
+ * should either be rejected or be a no-op.
+ */
+ if (__check_memory_contents(0, 1, gopts, gopts->area_dst_alias))
+ err("unexpected memory contents before collapse");
+
+ madvise(gopts->area_dst_alias, gopts->nr_pages * gopts->page_size,
+ MADV_COLLAPSE);
+ /*
+ * If the above collapse mapped pages that were not explicitly
+ * CONTINUE'd, the below __check_memory_contents() will not
+ * fault on some pages, resulting in incorrect contents. The
+ * PTE for the first page may get retracted, so avoid checking
+ * that page, as we might take a second fault, flipping the
+ * contents a second time.
+ */
+ checked = 1;
+ }
+
/*
* Read each of the pages back using the UFFD-registered mapping. We
* expect that the first time we touch a page, it will result in a minor
* fault. uffd_poll_thread will resolve the fault by bit-flipping the
* page's contents, and then issuing a CONTINUE ioctl.
*/
- check_memory_contents(gopts, gopts->area_dst_alias);
+ bad_contents = !!__check_memory_contents(checked, gopts->nr_pages - checked,
+ gopts, gopts->area_dst_alias);
if (write(gopts->pipefd[1], &c, sizeof(c)) != sizeof(c))
err("pipe write");
if (pthread_join(uffd_mon, NULL))
err("join() failed");
+ if (bad_contents && test_collapse) {
+ uffd_test_fail("unexpected memory contents after collapse");
+ return;
+ }
+
+ if (bad_contents) {
+ uffd_test_fail("unexpected memory contents");
+ return;
+ }
+
if (test_collapse) {
if (madvise(gopts->area_dst_alias, gopts->nr_pages * gopts->page_size,
MADV_COLLAPSE)) {
@@ -593,7 +642,10 @@ static void uffd_minor_test_common(uffd_global_test_opts_t *gopts, bool test_col
* This won't cause uffd-fault - it purely just makes sure there
* was no corruption.
*/
- check_memory_contents(gopts, gopts->area_dst_alias);
+ if (check_memory_contents(gopts, gopts->area_dst_alias)) {
+ uffd_test_fail("unexpected memory contents");
+ return;
+ }
}
if (args.missing_faults != 0 || args.minor_faults != gopts->nr_pages)
next prev parent reply other threads:[~2026-08-29 2:50 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-28 22:26 [PATCH v2] mm/khugepaged: Don't install PMDs in uffd-minor-registered VMAs James Houghton
2026-08-29 0:33 ` Andrew Morton
2026-08-29 1:09 ` James Houghton
2026-08-29 1:18 ` James Houghton
2026-08-29 1:38 ` Andrew Morton
2026-08-29 2:50 ` James Houghton [this message]
2026-08-29 1:29 ` Andrew Morton
2026-08-29 4:38 ` Lance Yang
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=20260829025034.2500920-1-jthoughton@google.com \
--to=jthoughton@google.com \
--cc=akpm@linux-foundation.org \
--cc=baohua@kernel.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=david@kernel.org \
--cc=dev.jain@arm.com \
--cc=hughd@google.com \
--cc=kas@kernel.org \
--cc=lance.yang@linux.dev \
--cc=liam@infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=nico.pache@linux.dev \
--cc=ryan.roberts@arm.com \
--cc=shy828301@gmail.com \
--cc=stable@vger.kernel.org \
--cc=usama.arif@linux.dev \
--cc=ziy@nvidia.com \
--cc=zokeefe@google.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.