* [PATCH v3 1/3] selftests/mm: allow PUD-level entries in compound testcase of hmm tests
2026-06-11 3:40 [PATCH v3 0/3] selftests/mm: assorted fixes for hmm-tests Aboorva Devarajan
@ 2026-06-11 3:41 ` Aboorva Devarajan
2026-06-11 3:41 ` [PATCH v3 2/3] selftests/mm: remove hardcoded THP sizing assumptions in " Aboorva Devarajan
2026-06-11 3:41 ` [PATCH v3 3/3] selftests/mm: fix exclusive_cow test fork() handling Aboorva Devarajan
2 siblings, 0 replies; 5+ messages in thread
From: Aboorva Devarajan @ 2026-06-11 3:41 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Jason Gunthorpe, Leon Romanovsky, David Hildenbrand,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Ralph Campbell,
Matthew Brost, Balbir Singh, Alistair Popple, Alex Sierra,
Matthew Wilcox, Sayali Patil, linux-kselftest, linux-kernel,
Aboorva Devarajan
From: Sayali Patil <sayalip@linux.ibm.com>
The HMM compound testcase currently assumes only PMD-level mappings and
fails on systems where default_hugepagesz=1G is set, because the region
is then reported by the device at PUD level.
Determine the mapping level (PMD or PUD) the device reports for the first
page of the range and require every page to match that level exactly via
ASSERT_EQ(). This accepts PUD-level mappings while preserving the
expected/observed protection values printed on failure, and rejects a
fragmented mapping that mixes PMD- and PUD-level entries within the same
range (which a per-page OR check would have let pass).
Fixes: e478425bec93 ("mm/hmm: add tests for hmm_pfn_to_map_order()")
Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
Co-developed-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
Signed-off-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
---
tools/testing/selftests/mm/hmm-tests.c | 32 +++++++++++++++++++-------
1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 46e0c8c921c3..deb6a7485650 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -1602,8 +1602,8 @@ TEST_F(hmm2, snapshot)
}
/*
- * Test the hmm_range_fault() HMM_PFN_PMD flag for large pages that
- * should be mapped by a large page table entry.
+ * Test the hmm_range_fault() handling of large pages (PMD or PUD)
+ * that should be mapped by a large page table entry.
*/
TEST_F(hmm, compound)
{
@@ -1613,6 +1613,7 @@ TEST_F(hmm, compound)
unsigned long default_hsize = default_huge_page_size();
int *ptr;
unsigned char *m;
+ unsigned char prot;
int ret;
unsigned long i;
@@ -1648,11 +1649,20 @@ TEST_F(hmm, compound)
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
- /* Check what the device saw. */
+ /*
+ * Check what the device saw. The region is backed by a single huge
+ * page that the device reports either at PMD or at PUD level depending
+ * on the configured default hugepage size. Determine that level from
+ * the first page and require every page in the range to match it
+ * exactly, so that a fragmented mapping mixing levels (or a missing
+ * large-page bit) is still caught and reported with its actual value.
+ */
m = buffer->mirror;
+ prot = HMM_DMIRROR_PROT_WRITE |
+ ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD :
+ HMM_DMIRROR_PROT_PMD);
for (i = 0; i < npages; ++i)
- ASSERT_EQ(m[i], HMM_DMIRROR_PROT_WRITE |
- HMM_DMIRROR_PROT_PMD);
+ ASSERT_EQ(m[i], prot);
/* Make the region read-only. */
ret = mprotect(buffer->ptr, size, PROT_READ);
@@ -1663,11 +1673,17 @@ TEST_F(hmm, compound)
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
- /* Check what the device saw. */
+ /*
+ * Check what the device saw after mprotect(PROT_READ). Same
+ * approach as above: determine the mapping level from the first
+ * page and require every page to match it exactly.
+ */
m = buffer->mirror;
+ prot = HMM_DMIRROR_PROT_READ |
+ ((m[0] & HMM_DMIRROR_PROT_PUD) ? HMM_DMIRROR_PROT_PUD :
+ HMM_DMIRROR_PROT_PMD);
for (i = 0; i < npages; ++i)
- ASSERT_EQ(m[i], HMM_DMIRROR_PROT_READ |
- HMM_DMIRROR_PROT_PMD);
+ ASSERT_EQ(m[i], prot);
munmap(buffer->ptr, buffer->size);
buffer->ptr = NULL;
base-commit: 801c5efa788952095586d076fed1ea4902facff8
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v3 2/3] selftests/mm: remove hardcoded THP sizing assumptions in hmm tests
2026-06-11 3:40 [PATCH v3 0/3] selftests/mm: assorted fixes for hmm-tests Aboorva Devarajan
2026-06-11 3:41 ` [PATCH v3 1/3] selftests/mm: allow PUD-level entries in compound testcase of hmm tests Aboorva Devarajan
@ 2026-06-11 3:41 ` Aboorva Devarajan
2026-06-11 3:41 ` [PATCH v3 3/3] selftests/mm: fix exclusive_cow test fork() handling Aboorva Devarajan
2 siblings, 0 replies; 5+ messages in thread
From: Aboorva Devarajan @ 2026-06-11 3:41 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Jason Gunthorpe, Leon Romanovsky, David Hildenbrand,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Ralph Campbell,
Matthew Brost, Balbir Singh, Alistair Popple, Alex Sierra,
Matthew Wilcox, Sayali Patil, linux-kselftest, linux-kernel,
Aboorva Devarajan
From: Sayali Patil <sayalip@linux.ibm.com>
migrate_partial_unmap_fault() and migrate_remap_fault() use
hardcoded offsets based on a 2MB PMD size. Similarly,
benchmark_thp_migration() assumes a fixed 2MB THP size when generating
test buffer sizes.
Derive offsets and test sizes from the runtime PMD page size returned
by read_pmd_pagesize(). If unavailable, fall back to TWOMEG.
This allows the tests to adapt correctly on systems where
PMD-sized THP differs from 2MB. Also replace the fixed 1MB unmap
size with a PMD-relative value derived from the runtime PMD size.
On systems with larger PMD sizes, computed test buffer sizes can exceed
INT_MAX. Skip such test cases to avoid overflow.
Fixes: 24c2c5b8ffbd ("selftests/mm/hmm-tests: partial unmap, mremap and anon_write tests")
Fixes: 271a7b2e3c13 ("selftests/mm/hmm-tests: new throughput tests including THP")
Signed-off-by: Sayali Patil <sayalip@linux.ibm.com>
Signed-off-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
---
tools/testing/selftests/mm/hmm-tests.c | 62 ++++++++++++++++++--------
1 file changed, 43 insertions(+), 19 deletions(-)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index deb6a7485650..8f4f82467043 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -22,6 +22,7 @@
#include <strings.h>
#include <time.h>
#include <pthread.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -2380,12 +2381,21 @@ TEST_F(hmm, migrate_partial_unmap_fault)
struct hmm_buffer *buffer;
unsigned long npages;
unsigned long size = read_pmd_pagesize();
+ unsigned long unmap_size;
+ unsigned long offsets[3];
unsigned long i;
void *old_ptr;
void *map;
int *ptr;
int ret, j, use_thp;
- int offsets[] = { 0, 512 * ONEKB, ONEMEG };
+
+ if (!size)
+ size = TWOMEG;
+
+ unmap_size = size / 2;
+ offsets[0] = 0;
+ offsets[1] = size / 4;
+ offsets[2] = size / 2;
for (use_thp = 0; use_thp < 2; ++use_thp) {
for (j = 0; j < ARRAY_SIZE(offsets); ++j) {
@@ -2427,12 +2437,12 @@ TEST_F(hmm, migrate_partial_unmap_fault)
for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i)
ASSERT_EQ(ptr[i], i);
- munmap(buffer->ptr + offsets[j], ONEMEG);
+ munmap(buffer->ptr + offsets[j], unmap_size);
/* Fault pages back to system memory and check them. */
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
if (i * sizeof(int) < offsets[j] ||
- i * sizeof(int) >= offsets[j] + ONEMEG)
+ i * sizeof(int) >= offsets[j] + unmap_size)
ASSERT_EQ(ptr[i], i);
buffer->ptr = old_ptr;
@@ -2446,12 +2456,19 @@ TEST_F(hmm, migrate_remap_fault)
struct hmm_buffer *buffer;
unsigned long npages;
unsigned long size = read_pmd_pagesize();
+ unsigned long offsets[3];
unsigned long i;
void *old_ptr, *new_ptr = NULL;
void *map;
int *ptr;
int ret, j, use_thp, dont_unmap, before;
- int offsets[] = { 0, 512 * ONEKB, ONEMEG };
+
+ if (!size)
+ size = TWOMEG;
+
+ offsets[0] = 0;
+ offsets[1] = size / 4;
+ offsets[2] = size / 2;
for (before = 0; before < 2; ++before) {
for (dont_unmap = 0; dont_unmap < 2; ++dont_unmap) {
@@ -2854,38 +2871,45 @@ static inline int run_migration_benchmark(int fd, int use_thp, size_t buffer_siz
TEST_F_TIMEOUT(hmm, benchmark_thp_migration, 120)
{
struct benchmark_results thp_results, regular_results;
- size_t thp_size = 2 * 1024 * 1024; /* 2MB - typical THP size */
+ size_t thp_size = read_pmd_pagesize();
int iterations = 5;
+ if (!thp_size)
+ thp_size = TWOMEG;
+
printf("\nHMM THP Migration Benchmark\n");
printf("---------------------------\n");
printf("System page size: %ld bytes\n", sysconf(_SC_PAGESIZE));
/* Test different buffer sizes */
size_t test_sizes[] = {
- thp_size / 4, /* 512KB - smaller than THP */
- thp_size / 2, /* 1MB - half THP */
- thp_size, /* 2MB - single THP */
- thp_size * 2, /* 4MB - two THPs */
- thp_size * 4, /* 8MB - four THPs */
- thp_size * 8, /* 16MB - eight THPs */
- thp_size * 128, /* 256MB - one twenty eight THPs */
+ thp_size / 4, /* quarter THP */
+ thp_size / 2, /* half THP */
+ thp_size, /* single THP */
+ thp_size * 2, /* two THPs */
+ thp_size * 4, /* four THPs */
+ thp_size * 8, /* eight THPs */
+ thp_size * 128, /* one twenty eight THPs */
};
static const char *const test_names[] = {
- "Small Buffer (512KB)",
- "Half THP Size (1MB)",
- "Single THP Size (2MB)",
- "Two THP Size (4MB)",
- "Four THP Size (8MB)",
- "Eight THP Size (16MB)",
- "One twenty eight THP Size (256MB)"
+ "Small Buffer",
+ "Half THP Size",
+ "Single THP Size",
+ "Two THP Size",
+ "Four THP Size",
+ "Eight THP Size",
+ "One twenty eight THP Size"
};
int num_tests = ARRAY_SIZE(test_sizes);
/* Run all tests */
for (int i = 0; i < num_tests; i++) {
+ /* Skip test sizes exceeding INT_MAX to avoid overflow */
+ if (test_sizes[i] > INT_MAX)
+ break;
+
/* Test with THP */
ASSERT_EQ(run_migration_benchmark(self->fd, 1, test_sizes[i],
iterations, &thp_results), 0);
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v3 3/3] selftests/mm: fix exclusive_cow test fork() handling
2026-06-11 3:40 [PATCH v3 0/3] selftests/mm: assorted fixes for hmm-tests Aboorva Devarajan
2026-06-11 3:41 ` [PATCH v3 1/3] selftests/mm: allow PUD-level entries in compound testcase of hmm tests Aboorva Devarajan
2026-06-11 3:41 ` [PATCH v3 2/3] selftests/mm: remove hardcoded THP sizing assumptions in " Aboorva Devarajan
@ 2026-06-11 3:41 ` Aboorva Devarajan
2026-06-11 4:59 ` Balbir Singh
2 siblings, 1 reply; 5+ messages in thread
From: Aboorva Devarajan @ 2026-06-11 3:41 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Jason Gunthorpe, Leon Romanovsky, David Hildenbrand,
Lorenzo Stoakes, Liam R . Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Ralph Campbell,
Matthew Brost, Balbir Singh, Alistair Popple, Alex Sierra,
Matthew Wilcox, Sayali Patil, linux-kselftest, linux-kernel,
Aboorva Devarajan
The test ignores the return value of fork(), so both the parent and
the (newly created) child run the COW verification loops and then
call hmm_buffer_free() before returning into the kselftest harness,
which _exit()s each side. This duplicated teardown sequence has
been observed to manifest as a SIGSEGV in the test child, e.g.:
hmm-tests[360141]: segfault (11) at 0 nip 10006964 lr 1000ac3c code 1
in hmm-tests[6964,10000000+30000]
Fix this by adopting the same fork()-then-wait pattern already used
by the nearby anon_write_child / anon_write_child_shared tests in
this file: the child performs the COW verification and then _exit(0)s
so it does not run the test teardown, while the parent independently
verifies COW, waits for the child, and only then frees the buffer.
Fixes: b659baea75469 ("mm: selftests for exclusive device memory")
Signed-off-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
---
tools/testing/selftests/mm/hmm-tests.c | 31 +++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 8f4f82467043..e4c49699f3f7 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -1884,6 +1884,8 @@ TEST_F(hmm, exclusive_cow)
unsigned long i;
int *ptr;
int ret;
+ pid_t pid;
+ int status;
npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
ASSERT_NE(npages, 0);
@@ -1912,14 +1914,37 @@ TEST_F(hmm, exclusive_cow)
ASSERT_EQ(ret, 0);
ASSERT_EQ(buffer->cpages, npages);
- fork();
+ pid = fork();
+ if (pid == -1)
+ ASSERT_EQ(pid, 0);
- /* Fault pages back to system memory and check them. */
+ if (pid == 0) {
+ /*
+ * Child verifies COW independently, then _exit(0)s so it does
+ * not run the test teardown. A failed ASSERT_* here makes the
+ * harness abort() the child, so the parent sees
+ * !WIFEXITED(status) below and fails in turn.
+ */
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i]++, i);
+
+ for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
+ ASSERT_EQ(ptr[i], i + 1);
+
+ _exit(0);
+ }
+
+ /* Parent: also increment to verify COW works for both processes. */
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
ASSERT_EQ(ptr[i]++, i);
for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
- ASSERT_EQ(ptr[i], i+1);
+ ASSERT_EQ(ptr[i], i + 1);
+
+ /* Parent: wait for child and then free the buffer. */
+ ASSERT_EQ(waitpid(pid, &status, 0), pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 0);
hmm_buffer_free(buffer);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH v3 3/3] selftests/mm: fix exclusive_cow test fork() handling
2026-06-11 3:41 ` [PATCH v3 3/3] selftests/mm: fix exclusive_cow test fork() handling Aboorva Devarajan
@ 2026-06-11 4:59 ` Balbir Singh
0 siblings, 0 replies; 5+ messages in thread
From: Balbir Singh @ 2026-06-11 4:59 UTC (permalink / raw)
To: Aboorva Devarajan
Cc: Andrew Morton, linux-mm, Jason Gunthorpe, Leon Romanovsky,
David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Shuah Khan, Ralph Campbell, Matthew Brost, Alistair Popple,
Alex Sierra, Matthew Wilcox, Sayali Patil, linux-kselftest,
linux-kernel
On Thu, Jun 11, 2026 at 09:11:02AM +0530, Aboorva Devarajan wrote:
> The test ignores the return value of fork(), so both the parent and
> the (newly created) child run the COW verification loops and then
> call hmm_buffer_free() before returning into the kselftest harness,
> which _exit()s each side. This duplicated teardown sequence has
> been observed to manifest as a SIGSEGV in the test child, e.g.:
>
> hmm-tests[360141]: segfault (11) at 0 nip 10006964 lr 1000ac3c code 1
> in hmm-tests[6964,10000000+30000]
>
Thanks for catching this!
> Fix this by adopting the same fork()-then-wait pattern already used
> by the nearby anon_write_child / anon_write_child_shared tests in
> this file: the child performs the COW verification and then _exit(0)s
> so it does not run the test teardown, while the parent independently
> verifies COW, waits for the child, and only then frees the buffer.
>
> Fixes: b659baea75469 ("mm: selftests for exclusive device memory")
> Signed-off-by: Aboorva Devarajan <aboorvad@linux.ibm.com>
> ---
> tools/testing/selftests/mm/hmm-tests.c | 31 +++++++++++++++++++++++---
> 1 file changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
> index 8f4f82467043..e4c49699f3f7 100644
> --- a/tools/testing/selftests/mm/hmm-tests.c
> +++ b/tools/testing/selftests/mm/hmm-tests.c
> @@ -1884,6 +1884,8 @@ TEST_F(hmm, exclusive_cow)
> unsigned long i;
> int *ptr;
> int ret;
> + pid_t pid;
> + int status;
>
> npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift;
> ASSERT_NE(npages, 0);
> @@ -1912,14 +1914,37 @@ TEST_F(hmm, exclusive_cow)
> ASSERT_EQ(ret, 0);
> ASSERT_EQ(buffer->cpages, npages);
>
> - fork();
> + pid = fork();
> + if (pid == -1)
> + ASSERT_EQ(pid, 0);
>
> - /* Fault pages back to system memory and check them. */
> + if (pid == 0) {
> + /*
> + * Child verifies COW independently, then _exit(0)s so it does
> + * not run the test teardown. A failed ASSERT_* here makes the
> + * harness abort() the child, so the parent sees
> + * !WIFEXITED(status) below and fails in turn.
> + */
> + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> + ASSERT_EQ(ptr[i]++, i);
> +
> + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> + ASSERT_EQ(ptr[i], i + 1);
> +
> + _exit(0);
> + }
> +
> + /* Parent: also increment to verify COW works for both processes. */
> for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> ASSERT_EQ(ptr[i]++, i);
>
> for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i)
> - ASSERT_EQ(ptr[i], i+1);
> + ASSERT_EQ(ptr[i], i + 1);
> +
> + /* Parent: wait for child and then free the buffer. */
> + ASSERT_EQ(waitpid(pid, &status, 0), pid);
> + ASSERT_TRUE(WIFEXITED(status));
> + ASSERT_EQ(WEXITSTATUS(status), 0);
>
> hmm_buffer_free(buffer);
> }
Acked-by: Balbir Singh <balbirs@nvidia.com>
^ permalink raw reply [flat|nested] 5+ messages in thread