From nobody Sun Feb 8 09:37:18 2026 Received: from canpmsgout11.his.huawei.com (canpmsgout11.his.huawei.com [113.46.200.226]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 155F7426D03; Tue, 20 Jan 2026 12:22:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.226 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768911723; cv=none; b=VKGCsiVTfOqXViUzs5+oVWksAn6FpKe30QIwbnoOnCVppjGKWdpPXQna+w4hBu+k7eDae0Wj0jajOZ1GvlE9CqlqikOvIie5NR/Xhi3NalkFEpQayw+mNN+XlsKSDk1Q7E+EZHo/6ugZrVezBEINgKDq/NAMcx7hnmB+lUlz7+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768911723; c=relaxed/simple; bh=dHe1+N/YhPVdvS1Kqo+1hmZXTlE9qjkcNvmtIL/uUvM=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=a8fzAYG56dfaelCMI1Fu8DRlOrjyhfVLG+2aMsIk75J7RvJHHlsD9ASG42xNar0ZPqkYGMkHUVvRE6I3t9jEAhkwgaVZIbmy4f61NHKxGRDyaTZPpLOnowCdfL0qku1/nGrfPTtcqSbmz1Sm4ZZeddO1hh9sZtcCr+8MBpuxFWo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=R3kIfzfY; arc=none smtp.client-ip=113.46.200.226 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="R3kIfzfY" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=hPpeWApS3iJ/pBNiDCjM8w/ys4LdENXWU9keA3bruaw=; b=R3kIfzfYvelm4lLQJNkrychetfrBrYvTnBofiKHGqi161QrkSK0sMHZtOH91u5e/PoEb2bxXw og1+sZIB9rOfUVvyb8z5sPngRzTNYs6/lrjphyI94/4rkPpr+DS1wloxY+woTmGdhF7RIyKaac8 L55iCF7Hrz0Q59jSK/INBEE= Received: from mail.maildlp.com (unknown [172.19.163.127]) by canpmsgout11.his.huawei.com (SkyGuard) with ESMTPS id 4dwRF12Xh0zKm6J; Tue, 20 Jan 2026 20:18:29 +0800 (CST) Received: from dggemv706-chm.china.huawei.com (unknown [10.3.19.33]) by mail.maildlp.com (Postfix) with ESMTPS id 4D1E4402AB; Tue, 20 Jan 2026 20:21:53 +0800 (CST) Received: from kwepemq500010.china.huawei.com (7.202.194.235) by dggemv706-chm.china.huawei.com (10.3.19.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 20 Jan 2026 20:21:53 +0800 Received: from huawei.com (10.173.125.37) by kwepemq500010.china.huawei.com (7.202.194.235) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 20 Jan 2026 20:21:52 +0800 From: Miaohe Lin To: , CC: , , , , , , , , , , , Subject: [PATCH v2 1/3] selftests/mm: add memory failure anonymous page test Date: Tue, 20 Jan 2026 20:32:37 +0800 Message-ID: <20260120123239.909882-2-linmiaohe@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260120123239.909882-1-linmiaohe@huawei.com> References: <20260120123239.909882-1-linmiaohe@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems500002.china.huawei.com (7.221.188.17) To kwepemq500010.china.huawei.com (7.202.194.235) Content-Type: text/plain; charset="utf-8" This patch adds a new kselftest to validate memory failure handling for anonymous pages. The test performs the following operations: 1. Allocates anonymous pages using mmap(). 2. Injects memory failure via madvise syscall. 3. Verifies expected error handling behavior. 4. Unpoison memory. This test helps ensure that memory failure handling for anonymous pages works correctly, including proper SIGBUS delivery to user processes, page isolation and recovery paths. Signed-off-by: Miaohe Lin --- MAINTAINERS | 1 + tools/testing/selftests/mm/.gitignore | 1 + tools/testing/selftests/mm/Makefile | 1 + tools/testing/selftests/mm/memory-failure.c | 239 ++++++++++++++++++++ tools/testing/selftests/mm/run_vmtests.sh | 21 ++ tools/testing/selftests/mm/vm_util.c | 41 ++++ tools/testing/selftests/mm/vm_util.h | 3 + 7 files changed, 307 insertions(+) create mode 100644 tools/testing/selftests/mm/memory-failure.c diff --git a/MAINTAINERS b/MAINTAINERS index 11720728d92f..5d5b1a16cfc6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11688,6 +11688,7 @@ F: include/linux/memory-failure.h F: include/trace/events/memory-failure.h F: mm/hwpoison-inject.c F: mm/memory-failure.c +F: tools/testing/selftests/mm/memory-failure.c =20 HYCON HY46XX TOUCHSCREEN SUPPORT M: Giulio Benetti diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftest= s/mm/.gitignore index c2a8586e51a1..83ad9454dd9d 100644 --- a/tools/testing/selftests/mm/.gitignore +++ b/tools/testing/selftests/mm/.gitignore @@ -12,6 +12,7 @@ map_hugetlb map_populate thuge-gen compaction_test +memory-failure migration mlock2-tests mrelease_test diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/= mm/Makefile index de4afc34e3b1..c4d12092fcb2 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -71,6 +71,7 @@ TEST_GEN_FILES +=3D map_populate ifneq (,$(filter $(ARCH),arm64 riscv riscv64 x86 x86_64)) TEST_GEN_FILES +=3D memfd_secret endif +TEST_GEN_FILES +=3D memory-failure TEST_GEN_FILES +=3D migration TEST_GEN_FILES +=3D mkdirty TEST_GEN_FILES +=3D mlock-random-test diff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/se= lftests/mm/memory-failure.c new file mode 100644 index 000000000000..37806a58f4b4 --- /dev/null +++ b/tools/testing/selftests/mm/memory-failure.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Memory-failure functional tests. + * + * Author(s): Miaohe Lin + */ + +#include "../kselftest_harness.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "vm_util.h" + +enum inject_type { + MADV_HARD, + MADV_SOFT, +}; + +enum result_type { + MADV_HARD_ANON, + MADV_SOFT_ANON, +}; + +static jmp_buf signal_jmp_buf; +static siginfo_t siginfo; +const char *pagemap_proc =3D "/proc/self/pagemap"; +const char *kpageflags_proc =3D "/proc/kpageflags"; + +FIXTURE(memory_failure) +{ + unsigned long page_size; + unsigned long corrupted_size; + unsigned long pfn; + int pagemap_fd; + int kpageflags_fd; + bool triggered; +}; + +FIXTURE_VARIANT(memory_failure) +{ + enum inject_type type; + int (*inject)(FIXTURE_DATA(memory_failure) * self, void *vaddr); +}; + +static int madv_hard_inject(FIXTURE_DATA(memory_failure) * self, void *vad= dr) +{ + return madvise(vaddr, self->page_size, MADV_HWPOISON); +} + +FIXTURE_VARIANT_ADD(memory_failure, madv_hard) +{ + .type =3D MADV_HARD, + .inject =3D madv_hard_inject, +}; + +static int madv_soft_inject(FIXTURE_DATA(memory_failure) * self, void *vad= dr) +{ + return madvise(vaddr, self->page_size, MADV_SOFT_OFFLINE); +} + +FIXTURE_VARIANT_ADD(memory_failure, madv_soft) +{ + .type =3D MADV_SOFT, + .inject =3D madv_soft_inject, +}; + +static void sigbus_action(int signo, siginfo_t *si, void *args) +{ + memcpy(&siginfo, si, sizeof(siginfo_t)); + siglongjmp(signal_jmp_buf, 1); +} + +static int setup_sighandler(void) +{ + struct sigaction sa =3D { + .sa_sigaction =3D sigbus_action, + .sa_flags =3D SA_SIGINFO, + }; + + return sigaction(SIGBUS, &sa, NULL); +} + +FIXTURE_SETUP(memory_failure) +{ + memset(self, 0, sizeof(*self)); + + self->page_size =3D (unsigned long)sysconf(_SC_PAGESIZE); + + memset(&siginfo, 0, sizeof(siginfo)); + if (setup_sighandler()) + SKIP(return, "setup sighandler failed.\n"); + + self->pagemap_fd =3D open(pagemap_proc, O_RDONLY); + if (self->pagemap_fd =3D=3D -1) + SKIP(return, "open %s failed.\n", pagemap_proc); + + self->kpageflags_fd =3D open(kpageflags_proc, O_RDONLY); + if (self->kpageflags_fd =3D=3D -1) + SKIP(return, "open %s failed.\n", kpageflags_proc); +} + +static void teardown_sighandler(void) +{ + struct sigaction sa =3D { + .sa_handler =3D SIG_DFL, + .sa_flags =3D SA_SIGINFO, + }; + + sigaction(SIGBUS, &sa, NULL); +} + +FIXTURE_TEARDOWN(memory_failure) +{ + close(self->kpageflags_fd); + close(self->pagemap_fd); + teardown_sighandler(); +} + +static void prepare(struct __test_metadata *_metadata, FIXTURE_DATA(memory= _failure) * self, + void *vaddr) +{ + self->pfn =3D pagemap_get_pfn(self->pagemap_fd, vaddr); + ASSERT_NE(self->pfn, -1UL); + + ASSERT_EQ(get_hardware_corrupted_size(&self->corrupted_size), 0); +} + +static bool check_memory(void *vaddr, unsigned long size) +{ + char buf[64]; + + memset(buf, 0xce, sizeof(buf)); + while (size >=3D sizeof(buf)) { + if (memcmp(vaddr, buf, sizeof(buf))) + return false; + size -=3D sizeof(buf); + vaddr +=3D sizeof(buf); + } + + return true; +} + +static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_f= ailure) * self, + void *vaddr, enum result_type type, int setjmp) +{ + unsigned long size; + uint64_t pfn_flags; + + switch (type) { + case MADV_SOFT_ANON: + /* It is not expected to receive a SIGBUS signal. */ + ASSERT_EQ(setjmp, 0); + + /* The page content should remain unchanged. */ + ASSERT_TRUE(check_memory(vaddr, self->page_size)); + + /* The backing pfn of addr should have changed. */ + ASSERT_NE(pagemap_get_pfn(self->pagemap_fd, vaddr), self->pfn); + break; + case MADV_HARD_ANON: + /* The SIGBUS signal should have been received. */ + ASSERT_EQ(setjmp, 1); + + /* Check if siginfo contains correct SIGBUS context. */ + ASSERT_EQ(siginfo.si_signo, SIGBUS); + ASSERT_EQ(siginfo.si_code, BUS_MCEERR_AR); + 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)); + break; + default: + SKIP(return, "unexpected inject type %d.\n", type); + } + + /* 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); + + /* Check if HWPoison flag is set. */ + ASSERT_EQ(pageflags_get(self->pfn, self->kpageflags_fd, &pfn_flags), 0); + ASSERT_EQ(pfn_flags & KPF_HWPOISON, KPF_HWPOISON); +} + +static void cleanup(struct __test_metadata *_metadata, FIXTURE_DATA(memory= _failure) * self, + void *vaddr) +{ + unsigned long size; + uint64_t pfn_flags; + + ASSERT_EQ(unpoison_memory(self->pfn), 0); + + /* Check if HWPoison flag is cleared. */ + ASSERT_EQ(pageflags_get(self->pfn, self->kpageflags_fd, &pfn_flags), 0); + ASSERT_NE(pfn_flags & KPF_HWPOISON, KPF_HWPOISON); + + /* Check if the value of HardwareCorrupted has decreased. */ + ASSERT_EQ(get_hardware_corrupted_size(&size), 0); + ASSERT_EQ(size, self->corrupted_size); +} + +TEST_F(memory_failure, anon) +{ + char *addr; + int ret; + + addr =3D mmap(0, self->page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (addr =3D=3D MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + memset(addr, 0xce, self->page_size); + + prepare(_metadata, self, addr); + + ret =3D sigsetjmp(signal_jmp_buf, 1); + if (!self->triggered) { + self->triggered =3D true; + ASSERT_EQ(variant->inject(self, addr), 0); + FORCE_READ(*addr); + } + + if (variant->type =3D=3D MADV_HARD) + check(_metadata, self, addr, MADV_HARD_ANON, ret); + else + check(_metadata, self, addr, MADV_SOFT_ANON, ret); + + cleanup(_metadata, self, addr); + + ASSERT_EQ(munmap(addr, self->page_size), 0); +} + +TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/self= tests/mm/run_vmtests.sh index 2dadbfc6e535..043ac00a98b8 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -87,6 +87,8 @@ separated by spaces: test VMA merge cases behave as expected - rmap test rmap behaves as expected +- memory-failure + test memory-failure behaves as expected =20 example: ./run_vmtests.sh -t "hmm mmap ksm" EOF @@ -535,6 +537,25 @@ CATEGORY=3D"page_frag" run_test ./test_page_frag.sh no= naligned =20 CATEGORY=3D"rmap" run_test ./rmap =20 +# Try to load hwpoison_inject if not present. +HWPOISON_DIR=3D/sys/kernel/debug/hwpoison/ +if [ ! -d "$HWPOISON_DIR" ]; then + if ! modprobe -q -R hwpoison_inject; then + echo "Module hwpoison_inject not found, skipping..." + else + modprobe hwpoison_inject > /dev/null 2>&1 + LOADED_MOD=3D1 + fi +fi + +if [ -d "$HWPOISON_DIR" ]; then + CATEGORY=3D"memory-failure" run_test ./memory-failure +fi + +if [ -n "${LOADED_MOD}" ]; then + modprobe -r hwpoison_inject > /dev/null 2>&1 +fi + if [ "${HAVE_HUGEPAGES}" =3D 1 ]; then echo "$orig_nr_hugepgs" > /proc/sys/vm/nr_hugepages fi diff --git a/tools/testing/selftests/mm/vm_util.c b/tools/testing/selftests= /mm/vm_util.c index d954bf91afd5..a6d4ff7dfdc0 100644 --- a/tools/testing/selftests/mm/vm_util.c +++ b/tools/testing/selftests/mm/vm_util.c @@ -723,3 +723,44 @@ int ksm_stop(void) close(ksm_fd); return ret =3D=3D 1 ? 0 : -errno; } + +int get_hardware_corrupted_size(unsigned long *val) +{ + unsigned long size; + char *line =3D NULL; + size_t linelen =3D 0; + FILE *f =3D fopen("/proc/meminfo", "r"); + int ret =3D -1; + + if (!f) + return ret; + + while (getline(&line, &linelen, f) > 0) { + if (sscanf(line, "HardwareCorrupted: %12lu kB", &size) =3D=3D 1) { + *val =3D size; + ret =3D 0; + break; + } + } + + free(line); + fclose(f); + return ret; +} + +int unpoison_memory(unsigned long pfn) +{ + int unpoison_fd, len; + char buf[32]; + ssize_t ret; + + unpoison_fd =3D open("/sys/kernel/debug/hwpoison/unpoison-pfn", O_WRONLY); + if (unpoison_fd < 0) + return -errno; + + len =3D sprintf(buf, "0x%lx\n", pfn); + ret =3D write(unpoison_fd, buf, len); + close(unpoison_fd); + + return ret > 0 ? 0 : -errno; +} diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests= /mm/vm_util.h index 74bdf96161d7..a08ac703b4f1 100644 --- a/tools/testing/selftests/mm/vm_util.h +++ b/tools/testing/selftests/mm/vm_util.h @@ -20,6 +20,7 @@ =20 #define KPF_COMPOUND_HEAD BIT_ULL(15) #define KPF_COMPOUND_TAIL BIT_ULL(16) +#define KPF_HWPOISON BIT_ULL(19) #define KPF_THP BIT_ULL(22) /* * Ignore the checkpatch warning, we must read from x but don't want to do @@ -153,6 +154,8 @@ long ksm_get_full_scans(void); int ksm_use_zero_pages(void); int ksm_start(void); int ksm_stop(void); +int get_hardware_corrupted_size(unsigned long *val); +int unpoison_memory(unsigned long pfn); =20 /* * On ppc64 this will only work with radix 2M hugepage size --=20 2.33.0 From nobody Sun Feb 8 09:37:18 2026 Received: from canpmsgout10.his.huawei.com (canpmsgout10.his.huawei.com [113.46.200.225]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F304C3D5242; Tue, 20 Jan 2026 12:21:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.225 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768911719; cv=none; b=trHytltRC9gRY3amx391FbvLYXHNlHfXm+LrKCp0nzLtLu4vl91vdxSOEE+y/A4MYqzMRcRlpPmzSIUax275K1B5dmdhezqUWZF9HN8QYzfClqcw8C2VOKItfTq2nebfYz6pTxf8Qt7BXyq6btCqHDwtgbneelRaLjsjnpuxqW4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768911719; c=relaxed/simple; bh=cekDGUVleARgDgj2ZRRqa++Pd4m+Vu73Q9vduf3WL40=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=QjNYs5ZWUa14yBsjnFfo0cned5a7s/IfO5SJqnFBkjWu3wTg8yUQDXm5KNYTnJQ27Ti8oNn1T9p+ZzlGms/3ugEGLiC1qzBM3wPpRigu4of+WlgaC6joN/KzYxm9yx4IXArPwdVnfHYa+jRAdRxsI1lkQPEIqTn/9MDid5WXJWY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=OVZf+HWc; arc=none smtp.client-ip=113.46.200.225 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="OVZf+HWc" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=mBOOYNYXFCfWOks6zujGii6P/ItlkH49gnpYx5qmEj0=; b=OVZf+HWcipbo88/UUufeT682Qw0LixFHuMyoJr9kztQYteb8FkKxYdPp5HxzztODMqzCYNxrN L6KB571pl+MMLOB0P5AhlRUAnbaEOAEGWgUXlo1IGqHbAkJxaJK+fgHqiqrNUaS1bJyv4NMtnPs GIt6D5covbai3OCo0PXzuBA= Received: from mail.maildlp.com (unknown [172.19.162.92]) by canpmsgout10.his.huawei.com (SkyGuard) with ESMTPS id 4dwRF46Yjzz1K96j; Tue, 20 Jan 2026 20:18:32 +0800 (CST) Received: from dggemv712-chm.china.huawei.com (unknown [10.1.198.32]) by mail.maildlp.com (Postfix) with ESMTPS id 2660540568; Tue, 20 Jan 2026 20:21:54 +0800 (CST) Received: from kwepemq500010.china.huawei.com (7.202.194.235) by dggemv712-chm.china.huawei.com (10.1.198.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 20 Jan 2026 20:21:53 +0800 Received: from huawei.com (10.173.125.37) by kwepemq500010.china.huawei.com (7.202.194.235) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 20 Jan 2026 20:21:52 +0800 From: Miaohe Lin To: , CC: , , , , , , , , , , , Subject: [PATCH v2 2/3] selftests/mm: add memory failure clean pagecache test Date: Tue, 20 Jan 2026 20:32:38 +0800 Message-ID: <20260120123239.909882-3-linmiaohe@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260120123239.909882-1-linmiaohe@huawei.com> References: <20260120123239.909882-1-linmiaohe@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems500002.china.huawei.com (7.221.188.17) To kwepemq500010.china.huawei.com (7.202.194.235) Content-Type: text/plain; charset="utf-8" This patch adds a new testcase to validate memory failure handling for clean pagecache. This test performs similar operations as anonymous pages except allocating memory using mmap() with a file fd. This test helps ensure that memory failure handling for clean pagecache works correctly, including unchanged page content, page isolation, and recovery paths. Signed-off-by: Miaohe Lin --- tools/testing/selftests/mm/memory-failure.c | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/se= lftests/mm/memory-failure.c index 37806a58f4b4..128c2ee6d332 100644 --- a/tools/testing/selftests/mm/memory-failure.c +++ b/tools/testing/selftests/mm/memory-failure.c @@ -10,10 +10,14 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include =20 #include "vm_util.h" =20 @@ -24,7 +28,9 @@ enum inject_type { =20 enum result_type { MADV_HARD_ANON, + MADV_HARD_CLEAN_PAGECACHE, MADV_SOFT_ANON, + MADV_SOFT_CLEAN_PAGECACHE, }; =20 static jmp_buf signal_jmp_buf; @@ -154,6 +160,8 @@ static void check(struct __test_metadata *_metadata, FI= XTURE_DATA(memory_failure =20 switch (type) { case MADV_SOFT_ANON: + case MADV_HARD_CLEAN_PAGECACHE: + case MADV_SOFT_CLEAN_PAGECACHE: /* It is not expected to receive a SIGBUS signal. */ ASSERT_EQ(setjmp, 0); =20 @@ -236,4 +244,62 @@ TEST_F(memory_failure, anon) ASSERT_EQ(munmap(addr, self->page_size), 0); } =20 +/* Borrowed from mm/gup_longterm.c. */ +static __fsword_t get_fs_type(int fd) +{ + struct statfs fs; + int ret; + + do { + ret =3D fstatfs(fd, &fs); + } while (ret && errno =3D=3D EINTR); + + return ret ? 0 : fs.f_type; +} + +TEST_F(memory_failure, clean_pagecache) +{ + const char *fname =3D "./clean-page-cache-test-file"; + int fd; + char *addr; + int ret; + __fsword_t fs_type; + + fd =3D open(fname, O_RDWR | O_CREAT, 0664); + if (fd < 0) + SKIP(return, "failed to open test file.\n"); + unlink(fname); + ftruncate(fd, self->page_size); + fs_type =3D get_fs_type(fd); + if (!fs_type || fs_type =3D=3D TMPFS_MAGIC) + SKIP(return, "unsupported filesystem :%lx\n", fs_type); + + addr =3D mmap(0, self->page_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (addr =3D=3D MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + memset(addr, 0xce, self->page_size); + fsync(fd); + + prepare(_metadata, self, addr); + + ret =3D sigsetjmp(signal_jmp_buf, 1); + if (!self->triggered) { + self->triggered =3D true; + ASSERT_EQ(variant->inject(self, addr), 0); + FORCE_READ(*addr); + } + + if (variant->type =3D=3D MADV_HARD) + check(_metadata, self, addr, MADV_HARD_CLEAN_PAGECACHE, ret); + else + check(_metadata, self, addr, MADV_SOFT_CLEAN_PAGECACHE, ret); + + cleanup(_metadata, self, addr); + + ASSERT_EQ(munmap(addr, self->page_size), 0); + + ASSERT_EQ(close(fd), 0); +} + TEST_HARNESS_MAIN --=20 2.33.0 From nobody Sun Feb 8 09:37:18 2026 Received: from canpmsgout04.his.huawei.com (canpmsgout04.his.huawei.com [113.46.200.219]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3C2EB407564; Tue, 20 Jan 2026 12:21:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.219 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768911720; cv=none; b=SI9fN2zf1Z6GnHsYHJ64oY1v1Z/KynUsh7ZHEGF6SxEa4jCsq86l7UkSUdT3g6A9w8kcXnJalwexYsCS5ttNTSQECRMcsTppkpRcZseGx1SOVVs3nUo1kwwvf7hr20yWYabPPtrGHg4NaxQyLAW12ovLrqvFMnrSn6PdWD6wE1Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768911720; c=relaxed/simple; bh=RiLFZInwNtAwflHRSZRZNM3I6qqrs0R8/jBSYyRv/lE=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p7hCxh45I0uiM5oTuMBlnhLhGD8nfNpe4pMHkggfbissUHQqcykQHn1XjueJjTJZAebg6Raf3p/igmzahBX57Osbnda8K/BdPxnhalGvJBwccLdBiAorHYEs8mXDqupp7I/dWHlivHO21tS6mQ3i3xg811K3VNQz6csgUzfEflc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=ZkOIGNta; arc=none smtp.client-ip=113.46.200.219 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="ZkOIGNta" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=XN5AlAlhJg9JIFTM4XSCb0KoyacQhShFHnroCAR8iys=; b=ZkOIGNtaBI3w1qyeubmh483u17JXwZ1FjOPx6UdjLrjnXD4lyZxmMvTYJWdhdqKw7y6zfy0Ee raRNtnX7p1Dptc9oZduEyC0Be0Q443XWmk2DFH/RFqI+1IVD/8QtREQUFEPiOEJYm8nyVxs/Im2 KEAUEE/iWoxvj2mC/9dW4QY= Received: from mail.maildlp.com (unknown [172.19.162.140]) by canpmsgout04.his.huawei.com (SkyGuard) with ESMTPS id 4dwRF13J6Tz1prL6; Tue, 20 Jan 2026 20:18:29 +0800 (CST) Received: from dggemv705-chm.china.huawei.com (unknown [10.3.19.32]) by mail.maildlp.com (Postfix) with ESMTPS id AAD1120104; Tue, 20 Jan 2026 20:21:54 +0800 (CST) Received: from kwepemq500010.china.huawei.com (7.202.194.235) by dggemv705-chm.china.huawei.com (10.3.19.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 20 Jan 2026 20:21:54 +0800 Received: from huawei.com (10.173.125.37) by kwepemq500010.china.huawei.com (7.202.194.235) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Tue, 20 Jan 2026 20:21:53 +0800 From: Miaohe Lin To: , CC: , , , , , , , , , , , Subject: [PATCH v2 3/3] selftests/mm: add memory failure dirty pagecache test Date: Tue, 20 Jan 2026 20:32:39 +0800 Message-ID: <20260120123239.909882-4-linmiaohe@huawei.com> X-Mailer: git-send-email 2.33.0 In-Reply-To: <20260120123239.909882-1-linmiaohe@huawei.com> References: <20260120123239.909882-1-linmiaohe@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: kwepems500002.china.huawei.com (7.221.188.17) To kwepemq500010.china.huawei.com (7.202.194.235) Content-Type: text/plain; charset="utf-8" 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. Signed-off-by: Miaohe Lin --- tools/testing/selftests/mm/memory-failure.c | 62 +++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/mm/memory-failure.c b/tools/testing/se= lftests/mm/memory-failure.c index 128c2ee6d332..a74a057599b4 100644 --- a/tools/testing/selftests/mm/memory-failure.c +++ b/tools/testing/selftests/mm/memory-failure.c @@ -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, }; =20 static jmp_buf signal_jmp_buf; @@ -162,6 +164,7 @@ static void check(struct __test_metadata *_metadata, FI= XTURE_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); =20 @@ -172,6 +175,7 @@ static void check(struct __test_metadata *_metadata, FI= XTURE_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); =20 @@ -244,6 +248,18 @@ TEST_F(memory_failure, anon) ASSERT_EQ(munmap(addr, self->page_size), 0); } =20 +static int prepare_file(const char *fname, unsigned long size) +{ + int fd; + + fd =3D open(fname, O_RDWR | O_CREAT, 0664); + if (fd >=3D 0) { + unlink(fname); + ftruncate(fd, size); + } + return fd; +} + /* Borrowed from mm/gup_longterm.c. */ static __fsword_t get_fs_type(int fd) { @@ -259,17 +275,14 @@ static __fsword_t get_fs_type(int fd) =20 TEST_F(memory_failure, clean_pagecache) { - const char *fname =3D "./clean-page-cache-test-file"; int fd; char *addr; int ret; __fsword_t fs_type; =20 - fd =3D open(fname, O_RDWR | O_CREAT, 0664); + fd =3D 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 =3D get_fs_type(fd); if (!fs_type || fs_type =3D=3D TMPFS_MAGIC) SKIP(return, "unsupported filesystem :%lx\n", fs_type); @@ -302,4 +315,45 @@ TEST_F(memory_failure, clean_pagecache) ASSERT_EQ(close(fd), 0); } =20 +TEST_F(memory_failure, dirty_pagecache) +{ + int fd; + char *addr; + int ret; + __fsword_t fs_type; + + fd =3D prepare_file("./dirty-page-cache-test-file", self->page_size); + if (fd < 0) + SKIP(return, "failed to open test file.\n"); + fs_type =3D get_fs_type(fd); + if (!fs_type || fs_type =3D=3D TMPFS_MAGIC) + SKIP(return, "unsupported filesystem :%lx\n", fs_type); + + addr =3D mmap(0, self->page_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (addr =3D=3D MAP_FAILED) + SKIP(return, "mmap failed, not enough memory.\n"); + memset(addr, 0xce, self->page_size); + + prepare(_metadata, self, addr); + + ret =3D sigsetjmp(signal_jmp_buf, 1); + if (!self->triggered) { + self->triggered =3D true; + ASSERT_EQ(variant->inject(self, addr), 0); + FORCE_READ(*addr); + } + + if (variant->type =3D=3D 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 --=20 2.33.0