xref: /linux/tools/testing/selftests/mm/memory-failure.c (revision eeccf287a2a517954b57cf9d733b3cf5d47afa34)
1ff4ef2fbSMiaohe Lin // SPDX-License-Identifier: GPL-2.0
2ff4ef2fbSMiaohe Lin /*
3ff4ef2fbSMiaohe Lin  * Memory-failure functional tests.
4ff4ef2fbSMiaohe Lin  *
5ff4ef2fbSMiaohe Lin  * Author(s): Miaohe Lin <linmiaohe@huawei.com>
6ff4ef2fbSMiaohe Lin  */
7ff4ef2fbSMiaohe Lin 
8ff4ef2fbSMiaohe Lin #include "../kselftest_harness.h"
9ff4ef2fbSMiaohe Lin 
10ff4ef2fbSMiaohe Lin #include <sys/mman.h>
11ff4ef2fbSMiaohe Lin #include <linux/mman.h>
12ff4ef2fbSMiaohe Lin #include <linux/string.h>
1312e8a2faSMiaohe Lin #include <unistd.h>
14ff4ef2fbSMiaohe Lin #include <signal.h>
15ff4ef2fbSMiaohe Lin #include <setjmp.h>
16ff4ef2fbSMiaohe Lin #include <unistd.h>
17ff4ef2fbSMiaohe Lin #include <fcntl.h>
1812e8a2faSMiaohe Lin #include <sys/vfs.h>
1912e8a2faSMiaohe Lin #include <linux/magic.h>
2012e8a2faSMiaohe Lin #include <errno.h>
21ff4ef2fbSMiaohe Lin 
22ff4ef2fbSMiaohe Lin #include "vm_util.h"
23ff4ef2fbSMiaohe Lin 
24ff4ef2fbSMiaohe Lin enum inject_type {
25ff4ef2fbSMiaohe Lin 	MADV_HARD,
26ff4ef2fbSMiaohe Lin 	MADV_SOFT,
27ff4ef2fbSMiaohe Lin };
28ff4ef2fbSMiaohe Lin 
29ff4ef2fbSMiaohe Lin enum result_type {
30ff4ef2fbSMiaohe Lin 	MADV_HARD_ANON,
3112e8a2faSMiaohe Lin 	MADV_HARD_CLEAN_PAGECACHE,
32*d51b5076SMiaohe Lin 	MADV_HARD_DIRTY_PAGECACHE,
33ff4ef2fbSMiaohe Lin 	MADV_SOFT_ANON,
3412e8a2faSMiaohe Lin 	MADV_SOFT_CLEAN_PAGECACHE,
35*d51b5076SMiaohe Lin 	MADV_SOFT_DIRTY_PAGECACHE,
36ff4ef2fbSMiaohe Lin };
37ff4ef2fbSMiaohe Lin 
38ff4ef2fbSMiaohe Lin static jmp_buf signal_jmp_buf;
39ff4ef2fbSMiaohe Lin static siginfo_t siginfo;
40ff4ef2fbSMiaohe Lin const char *pagemap_proc = "/proc/self/pagemap";
41ff4ef2fbSMiaohe Lin const char *kpageflags_proc = "/proc/kpageflags";
42ff4ef2fbSMiaohe Lin 
FIXTURE(memory_failure)43ff4ef2fbSMiaohe Lin FIXTURE(memory_failure)
44ff4ef2fbSMiaohe Lin {
45ff4ef2fbSMiaohe Lin 	unsigned long page_size;
46ff4ef2fbSMiaohe Lin 	unsigned long corrupted_size;
47ff4ef2fbSMiaohe Lin 	unsigned long pfn;
48ff4ef2fbSMiaohe Lin 	int pagemap_fd;
49ff4ef2fbSMiaohe Lin 	int kpageflags_fd;
50ff4ef2fbSMiaohe Lin 	bool triggered;
51ff4ef2fbSMiaohe Lin };
52ff4ef2fbSMiaohe Lin 
FIXTURE_VARIANT(memory_failure)53ff4ef2fbSMiaohe Lin FIXTURE_VARIANT(memory_failure)
54ff4ef2fbSMiaohe Lin {
55ff4ef2fbSMiaohe Lin 	enum inject_type type;
56ff4ef2fbSMiaohe Lin 	int (*inject)(FIXTURE_DATA(memory_failure) * self, void *vaddr);
57ff4ef2fbSMiaohe Lin };
58ff4ef2fbSMiaohe Lin 
madv_hard_inject(FIXTURE_DATA (memory_failure)* self,void * vaddr)59ff4ef2fbSMiaohe Lin static int madv_hard_inject(FIXTURE_DATA(memory_failure) * self, void *vaddr)
60ff4ef2fbSMiaohe Lin {
61ff4ef2fbSMiaohe Lin 	return madvise(vaddr, self->page_size, MADV_HWPOISON);
62ff4ef2fbSMiaohe Lin }
63ff4ef2fbSMiaohe Lin 
FIXTURE_VARIANT_ADD(memory_failure,madv_hard)64ff4ef2fbSMiaohe Lin FIXTURE_VARIANT_ADD(memory_failure, madv_hard)
65ff4ef2fbSMiaohe Lin {
66ff4ef2fbSMiaohe Lin 	.type = MADV_HARD,
67ff4ef2fbSMiaohe Lin 	.inject = madv_hard_inject,
68ff4ef2fbSMiaohe Lin };
69ff4ef2fbSMiaohe Lin 
madv_soft_inject(FIXTURE_DATA (memory_failure)* self,void * vaddr)70ff4ef2fbSMiaohe Lin static int madv_soft_inject(FIXTURE_DATA(memory_failure) * self, void *vaddr)
71ff4ef2fbSMiaohe Lin {
72ff4ef2fbSMiaohe Lin 	return madvise(vaddr, self->page_size, MADV_SOFT_OFFLINE);
73ff4ef2fbSMiaohe Lin }
74ff4ef2fbSMiaohe Lin 
FIXTURE_VARIANT_ADD(memory_failure,madv_soft)75ff4ef2fbSMiaohe Lin FIXTURE_VARIANT_ADD(memory_failure, madv_soft)
76ff4ef2fbSMiaohe Lin {
77ff4ef2fbSMiaohe Lin 	.type = MADV_SOFT,
78ff4ef2fbSMiaohe Lin 	.inject = madv_soft_inject,
79ff4ef2fbSMiaohe Lin };
80ff4ef2fbSMiaohe Lin 
sigbus_action(int signo,siginfo_t * si,void * args)81ff4ef2fbSMiaohe Lin static void sigbus_action(int signo, siginfo_t *si, void *args)
82ff4ef2fbSMiaohe Lin {
83ff4ef2fbSMiaohe Lin 	memcpy(&siginfo, si, sizeof(siginfo_t));
84ff4ef2fbSMiaohe Lin 	siglongjmp(signal_jmp_buf, 1);
85ff4ef2fbSMiaohe Lin }
86ff4ef2fbSMiaohe Lin 
setup_sighandler(void)87ff4ef2fbSMiaohe Lin static int setup_sighandler(void)
88ff4ef2fbSMiaohe Lin {
89ff4ef2fbSMiaohe Lin 	struct sigaction sa = {
90ff4ef2fbSMiaohe Lin 		.sa_sigaction = sigbus_action,
91ff4ef2fbSMiaohe Lin 		.sa_flags = SA_SIGINFO,
92ff4ef2fbSMiaohe Lin 	};
93ff4ef2fbSMiaohe Lin 
94ff4ef2fbSMiaohe Lin 	return sigaction(SIGBUS, &sa, NULL);
95ff4ef2fbSMiaohe Lin }
96ff4ef2fbSMiaohe Lin 
FIXTURE_SETUP(memory_failure)97ff4ef2fbSMiaohe Lin FIXTURE_SETUP(memory_failure)
98ff4ef2fbSMiaohe Lin {
99ff4ef2fbSMiaohe Lin 	memset(self, 0, sizeof(*self));
100ff4ef2fbSMiaohe Lin 
101ff4ef2fbSMiaohe Lin 	self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
102ff4ef2fbSMiaohe Lin 
103ff4ef2fbSMiaohe Lin 	memset(&siginfo, 0, sizeof(siginfo));
104ff4ef2fbSMiaohe Lin 	if (setup_sighandler())
105ff4ef2fbSMiaohe Lin 		SKIP(return, "setup sighandler failed.\n");
106ff4ef2fbSMiaohe Lin 
107ff4ef2fbSMiaohe Lin 	self->pagemap_fd = open(pagemap_proc, O_RDONLY);
108ff4ef2fbSMiaohe Lin 	if (self->pagemap_fd == -1)
109ff4ef2fbSMiaohe Lin 		SKIP(return, "open %s failed.\n", pagemap_proc);
110ff4ef2fbSMiaohe Lin 
111ff4ef2fbSMiaohe Lin 	self->kpageflags_fd = open(kpageflags_proc, O_RDONLY);
112ff4ef2fbSMiaohe Lin 	if (self->kpageflags_fd == -1)
113ff4ef2fbSMiaohe Lin 		SKIP(return, "open %s failed.\n", kpageflags_proc);
114ff4ef2fbSMiaohe Lin }
115ff4ef2fbSMiaohe Lin 
teardown_sighandler(void)116ff4ef2fbSMiaohe Lin static void teardown_sighandler(void)
117ff4ef2fbSMiaohe Lin {
118ff4ef2fbSMiaohe Lin 	struct sigaction sa = {
119ff4ef2fbSMiaohe Lin 		.sa_handler = SIG_DFL,
120ff4ef2fbSMiaohe Lin 		.sa_flags = SA_SIGINFO,
121ff4ef2fbSMiaohe Lin 	};
122ff4ef2fbSMiaohe Lin 
123ff4ef2fbSMiaohe Lin 	sigaction(SIGBUS, &sa, NULL);
124ff4ef2fbSMiaohe Lin }
125ff4ef2fbSMiaohe Lin 
FIXTURE_TEARDOWN(memory_failure)126ff4ef2fbSMiaohe Lin FIXTURE_TEARDOWN(memory_failure)
127ff4ef2fbSMiaohe Lin {
128ff4ef2fbSMiaohe Lin 	close(self->kpageflags_fd);
129ff4ef2fbSMiaohe Lin 	close(self->pagemap_fd);
130ff4ef2fbSMiaohe Lin 	teardown_sighandler();
131ff4ef2fbSMiaohe Lin }
132ff4ef2fbSMiaohe Lin 
prepare(struct __test_metadata * _metadata,FIXTURE_DATA (memory_failure)* self,void * vaddr)133ff4ef2fbSMiaohe Lin static void prepare(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure) * self,
134ff4ef2fbSMiaohe Lin 		    void *vaddr)
135ff4ef2fbSMiaohe Lin {
136ff4ef2fbSMiaohe Lin 	self->pfn = pagemap_get_pfn(self->pagemap_fd, vaddr);
137ff4ef2fbSMiaohe Lin 	ASSERT_NE(self->pfn, -1UL);
138ff4ef2fbSMiaohe Lin 
139ff4ef2fbSMiaohe Lin 	ASSERT_EQ(get_hardware_corrupted_size(&self->corrupted_size), 0);
140ff4ef2fbSMiaohe Lin }
141ff4ef2fbSMiaohe Lin 
check_memory(void * vaddr,unsigned long size)142ff4ef2fbSMiaohe Lin static bool check_memory(void *vaddr, unsigned long size)
143ff4ef2fbSMiaohe Lin {
144ff4ef2fbSMiaohe Lin 	char buf[64];
145ff4ef2fbSMiaohe Lin 
146ff4ef2fbSMiaohe Lin 	memset(buf, 0xce, sizeof(buf));
147ff4ef2fbSMiaohe Lin 	while (size >= sizeof(buf)) {
148ff4ef2fbSMiaohe Lin 		if (memcmp(vaddr, buf, sizeof(buf)))
149ff4ef2fbSMiaohe Lin 			return false;
150ff4ef2fbSMiaohe Lin 		size -= sizeof(buf);
151ff4ef2fbSMiaohe Lin 		vaddr += sizeof(buf);
152ff4ef2fbSMiaohe Lin 	}
153ff4ef2fbSMiaohe Lin 
154ff4ef2fbSMiaohe Lin 	return true;
155ff4ef2fbSMiaohe Lin }
156ff4ef2fbSMiaohe Lin 
check(struct __test_metadata * _metadata,FIXTURE_DATA (memory_failure)* self,void * vaddr,enum result_type type,int setjmp)157ff4ef2fbSMiaohe Lin static void check(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure) * self,
158ff4ef2fbSMiaohe Lin 		  void *vaddr, enum result_type type, int setjmp)
159ff4ef2fbSMiaohe Lin {
160ff4ef2fbSMiaohe Lin 	unsigned long size;
161ff4ef2fbSMiaohe Lin 	uint64_t pfn_flags;
162ff4ef2fbSMiaohe Lin 
163ff4ef2fbSMiaohe Lin 	switch (type) {
164ff4ef2fbSMiaohe Lin 	case MADV_SOFT_ANON:
16512e8a2faSMiaohe Lin 	case MADV_HARD_CLEAN_PAGECACHE:
16612e8a2faSMiaohe Lin 	case MADV_SOFT_CLEAN_PAGECACHE:
167*d51b5076SMiaohe Lin 	case MADV_SOFT_DIRTY_PAGECACHE:
168ff4ef2fbSMiaohe Lin 		/* It is not expected to receive a SIGBUS signal. */
169ff4ef2fbSMiaohe Lin 		ASSERT_EQ(setjmp, 0);
170ff4ef2fbSMiaohe Lin 
171ff4ef2fbSMiaohe Lin 		/* The page content should remain unchanged. */
172ff4ef2fbSMiaohe Lin 		ASSERT_TRUE(check_memory(vaddr, self->page_size));
173ff4ef2fbSMiaohe Lin 
174ff4ef2fbSMiaohe Lin 		/* The backing pfn of addr should have changed. */
175ff4ef2fbSMiaohe Lin 		ASSERT_NE(pagemap_get_pfn(self->pagemap_fd, vaddr), self->pfn);
176ff4ef2fbSMiaohe Lin 		break;
177ff4ef2fbSMiaohe Lin 	case MADV_HARD_ANON:
178*d51b5076SMiaohe Lin 	case MADV_HARD_DIRTY_PAGECACHE:
179ff4ef2fbSMiaohe Lin 		/* The SIGBUS signal should have been received. */
180ff4ef2fbSMiaohe Lin 		ASSERT_EQ(setjmp, 1);
181ff4ef2fbSMiaohe Lin 
182ff4ef2fbSMiaohe Lin 		/* Check if siginfo contains correct SIGBUS context. */
183ff4ef2fbSMiaohe Lin 		ASSERT_EQ(siginfo.si_signo, SIGBUS);
184ff4ef2fbSMiaohe Lin 		ASSERT_EQ(siginfo.si_code, BUS_MCEERR_AR);
185ff4ef2fbSMiaohe Lin 		ASSERT_EQ(1UL << siginfo.si_addr_lsb, self->page_size);
186ff4ef2fbSMiaohe Lin 		ASSERT_EQ(siginfo.si_addr, vaddr);
187ff4ef2fbSMiaohe Lin 
188ff4ef2fbSMiaohe Lin 		/* XXX Check backing pte is hwpoison entry when supported. */
189ff4ef2fbSMiaohe Lin 		ASSERT_TRUE(pagemap_is_swapped(self->pagemap_fd, vaddr));
190ff4ef2fbSMiaohe Lin 		break;
191ff4ef2fbSMiaohe Lin 	default:
192ff4ef2fbSMiaohe Lin 		SKIP(return, "unexpected inject type %d.\n", type);
193ff4ef2fbSMiaohe Lin 	}
194ff4ef2fbSMiaohe Lin 
195ff4ef2fbSMiaohe Lin 	/* Check if the value of HardwareCorrupted has increased. */
196ff4ef2fbSMiaohe Lin 	ASSERT_EQ(get_hardware_corrupted_size(&size), 0);
197ff4ef2fbSMiaohe Lin 	ASSERT_EQ(size, self->corrupted_size + self->page_size / 1024);
198ff4ef2fbSMiaohe Lin 
199ff4ef2fbSMiaohe Lin 	/* Check if HWPoison flag is set. */
200ff4ef2fbSMiaohe Lin 	ASSERT_EQ(pageflags_get(self->pfn, self->kpageflags_fd, &pfn_flags), 0);
201ff4ef2fbSMiaohe Lin 	ASSERT_EQ(pfn_flags & KPF_HWPOISON, KPF_HWPOISON);
202ff4ef2fbSMiaohe Lin }
203ff4ef2fbSMiaohe Lin 
cleanup(struct __test_metadata * _metadata,FIXTURE_DATA (memory_failure)* self,void * vaddr)204ff4ef2fbSMiaohe Lin static void cleanup(struct __test_metadata *_metadata, FIXTURE_DATA(memory_failure) * self,
205ff4ef2fbSMiaohe Lin 		    void *vaddr)
206ff4ef2fbSMiaohe Lin {
207ff4ef2fbSMiaohe Lin 	unsigned long size;
208ff4ef2fbSMiaohe Lin 	uint64_t pfn_flags;
209ff4ef2fbSMiaohe Lin 
210ff4ef2fbSMiaohe Lin 	ASSERT_EQ(unpoison_memory(self->pfn), 0);
211ff4ef2fbSMiaohe Lin 
212ff4ef2fbSMiaohe Lin 	/* Check if HWPoison flag is cleared. */
213ff4ef2fbSMiaohe Lin 	ASSERT_EQ(pageflags_get(self->pfn, self->kpageflags_fd, &pfn_flags), 0);
214ff4ef2fbSMiaohe Lin 	ASSERT_NE(pfn_flags & KPF_HWPOISON, KPF_HWPOISON);
215ff4ef2fbSMiaohe Lin 
216ff4ef2fbSMiaohe Lin 	/* Check if the value of HardwareCorrupted has decreased. */
217ff4ef2fbSMiaohe Lin 	ASSERT_EQ(get_hardware_corrupted_size(&size), 0);
218ff4ef2fbSMiaohe Lin 	ASSERT_EQ(size, self->corrupted_size);
219ff4ef2fbSMiaohe Lin }
220ff4ef2fbSMiaohe Lin 
TEST_F(memory_failure,anon)221ff4ef2fbSMiaohe Lin TEST_F(memory_failure, anon)
222ff4ef2fbSMiaohe Lin {
223ff4ef2fbSMiaohe Lin 	char *addr;
224ff4ef2fbSMiaohe Lin 	int ret;
225ff4ef2fbSMiaohe Lin 
226ff4ef2fbSMiaohe Lin 	addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
227ff4ef2fbSMiaohe Lin 		    MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
228ff4ef2fbSMiaohe Lin 	if (addr == MAP_FAILED)
229ff4ef2fbSMiaohe Lin 		SKIP(return, "mmap failed, not enough memory.\n");
230ff4ef2fbSMiaohe Lin 	memset(addr, 0xce, self->page_size);
231ff4ef2fbSMiaohe Lin 
232ff4ef2fbSMiaohe Lin 	prepare(_metadata, self, addr);
233ff4ef2fbSMiaohe Lin 
234ff4ef2fbSMiaohe Lin 	ret = sigsetjmp(signal_jmp_buf, 1);
235ff4ef2fbSMiaohe Lin 	if (!self->triggered) {
236ff4ef2fbSMiaohe Lin 		self->triggered = true;
237ff4ef2fbSMiaohe Lin 		ASSERT_EQ(variant->inject(self, addr), 0);
238ff4ef2fbSMiaohe Lin 		FORCE_READ(*addr);
239ff4ef2fbSMiaohe Lin 	}
240ff4ef2fbSMiaohe Lin 
241ff4ef2fbSMiaohe Lin 	if (variant->type == MADV_HARD)
242ff4ef2fbSMiaohe Lin 		check(_metadata, self, addr, MADV_HARD_ANON, ret);
243ff4ef2fbSMiaohe Lin 	else
244ff4ef2fbSMiaohe Lin 		check(_metadata, self, addr, MADV_SOFT_ANON, ret);
245ff4ef2fbSMiaohe Lin 
246ff4ef2fbSMiaohe Lin 	cleanup(_metadata, self, addr);
247ff4ef2fbSMiaohe Lin 
248ff4ef2fbSMiaohe Lin 	ASSERT_EQ(munmap(addr, self->page_size), 0);
249ff4ef2fbSMiaohe Lin }
250ff4ef2fbSMiaohe Lin 
prepare_file(const char * fname,unsigned long size)251*d51b5076SMiaohe Lin static int prepare_file(const char *fname, unsigned long size)
252*d51b5076SMiaohe Lin {
253*d51b5076SMiaohe Lin 	int fd;
254*d51b5076SMiaohe Lin 
255*d51b5076SMiaohe Lin 	fd = open(fname, O_RDWR | O_CREAT, 0664);
256*d51b5076SMiaohe Lin 	if (fd >= 0) {
257*d51b5076SMiaohe Lin 		unlink(fname);
258*d51b5076SMiaohe Lin 		ftruncate(fd, size);
259*d51b5076SMiaohe Lin 	}
260*d51b5076SMiaohe Lin 	return fd;
261*d51b5076SMiaohe Lin }
262*d51b5076SMiaohe Lin 
26312e8a2faSMiaohe Lin /* Borrowed from mm/gup_longterm.c. */
get_fs_type(int fd)26412e8a2faSMiaohe Lin static int get_fs_type(int fd)
26512e8a2faSMiaohe Lin {
26612e8a2faSMiaohe Lin 	struct statfs fs;
26712e8a2faSMiaohe Lin 	int ret;
26812e8a2faSMiaohe Lin 
26912e8a2faSMiaohe Lin 	do {
27012e8a2faSMiaohe Lin 		ret = fstatfs(fd, &fs);
27112e8a2faSMiaohe Lin 	} while (ret && errno == EINTR);
27212e8a2faSMiaohe Lin 
27312e8a2faSMiaohe Lin 	return ret ? 0 : (int)fs.f_type;
27412e8a2faSMiaohe Lin }
27512e8a2faSMiaohe Lin 
TEST_F(memory_failure,clean_pagecache)27612e8a2faSMiaohe Lin TEST_F(memory_failure, clean_pagecache)
27712e8a2faSMiaohe Lin {
27812e8a2faSMiaohe Lin 	int fd;
27912e8a2faSMiaohe Lin 	char *addr;
28012e8a2faSMiaohe Lin 	int ret;
28112e8a2faSMiaohe Lin 	int fs_type;
28212e8a2faSMiaohe Lin 
283*d51b5076SMiaohe Lin 	fd = prepare_file("./clean-page-cache-test-file", self->page_size);
28412e8a2faSMiaohe Lin 	if (fd < 0)
28512e8a2faSMiaohe Lin 		SKIP(return, "failed to open test file.\n");
28612e8a2faSMiaohe Lin 	fs_type = get_fs_type(fd);
28712e8a2faSMiaohe Lin 	if (!fs_type || fs_type == TMPFS_MAGIC)
28812e8a2faSMiaohe Lin 		SKIP(return, "unsupported filesystem :%x\n", fs_type);
28912e8a2faSMiaohe Lin 
29012e8a2faSMiaohe Lin 	addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
29112e8a2faSMiaohe Lin 		    MAP_SHARED, fd, 0);
29212e8a2faSMiaohe Lin 	if (addr == MAP_FAILED)
29312e8a2faSMiaohe Lin 		SKIP(return, "mmap failed, not enough memory.\n");
29412e8a2faSMiaohe Lin 	memset(addr, 0xce, self->page_size);
29512e8a2faSMiaohe Lin 	fsync(fd);
29612e8a2faSMiaohe Lin 
29712e8a2faSMiaohe Lin 	prepare(_metadata, self, addr);
29812e8a2faSMiaohe Lin 
29912e8a2faSMiaohe Lin 	ret = sigsetjmp(signal_jmp_buf, 1);
30012e8a2faSMiaohe Lin 	if (!self->triggered) {
30112e8a2faSMiaohe Lin 		self->triggered = true;
30212e8a2faSMiaohe Lin 		ASSERT_EQ(variant->inject(self, addr), 0);
30312e8a2faSMiaohe Lin 		FORCE_READ(*addr);
30412e8a2faSMiaohe Lin 	}
30512e8a2faSMiaohe Lin 
30612e8a2faSMiaohe Lin 	if (variant->type == MADV_HARD)
30712e8a2faSMiaohe Lin 		check(_metadata, self, addr, MADV_HARD_CLEAN_PAGECACHE, ret);
30812e8a2faSMiaohe Lin 	else
30912e8a2faSMiaohe Lin 		check(_metadata, self, addr, MADV_SOFT_CLEAN_PAGECACHE, ret);
31012e8a2faSMiaohe Lin 
31112e8a2faSMiaohe Lin 	cleanup(_metadata, self, addr);
31212e8a2faSMiaohe Lin 
31312e8a2faSMiaohe Lin 	ASSERT_EQ(munmap(addr, self->page_size), 0);
31412e8a2faSMiaohe Lin 
31512e8a2faSMiaohe Lin 	ASSERT_EQ(close(fd), 0);
31612e8a2faSMiaohe Lin }
31712e8a2faSMiaohe Lin 
TEST_F(memory_failure,dirty_pagecache)318*d51b5076SMiaohe Lin TEST_F(memory_failure, dirty_pagecache)
319*d51b5076SMiaohe Lin {
320*d51b5076SMiaohe Lin 	int fd;
321*d51b5076SMiaohe Lin 	char *addr;
322*d51b5076SMiaohe Lin 	int ret;
323*d51b5076SMiaohe Lin 	int fs_type;
324*d51b5076SMiaohe Lin 
325*d51b5076SMiaohe Lin 	fd = prepare_file("./dirty-page-cache-test-file", self->page_size);
326*d51b5076SMiaohe Lin 	if (fd < 0)
327*d51b5076SMiaohe Lin 		SKIP(return, "failed to open test file.\n");
328*d51b5076SMiaohe Lin 	fs_type = get_fs_type(fd);
329*d51b5076SMiaohe Lin 	if (!fs_type || fs_type == TMPFS_MAGIC)
330*d51b5076SMiaohe Lin 		SKIP(return, "unsupported filesystem :%x\n", fs_type);
331*d51b5076SMiaohe Lin 
332*d51b5076SMiaohe Lin 	addr = mmap(0, self->page_size, PROT_READ | PROT_WRITE,
333*d51b5076SMiaohe Lin 		    MAP_SHARED, fd, 0);
334*d51b5076SMiaohe Lin 	if (addr == MAP_FAILED)
335*d51b5076SMiaohe Lin 		SKIP(return, "mmap failed, not enough memory.\n");
336*d51b5076SMiaohe Lin 	memset(addr, 0xce, self->page_size);
337*d51b5076SMiaohe Lin 
338*d51b5076SMiaohe Lin 	prepare(_metadata, self, addr);
339*d51b5076SMiaohe Lin 
340*d51b5076SMiaohe Lin 	ret = sigsetjmp(signal_jmp_buf, 1);
341*d51b5076SMiaohe Lin 	if (!self->triggered) {
342*d51b5076SMiaohe Lin 		self->triggered = true;
343*d51b5076SMiaohe Lin 		ASSERT_EQ(variant->inject(self, addr), 0);
344*d51b5076SMiaohe Lin 		FORCE_READ(*addr);
345*d51b5076SMiaohe Lin 	}
346*d51b5076SMiaohe Lin 
347*d51b5076SMiaohe Lin 	if (variant->type == MADV_HARD)
348*d51b5076SMiaohe Lin 		check(_metadata, self, addr, MADV_HARD_DIRTY_PAGECACHE, ret);
349*d51b5076SMiaohe Lin 	else
350*d51b5076SMiaohe Lin 		check(_metadata, self, addr, MADV_SOFT_DIRTY_PAGECACHE, ret);
351*d51b5076SMiaohe Lin 
352*d51b5076SMiaohe Lin 	cleanup(_metadata, self, addr);
353*d51b5076SMiaohe Lin 
354*d51b5076SMiaohe Lin 	ASSERT_EQ(munmap(addr, self->page_size), 0);
355*d51b5076SMiaohe Lin 
356*d51b5076SMiaohe Lin 	ASSERT_EQ(close(fd), 0);
357*d51b5076SMiaohe Lin }
358*d51b5076SMiaohe Lin 
359ff4ef2fbSMiaohe Lin TEST_HARNESS_MAIN
360