[PATCH v7 3/3] selftests/mm: add hard memory failure anonymous HugeTLB test
From: Jiaqi Yan
Date: Tue Nov 10 2026 - 00:36:10 EST
Add a new testcase to validate memory failure recovery for HWPoison
anonymous HugeTLB page, including proper SIGBUS delivery, releasing
a HugeTLB page containing one HWPoison page to buddy allocator, and
isolation of the raw HWPoison page.
The test uses HugeTLB's default hugesize.
Although can be added in future, this patch does not support testing
the MADV_SOFT variant.
Acked-by: Miaohe Lin <linmiaohe@xxxxxxxxxx>
Signed-off-by: Jiaqi Yan <jiaqiyan@xxxxxxxxxx>
---
tools/testing/selftests/mm/memory-failure.c | 67 ++++++++++++++++++++-
1 file changed, 64 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/selftests/mm/memory-failure.c
index 135a2e8069ba..89fe137ea0e6 100644
--- a/tools/testing/selftests/mm/memory-failure.c
+++ b/tools/testing/selftests/mm/memory-failure.c
@@ -27,6 +27,7 @@ enum inject_type {
enum result_type {
MADV_HARD_ANON,
+ MADV_HARD_ANON_HUGETLB,
MADV_HARD_CLEAN_PAGECACHE,
MADV_HARD_DIRTY_PAGECACHE,
MADV_HARD_CLEAN_SHMEM,
@@ -44,6 +45,10 @@ static siginfo_t siginfo;
const char *pagemap_proc = "/proc/self/pagemap";
const char *kpageflags_proc = "/proc/kpageflags";
+/* HugeTLB settings will be automatically restored when test exits. */
+#define NR_ALLOC_HUGETLB_PAGES 4
+HUGETLB_SETUP_DEFAULT_PAGES(NR_ALLOC_HUGETLB_PAGES)
+
FIXTURE(memory_failure)
{
unsigned long page_size;
@@ -52,6 +57,8 @@ FIXTURE(memory_failure)
int pagemap_fd;
int kpageflags_fd;
bool injection_attempted;
+ /* Number of initial HugeTLB pages with default hugepagesize. */
+ unsigned long nr_hugetlb_pages;
};
FIXTURE_VARIANT(memory_failure)
@@ -155,6 +162,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
void *vaddr, enum result_type type, int setjmp)
{
unsigned long size;
+ unsigned long nr_hugetlb_pages;
uint64_t pfn_flags;
switch (type) {
@@ -175,6 +183,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
ASSERT_NE(pagemap_get_pfn(self->pagemap_fd, vaddr), self->pfn);
break;
case MADV_HARD_ANON:
+ case MADV_HARD_ANON_HUGETLB:
case MADV_HARD_DIRTY_PAGECACHE:
case MADV_HARD_DIRTY_SHMEM:
case READ_ERROR:
@@ -187,8 +196,15 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
ASSERT_EQ(1UL << siginfo.si_addr_lsb, self->page_size);
ASSERT_EQ(siginfo.si_addr, vaddr);
- /* XXX Check backing pte is hwpoison entry when supported. */
- ASSERT_TRUE(pagemap_is_swapped(self->pagemap_fd, vaddr));
+ if (type != MADV_HARD_ANON_HUGETLB)
+ /*
+ * Check backing pte is hwpoison entry when supported.
+ * Although try_to_unmap_one() also installs hwpoison
+ * entry for HugeTLB, pagemap_hugetlb_range() doesn't
+ * parse swap entries at all.
+ */
+ ASSERT_TRUE(pagemap_is_swapped(self->pagemap_fd, vaddr));
+
break;
default:
SKIP(return, "unexpected inject type %d.\n", type);
@@ -196,7 +212,19 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
/* Check if the value of HardwareCorrupted has increased. */
ASSERT_EQ(get_hardware_corrupted_size(&size), 0);
- ASSERT_EQ(size, self->corrupted_size + self->page_size / 1024);
+
+ if (type == MADV_HARD_ANON_HUGETLB) {
+ /*
+ * Only one page is hardware corrupted; the rest should all be
+ * released to buddy allocator.
+ */
+ ASSERT_EQ(size, self->corrupted_size + getpagesize() / 1024);
+ /* HugeTLB should have lost the HWPoison HugeTLB page. */
+ nr_hugetlb_pages = hugetlb_nr_default_pages();
+ ASSERT_EQ(nr_hugetlb_pages + 1, self->nr_hugetlb_pages);
+ } else {
+ ASSERT_EQ(size, self->corrupted_size + self->page_size / 1024);
+ }
/* Check if HWPoison flag is set. */
ASSERT_EQ(pageflags_get(self->pfn, self->kpageflags_fd, &pfn_flags), 0);
@@ -261,6 +289,39 @@ TEST_F(memory_failure, anon)
ASSERT_EQ(munmap(addr, self->page_size), 0);
}
+TEST_F(memory_failure, anon_hugetlb)
+{
+ char *addr;
+ int ret;
+ unsigned long alloc_size;
+
+ if (variant->type == MADV_SOFT)
+ SKIP(return, "Soft offline test is not implemented");
+
+ alloc_size = default_huge_page_size() * NR_ALLOC_HUGETLB_PAGES;
+ self->page_size = default_huge_page_size();
+ self->nr_hugetlb_pages = hugetlb_nr_default_pages();
+
+ addr = mmap(0, alloc_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0);
+ if (addr == MAP_FAILED)
+ SKIP(return, "mmap failed, not enough memory or hugetlb not supported.\n");
+ memset(addr, 0xce, alloc_size);
+
+ prepare(_metadata, self, addr);
+
+ ret = sigsetjmp(signal_jmp_buf, 1);
+ if (!self->injection_attempted) {
+ self->injection_attempted = true;
+ ASSERT_EQ(variant->inject(self, addr), 0);
+ FORCE_READ(*addr);
+ }
+
+ check(_metadata, self, addr, MADV_HARD_ANON_HUGETLB, ret);
+
+ ASSERT_EQ(munmap(addr, alloc_size), 0);
+}
+
static int prepare_file(const char *fname, unsigned long size)
{
int fd;
--
2.56.0.506.g506a7eabc5-goog