[PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test

Lisa Wang posted 7 patches 2 weeks, 3 days ago
[PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test
Posted by Lisa Wang 2 weeks, 3 days ago
Add a shmem memory failure selftest to test the shmem memory failure is
correct after modifying shmem return value.

Test that
+ madvise() call returns 0 at the first time
+ trigger a SIGBUS when the poisoned shmem page is fault-in again.

Signed-off-by: Lisa Wang <wyihan@google.com>
---
 tools/testing/selftests/mm/Makefile                |  3 +
 tools/testing/selftests/mm/run_vmtests.sh          |  1 +
 .../selftests/mm/shmem_memory_failure_test.c       | 98 ++++++++++++++++++++++
 3 files changed, 102 insertions(+)

diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index 7a5de4e9bf52..ac033851c9eb 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -72,6 +72,7 @@ TEST_GEN_FILES += madv_populate
 TEST_GEN_FILES += map_fixed_noreplace
 TEST_GEN_FILES += map_hugetlb
 TEST_GEN_FILES += map_populate
+TEST_GEN_FILES += shmem_memory_failure_test
 ifneq (,$(filter $(ARCH),arm64 riscv riscv64 x86 x86_64 loongarch32 loongarch64))
 TEST_GEN_FILES += memfd_secret
 endif
@@ -259,6 +260,8 @@ $(OUTPUT)/migration: LDLIBS += -lnuma
 
 $(OUTPUT)/rmap: LDLIBS += -lnuma
 
+$(OUTPUT)/shmem_memory_failure_test: CFLAGS += -I$(top_srcdir)/tools/include
+
 local_config.mk local_config.h: check_config.sh
 	CC="$(CC)" CFLAGS="$(CFLAGS)" ./check_config.sh
 
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index afdcfd0d7cef..58fb959a7936 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -402,6 +402,7 @@ CATEGORY="hugetlb" run_test ./hugetlb-soft-offline
 echo "$nr_hugepages_tmp" > /proc/sys/vm/nr_hugepages
 echo "$enable_soft_offline" > /proc/sys/vm/enable_soft_offline
 CATEGORY="hugetlb" run_test ./hugetlb-read-hwpoison
+CATEGORY="mmap" run_test ./shmem_memory_failure_test
 fi
 
 if [ $VADDR64 -ne 0 ]; then
diff --git a/tools/testing/selftests/mm/shmem_memory_failure_test.c b/tools/testing/selftests/mm/shmem_memory_failure_test.c
new file mode 100644
index 000000000000..44752024a7fc
--- /dev/null
+++ b/tools/testing/selftests/mm/shmem_memory_failure_test.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This test makes sure when memory failure happens, shmem can handle
+ * successfully.
+ */
+#include <linux/compiler.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <errno.h>
+#include "kselftest.h"
+#include "vm_util.h"
+
+static sigjmp_buf sigbuf;
+
+static void signal_handler(int sig, siginfo_t *info, void *ucontext)
+{
+	siglongjmp(sigbuf, 1);
+}
+
+static void set_signal_handler(int sig, void (*handler)(int, siginfo_t *, void *))
+{
+	struct sigaction sa = {};
+
+	sa.sa_sigaction = handler;
+	sa.sa_flags = SA_SIGINFO;
+	sigemptyset(&sa.sa_mask);
+	if (sigaction(sig, &sa, NULL) == -1)
+		ksft_exit_fail_msg("Failed to set SIGBUS handler: %s\n", strerror(errno));
+}
+
+static unsigned long addr_to_pfn(char *addr)
+{
+	int pagemap_fd;
+	unsigned long pfn;
+
+	pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+	if (pagemap_fd < 0)
+		ksft_exit_fail_msg("Failed to open /proc/self/pagemap: %s\n", strerror(errno));
+	pfn = pagemap_get_pfn(pagemap_fd, addr);
+	close(pagemap_fd);
+
+	return pfn;
+}
+
+static void test_shmem_memory_failure(size_t total_size, size_t page_size)
+{
+	unsigned long memory_failure_pfn;
+	char *memory_failure_mem;
+	char *memory_failure_addr;
+	int fd;
+
+	fd = memfd_create("shmem_hwpoison_test", 0);
+	if (fd < 0)
+		ksft_exit_skip("memfd_create failed: %s\n", strerror(errno));
+
+	if (ftruncate(fd, total_size) < 0)
+		ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno));
+
+	memory_failure_mem = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if (memory_failure_mem == MAP_FAILED)
+		ksft_exit_fail_msg("mmap failed: %s\n", strerror(errno));
+	memory_failure_addr = memory_failure_mem + page_size;
+	READ_ONCE(memory_failure_addr[0]);
+	memory_failure_pfn = addr_to_pfn(memory_failure_addr);
+
+	if (madvise(memory_failure_addr, page_size, MADV_HWPOISON) != 0)
+		ksft_exit_fail_msg("MADV_HWPOISON failed: %s\n", strerror(errno));
+
+	if (sigsetjmp(sigbuf, 1) == 0) {
+		READ_ONCE(memory_failure_addr[0]);
+		ksft_test_result_fail("Read from poisoned page should have triggered SIGBUS\n");
+	} else {
+		ksft_test_result_pass("SIGBUS triggered as expected on poisoned page\n");
+	}
+
+	munmap(memory_failure_mem, total_size);
+	close(fd);
+	if (unpoison_memory(memory_failure_pfn) < 0)
+		ksft_exit_fail_msg("unpoison_memory failed: %s\n", strerror(errno));
+}
+
+int main(int argc, char *argv[])
+{
+	const size_t pagesize = getpagesize();
+
+	ksft_print_header();
+	ksft_set_plan(1);
+
+	set_signal_handler(SIGBUS, signal_handler);
+	test_shmem_memory_failure(pagesize * 4, pagesize);
+	ksft_finished();
+}

-- 
2.53.0.959.g497ff81fa9-goog
Re: [PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test
Posted by Baolin Wang 2 weeks, 2 days ago

On 3/20/26 7:30 AM, Lisa Wang wrote:
> Add a shmem memory failure selftest to test the shmem memory failure is
> correct after modifying shmem return value.
> 
> Test that
> + madvise() call returns 0 at the first time
> + trigger a SIGBUS when the poisoned shmem page is fault-in again.
> 
> Signed-off-by: Lisa Wang <wyihan@google.com>
> ---

Why not move the shmem memory failure test into memory-failure.c?
Re: [PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test
Posted by Lisa Wang 1 week, 6 days ago
On Sat, Mar 21, 2026 at 02:30:04PM +0800, Baolin Wang wrote:
> 
> 
> On 3/20/26 7:30 AM, Lisa Wang wrote:
> > Add a shmem memory failure selftest to test the shmem memory failure is
> > correct after modifying shmem return value.
> > 
> > Test that
> > + madvise() call returns 0 at the first time
> > + trigger a SIGBUS when the poisoned shmem page is fault-in again.
> > 
> > Signed-off-by: Lisa Wang <wyihan@google.com>
> > ---
> 
> Why not move the shmem memory failure test into memory-failure.c?

Do you mean let memory-failure.c kernel code check by itself?
The reason I write the selftest instead of combining in memory-failure.c
is because
+ do not need extra checking code in kernel code
+ make it easier to trace the entire execution flow, starting from the
  madvise() down through shmem_error_remove_folio() and into the
  truncate_error_folio() logic.

Pleas let me know if I've missed something. Thanks!
Re: [PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test
Posted by Baolin Wang 1 week, 5 days ago

On 3/24/26 8:43 AM, Lisa Wang wrote:
> On Sat, Mar 21, 2026 at 02:30:04PM +0800, Baolin Wang wrote:
>>
>>
>> On 3/20/26 7:30 AM, Lisa Wang wrote:
>>> Add a shmem memory failure selftest to test the shmem memory failure is
>>> correct after modifying shmem return value.
>>>
>>> Test that
>>> + madvise() call returns 0 at the first time
>>> + trigger a SIGBUS when the poisoned shmem page is fault-in again.
>>>
>>> Signed-off-by: Lisa Wang <wyihan@google.com>
>>> ---
>>
>> Why not move the shmem memory failure test into memory-failure.c?
> 
> Do you mean let memory-failure.c kernel code check by itself?
> The reason I write the selftest instead of combining in memory-failure.c
> is because
> + do not need extra checking code in kernel code
> + make it easier to trace the entire execution flow, starting from the
>    madvise() down through shmem_error_remove_folio() and into the
>    truncate_error_folio() logic.
> 
> Pleas let me know if I've missed something. Thanks!

That's not quite what I meant. I mean, since there is already a 
memory-failure.c in mm selftests (see [1]), I think we should move the 
shmem memory failure test cases into that file.

[1] 
https://lore.kernel.org/all/20260206031639.2707102-1-linmiaohe@huawei.com/T/#m18e62ccb3e87316ec37dcde9389c1ba1c56d0951
Re: [PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test
Posted by Lisa Wang 1 week, 2 days ago
On Tue, Mar 24, 2026 at 08:36:36PM +0800, Baolin Wang wrote:
> 
> 
> On 3/24/26 8:43 AM, Lisa Wang wrote:
> > On Sat, Mar 21, 2026 at 02:30:04PM +0800, Baolin Wang wrote:
> > > 
> > > 
> > > On 3/20/26 7:30 AM, Lisa Wang wrote:
> > > > Add a shmem memory failure selftest to test the shmem memory failure is
> > > > correct after modifying shmem return value.
> > > > 
> > > > Test that
> > > > + madvise() call returns 0 at the first time
> > > > + trigger a SIGBUS when the poisoned shmem page is fault-in again.
> > > > 
> > > > Signed-off-by: Lisa Wang <wyihan@google.com>
> > > > ---
> > > 
> > > Why not move the shmem memory failure test into memory-failure.c?
> > 
> > Do you mean let memory-failure.c kernel code check by itself?
> > The reason I write the selftest instead of combining in memory-failure.c
> > is because
> > + do not need extra checking code in kernel code
> > + make it easier to trace the entire execution flow, starting from the
> >    madvise() down through shmem_error_remove_folio() and into the
> >    truncate_error_folio() logic.
> > 
> > Pleas let me know if I've missed something. Thanks!
> 
> That's not quite what I meant. I mean, since there is already a
> memory-failure.c in mm selftests (see [1]), I think we should move the shmem
> memory failure test cases into that file.

Got it. Thank you for pointing out.
Is anyone currently working on the shmem memory failure test? If not, I
will merge it into my next version.

I have a question regarding the current implementation:
```
ret = sigsetjmp(signal_jmp_buf, 1);
if (!self->triggered) {
	self->triggered = true;
	ASSERT_EQ(variant->inject(self, addr), 0);
	FORCE_READ(*addr);
}
```
Here is difficult to distinguish whether the SIGBUS is triggered by the
injection or the read operation. I am considering splitting these into
two separate SIGBUS jump blocks. Is it reasonable for me to split them?


> [1] https://lore.kernel.org/all/20260206031639.2707102-1-linmiaohe@huawei.com/T/#m18e62ccb3e87316ec37dcde9389c1ba1c56d0951
Re: [PATCH RFC v2 5/7] mm: selftests: Add shmem memory failure test
Posted by Miaohe Lin 1 week ago
On 2026/3/28 8:40, Lisa Wang wrote:
> On Tue, Mar 24, 2026 at 08:36:36PM +0800, Baolin Wang wrote:
>>
>>
>> On 3/24/26 8:43 AM, Lisa Wang wrote:
>>> On Sat, Mar 21, 2026 at 02:30:04PM +0800, Baolin Wang wrote:
>>>>
>>>>
>>>> On 3/20/26 7:30 AM, Lisa Wang wrote:
>>>>> Add a shmem memory failure selftest to test the shmem memory failure is
>>>>> correct after modifying shmem return value.
>>>>>
>>>>> Test that
>>>>> + madvise() call returns 0 at the first time
>>>>> + trigger a SIGBUS when the poisoned shmem page is fault-in again.
>>>>>
>>>>> Signed-off-by: Lisa Wang <wyihan@google.com>
>>>>> ---
>>>>
>>>> Why not move the shmem memory failure test into memory-failure.c?
>>>
>>> Do you mean let memory-failure.c kernel code check by itself?
>>> The reason I write the selftest instead of combining in memory-failure.c
>>> is because
>>> + do not need extra checking code in kernel code
>>> + make it easier to trace the entire execution flow, starting from the
>>>    madvise() down through shmem_error_remove_folio() and into the
>>>    truncate_error_folio() logic.
>>>
>>> Pleas let me know if I've missed something. Thanks!
>>
>> That's not quite what I meant. I mean, since there is already a
>> memory-failure.c in mm selftests (see [1]), I think we should move the shmem
>> memory failure test cases into that file.
> 
> Got it. Thank you for pointing out.
> Is anyone currently working on the shmem memory failure test? If not, I
> will merge it into my next version.

I'm working on shmem testcases. But please feel free to add it. I could move to
work on other scenarios.

> 
> I have a question regarding the current implementation:
> ```
> ret = sigsetjmp(signal_jmp_buf, 1);
> if (!self->triggered) {
> 	self->triggered = true;
> 	ASSERT_EQ(variant->inject(self, addr), 0);
> 	FORCE_READ(*addr);
> }
> ```
> Here is difficult to distinguish whether the SIGBUS is triggered by the
> injection or the read operation. I am considering splitting these into
> two separate SIGBUS jump blocks. Is it reasonable for me to split them?

It might be better to add two separate testcases, i.e. one for SIGBUS triggered
by injection, another one for SIGBUS triggered by read operation if possible.

Thanks.
.