* [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions
@ 2026-07-24 10:24 Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 1/5] selftests/mm: skip COW tmpfile cases when fallocate() is unsupported Muhammad Usama Anjum
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 10:24 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel, sarthak.sharma
Cc: Muhammad Usama Anjum
Several MM selftests report failures when the test environment lacks
an underlying prerequisite, such as fallocate() support, MADV_REMOVE,
local page-cache semantics, or swap.
This series converts those unsupported cases to SKIP while preserving
failures for unexpected errors. It also allows migration tests to retry
transient move_pages() failures.
Tested on an arm64 Ampere system using an NFS root filesystem. The MM
selftest run completed without failures or errors. Also verified on
x86_64 to ensure the changes do not cause unexpected errors.
Changes since v1:
- Print swapout diagnostics before reporting no-swap skips.
- Limit the NFS skip to the hard dirty-page variant.
- Retry per-page failures for the full runtime.
- Verify that both alternating NUMA targets are reached.
Muhammad Usama Anjum (5):
selftests/mm: skip COW tmpfile cases when fallocate() is unsupported
selftests/mm: skip guard hole-punch test if MADV_REMOVE is unsupported
selftests/mm: skip khugepaged swap tests without swap
selftests/mm: skip hard dirty page-cache test on NFS
selftests/mm: retry migration failures for the full runtime
tools/testing/selftests/mm/cow.c | 9 +++--
tools/testing/selftests/mm/guard-regions.c | 10 ++++--
tools/testing/selftests/mm/khugepaged.c | 38 ++++++++++++++++++--
tools/testing/selftests/mm/memory-failure.c | 9 +++--
tools/testing/selftests/mm/migration.c | 39 +++++++++++----------
5 files changed, 78 insertions(+), 27 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/5] selftests/mm: skip COW tmpfile cases when fallocate() is unsupported
2026-07-24 10:24 [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions Muhammad Usama Anjum
@ 2026-07-24 10:24 ` Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 2/5] selftests/mm: skip guard hole-punch test if MADV_REMOVE " Muhammad Usama Anjum
` (3 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 10:24 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel, sarthak.sharma
Cc: Muhammad Usama Anjum
The tmpfile-backed COW cases allocate a one-page file with fallocate()
before exercising private and shared mappings. When the filesystem
backing tmpfile() does not implement fallocate(), setup fails with
EOPNOTSUPP and no COW behavior is exercised.
This occurs when the temporary directory resides on a filesystem with
limited allocation support, such as NFSv3. Reporting a failure adds
noise because the test prerequisite is absent rather than the COW
implementation being broken.
Report EOPNOTSUPP as a skip. Continue treating every other fallocate()
error as a failure so unexpected setup regressions remain visible.
Tested-by: Sarthak Sharma <sarthak.sharma@arm.com>
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
tools/testing/selftests/mm/cow.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/mm/cow.c b/tools/testing/selftests/mm/cow.c
index 0c627ea89ff7b..c1b8920e29342 100644
--- a/tools/testing/selftests/mm/cow.c
+++ b/tools/testing/selftests/mm/cow.c
@@ -1718,8 +1718,13 @@ static void run_with_tmpfile(non_anon_test_fn fn, const char *desc)
/* File consists of a single page filled with zeroes. */
if (fallocate(fd, 0, 0, pagesize)) {
- ksft_perror("fallocate() failed");
- log_test_result(KSFT_FAIL);
+ if (errno == EOPNOTSUPP) {
+ ksft_print_msg("fallocate() not supported by filesystem\n");
+ log_test_result(KSFT_SKIP);
+ } else {
+ ksft_perror("fallocate() failed");
+ log_test_result(KSFT_FAIL);
+ }
goto close;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/5] selftests/mm: skip guard hole-punch test if MADV_REMOVE is unsupported
2026-07-24 10:24 [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 1/5] selftests/mm: skip COW tmpfile cases when fallocate() is unsupported Muhammad Usama Anjum
@ 2026-07-24 10:24 ` Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 3/5] selftests/mm: skip khugepaged swap tests without swap Muhammad Usama Anjum
` (2 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 10:24 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel, sarthak.sharma
Cc: Muhammad Usama Anjum
The hole_punch case verifies that guard regions survive MADV_REMOVE and
that the backing range is punched out. MADV_REMOVE delegates the hole
punch to the backing filesystem, which may reject the operation with
EOPNOTSUPP.
That result means the test cannot establish the state whose guard
semantics it intends to validate. Treating the missing filesystem
capability as a guard-region failure creates a false regression.
Unmap the range and skip only when MADV_REMOVE fails with EOPNOTSUPP.
Preserve the assertion for all other errors so failures on supported
configurations remain visible.
Tested-by: Sarthak Sharma <sarthak.sharma@arm.com>
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
tools/testing/selftests/mm/guard-regions.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/mm/guard-regions.c b/tools/testing/selftests/mm/guard-regions.c
index b21df3040b1c7..5c8ec3ca75d7d 100644
--- a/tools/testing/selftests/mm/guard-regions.c
+++ b/tools/testing/selftests/mm/guard-regions.c
@@ -1912,7 +1912,7 @@ TEST_F(guard_regions, hole_punch)
{
const unsigned long page_size = self->page_size;
char *ptr;
- int i;
+ int i, ret;
if (variant->backing == ANON_BACKED)
SKIP(return, "Truncation test specific to file-backed");
@@ -1944,8 +1944,12 @@ TEST_F(guard_regions, hole_punch)
}
/* Now hole punch the guarded region. */
- ASSERT_EQ(madvise(&ptr[3 * page_size], 4 * page_size,
- MADV_REMOVE), 0);
+ ret = madvise(&ptr[3 * page_size], 4 * page_size, MADV_REMOVE);
+ if (ret == -1 && errno == EOPNOTSUPP) {
+ ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
+ SKIP(return, "MADV_REMOVE not supported by filesystem");
+ }
+ ASSERT_EQ(ret, 0);
/* Ensure guard regions remain. */
for (i = 0; i < 10; i++) {
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 3/5] selftests/mm: skip khugepaged swap tests without swap
2026-07-24 10:24 [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 1/5] selftests/mm: skip COW tmpfile cases when fallocate() is unsupported Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 2/5] selftests/mm: skip guard hole-punch test if MADV_REMOVE " Muhammad Usama Anjum
@ 2026-07-24 10:24 ` Muhammad Usama Anjum
2026-07-24 11:16 ` Sarthak Sharma
2026-07-24 10:24 ` [PATCH v2 4/5] selftests/mm: skip hard dirty page-cache test on NFS Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime Muhammad Usama Anjum
4 siblings, 1 reply; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 10:24 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel, sarthak.sharma
Cc: Muhammad Usama Anjum
collapse_swapin_single_pte and collapse_max_ptes_swap require
MADV_PAGEOUT to replace anonymous pages with swap entries. On swapless
systems there is no backing store with which to create those entries,
so check_swap() reports missing setup rather than broken khugepaged
behavior.
Swapless configurations are common on Android and other constrained
test devices. Failing these cases obscures actionable results from the
rest of the khugepaged suite.
Check /proc/swaps before either swap-dependent case and skip when no
active swap area exists. With swap present, retain the existing
MADV_PAGEOUT and swap-entry assertions unchanged.
Print each existing swapout diagnostic before the prerequisite check
so skip() completes a KTAP diagnostic line instead of emitting an
unprefixed message.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
Changes since v1:
- Print swapout diagnostics before reporting no-swap skips.
---
tools/testing/selftests/mm/khugepaged.c | 38 +++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/mm/khugepaged.c b/tools/testing/selftests/mm/khugepaged.c
index 10e8dedcb087d..54e888eb48bbc 100644
--- a/tools/testing/selftests/mm/khugepaged.c
+++ b/tools/testing/selftests/mm/khugepaged.c
@@ -100,6 +100,28 @@ static void skip(const char *msg)
exit_status = KSFT_SKIP;
}
+static bool is_swap_enabled(void)
+{
+ char buf[MAX_LINE_LENGTH];
+ FILE *file;
+ bool enabled = false;
+
+ file = fopen("/proc/swaps", "r");
+ if (!file)
+ return false;
+
+ if (!fgets(buf, sizeof(buf), file))
+ goto out;
+
+ /* Check for first active swap entry. */
+ if (fgets(buf, sizeof(buf), file))
+ enabled = true;
+
+out:
+ fclose(file);
+ return enabled;
+}
+
static void save_settings(void)
{
ksft_print_msg("Save THP and khugepaged settings...");
@@ -734,10 +756,16 @@ static void collapse_swapin_single_pte(struct collapse_context *c, struct mem_op
{
void *p;
+ ksft_print_msg("Swapout one page...");
+ if (!is_swap_enabled()) {
+ skip("No active swap");
+ ksft_test_result_report(exit_status, "%s\n", __func__);
+ return;
+ }
+
p = ops->setup_area(1);
ops->fault(p, 0, hpage_pmd_size);
- ksft_print_msg("Swapout one page...");
if (madvise(p, page_size, MADV_PAGEOUT))
ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
if (check_swap(p, page_size)) {
@@ -760,10 +788,16 @@ static void collapse_max_ptes_swap(struct collapse_context *c, struct mem_ops *o
int max_ptes_swap = thp_read_num("khugepaged/max_ptes_swap");
void *p;
+ ksft_print_msg("Swapout %d of %d pages...", max_ptes_swap + 1, hpage_pmd_nr);
+ if (!is_swap_enabled()) {
+ skip("No active swap");
+ ksft_test_result_report(exit_status, "%s\n", __func__);
+ return;
+ }
+
p = ops->setup_area(1);
ops->fault(p, 0, hpage_pmd_size);
- ksft_print_msg("Swapout %d of %d pages...", max_ptes_swap + 1, hpage_pmd_nr);
if (madvise(p, (max_ptes_swap + 1) * page_size, MADV_PAGEOUT))
ksft_exit_fail_perror("madvise(MADV_PAGEOUT)");
if (check_swap(p, (max_ptes_swap + 1) * page_size)) {
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 4/5] selftests/mm: skip hard dirty page-cache test on NFS
2026-07-24 10:24 [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions Muhammad Usama Anjum
` (2 preceding siblings ...)
2026-07-24 10:24 ` [PATCH v2 3/5] selftests/mm: skip khugepaged swap tests without swap Muhammad Usama Anjum
@ 2026-07-24 10:24 ` Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime Muhammad Usama Anjum
4 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 10:24 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel, sarthak.sharma
Cc: Muhammad Usama Anjum
The hard dirty_pagecache variant uses MADV_HWPOISON to exercise recovery
of a dirty file-backed page. The recovery path records -EIO in the
address_space mapping, which NFS later reports when the test closes the
file. This makes the test fail after the hwpoison checks have completed.
Skip this variant when the test file is on NFS. Keep the hard clean-page
and both soft-offline variants enabled because they use folio removal,
invalidation, or migration rather than recording a delayed writeback
error.
The unsupported-filesystem path in clean_pagecache() also returns
without closing the opened test file. Close the descriptor before
skipping there and in dirty_pagecache().
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
Changes since v1:
- Limit the NFS skip to the hard dirty-page variant.
---
tools/testing/selftests/mm/memory-failure.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/selftests/mm/memory-failure.c
index 032ed952057c6..3c72084f57e8b 100644
--- a/tools/testing/selftests/mm/memory-failure.c
+++ b/tools/testing/selftests/mm/memory-failure.c
@@ -283,8 +283,10 @@ TEST_F(memory_failure, clean_pagecache)
if (fd < 0)
SKIP(return, "failed to open test file.\n");
fs_type = get_fs_type(fd);
- if (!fs_type || fs_type == TMPFS_MAGIC)
+ if (!fs_type || fs_type == TMPFS_MAGIC) {
+ close(fd);
SKIP(return, "unsupported filesystem :%x\n", fs_type);
+ }
addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
@@ -325,8 +327,11 @@ TEST_F(memory_failure, dirty_pagecache)
if (fd < 0)
SKIP(return, "failed to open test file.\n");
fs_type = get_fs_type(fd);
- if (!fs_type || fs_type == TMPFS_MAGIC)
+ if (!fs_type || fs_type == TMPFS_MAGIC ||
+ (fs_type == NFS_SUPER_MAGIC && variant->type == MADV_HARD)) {
+ close(fd);
SKIP(return, "unsupported filesystem :%x\n", fs_type);
+ }
addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime
2026-07-24 10:24 [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions Muhammad Usama Anjum
` (3 preceding siblings ...)
2026-07-24 10:24 ` [PATCH v2 4/5] selftests/mm: skip hard dirty page-cache test on NFS Muhammad Usama Anjum
@ 2026-07-24 10:24 ` Muhammad Usama Anjum
2026-07-24 11:46 ` Dev Jain
4 siblings, 1 reply; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 10:24 UTC (permalink / raw)
To: Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel, sarthak.sharma
Cc: Muhammad Usama Anjum
move_pages() is best effort and can temporarily fail when concurrent
faults race with page unmapping. A busy shared-anon workload can exhaust
the current 100 retries long before the intended 20-second runtime and
produce a false failure.
Use the full runtime as the retry window. Since the initial page location
is unknown, require it to reach both alternating NUMA targets to confirm
that cross-node migration made progress despite transient contention.
Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
---
Changes since v1:
- Retry per-page failures for the full runtime
- Verify that both alternating NUMA targets are reached
---
tools/testing/selftests/mm/migration.c | 39 ++++++++++++++------------
1 file changed, 21 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
index 29f7492453d43..4d55a424058a9 100644
--- a/tools/testing/selftests/mm/migration.c
+++ b/tools/testing/selftests/mm/migration.c
@@ -7,7 +7,7 @@
#include "kselftest_harness.h"
#include "hugepage_settings.h"
-#include <strings.h>
+#include <string.h>
#include <pthread.h>
#include <numa.h>
#include <numaif.h>
@@ -20,7 +20,6 @@
#define TWOMEG (2<<20)
#define RUNTIME (20)
-#define MAX_RETRIES 100
#define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
HUGETLB_SETUP_DEFAULT_PAGES(1)
@@ -110,7 +109,7 @@ int migrate(uint64_t *ptr, int n1, int n2)
int ret, tmp;
int status = 0;
struct timespec ts1, ts2;
- int failures = 0;
+ int success = 0;
if (clock_gettime(CLOCK_MONOTONIC, &ts1))
return -1;
@@ -119,29 +118,33 @@ int migrate(uint64_t *ptr, int n1, int n2)
if (clock_gettime(CLOCK_MONOTONIC, &ts2))
return -1;
- if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
- return 0;
+ if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) {
+ /* Reaching both targets verifies a cross-node move. */
+ if (success >= 2)
+ return 0;
+ else
+ return -2;
+ }
ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
MPOL_MF_MOVE_ALL);
- if (ret) {
- if (ret > 0) {
- /* Migration is best effort; try again */
- if (++failures < MAX_RETRIES)
- continue;
- printf("Didn't migrate %d pages\n", ret);
- }
- else
- perror("Couldn't migrate pages");
- return -2;
+ if (ret < 0) {
+ perror("Couldn't migrate pages");
+ return ret;
}
- failures = 0;
+ /* Migration is best effort. Try again */
+ if (ret > 0 || status < 0)
+ continue;
+ if (status != n2) {
+ printf("Page is on node %d instead of target node %d\n",
+ status, n2);
+ return status;
+ }
+ success++;
tmp = n2;
n2 = n1;
n1 = tmp;
}
-
- return 0;
}
void *access_mem(void *ptr)
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 3/5] selftests/mm: skip khugepaged swap tests without swap
2026-07-24 10:24 ` [PATCH v2 3/5] selftests/mm: skip khugepaged swap tests without swap Muhammad Usama Anjum
@ 2026-07-24 11:16 ` Sarthak Sharma
0 siblings, 0 replies; 11+ messages in thread
From: Sarthak Sharma @ 2026-07-24 11:16 UTC (permalink / raw)
To: Muhammad Usama Anjum, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Dev Jain, Barry Song, Lance Yang,
Usama Arif, Miaohe Lin, Naoya Horiguchi, linux-mm,
linux-kselftest, linux-kernel
Hi Usama!
On 7/24/26 3:54 PM, Muhammad Usama Anjum wrote:
> collapse_swapin_single_pte and collapse_max_ptes_swap require
> MADV_PAGEOUT to replace anonymous pages with swap entries. On swapless
> systems there is no backing store with which to create those entries,
> so check_swap() reports missing setup rather than broken khugepaged
> behavior.
>
> Swapless configurations are common on Android and other constrained
> test devices. Failing these cases obscures actionable results from the
> rest of the khugepaged suite.
>
> Check /proc/swaps before either swap-dependent case and skip when no
> active swap area exists. With swap present, retain the existing
> MADV_PAGEOUT and swap-entry assertions unchanged.
>
> Print each existing swapout diagnostic before the prerequisite check
> so skip() completes a KTAP diagnostic line instead of emitting an
> unprefixed message.
>
> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
> ---
> Changes since v1:
> - Print swapout diagnostics before reporting no-swap skips.
Thank you for the changes. The test output is now KTAP formatted as:
# Run test: collapse_swapin_single_pte (khugepaged:anon)
# Swapout one page... No active swap
ok 16 # SKIP collapse_swapin_single_pte
#
# Run test: collapse_swapin_single_pte (madvise:anon)
# Swapout one page... No active swap
ok 17 # SKIP collapse_swapin_single_pte
#
# Run test: collapse_max_ptes_swap (khugepaged:anon)
# Swapout 65 of 512 pages... No active swap
ok 18 # SKIP collapse_max_ptes_swap
#
# Run test: collapse_max_ptes_swap (madvise:anon)
# Swapout 65 of 512 pages... No active swap
ok 19 # SKIP collapse_max_ptes_swap
Tested-by: Sarthak Sharma <sarthak.sharma@arm.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime
2026-07-24 10:24 ` [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime Muhammad Usama Anjum
@ 2026-07-24 11:46 ` Dev Jain
2026-07-24 11:53 ` Muhammad Usama Anjum
0 siblings, 1 reply; 11+ messages in thread
From: Dev Jain @ 2026-07-24 11:46 UTC (permalink / raw)
To: Muhammad Usama Anjum, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Barry Song, Lance Yang, Usama Arif,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kselftest,
linux-kernel, sarthak.sharma
On 24/07/26 3:54 pm, Muhammad Usama Anjum wrote:
> move_pages() is best effort and can temporarily fail when concurrent
> faults race with page unmapping. A busy shared-anon workload can exhaust
> the current 100 retries long before the intended 20-second runtime and
> produce a false failure.
>
> Use the full runtime as the retry window. Since the initial page location
> is unknown, require it to reach both alternating NUMA targets to confirm
> that cross-node migration made progress despite transient contention.
>
> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
> ---
Makes sense, but see below.
> Changes since v1:
> - Retry per-page failures for the full runtime
> - Verify that both alternating NUMA targets are reached
> ---
> tools/testing/selftests/mm/migration.c | 39 ++++++++++++++------------
> 1 file changed, 21 insertions(+), 18 deletions(-)
>
> diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
> index 29f7492453d43..4d55a424058a9 100644
> --- a/tools/testing/selftests/mm/migration.c
> +++ b/tools/testing/selftests/mm/migration.c
> @@ -7,7 +7,7 @@
> #include "kselftest_harness.h"
> #include "hugepage_settings.h"
>
> -#include <strings.h>
> +#include <string.h>
> #include <pthread.h>
> #include <numa.h>
> #include <numaif.h>
> @@ -20,7 +20,6 @@
>
> #define TWOMEG (2<<20)
> #define RUNTIME (20)
> -#define MAX_RETRIES 100
> #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
>
> HUGETLB_SETUP_DEFAULT_PAGES(1)
> @@ -110,7 +109,7 @@ int migrate(uint64_t *ptr, int n1, int n2)
> int ret, tmp;
> int status = 0;
> struct timespec ts1, ts2;
> - int failures = 0;
> + int success = 0;
>
> if (clock_gettime(CLOCK_MONOTONIC, &ts1))
> return -1;
> @@ -119,29 +118,33 @@ int migrate(uint64_t *ptr, int n1, int n2)
> if (clock_gettime(CLOCK_MONOTONIC, &ts2))
> return -1;
>
> - if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
> - return 0;
> + if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) {
> + /* Reaching both targets verifies a cross-node move. */
> + if (success >= 2)
> + return 0;
> + else
> + return -2;
> + }
>
> ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
> MPOL_MF_MOVE_ALL);
> - if (ret) {
> - if (ret > 0) {
> - /* Migration is best effort; try again */
> - if (++failures < MAX_RETRIES)
> - continue;
> - printf("Didn't migrate %d pages\n", ret);
> - }
> - else
> - perror("Couldn't migrate pages");
> - return -2;
> + if (ret < 0) {
> + perror("Couldn't migrate pages");
> + return ret;
> }
> - failures = 0;
> + /* Migration is best effort. Try again */
> + if (ret > 0 || status < 0)
old code wasn't using status, so why now?
> + continue;
> + if (status != n2) {
> + printf("Page is on node %d instead of target node %d\n",
> + status, n2);
> + return status;
> + }
> + success++;
> tmp = n2;
> n2 = n1;
> n1 = tmp;
> }
> -
> - return 0;
> }
>
> void *access_mem(void *ptr)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime
2026-07-24 11:46 ` Dev Jain
@ 2026-07-24 11:53 ` Muhammad Usama Anjum
2026-07-24 15:23 ` Dev Jain
0 siblings, 1 reply; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 11:53 UTC (permalink / raw)
To: Dev Jain, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Barry Song, Lance Yang, Usama Arif,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kselftest,
linux-kernel, sarthak.sharma
Cc: usama.anjum
On 24/07/2026 12:46 pm, Dev Jain wrote:
>
>
> On 24/07/26 3:54 pm, Muhammad Usama Anjum wrote:
>> move_pages() is best effort and can temporarily fail when concurrent
>> faults race with page unmapping. A busy shared-anon workload can exhaust
>> the current 100 retries long before the intended 20-second runtime and
>> produce a false failure.
>>
>> Use the full runtime as the retry window. Since the initial page location
>> is unknown, require it to reach both alternating NUMA targets to confirm
>> that cross-node migration made progress despite transient contention.
>>
>> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
>> ---
>
> Makes sense, but see below.
>
>
>> Changes since v1:
>> - Retry per-page failures for the full runtime
>> - Verify that both alternating NUMA targets are reached
>> ---
>> tools/testing/selftests/mm/migration.c | 39 ++++++++++++++------------
>> 1 file changed, 21 insertions(+), 18 deletions(-)
>>
>> diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
>> index 29f7492453d43..4d55a424058a9 100644
>> --- a/tools/testing/selftests/mm/migration.c
>> +++ b/tools/testing/selftests/mm/migration.c
>> @@ -7,7 +7,7 @@
>> #include "kselftest_harness.h"
>> #include "hugepage_settings.h"
>>
>> -#include <strings.h>
>> +#include <string.h>
>> #include <pthread.h>
>> #include <numa.h>
>> #include <numaif.h>
>> @@ -20,7 +20,6 @@
>>
>> #define TWOMEG (2<<20)
>> #define RUNTIME (20)
>> -#define MAX_RETRIES 100
>> #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
>>
>> HUGETLB_SETUP_DEFAULT_PAGES(1)
>> @@ -110,7 +109,7 @@ int migrate(uint64_t *ptr, int n1, int n2)
>> int ret, tmp;
>> int status = 0;
>> struct timespec ts1, ts2;
>> - int failures = 0;
>> + int success = 0;
>>
>> if (clock_gettime(CLOCK_MONOTONIC, &ts1))
>> return -1;
>> @@ -119,29 +118,33 @@ int migrate(uint64_t *ptr, int n1, int n2)
>> if (clock_gettime(CLOCK_MONOTONIC, &ts2))
>> return -1;
>>
>> - if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
>> - return 0;
>> + if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) {
>> + /* Reaching both targets verifies a cross-node move. */
>> + if (success >= 2)
>> + return 0;
>> + else
>> + return -2;
>> + }
>>
>> ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
>> MPOL_MF_MOVE_ALL);
>> - if (ret) {
>> - if (ret > 0) {
>> - /* Migration is best effort; try again */
>> - if (++failures < MAX_RETRIES)
>> - continue;
>> - printf("Didn't migrate %d pages\n", ret);
>> - }
>> - else
>> - perror("Couldn't migrate pages");
>> - return -2;
>> + if (ret < 0) {
>> + perror("Couldn't migrate pages");
>> + return ret;
>> }
>> - failures = 0;
>> + /* Migration is best effort. Try again */
>> + if (ret > 0 || status < 0)
>
> old code wasn't using status, so why now?
The old code not using it does not mean we cannot use it now. ret gives the
aggregate result, while status gives the per-page result: the destination
node on success or a negative errno explaining the failure. In particular,
move_pages() can return 0 with a negative status, so checking it prevents a
failed move from being counted as successful.
>
>> + continue;
>> + if (status != n2) {
>> + printf("Page is on node %d instead of target node %d\n",
>> + status, n2);
>> + return status;
>> + }
>> + success++;
>> tmp = n2;
>> n2 = n1;
>> n1 = tmp;
>> }
>> -
>> - return 0;
>> }
>>
>> void *access_mem(void *ptr)
>
--
Thanks,
Usama
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime
2026-07-24 11:53 ` Muhammad Usama Anjum
@ 2026-07-24 15:23 ` Dev Jain
2026-07-24 15:57 ` Muhammad Usama Anjum
0 siblings, 1 reply; 11+ messages in thread
From: Dev Jain @ 2026-07-24 15:23 UTC (permalink / raw)
To: Muhammad Usama Anjum, Andrew Morton, David Hildenbrand,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Barry Song, Lance Yang, Usama Arif,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kselftest,
linux-kernel, sarthak.sharma
On 24/07/26 5:23 pm, Muhammad Usama Anjum wrote:
> On 24/07/2026 12:46 pm, Dev Jain wrote:
>>
>>
>> On 24/07/26 3:54 pm, Muhammad Usama Anjum wrote:
>>> move_pages() is best effort and can temporarily fail when concurrent
>>> faults race with page unmapping. A busy shared-anon workload can exhaust
>>> the current 100 retries long before the intended 20-second runtime and
>>> produce a false failure.
>>>
>>> Use the full runtime as the retry window. Since the initial page location
>>> is unknown, require it to reach both alternating NUMA targets to confirm
>>> that cross-node migration made progress despite transient contention.
>>>
>>> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
>>> ---
>>
>> Makes sense, but see below.
>>
>>
>>> Changes since v1:
>>> - Retry per-page failures for the full runtime
>>> - Verify that both alternating NUMA targets are reached
>>> ---
>>> tools/testing/selftests/mm/migration.c | 39 ++++++++++++++------------
>>> 1 file changed, 21 insertions(+), 18 deletions(-)
>>>
>>> diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
>>> index 29f7492453d43..4d55a424058a9 100644
>>> --- a/tools/testing/selftests/mm/migration.c
>>> +++ b/tools/testing/selftests/mm/migration.c
>>> @@ -7,7 +7,7 @@
>>> #include "kselftest_harness.h"
>>> #include "hugepage_settings.h"
>>>
>>> -#include <strings.h>
>>> +#include <string.h>
>>> #include <pthread.h>
>>> #include <numa.h>
>>> #include <numaif.h>
>>> @@ -20,7 +20,6 @@
>>>
>>> #define TWOMEG (2<<20)
>>> #define RUNTIME (20)
>>> -#define MAX_RETRIES 100
>>> #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
>>>
>>> HUGETLB_SETUP_DEFAULT_PAGES(1)
>>> @@ -110,7 +109,7 @@ int migrate(uint64_t *ptr, int n1, int n2)
>>> int ret, tmp;
>>> int status = 0;
>>> struct timespec ts1, ts2;
>>> - int failures = 0;
>>> + int success = 0;
>>>
>>> if (clock_gettime(CLOCK_MONOTONIC, &ts1))
>>> return -1;
>>> @@ -119,29 +118,33 @@ int migrate(uint64_t *ptr, int n1, int n2)
>>> if (clock_gettime(CLOCK_MONOTONIC, &ts2))
>>> return -1;
>>>
>>> - if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
>>> - return 0;
>>> + if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) {
>>> + /* Reaching both targets verifies a cross-node move. */
>>> + if (success >= 2)
>>> + return 0;
>>> + else
>>> + return -2;
>>> + }
>>>
>>> ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
>>> MPOL_MF_MOVE_ALL);
>>> - if (ret) {
>>> - if (ret > 0) {
>>> - /* Migration is best effort; try again */
>>> - if (++failures < MAX_RETRIES)
>>> - continue;
>>> - printf("Didn't migrate %d pages\n", ret);
>>> - }
>>> - else
>>> - perror("Couldn't migrate pages");
>>> - return -2;
>>> + if (ret < 0) {
>>> + perror("Couldn't migrate pages");
>>> + return ret;
>>> }
>>> - failures = 0;
>>> + /* Migration is best effort. Try again */
>>> + if (ret > 0 || status < 0)
Also what if you migrated accidentally to node 0? In which case you
will return a false success below.
>>
>> old code wasn't using status, so why now?
> The old code not using it does not mean we cannot use it now. ret gives the
I never implied that :) The observation was because I didn't see it mentioned
in the commit message, this change is orthogonal to the description.
> aggregate result, while status gives the per-page result: the destination
> node on success or a negative errno explaining the failure. In particular,
> move_pages() can return 0 with a negative status, so checking it prevents a
> failed move from being counted as successful.
Crazy stuff. So move_pages() can return zero but that return value is not
really the return value and you have to check status.
The man page reads:
"status is an array of integers that return the status of each page.
The array contains valid values only if move_pages() did not return
an error."
But then says
"On success move_pages() returns zero. On error, it returns -1, and sets
errno to indicate the error. If positive value is returned, it is the
number of nonmigrated pages."
I don't know which one to fix, the manpage or the ABI, I suspect the latter,
storing error codes in a variable and then returning zero sounds wrong.
>
>>
>>> + continue;
>>> + if (status != n2) {
>>> + printf("Page is on node %d instead of target node %d\n",
>>> + status, n2);
>>> + return status;
>>> + }
>>> + success++;
>>> tmp = n2;
>>> n2 = n1;
>>> n1 = tmp;
>>> }
>>> -
>>> - return 0;
There is no return statement after the while loop. I suspect on some compiler
config it is gonna complain about this, or someone inevitably will run the
selftests with an old compiler and complain about this :)
>>> }
>>>
>>> void *access_mem(void *ptr)
>>
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime
2026-07-24 15:23 ` Dev Jain
@ 2026-07-24 15:57 ` Muhammad Usama Anjum
0 siblings, 0 replies; 11+ messages in thread
From: Muhammad Usama Anjum @ 2026-07-24 15:57 UTC (permalink / raw)
To: Dev Jain, Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Shuah Khan, Zi Yan, Baolin Wang,
Nico Pache, Ryan Roberts, Barry Song, Lance Yang, Usama Arif,
Miaohe Lin, Naoya Horiguchi, linux-mm, linux-kselftest,
linux-kernel, sarthak.sharma
Cc: usama.anjum
On 24/07/2026 4:23 pm, Dev Jain wrote:
>
>
> On 24/07/26 5:23 pm, Muhammad Usama Anjum wrote:
>> On 24/07/2026 12:46 pm, Dev Jain wrote:
>>>
>>>
>>> On 24/07/26 3:54 pm, Muhammad Usama Anjum wrote:
>>>> move_pages() is best effort and can temporarily fail when concurrent
>>>> faults race with page unmapping. A busy shared-anon workload can exhaust
>>>> the current 100 retries long before the intended 20-second runtime and
>>>> produce a false failure.
>>>>
>>>> Use the full runtime as the retry window. Since the initial page location
>>>> is unknown, require it to reach both alternating NUMA targets to confirm
>>>> that cross-node migration made progress despite transient contention.
>>>>
>>>> Signed-off-by: Muhammad Usama Anjum <usama.anjum@arm.com>
>>>> ---
>>>
>>> Makes sense, but see below.
>>>
>>>
>>>> Changes since v1:
>>>> - Retry per-page failures for the full runtime
>>>> - Verify that both alternating NUMA targets are reached
>>>> ---
>>>> tools/testing/selftests/mm/migration.c | 39 ++++++++++++++------------
>>>> 1 file changed, 21 insertions(+), 18 deletions(-)
>>>>
>>>> diff --git a/tools/testing/selftests/mm/migration.c b/tools/testing/selftests/mm/migration.c
>>>> index 29f7492453d43..4d55a424058a9 100644
>>>> --- a/tools/testing/selftests/mm/migration.c
>>>> +++ b/tools/testing/selftests/mm/migration.c
>>>> @@ -7,7 +7,7 @@
>>>> #include "kselftest_harness.h"
>>>> #include "hugepage_settings.h"
>>>>
>>>> -#include <strings.h>
>>>> +#include <string.h>
>>>> #include <pthread.h>
>>>> #include <numa.h>
>>>> #include <numaif.h>
>>>> @@ -20,7 +20,6 @@
>>>>
>>>> #define TWOMEG (2<<20)
>>>> #define RUNTIME (20)
>>>> -#define MAX_RETRIES 100
>>>> #define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
>>>>
>>>> HUGETLB_SETUP_DEFAULT_PAGES(1)
>>>> @@ -110,7 +109,7 @@ int migrate(uint64_t *ptr, int n1, int n2)
>>>> int ret, tmp;
>>>> int status = 0;
>>>> struct timespec ts1, ts2;
>>>> - int failures = 0;
>>>> + int success = 0;
>>>>
>>>> if (clock_gettime(CLOCK_MONOTONIC, &ts1))
>>>> return -1;
>>>> @@ -119,29 +118,33 @@ int migrate(uint64_t *ptr, int n1, int n2)
>>>> if (clock_gettime(CLOCK_MONOTONIC, &ts2))
>>>> return -1;
>>>>
>>>> - if (ts2.tv_sec - ts1.tv_sec >= RUNTIME)
>>>> - return 0;
>>>> + if (ts2.tv_sec - ts1.tv_sec >= RUNTIME) {
>>>> + /* Reaching both targets verifies a cross-node move. */
>>>> + if (success >= 2)
>>>> + return 0;
>>>> + else
>>>> + return -2;
>>>> + }
>>>>
>>>> ret = move_pages(0, 1, (void **) &ptr, &n2, &status,
>>>> MPOL_MF_MOVE_ALL);
>>>> - if (ret) {
>>>> - if (ret > 0) {
>>>> - /* Migration is best effort; try again */
>>>> - if (++failures < MAX_RETRIES)
>>>> - continue;
>>>> - printf("Didn't migrate %d pages\n", ret);
>>>> - }
>>>> - else
>>>> - perror("Couldn't migrate pages");
>>>> - return -2;
>>>> + if (ret < 0) {
>>>> + perror("Couldn't migrate pages");
>>>> + return ret;
>>>> }
>>>> - failures = 0;
>>>> + /* Migration is best effort. Try again */
>>>> + if (ret > 0 || status < 0)
>
> Also what if you migrated accidentally to node 0? In which case you
> will return a false success below.
What do you mean by "migrated accidentally to node 0"? Can you give an
example?
>
>
>>>
>>> old code wasn't using status, so why now?
>> The old code not using it does not mean we cannot use it now. ret gives the
>
> I never implied that :) The observation was because I didn't see it mentioned
> in the commit message, this change is orthogonal to the description.
>
>
>> aggregate result, while status gives the per-page result: the destination
>> node on success or a negative errno explaining the failure. In particular,
>> move_pages() can return 0 with a negative status, so checking it prevents a
>> failed move from being counted as successful.
>
> Crazy stuff. So move_pages() can return zero but that return value is not
> really the return value and you have to check status.
>
> The man page reads:
>
> "status is an array of integers that return the status of each page.
> The array contains valid values only if move_pages() did not return
> an error."
>
> But then says
>
> "On success move_pages() returns zero. On error, it returns -1, and sets
> errno to indicate the error. If positive value is returned, it is the
> number of nonmigrated pages."
>
> I don't know which one to fix, the manpage or the ABI, I suspect the latter,
> storing error codes in a variable and then returning zero sounds wrong.
>
>>
>>>
>>>> + continue;
>>>> + if (status != n2) {
>>>> + printf("Page is on node %d instead of target node %d\n",
>>>> + status, n2);
>>>> + return status;
>>>> + }
>>>> + success++;
>>>> tmp = n2;
>>>> n2 = n1;
>>>> n1 = tmp;
>>>> }
>>>> -
>>>> - return 0;
>
> There is no return statement after the while loop. I suspect on some compiler
> config it is gonna complain about this, or someone inevitably will run the
> selftests with an old compiler and complain about this :)
The loop cannot fall through, and the minimum supported GCC and Clang versions
recognize that. No trailing return is needed.
>
>>>> }
>>>>
>>>> void *access_mem(void *ptr)
>>>
>>
>
--
Thanks,
Usama
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-07-24 15:59 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 10:24 [PATCH v2 0/5] selftests/mm: Handle unsupported and transient test conditions Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 1/5] selftests/mm: skip COW tmpfile cases when fallocate() is unsupported Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 2/5] selftests/mm: skip guard hole-punch test if MADV_REMOVE " Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 3/5] selftests/mm: skip khugepaged swap tests without swap Muhammad Usama Anjum
2026-07-24 11:16 ` Sarthak Sharma
2026-07-24 10:24 ` [PATCH v2 4/5] selftests/mm: skip hard dirty page-cache test on NFS Muhammad Usama Anjum
2026-07-24 10:24 ` [PATCH v2 5/5] selftests/mm: retry migration failures for the full runtime Muhammad Usama Anjum
2026-07-24 11:46 ` Dev Jain
2026-07-24 11:53 ` Muhammad Usama Anjum
2026-07-24 15:23 ` Dev Jain
2026-07-24 15:57 ` Muhammad Usama Anjum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox