]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/mm: add memory failure dirty pagecache test
authorMiaohe Lin <linmiaohe@huawei.com>
Fri, 6 Feb 2026 03:16:39 +0000 (11:16 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 12 Feb 2026 23:43:01 +0000 (15:43 -0800)
This patch adds a new testcase to validate memory failure handling for
dirty pagecache.  This performs similar operations as clean pagecaches
except fsync() is not used to keep pages dirty.

This test helps ensure that memory failure handling for dirty pagecache
works correctly, including proper SIGBUS delivery, page isolation, and
recovery paths.

Link: https://lkml.kernel.org/r/20260206031639.2707102-4-linmiaohe@huawei.com
Signed-off-by: Miaohe Lin <linmiaohe@huawei.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: kernel test robot <lkp@intel.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Naoya Horiguchi <nao.horiguchi@gmail.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
tools/testing/selftests/mm/memory-failure.c

index 3aa624db95771008dd5e466c3ce2ac34a7c94196..3d9e0b9ffb411ac68ed2829c4493233d372e1a3d 100644 (file)
@@ -29,8 +29,10 @@ enum inject_type {
 enum result_type {
        MADV_HARD_ANON,
        MADV_HARD_CLEAN_PAGECACHE,
+       MADV_HARD_DIRTY_PAGECACHE,
        MADV_SOFT_ANON,
        MADV_SOFT_CLEAN_PAGECACHE,
+       MADV_SOFT_DIRTY_PAGECACHE,
 };
 
 static jmp_buf signal_jmp_buf;
@@ -162,6 +164,7 @@ static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure
        case MADV_SOFT_ANON:
        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);
 
@@ -172,6 +175,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_DIRTY_PAGECACHE:
                /* The SIGBUS signal should have been received. */
                ASSERT_EQ(setjmp, 1);
 
@@ -244,6 +248,18 @@ TEST_F(memory_failure, anon)
        ASSERT_EQ(munmap(addr, self->page_size), 0);
 }
 
+static int prepare_file(const char *fname, unsigned long size)
+{
+       int fd;
+
+       fd = open(fname, O_RDWR | O_CREAT, 0664);
+       if (fd >= 0) {
+               unlink(fname);
+               ftruncate(fd, size);
+       }
+       return fd;
+}
+
 /* Borrowed from mm/gup_longterm.c. */
 static int get_fs_type(int fd)
 {
@@ -259,17 +275,14 @@ static int get_fs_type(int fd)
 
 TEST_F(memory_failure, clean_pagecache)
 {
-       const char *fname = "./clean-page-cache-test-file";
        int fd;
        char *addr;
        int ret;
        int fs_type;
 
-       fd = open(fname, O_RDWR | O_CREAT, 0664);
+       fd = prepare_file("./clean-page-cache-test-file", self->page_size);
        if (fd < 0)
                SKIP(return, "failed to open test file.\n");
-       unlink(fname);
-       ftruncate(fd, self->page_size);
        fs_type = get_fs_type(fd);
        if (!fs_type || fs_type == TMPFS_MAGIC)
                SKIP(return, "unsupported filesystem :%x\n", fs_type);
@@ -302,4 +315,45 @@ TEST_F(memory_failure, clean_pagecache)
        ASSERT_EQ(close(fd), 0);
 }
 
+TEST_F(memory_failure, dirty_pagecache)
+{
+       int fd;
+       char *addr;
+       int ret;
+       int fs_type;
+
+       fd = prepare_file("./dirty-page-cache-test-file", self->page_size);
+       if (fd < 0)
+               SKIP(return, "failed to open test file.\n");
+       fs_type = get_fs_type(fd);
+       if (!fs_type || fs_type == TMPFS_MAGIC)
+               SKIP(return, "unsupported filesystem :%x\n", fs_type);
+
+       addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
+                   MAP_SHARED, fd, 0);
+       if (addr == MAP_FAILED)
+               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 (!self->triggered) {
+               self->triggered = true;
+               ASSERT_EQ(variant->inject(self, addr), 0);
+               FORCE_READ(*addr);
+       }
+
+       if (variant->type == MADV_HARD)
+               check(_metadata, self, addr, MADV_HARD_DIRTY_PAGECACHE, ret);
+       else
+               check(_metadata, self, addr, MADV_SOFT_DIRTY_PAGECACHE, ret);
+
+       cleanup(_metadata, self, addr);
+
+       ASSERT_EQ(munmap(addr, self->page_size), 0);
+
+       ASSERT_EQ(close(fd), 0);
+}
+
 TEST_HARNESS_MAIN