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
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?
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!
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
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
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.
.
© 2016 - 2026 Red Hat, Inc.