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 when the poisoned shmem page is clean
+ trigger a SIGBUS when the poisoned shmem page is dirty
+ trigger another SIGBUS when the poisoned shmem page is fault-in again.

Signed-off-by: Lisa Wang <[email protected]>
---
 tools/testing/selftests/mm/memory-failure.c | 109 +++++++++++++++++++++++++++-
 1 file changed, 106 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/mm/memory-failure.c 
b/tools/testing/selftests/mm/memory-failure.c
index 3d9e0b9ffb41..eb3f8d98f6c9 100644
--- a/tools/testing/selftests/mm/memory-failure.c
+++ b/tools/testing/selftests/mm/memory-failure.c
@@ -30,9 +30,14 @@ enum result_type {
        MADV_HARD_ANON,
        MADV_HARD_CLEAN_PAGECACHE,
        MADV_HARD_DIRTY_PAGECACHE,
+       MADV_HARD_CLEAN_SHMEM,
+       MADV_HARD_DIRTY_SHMEM,
        MADV_SOFT_ANON,
        MADV_SOFT_CLEAN_PAGECACHE,
        MADV_SOFT_DIRTY_PAGECACHE,
+       MADV_SOFT_CLEAN_SHMEM,
+       MADV_SOFT_DIRTY_SHMEM,
+       READ_ERROR,
 };
 
 static jmp_buf signal_jmp_buf;
@@ -165,17 +170,21 @@ static void check(struct __test_metadata *_metadata, 
FIXTURE_DATA(memory_failure
        case MADV_HARD_CLEAN_PAGECACHE:
        case MADV_SOFT_CLEAN_PAGECACHE:
        case MADV_SOFT_DIRTY_PAGECACHE:
-               /* It is not expected to receive a SIGBUS signal. */
-               ASSERT_EQ(setjmp, 0);
-
+       case MADV_SOFT_DIRTY_SHMEM:
                /* The page content should remain unchanged. */
                ASSERT_TRUE(check_memory(vaddr, self->page_size));
+       case MADV_HARD_CLEAN_SHMEM:
+       case MADV_SOFT_CLEAN_SHMEM:
+               /* It is not expected to receive a SIGBUS signal. */
+               ASSERT_EQ(setjmp, 0);
 
                /* The backing pfn of addr should have changed. */
                ASSERT_NE(pagemap_get_pfn(self->pagemap_fd, vaddr), self->pfn);
                break;
        case MADV_HARD_ANON:
        case MADV_HARD_DIRTY_PAGECACHE:
+       case MADV_HARD_DIRTY_SHMEM:
+       case READ_ERROR:
                /* The SIGBUS signal should have been received. */
                ASSERT_EQ(setjmp, 1);
 
@@ -260,6 +269,20 @@ static int prepare_file(const char *fname, unsigned long 
size)
        return fd;
 }
 
+static int prepare_shmem(const char *fname, unsigned long size)
+{
+       int fd;
+
+       fd = memfd_create(fname, 0);
+       if (fd < 0)
+               return -1;
+       if (ftruncate(fd, size) < 0) {
+               close(fd);
+               return -1;
+       }
+       return fd;
+}
+
 /* Borrowed from mm/gup_longterm.c. */
 static int get_fs_type(int fd)
 {
@@ -356,4 +379,84 @@ TEST_F(memory_failure, dirty_pagecache)
        ASSERT_EQ(close(fd), 0);
 }
 
+TEST_F(memory_failure, dirty_shmem)
+{
+       int fd;
+       char *addr;
+       int ret;
+
+       fd = prepare_shmem("shmem-file", self->page_size);
+       if (fd < 0)
+               SKIP(return, "failed to open test shmem-file.\n");
+
+       addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, fd, 0);
+       if (addr == MAP_FAILED) {
+               close(fd);
+               SKIP(return, "mmap failed, not enough memory.\n");
+       }
+       memset(addr, 0xce, self->page_size);
+
+       prepare(_metadata, self, addr);
+
+       ret = sigsetjmp(signal_jmp_buf, 1);
+       if (ret == 0)
+               ASSERT_EQ(variant->inject(self, addr), 0);
+
+       if (variant->type == MADV_HARD) {
+               check(_metadata, self, addr, MADV_HARD_DIRTY_SHMEM, ret);
+               ret = sigsetjmp(signal_jmp_buf, 1);
+               if (ret == 0)
+                       FORCE_READ(*addr);
+               check(_metadata, self, addr, READ_ERROR, ret);
+       } else {
+               check(_metadata, self, addr, MADV_SOFT_DIRTY_SHMEM, ret);
+       }
+
+       ASSERT_EQ(munmap(addr, self->page_size), 0);
+
+       ASSERT_EQ(close(fd), 0);
+       cleanup(_metadata, self, addr);
+}
+
+TEST_F(memory_failure, clean_shmem)
+{
+       int fd;
+       char *addr;
+       int ret;
+
+       fd = prepare_shmem("shmem-file", self->page_size);
+       if (fd < 0)
+               SKIP(return, "failed to open test shmem-file.\n");
+
+       addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, fd, 0);
+       if (addr == MAP_FAILED) {
+               close(fd);
+               SKIP(return, "mmap failed, not enough memory.\n");
+       }
+       FORCE_READ(*addr);
+
+       prepare(_metadata, self, addr);
+
+       ret = sigsetjmp(signal_jmp_buf, 1);
+       if (ret == 0)
+               ASSERT_EQ(variant->inject(self, addr), 0);
+
+       if (variant->type == MADV_HARD) {
+               check(_metadata, self, addr, MADV_HARD_CLEAN_SHMEM, ret);
+               ret = sigsetjmp(signal_jmp_buf, 1);
+               if (ret == 0)
+                       FORCE_READ(*addr);
+               check(_metadata, self, addr, READ_ERROR, ret);
+       } else {
+               check(_metadata, self, addr, MADV_SOFT_CLEAN_SHMEM, ret);
+       }
+
+       ASSERT_EQ(munmap(addr, self->page_size), 0);
+
+       ASSERT_EQ(close(fd), 0);
+       cleanup(_metadata, self, addr);
+}
+
 TEST_HARNESS_MAIN

-- 
2.53.0.1213.gd9a14994de-goog


Reply via email to