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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox