1f08c3bdfSopenharmony_ci/*
2f08c3bdfSopenharmony_ci * Test program for Linux poison memory error recovery.
3f08c3bdfSopenharmony_ci * This injects poison into various mapping cases and triggers the poison
4f08c3bdfSopenharmony_ci * handling.  Requires special injection support in the kernel.
5f08c3bdfSopenharmony_ci *
6f08c3bdfSopenharmony_ci * Copyright 2009, 2010 Intel Corporation
7f08c3bdfSopenharmony_ci *
8f08c3bdfSopenharmony_ci * tinjpage is free software; you can redistribute it and/or
9f08c3bdfSopenharmony_ci * modify it under the terms of the GNU General Public
10f08c3bdfSopenharmony_ci * License as published by the Free Software Foundation; version
11f08c3bdfSopenharmony_ci * 2.
12f08c3bdfSopenharmony_ci *
13f08c3bdfSopenharmony_ci * tinjpage is distributed in the hope that it will be useful,
14f08c3bdfSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
15f08c3bdfSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16f08c3bdfSopenharmony_ci * General Public License for more details.
17f08c3bdfSopenharmony_ci *
18f08c3bdfSopenharmony_ci * You should find a copy of v2 of the GNU General Public License somewhere
19f08c3bdfSopenharmony_ci * on your Linux system; if not, write to the Free Software Foundation,
20f08c3bdfSopenharmony_ci * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * Authors: Andi Kleen, Fengguang Wu
23f08c3bdfSopenharmony_ci */
24f08c3bdfSopenharmony_ci#define _GNU_SOURCE 1
25f08c3bdfSopenharmony_ci#include <stdio.h>
26f08c3bdfSopenharmony_ci#include <signal.h>
27f08c3bdfSopenharmony_ci#include <unistd.h>
28f08c3bdfSopenharmony_ci#include <sys/fcntl.h>
29f08c3bdfSopenharmony_ci#include <sys/wait.h>
30f08c3bdfSopenharmony_ci#include <sys/mman.h>
31f08c3bdfSopenharmony_ci#include <stdlib.h>
32f08c3bdfSopenharmony_ci#include <setjmp.h>
33f08c3bdfSopenharmony_ci#include <errno.h>
34f08c3bdfSopenharmony_ci#include <string.h>
35f08c3bdfSopenharmony_ci#include <time.h>
36f08c3bdfSopenharmony_ci#include <pthread.h>
37f08c3bdfSopenharmony_ci#include <sys/ipc.h>
38f08c3bdfSopenharmony_ci#include <sys/shm.h>
39f08c3bdfSopenharmony_ci#include <sys/sem.h>
40f08c3bdfSopenharmony_ci#include "utils.h"
41f08c3bdfSopenharmony_ci#include "hugepage.h"
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci#define MADV_POISON 100
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_ci#define TMPDIR "./"
46f08c3bdfSopenharmony_ci#define PATHBUFLEN 100
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci#define Perror(x) failure++, perror(x)
49f08c3bdfSopenharmony_ci#define PAIR(x) x, sizeof(x)-1
50f08c3bdfSopenharmony_ci#define mb() asm volatile("" ::: "memory")
51f08c3bdfSopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
52f08c3bdfSopenharmony_ci#define cpu_relax() asm volatile("rep ; nop" ::: "memory")
53f08c3bdfSopenharmony_ci#else
54f08c3bdfSopenharmony_ci#define cpu_relax() mb()
55f08c3bdfSopenharmony_ci#endif
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_citypedef unsigned long long u64;
58f08c3bdfSopenharmony_ci
59f08c3bdfSopenharmony_ciint PS;
60f08c3bdfSopenharmony_ciint failure;
61f08c3bdfSopenharmony_ciint unexpected;
62f08c3bdfSopenharmony_ciint early_kill;
63f08c3bdfSopenharmony_ciint test_hugepage;
64f08c3bdfSopenharmony_ci
65f08c3bdfSopenharmony_civoid *checked_mmap(void *start, size_t length, int prot, int flags,
66f08c3bdfSopenharmony_ci                   int fd, off_t offset)
67f08c3bdfSopenharmony_ci{
68f08c3bdfSopenharmony_ci	void *map = mmap(start, length, prot, flags, fd, offset);
69f08c3bdfSopenharmony_ci	if (map == (void*)-1L)
70f08c3bdfSopenharmony_ci		err("mmap");
71f08c3bdfSopenharmony_ci	return map;
72f08c3bdfSopenharmony_ci}
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_civoid munmap_reserve(void *page, int size)
75f08c3bdfSopenharmony_ci{
76f08c3bdfSopenharmony_ci	if (munmap(page, size) < 0)
77f08c3bdfSopenharmony_ci		err("munmap");
78f08c3bdfSopenharmony_ci	if (mmap(page, size, PROT_NONE, MAP_PRIVATE|MAP_FIXED, 0, 0) < 0)
79f08c3bdfSopenharmony_ci		err("mmap2");
80f08c3bdfSopenharmony_ci}
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_civoid *xmalloc(size_t s)
83f08c3bdfSopenharmony_ci{
84f08c3bdfSopenharmony_ci	void *p = malloc(s);
85f08c3bdfSopenharmony_ci	if (!p)
86f08c3bdfSopenharmony_ci		exit(ENOMEM);
87f08c3bdfSopenharmony_ci	return p;
88f08c3bdfSopenharmony_ci}
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_cistatic int ilog2(int n)
91f08c3bdfSopenharmony_ci{
92f08c3bdfSopenharmony_ci	int r = 0;
93f08c3bdfSopenharmony_ci	n--;
94f08c3bdfSopenharmony_ci	while (n) {
95f08c3bdfSopenharmony_ci		n >>= 1;
96f08c3bdfSopenharmony_ci		r++;
97f08c3bdfSopenharmony_ci	}
98f08c3bdfSopenharmony_ci	return r;
99f08c3bdfSopenharmony_ci}
100f08c3bdfSopenharmony_ci
101f08c3bdfSopenharmony_ciint recovercount;
102f08c3bdfSopenharmony_cisigjmp_buf recover_ctx;
103f08c3bdfSopenharmony_cisigjmp_buf early_recover_ctx;
104f08c3bdfSopenharmony_civoid *expected_addr;
105f08c3bdfSopenharmony_ci
106f08c3bdfSopenharmony_ci/* Work around glibc not defining this yet */
107f08c3bdfSopenharmony_cistruct my_siginfo {
108f08c3bdfSopenharmony_ci	int si_signo;
109f08c3bdfSopenharmony_ci	int si_errno;
110f08c3bdfSopenharmony_ci	int si_code;
111f08c3bdfSopenharmony_ci	union {
112f08c3bdfSopenharmony_ci	struct {
113f08c3bdfSopenharmony_ci		void  *_addr; /* faulting insn/memory ref. */
114f08c3bdfSopenharmony_ci#ifdef __ARCH_SI_TRAPNO
115f08c3bdfSopenharmony_ci		int _trapno;	/* TRAP # which caused the signal */
116f08c3bdfSopenharmony_ci#endif
117f08c3bdfSopenharmony_ci		short _addr_lsb; /* LSB of the reported address */
118f08c3bdfSopenharmony_ci	} _sigfault;
119f08c3bdfSopenharmony_ci	} _sifields;
120f08c3bdfSopenharmony_ci};
121f08c3bdfSopenharmony_ci#undef si_addr_lsb
122f08c3bdfSopenharmony_ci#define si_addr_lsb _sifields._sigfault._addr_lsb
123f08c3bdfSopenharmony_ci
124f08c3bdfSopenharmony_civoid sighandler(int sig, siginfo_t *si, void *arg)
125f08c3bdfSopenharmony_ci{
126f08c3bdfSopenharmony_ci	if (si->si_addr != expected_addr) {
127f08c3bdfSopenharmony_ci		printf("XXX: Unexpected address in signal %p (expected %p)\n", si->si_addr,
128f08c3bdfSopenharmony_ci			expected_addr);
129f08c3bdfSopenharmony_ci		failure++;
130f08c3bdfSopenharmony_ci	}
131f08c3bdfSopenharmony_ci
132f08c3bdfSopenharmony_ci	int lsb = ((struct my_siginfo *)si)->si_addr_lsb;
133f08c3bdfSopenharmony_ci	if (test_hugepage) {
134f08c3bdfSopenharmony_ci		if (lsb != ilog2(HPS)) {
135f08c3bdfSopenharmony_ci			printf("LATER: Unexpected addr lsb in siginfo %d\n", lsb);
136f08c3bdfSopenharmony_ci		}
137f08c3bdfSopenharmony_ci	} else {
138f08c3bdfSopenharmony_ci		if (lsb != ilog2(sysconf(_SC_PAGE_SIZE))) {
139f08c3bdfSopenharmony_ci			printf("LATER: Unexpected addr lsb in siginfo %d\n", lsb);
140f08c3bdfSopenharmony_ci		}
141f08c3bdfSopenharmony_ci	}
142f08c3bdfSopenharmony_ci
143f08c3bdfSopenharmony_ci	printf("\tsignal %d code %d addr %p\n", sig, si->si_code, si->si_addr);
144f08c3bdfSopenharmony_ci
145f08c3bdfSopenharmony_ci	if (--recovercount == 0) {
146f08c3bdfSopenharmony_ci		write(1, PAIR("I seem to be in a signal loop. bailing out.\n"));
147f08c3bdfSopenharmony_ci		exit(1);
148f08c3bdfSopenharmony_ci	}
149f08c3bdfSopenharmony_ci
150f08c3bdfSopenharmony_ci	if (si->si_code == 4)
151f08c3bdfSopenharmony_ci		siglongjmp(recover_ctx, 1);
152f08c3bdfSopenharmony_ci	else
153f08c3bdfSopenharmony_ci		siglongjmp(early_recover_ctx, 1);
154f08c3bdfSopenharmony_ci}
155f08c3bdfSopenharmony_ci
156f08c3bdfSopenharmony_cienum rmode {
157f08c3bdfSopenharmony_ci	MREAD = 0,
158f08c3bdfSopenharmony_ci	MWRITE = 1,
159f08c3bdfSopenharmony_ci	MREAD_OK = 2,
160f08c3bdfSopenharmony_ci	MWRITE_OK = 3,
161f08c3bdfSopenharmony_ci	MNOTHING = -1,
162f08c3bdfSopenharmony_ci};
163f08c3bdfSopenharmony_ci
164f08c3bdfSopenharmony_civoid inject_madvise(char *page)
165f08c3bdfSopenharmony_ci{
166f08c3bdfSopenharmony_ci	if (madvise(page, PS, MADV_POISON) != 0) {
167f08c3bdfSopenharmony_ci		if (errno == EINVAL) {
168f08c3bdfSopenharmony_ci			printf("Kernel doesn't support poison injection\n");
169f08c3bdfSopenharmony_ci			exit(0);
170f08c3bdfSopenharmony_ci		}
171f08c3bdfSopenharmony_ci		Perror("madvise");
172f08c3bdfSopenharmony_ci	}
173f08c3bdfSopenharmony_ci}
174f08c3bdfSopenharmony_ci
175f08c3bdfSopenharmony_ciu64 page_to_pfn(char *page)
176f08c3bdfSopenharmony_ci{
177f08c3bdfSopenharmony_ci	static int pagemap_fd = -1;
178f08c3bdfSopenharmony_ci	u64 pfn;
179f08c3bdfSopenharmony_ci
180f08c3bdfSopenharmony_ci	if (pagemap_fd < 0)  {
181f08c3bdfSopenharmony_ci		pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
182f08c3bdfSopenharmony_ci		if (pagemap_fd < 0)
183f08c3bdfSopenharmony_ci			err("/proc/self/pagemap not supported");
184f08c3bdfSopenharmony_ci	}
185f08c3bdfSopenharmony_ci
186f08c3bdfSopenharmony_ci	if (pread(pagemap_fd, &pfn, sizeof(u64),
187f08c3bdfSopenharmony_ci		((u64)page / PS)*sizeof(u64)) != sizeof(u64))
188f08c3bdfSopenharmony_ci		err("Cannot read from pagemap");
189f08c3bdfSopenharmony_ci
190f08c3bdfSopenharmony_ci	pfn &= (1ULL<<56)-1;
191f08c3bdfSopenharmony_ci	return pfn;
192f08c3bdfSopenharmony_ci}
193f08c3bdfSopenharmony_ci
194f08c3bdfSopenharmony_ci/*
195f08c3bdfSopenharmony_ci * Inject Action Optional #MC
196f08c3bdfSopenharmony_ci * with mce-inject using the software injector.
197f08c3bdfSopenharmony_ci *
198f08c3bdfSopenharmony_ci * This tests the low level machine check handler too.
199f08c3bdfSopenharmony_ci *
200f08c3bdfSopenharmony_ci * Slightly racy with page migration because we don't mlock the page.
201f08c3bdfSopenharmony_ci */
202f08c3bdfSopenharmony_civoid inject_mce_inject(char *page)
203f08c3bdfSopenharmony_ci{
204f08c3bdfSopenharmony_ci	u64 pfn = page_to_pfn(page);
205f08c3bdfSopenharmony_ci	FILE *mce_inject;
206f08c3bdfSopenharmony_ci
207f08c3bdfSopenharmony_ci	mce_inject = popen("mce-inject", "w");
208f08c3bdfSopenharmony_ci	if (!mce_inject) {
209f08c3bdfSopenharmony_ci		fprintf(stderr, "Cannot open pipe to mce-inject: %s\n",
210f08c3bdfSopenharmony_ci				strerror(errno));
211f08c3bdfSopenharmony_ci		exit(1);
212f08c3bdfSopenharmony_ci	}
213f08c3bdfSopenharmony_ci
214f08c3bdfSopenharmony_ci	fprintf(mce_inject,
215f08c3bdfSopenharmony_ci		"CPU 0 BANK 3 STATUS UNCORRECTED SRAO 0xc0\n"
216f08c3bdfSopenharmony_ci		"MCGSTATUS RIPV MCIP\n"
217f08c3bdfSopenharmony_ci		"ADDR %#llx\n"
218f08c3bdfSopenharmony_ci		"MISC 0x8c\n"
219f08c3bdfSopenharmony_ci		"RIP 0x73:0x1eadbabe\n", pfn);
220f08c3bdfSopenharmony_ci
221f08c3bdfSopenharmony_ci	if (ferror(mce_inject) || fclose(mce_inject) < 0) {
222f08c3bdfSopenharmony_ci		fprintf(stderr, "mce-inject failed: %s\n", strerror(errno));
223f08c3bdfSopenharmony_ci		exit(1);
224f08c3bdfSopenharmony_ci	}
225f08c3bdfSopenharmony_ci}
226f08c3bdfSopenharmony_ci
227f08c3bdfSopenharmony_civoid (*inject)(char *page) = inject_madvise;
228f08c3bdfSopenharmony_ci
229f08c3bdfSopenharmony_civoid poison(char *msg, char *page, enum rmode mode)
230f08c3bdfSopenharmony_ci{
231f08c3bdfSopenharmony_ci	expected_addr = page;
232f08c3bdfSopenharmony_ci	recovercount = 5;
233f08c3bdfSopenharmony_ci
234f08c3bdfSopenharmony_ci	if (sigsetjmp(early_recover_ctx, 1) == 0) {
235f08c3bdfSopenharmony_ci		inject(page);
236f08c3bdfSopenharmony_ci
237f08c3bdfSopenharmony_ci		if (early_kill && (mode == MWRITE || mode == MREAD)) {
238f08c3bdfSopenharmony_ci			printf("XXX: %s: process is not early killed\n", msg);
239f08c3bdfSopenharmony_ci			failure++;
240f08c3bdfSopenharmony_ci		}
241f08c3bdfSopenharmony_ci
242f08c3bdfSopenharmony_ci		return;
243f08c3bdfSopenharmony_ci	}
244f08c3bdfSopenharmony_ci
245f08c3bdfSopenharmony_ci	if (early_kill) {
246f08c3bdfSopenharmony_ci		if (mode == MREAD_OK || mode == MWRITE_OK) {
247f08c3bdfSopenharmony_ci			printf("XXX: %s: killed\n", msg);
248f08c3bdfSopenharmony_ci			failure++;
249f08c3bdfSopenharmony_ci		} else
250f08c3bdfSopenharmony_ci			printf("\trecovered\n");
251f08c3bdfSopenharmony_ci	}
252f08c3bdfSopenharmony_ci}
253f08c3bdfSopenharmony_ci
254f08c3bdfSopenharmony_civoid recover(char *msg, char *page, enum rmode mode)
255f08c3bdfSopenharmony_ci{
256f08c3bdfSopenharmony_ci	expected_addr = page;
257f08c3bdfSopenharmony_ci	recovercount = 5;
258f08c3bdfSopenharmony_ci
259f08c3bdfSopenharmony_ci	if (sigsetjmp(recover_ctx, 1) == 0) {
260f08c3bdfSopenharmony_ci		switch (mode) {
261f08c3bdfSopenharmony_ci		case MWRITE:
262f08c3bdfSopenharmony_ci			printf("\twriting 2\n");
263f08c3bdfSopenharmony_ci			*page = 2;
264f08c3bdfSopenharmony_ci			break;
265f08c3bdfSopenharmony_ci		case MWRITE_OK:
266f08c3bdfSopenharmony_ci			printf("\twriting 4\n");
267f08c3bdfSopenharmony_ci			*page = 4;
268f08c3bdfSopenharmony_ci			return;
269f08c3bdfSopenharmony_ci		case MREAD:
270f08c3bdfSopenharmony_ci			printf("\treading %x\n", *(unsigned char *)page);
271f08c3bdfSopenharmony_ci			break;
272f08c3bdfSopenharmony_ci		case MREAD_OK:
273f08c3bdfSopenharmony_ci			printf("\treading %x\n", *(unsigned char *)page);
274f08c3bdfSopenharmony_ci			return;
275f08c3bdfSopenharmony_ci		case MNOTHING:
276f08c3bdfSopenharmony_ci			return;
277f08c3bdfSopenharmony_ci		}
278f08c3bdfSopenharmony_ci		/* signal or kill should have happened */
279f08c3bdfSopenharmony_ci		printf("XXX: %s: page not poisoned after injection\n", msg);
280f08c3bdfSopenharmony_ci		failure++;
281f08c3bdfSopenharmony_ci		return;
282f08c3bdfSopenharmony_ci	}
283f08c3bdfSopenharmony_ci	if (mode == MREAD_OK || mode == MWRITE_OK) {
284f08c3bdfSopenharmony_ci		printf("XXX: %s: killed\n", msg);
285f08c3bdfSopenharmony_ci		failure++;
286f08c3bdfSopenharmony_ci	} else
287f08c3bdfSopenharmony_ci		printf("\trecovered\n");
288f08c3bdfSopenharmony_ci}
289f08c3bdfSopenharmony_ci
290f08c3bdfSopenharmony_civoid testmem(char *msg, char *page, enum rmode mode)
291f08c3bdfSopenharmony_ci{
292f08c3bdfSopenharmony_ci	printf("\t%s poisoning page %p\n", msg, page);
293f08c3bdfSopenharmony_ci	poison(msg, page, mode);
294f08c3bdfSopenharmony_ci	recover(msg, page, mode);
295f08c3bdfSopenharmony_ci}
296f08c3bdfSopenharmony_ci
297f08c3bdfSopenharmony_civoid expecterr(char *msg, int err)
298f08c3bdfSopenharmony_ci{
299f08c3bdfSopenharmony_ci	if (err) {
300f08c3bdfSopenharmony_ci		printf("\texpected error %d on %s\n", errno, msg);
301f08c3bdfSopenharmony_ci	} else {
302f08c3bdfSopenharmony_ci		failure++;
303f08c3bdfSopenharmony_ci		printf("XXX: unexpected no error on %s\n", msg);
304f08c3bdfSopenharmony_ci	}
305f08c3bdfSopenharmony_ci}
306f08c3bdfSopenharmony_ci
307f08c3bdfSopenharmony_ci/*
308f08c3bdfSopenharmony_ci * Any optional error is really a deficiency in the kernel VFS error reporting
309f08c3bdfSopenharmony_ci * and should be eventually fixed and turned into a expecterr
310f08c3bdfSopenharmony_ci */
311f08c3bdfSopenharmony_civoid optionalerr(char *msg, int err)
312f08c3bdfSopenharmony_ci{
313f08c3bdfSopenharmony_ci	if (err) {
314f08c3bdfSopenharmony_ci		printf("\texpected optional error %d on %s\n", errno, msg);
315f08c3bdfSopenharmony_ci	} else {
316f08c3bdfSopenharmony_ci		unexpected++;
317f08c3bdfSopenharmony_ci		printf("LATER: expected likely incorrect no error on %s\n", msg);
318f08c3bdfSopenharmony_ci	}
319f08c3bdfSopenharmony_ci}
320f08c3bdfSopenharmony_ci
321f08c3bdfSopenharmony_cistatic int tmpcount;
322f08c3bdfSopenharmony_ciint tempfd(void)
323f08c3bdfSopenharmony_ci{
324f08c3bdfSopenharmony_ci	int fd;
325f08c3bdfSopenharmony_ci	char buf[PATHBUFLEN];
326f08c3bdfSopenharmony_ci	snprintf(buf, sizeof buf, TMPDIR "~poison%d",tmpcount++);
327f08c3bdfSopenharmony_ci	fd = open(buf, O_CREAT|O_RDWR, 0600);
328f08c3bdfSopenharmony_ci	if (fd >= 0)
329f08c3bdfSopenharmony_ci		unlink(buf);
330f08c3bdfSopenharmony_ci	if (fd < 0)
331f08c3bdfSopenharmony_ci		err("opening temporary file in " TMPDIR);
332f08c3bdfSopenharmony_ci	return fd;
333f08c3bdfSopenharmony_ci}
334f08c3bdfSopenharmony_ci
335f08c3bdfSopenharmony_ciint playfile(char *buf)
336f08c3bdfSopenharmony_ci{
337f08c3bdfSopenharmony_ci	int fd;
338f08c3bdfSopenharmony_ci	if (buf[0] == 0)
339f08c3bdfSopenharmony_ci		snprintf(buf, PATHBUFLEN, TMPDIR "~poison%d", tmpcount++);
340f08c3bdfSopenharmony_ci	fd = open(buf, O_CREAT|O_RDWR|O_TRUNC, 0600);
341f08c3bdfSopenharmony_ci	if (fd < 0)
342f08c3bdfSopenharmony_ci		err("opening temporary file in " TMPDIR);
343f08c3bdfSopenharmony_ci
344f08c3bdfSopenharmony_ci	const int NPAGES = 5;
345f08c3bdfSopenharmony_ci	char *tmp = xmalloc(PS * NPAGES);
346f08c3bdfSopenharmony_ci	int i;
347f08c3bdfSopenharmony_ci	for (i = 0; i < PS*NPAGES; i++)
348f08c3bdfSopenharmony_ci		tmp[i] = i;
349f08c3bdfSopenharmony_ci	write(fd, tmp, PS*NPAGES);
350f08c3bdfSopenharmony_ci
351f08c3bdfSopenharmony_ci	lseek(fd, 0, SEEK_SET);
352f08c3bdfSopenharmony_ci	return fd;
353f08c3bdfSopenharmony_ci}
354f08c3bdfSopenharmony_ci
355f08c3bdfSopenharmony_cistatic void dirty_anonymous(void)
356f08c3bdfSopenharmony_ci{
357f08c3bdfSopenharmony_ci	char *page;
358f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, 0, 0);
359f08c3bdfSopenharmony_ci	testmem("dirty", page, MWRITE);
360f08c3bdfSopenharmony_ci}
361f08c3bdfSopenharmony_ci
362f08c3bdfSopenharmony_cistatic void dirty_anonymous_unmap(void)
363f08c3bdfSopenharmony_ci{
364f08c3bdfSopenharmony_ci	char *page;
365f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, 0, 0);
366f08c3bdfSopenharmony_ci	testmem("dirty", page, MWRITE);
367f08c3bdfSopenharmony_ci	munmap_reserve(page, PS);
368f08c3bdfSopenharmony_ci}
369f08c3bdfSopenharmony_ci
370f08c3bdfSopenharmony_cistatic void mlocked_anonymous(void)
371f08c3bdfSopenharmony_ci{
372f08c3bdfSopenharmony_ci	char *page;
373f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, 0, 0);
374f08c3bdfSopenharmony_ci	testmem("mlocked", page, MWRITE);
375f08c3bdfSopenharmony_ci}
376f08c3bdfSopenharmony_ci
377f08c3bdfSopenharmony_cistatic void do_file_clean(int flags, char *name)
378f08c3bdfSopenharmony_ci{
379f08c3bdfSopenharmony_ci	char *page;
380f08c3bdfSopenharmony_ci	char fn[30];
381f08c3bdfSopenharmony_ci	snprintf(fn, 30, TMPDIR "~test%d", tmpcount++);
382f08c3bdfSopenharmony_ci	int fd = open(fn, O_RDWR|O_TRUNC|O_CREAT);
383f08c3bdfSopenharmony_ci	if (fd < 0)
384f08c3bdfSopenharmony_ci		err("open temp file");
385f08c3bdfSopenharmony_ci	write(fd, fn, 4);
386f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED|flags,
387f08c3bdfSopenharmony_ci		fd, 0);
388f08c3bdfSopenharmony_ci	fsync(fd);
389f08c3bdfSopenharmony_ci	close(fd);
390f08c3bdfSopenharmony_ci	testmem(name, page, MREAD_OK);
391f08c3bdfSopenharmony_ci	 /* reread page from disk */
392f08c3bdfSopenharmony_ci	printf("\t reading %x\n", *(unsigned char *)page);
393f08c3bdfSopenharmony_ci	testmem(name, page, MWRITE_OK);
394f08c3bdfSopenharmony_ci}
395f08c3bdfSopenharmony_ci
396f08c3bdfSopenharmony_cistatic void file_clean(void)
397f08c3bdfSopenharmony_ci{
398f08c3bdfSopenharmony_ci	do_file_clean(0, "file clean");
399f08c3bdfSopenharmony_ci}
400f08c3bdfSopenharmony_ci
401f08c3bdfSopenharmony_cistatic void file_clean_mlocked(void)
402f08c3bdfSopenharmony_ci{
403f08c3bdfSopenharmony_ci	do_file_clean(MAP_LOCKED, "file clean mlocked");
404f08c3bdfSopenharmony_ci}
405f08c3bdfSopenharmony_ci
406f08c3bdfSopenharmony_cistatic char *ndesc(char *buf, char *name, char *add)
407f08c3bdfSopenharmony_ci{
408f08c3bdfSopenharmony_ci	snprintf(buf, 100, "%s %s", name, add);
409f08c3bdfSopenharmony_ci	return buf;
410f08c3bdfSopenharmony_ci}
411f08c3bdfSopenharmony_ci
412f08c3bdfSopenharmony_cistatic void do_file_dirty(int flags, char *name)
413f08c3bdfSopenharmony_ci{
414f08c3bdfSopenharmony_ci	char nbuf[100];
415f08c3bdfSopenharmony_ci	char *page;
416f08c3bdfSopenharmony_ci	char fn[PATHBUFLEN];
417f08c3bdfSopenharmony_ci	fn[0] = 0;
418f08c3bdfSopenharmony_ci	int fd = playfile(fn);
419f08c3bdfSopenharmony_ci
420f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ,
421f08c3bdfSopenharmony_ci			MAP_SHARED|MAP_POPULATE|flags, fd, 0);
422f08c3bdfSopenharmony_ci	testmem(ndesc(nbuf, name, "initial"), page, MREAD);
423f08c3bdfSopenharmony_ci	expecterr("msync expect error", msync(page, PS, MS_SYNC) < 0);
424f08c3bdfSopenharmony_ci	close(fd);
425f08c3bdfSopenharmony_ci	munmap_reserve(page, PS);
426f08c3bdfSopenharmony_ci
427f08c3bdfSopenharmony_ci	fd = open(fn, O_RDONLY);
428f08c3bdfSopenharmony_ci	if (fd < 0) err("reopening temp file");
429f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ, MAP_SHARED|MAP_POPULATE|flags,
430f08c3bdfSopenharmony_ci				fd, 0);
431f08c3bdfSopenharmony_ci	recover(ndesc(nbuf, name, "populated"), page, MREAD_OK);
432f08c3bdfSopenharmony_ci	close(fd);
433f08c3bdfSopenharmony_ci	munmap_reserve(page, PS);
434f08c3bdfSopenharmony_ci
435f08c3bdfSopenharmony_ci	fd = open(fn, O_RDONLY);
436f08c3bdfSopenharmony_ci	if (fd < 0) err("reopening temp file");
437f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ, MAP_SHARED|flags, fd, 0);
438f08c3bdfSopenharmony_ci	recover(ndesc(nbuf, name, "fault"), page, MREAD_OK);
439f08c3bdfSopenharmony_ci	close(fd);
440f08c3bdfSopenharmony_ci	munmap_reserve(page, PS);
441f08c3bdfSopenharmony_ci
442f08c3bdfSopenharmony_ci	fd = open(fn, O_RDWR);
443f08c3bdfSopenharmony_ci	char buf[128];
444f08c3bdfSopenharmony_ci	/* the earlier close has eaten the error */
445f08c3bdfSopenharmony_ci	optionalerr("explicit read after poison", read(fd, buf, sizeof buf) < 0);
446f08c3bdfSopenharmony_ci	optionalerr("explicit write after poison", write(fd, "foobar", 6) < 0);
447f08c3bdfSopenharmony_ci	optionalerr("fsync expect error", fsync(fd) < 0);
448f08c3bdfSopenharmony_ci	close(fd);
449f08c3bdfSopenharmony_ci
450f08c3bdfSopenharmony_ci	/* should unlink return an error here? */
451f08c3bdfSopenharmony_ci	if (unlink(fn) < 0)
452f08c3bdfSopenharmony_ci		perror("unlink");
453f08c3bdfSopenharmony_ci}
454f08c3bdfSopenharmony_ci
455f08c3bdfSopenharmony_cistatic void file_dirty(void)
456f08c3bdfSopenharmony_ci{
457f08c3bdfSopenharmony_ci	do_file_dirty(0, "file dirty");
458f08c3bdfSopenharmony_ci}
459f08c3bdfSopenharmony_ci
460f08c3bdfSopenharmony_cistatic void file_dirty_mlocked(void)
461f08c3bdfSopenharmony_ci{
462f08c3bdfSopenharmony_ci	do_file_dirty(MAP_LOCKED, "file dirty mlocked");
463f08c3bdfSopenharmony_ci}
464f08c3bdfSopenharmony_ci
465f08c3bdfSopenharmony_ci/* TBD */
466f08c3bdfSopenharmony_cistatic void file_hole(void)
467f08c3bdfSopenharmony_ci{
468f08c3bdfSopenharmony_ci	int fd = tempfd();
469f08c3bdfSopenharmony_ci	char *page;
470f08c3bdfSopenharmony_ci
471f08c3bdfSopenharmony_ci	ftruncate(fd, PS);
472f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
473f08c3bdfSopenharmony_ci	*page = 1;
474f08c3bdfSopenharmony_ci	testmem("hole file dirty", page, MREAD);
475f08c3bdfSopenharmony_ci	/* hole error reporting doesn't work in kernel currently, so optional */
476f08c3bdfSopenharmony_ci	optionalerr("hole fsync expect error", fsync(fd) < 0);
477f08c3bdfSopenharmony_ci	optionalerr("hole msync expect error", msync(page, PS, MS_SYNC) < 0);
478f08c3bdfSopenharmony_ci	close(fd);
479f08c3bdfSopenharmony_ci}
480f08c3bdfSopenharmony_ci
481f08c3bdfSopenharmony_cistatic void nonlinear(void)
482f08c3bdfSopenharmony_ci{
483f08c3bdfSopenharmony_ci	int fd;
484f08c3bdfSopenharmony_ci	const int NPAGES = 10;
485f08c3bdfSopenharmony_ci	int i;
486f08c3bdfSopenharmony_ci	char *page;
487f08c3bdfSopenharmony_ci	char *tmp;
488f08c3bdfSopenharmony_ci
489f08c3bdfSopenharmony_ci	fd = tempfd();
490f08c3bdfSopenharmony_ci	tmp = xmalloc(PS);
491f08c3bdfSopenharmony_ci	for (i = 0; i < NPAGES; i++)  {
492f08c3bdfSopenharmony_ci		memset(tmp, i, PS);
493f08c3bdfSopenharmony_ci		write(fd, tmp, PS);
494f08c3bdfSopenharmony_ci	}
495f08c3bdfSopenharmony_ci	free(tmp);
496f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS*NPAGES, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
497f08c3bdfSopenharmony_ci	int k = NPAGES - 1;
498f08c3bdfSopenharmony_ci	for (i = 0; i < NPAGES; i++, k--) {
499f08c3bdfSopenharmony_ci		if (remap_file_pages(page + i*PS, PS, 0, k, 0))
500f08c3bdfSopenharmony_ci			perror("remap_file_pages");
501f08c3bdfSopenharmony_ci	}
502f08c3bdfSopenharmony_ci	*page = 1;
503f08c3bdfSopenharmony_ci	testmem("rfp file dirty", page, MREAD);
504f08c3bdfSopenharmony_ci	expecterr("rfp fsync expect error", fsync(fd) < 0);
505f08c3bdfSopenharmony_ci	optionalerr("rfp msync expect error", msync(page, PS, MS_SYNC) < 0);
506f08c3bdfSopenharmony_ci	close(fd);
507f08c3bdfSopenharmony_ci}
508f08c3bdfSopenharmony_ci
509f08c3bdfSopenharmony_ci/*
510f08c3bdfSopenharmony_ci * These tests are currently too racy to be enabled.
511f08c3bdfSopenharmony_ci */
512f08c3bdfSopenharmony_ci
513f08c3bdfSopenharmony_ci/*
514f08c3bdfSopenharmony_ci * This is quite timing dependent. The sniper might hit the page
515f08c3bdfSopenharmony_ci * before it is dirtied. If that happens tweak the delay
516f08c3bdfSopenharmony_ci * (should auto tune)
517f08c3bdfSopenharmony_ci */
518f08c3bdfSopenharmony_cienum {
519f08c3bdfSopenharmony_ci	DELAY_NS = 30,
520f08c3bdfSopenharmony_ci};
521f08c3bdfSopenharmony_ci
522f08c3bdfSopenharmony_civolatile enum sstate { START, WAITING, SNIPE } sstate;
523f08c3bdfSopenharmony_ci
524f08c3bdfSopenharmony_civoid waitfor(enum sstate w, enum sstate s)
525f08c3bdfSopenharmony_ci{
526f08c3bdfSopenharmony_ci	sstate = w;
527f08c3bdfSopenharmony_ci	mb();
528f08c3bdfSopenharmony_ci	while (sstate != s)
529f08c3bdfSopenharmony_ci		cpu_relax();
530f08c3bdfSopenharmony_ci}
531f08c3bdfSopenharmony_ci
532f08c3bdfSopenharmony_cistruct poison_arg {
533f08c3bdfSopenharmony_ci	char *msg;
534f08c3bdfSopenharmony_ci	char *page;
535f08c3bdfSopenharmony_ci	enum rmode mode;
536f08c3bdfSopenharmony_ci};
537f08c3bdfSopenharmony_ci
538f08c3bdfSopenharmony_civoid *sniper(void *p)
539f08c3bdfSopenharmony_ci{
540f08c3bdfSopenharmony_ci	struct poison_arg *arg = p;
541f08c3bdfSopenharmony_ci
542f08c3bdfSopenharmony_ci	waitfor(START, WAITING);
543f08c3bdfSopenharmony_ci	nanosleep(&((struct timespec) { .tv_nsec = DELAY_NS }), NULL);
544f08c3bdfSopenharmony_ci	poison(arg->msg, arg->page, arg->mode);
545f08c3bdfSopenharmony_ci	return NULL;
546f08c3bdfSopenharmony_ci}
547f08c3bdfSopenharmony_ci
548f08c3bdfSopenharmony_ciint setup_sniper(struct poison_arg *arg)
549f08c3bdfSopenharmony_ci{
550f08c3bdfSopenharmony_ci	if (sysconf(_SC_NPROCESSORS_ONLN) < 2)  {
551f08c3bdfSopenharmony_ci		printf("%s: Need at least two CPUs. Not tested\n", arg->msg);
552f08c3bdfSopenharmony_ci		return -1;
553f08c3bdfSopenharmony_ci	}
554f08c3bdfSopenharmony_ci	sstate = START;
555f08c3bdfSopenharmony_ci	mb();
556f08c3bdfSopenharmony_ci	pthread_t thr;
557f08c3bdfSopenharmony_ci	if (pthread_create(&thr, NULL, sniper, arg) < 0)
558f08c3bdfSopenharmony_ci		err("pthread_create");
559f08c3bdfSopenharmony_ci	pthread_detach(thr);
560f08c3bdfSopenharmony_ci	return 0;
561f08c3bdfSopenharmony_ci}
562f08c3bdfSopenharmony_ci
563f08c3bdfSopenharmony_cistatic void under_io_dirty(void)
564f08c3bdfSopenharmony_ci{
565f08c3bdfSopenharmony_ci	struct poison_arg arg;
566f08c3bdfSopenharmony_ci	int fd = tempfd();
567f08c3bdfSopenharmony_ci	char *page;
568f08c3bdfSopenharmony_ci
569f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, fd, 0);
570f08c3bdfSopenharmony_ci
571f08c3bdfSopenharmony_ci	arg.page = page;
572f08c3bdfSopenharmony_ci	arg.msg  = "under io dirty";
573f08c3bdfSopenharmony_ci	arg.mode = MWRITE;
574f08c3bdfSopenharmony_ci	if (setup_sniper(&arg) < 0)
575f08c3bdfSopenharmony_ci		return;
576f08c3bdfSopenharmony_ci
577f08c3bdfSopenharmony_ci	write(fd, "xyz", 3);
578f08c3bdfSopenharmony_ci	waitfor(WAITING, WAITING);
579f08c3bdfSopenharmony_ci	expecterr("write under io", fsync(fd) < 0);
580f08c3bdfSopenharmony_ci	close(fd);
581f08c3bdfSopenharmony_ci}
582f08c3bdfSopenharmony_ci
583f08c3bdfSopenharmony_cistatic void under_io_clean(void)
584f08c3bdfSopenharmony_ci{
585f08c3bdfSopenharmony_ci	struct poison_arg arg;
586f08c3bdfSopenharmony_ci	char fn[PATHBUFLEN];
587f08c3bdfSopenharmony_ci	int fd;
588f08c3bdfSopenharmony_ci	char *page;
589f08c3bdfSopenharmony_ci	char buf[10];
590f08c3bdfSopenharmony_ci
591f08c3bdfSopenharmony_ci 	fd = playfile(fn);
592f08c3bdfSopenharmony_ci	page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, fd, 0);
593f08c3bdfSopenharmony_ci	madvise(page, PS, MADV_DONTNEED);
594f08c3bdfSopenharmony_ci
595f08c3bdfSopenharmony_ci	arg.page = page;
596f08c3bdfSopenharmony_ci	arg.msg  = "under io clean";
597f08c3bdfSopenharmony_ci	arg.mode = MREAD_OK;
598f08c3bdfSopenharmony_ci	if (setup_sniper(&arg) < 0)
599f08c3bdfSopenharmony_ci		return;
600f08c3bdfSopenharmony_ci
601f08c3bdfSopenharmony_ci	waitfor(WAITING, WAITING);
602f08c3bdfSopenharmony_ci	// what is correct here?
603f08c3bdfSopenharmony_ci	if (pread(fd, buf, 10, 0) != 0)
604f08c3bdfSopenharmony_ci		perror("pread under io clean");
605f08c3bdfSopenharmony_ci	close(fd);
606f08c3bdfSopenharmony_ci}
607f08c3bdfSopenharmony_ci
608f08c3bdfSopenharmony_ci/*
609f08c3bdfSopenharmony_ci * semaphore get/put wrapper
610f08c3bdfSopenharmony_ci */
611f08c3bdfSopenharmony_ciint get_semaphore(int sem_id, struct sembuf *sembuffer)
612f08c3bdfSopenharmony_ci{
613f08c3bdfSopenharmony_ci	sembuffer->sem_num = 0;
614f08c3bdfSopenharmony_ci	sembuffer->sem_op  = -1;
615f08c3bdfSopenharmony_ci	sembuffer->sem_flg = SEM_UNDO;
616f08c3bdfSopenharmony_ci	return semop(sem_id, sembuffer, 1);
617f08c3bdfSopenharmony_ci}
618f08c3bdfSopenharmony_ci
619f08c3bdfSopenharmony_ciint put_semaphore(int sem_id, struct sembuf *sembuffer)
620f08c3bdfSopenharmony_ci{
621f08c3bdfSopenharmony_ci	sembuffer->sem_num = 0;
622f08c3bdfSopenharmony_ci	sembuffer->sem_op  = 1;
623f08c3bdfSopenharmony_ci	sembuffer->sem_flg = SEM_UNDO;
624f08c3bdfSopenharmony_ci	return semop(sem_id, sembuffer, 1);
625f08c3bdfSopenharmony_ci}
626f08c3bdfSopenharmony_ci
627f08c3bdfSopenharmony_ci/* memory sharing mode */
628f08c3bdfSopenharmony_cienum shared_mode {
629f08c3bdfSopenharmony_ci	MMAP_SHARED = 0,
630f08c3bdfSopenharmony_ci	IPV_SHARED  = 1,
631f08c3bdfSopenharmony_ci};
632f08c3bdfSopenharmony_ci
633f08c3bdfSopenharmony_ci/*
634f08c3bdfSopenharmony_ci * testcase for shared pages, where
635f08c3bdfSopenharmony_ci *  if early_kill == 0, parent access the shared page hwpoisoned by child, and
636f08c3bdfSopenharmony_ci *  if early_kill == 1, parent will be killed by SIGBUS from child.
637f08c3bdfSopenharmony_ci * This testcase checks whether if a shared page is hwpoisoned by one process,
638f08c3bdfSopenharmony_ci * another process sharing the page will be killed expectedly.
639f08c3bdfSopenharmony_ci */
640f08c3bdfSopenharmony_cistatic void do_shared(int shared_mode)
641f08c3bdfSopenharmony_ci{
642f08c3bdfSopenharmony_ci	int shm_id = -1, sem_id = -1, semaphore;
643f08c3bdfSopenharmony_ci	pid_t pid;
644f08c3bdfSopenharmony_ci	char *shared_page = NULL;
645f08c3bdfSopenharmony_ci	struct sembuf sembuffer;
646f08c3bdfSopenharmony_ci
647f08c3bdfSopenharmony_ci	if (shared_mode == MMAP_SHARED) {
648f08c3bdfSopenharmony_ci		shared_page = checked_mmap(NULL, PS, PROT_READ|PROT_WRITE,
649f08c3bdfSopenharmony_ci				MAP_SHARED|MAP_ANONYMOUS|MAP_POPULATE, 0, 0);
650f08c3bdfSopenharmony_ci	} else if (shared_mode == IPV_SHARED) {
651f08c3bdfSopenharmony_ci		shm_id = shmget(IPC_PRIVATE, PS, 0666|IPC_CREAT);
652f08c3bdfSopenharmony_ci		if (shm_id == -1)
653f08c3bdfSopenharmony_ci			err("shmget");
654f08c3bdfSopenharmony_ci	} else {
655f08c3bdfSopenharmony_ci		printf("XXX: invalid shared_mode\n");
656f08c3bdfSopenharmony_ci		return;
657f08c3bdfSopenharmony_ci	}
658f08c3bdfSopenharmony_ci
659f08c3bdfSopenharmony_ci	if (early_kill) {
660f08c3bdfSopenharmony_ci		sem_id = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT);
661f08c3bdfSopenharmony_ci		if (sem_id == -1) {
662f08c3bdfSopenharmony_ci			perror("semget");
663f08c3bdfSopenharmony_ci			goto cleanup;
664f08c3bdfSopenharmony_ci		}
665f08c3bdfSopenharmony_ci		semaphore = semctl(sem_id, 0, SETVAL, 1);
666f08c3bdfSopenharmony_ci		if (semaphore == -1) {
667f08c3bdfSopenharmony_ci			perror("semctl");
668f08c3bdfSopenharmony_ci			goto cleanup;
669f08c3bdfSopenharmony_ci		}
670f08c3bdfSopenharmony_ci		if (get_semaphore(sem_id, &sembuffer)) {
671f08c3bdfSopenharmony_ci			perror("get_semaphore");
672f08c3bdfSopenharmony_ci			goto cleanup;
673f08c3bdfSopenharmony_ci		}
674f08c3bdfSopenharmony_ci	}
675f08c3bdfSopenharmony_ci
676f08c3bdfSopenharmony_ci	pid = fork();
677f08c3bdfSopenharmony_ci	if (pid < 0) {
678f08c3bdfSopenharmony_ci		perror("fork");
679f08c3bdfSopenharmony_ci		goto cleanup;
680f08c3bdfSopenharmony_ci	}
681f08c3bdfSopenharmony_ci
682f08c3bdfSopenharmony_ci	if (shared_mode == IPV_SHARED) {
683f08c3bdfSopenharmony_ci		shared_page = shmat(shm_id, NULL, 0);
684f08c3bdfSopenharmony_ci		if (shared_page == (char *)-1) {
685f08c3bdfSopenharmony_ci			perror("shmat");
686f08c3bdfSopenharmony_ci			goto cleanup;
687f08c3bdfSopenharmony_ci		}
688f08c3bdfSopenharmony_ci	}
689f08c3bdfSopenharmony_ci
690f08c3bdfSopenharmony_ci	memset(shared_page, 'a', 3);
691f08c3bdfSopenharmony_ci
692f08c3bdfSopenharmony_ci	if (early_kill) {
693f08c3bdfSopenharmony_ci		struct sigaction sa = {
694f08c3bdfSopenharmony_ci			.sa_sigaction = sighandler,
695f08c3bdfSopenharmony_ci			.sa_flags = SA_SIGINFO
696f08c3bdfSopenharmony_ci		};
697f08c3bdfSopenharmony_ci		sigaction(SIGBUS, &sa, NULL);
698f08c3bdfSopenharmony_ci		expected_addr = shared_page;
699f08c3bdfSopenharmony_ci	}
700f08c3bdfSopenharmony_ci
701f08c3bdfSopenharmony_ci	if (pid) {
702f08c3bdfSopenharmony_ci		siginfo_t sig;
703f08c3bdfSopenharmony_ci
704f08c3bdfSopenharmony_ci		if (early_kill && sigsetjmp(early_recover_ctx, 1) == 0) {
705f08c3bdfSopenharmony_ci			if (put_semaphore(sem_id, &sembuffer))
706f08c3bdfSopenharmony_ci				err("get_semaphore");
707f08c3bdfSopenharmony_ci			/* waiting for SIGBUS from child */
708f08c3bdfSopenharmony_ci			sleep(10);
709f08c3bdfSopenharmony_ci			printf("XXX timeout: child process does not send signal\n");
710f08c3bdfSopenharmony_ci			failure++;
711f08c3bdfSopenharmony_ci			goto cleanup;
712f08c3bdfSopenharmony_ci		}
713f08c3bdfSopenharmony_ci		waitid(P_PID, pid, &sig, WEXITED);
714f08c3bdfSopenharmony_ci
715f08c3bdfSopenharmony_ci		/*
716f08c3bdfSopenharmony_ci		 * check child termination status
717f08c3bdfSopenharmony_ci		 * late kill       : child should exit
718f08c3bdfSopenharmony_ci		 * suicide version : child should be killed by signal
719f08c3bdfSopenharmony_ci		 * early kill      : child should be killed by signal
720f08c3bdfSopenharmony_ci		 */
721f08c3bdfSopenharmony_ci		if (!early_kill) {
722f08c3bdfSopenharmony_ci			struct sigaction sigact;
723f08c3bdfSopenharmony_ci			sigaction(SIGBUS, NULL, &sigact);
724f08c3bdfSopenharmony_ci
725f08c3bdfSopenharmony_ci			if (sigact.sa_handler == SIG_DFL) {/* suicide version */
726f08c3bdfSopenharmony_ci				if (sig.si_code != CLD_KILLED)
727f08c3bdfSopenharmony_ci					goto child_error;
728f08c3bdfSopenharmony_ci			} else { /* late kill */
729f08c3bdfSopenharmony_ci				if (sig.si_code != CLD_EXITED)
730f08c3bdfSopenharmony_ci					goto child_error;
731f08c3bdfSopenharmony_ci			}
732f08c3bdfSopenharmony_ci		} else { /* early kill */
733f08c3bdfSopenharmony_ci			if (sig.si_code != CLD_EXITED)
734f08c3bdfSopenharmony_ci				goto child_error;
735f08c3bdfSopenharmony_ci		}
736f08c3bdfSopenharmony_ci
737f08c3bdfSopenharmony_ci		if (!early_kill)
738f08c3bdfSopenharmony_ci			recover("ipv shared page (parent)",
739f08c3bdfSopenharmony_ci				shared_page, MWRITE);
740f08c3bdfSopenharmony_ci
741f08c3bdfSopenharmony_ci		if (shared_mode == IPV_SHARED && shmdt(shared_page) == -1) {
742f08c3bdfSopenharmony_ci			perror("shmdt");
743f08c3bdfSopenharmony_ci			goto cleanup;
744f08c3bdfSopenharmony_ci		}
745f08c3bdfSopenharmony_ci	}
746f08c3bdfSopenharmony_ci
747f08c3bdfSopenharmony_ci	if (!pid) {
748f08c3bdfSopenharmony_ci		failure = 0;
749f08c3bdfSopenharmony_ci
750f08c3bdfSopenharmony_ci		if (early_kill)
751f08c3bdfSopenharmony_ci			if (get_semaphore(sem_id, &sembuffer))
752f08c3bdfSopenharmony_ci				err("get_semaphore");
753f08c3bdfSopenharmony_ci		testmem("ipv shared page", shared_page, MWRITE);
754f08c3bdfSopenharmony_ci
755f08c3bdfSopenharmony_ci		if (shared_mode == IPV_SHARED && shmdt(shared_page) == -1)
756f08c3bdfSopenharmony_ci			err("shmdt");
757f08c3bdfSopenharmony_ci
758f08c3bdfSopenharmony_ci		fflush(stdout);
759f08c3bdfSopenharmony_ci		_exit(failure);
760f08c3bdfSopenharmony_ci	}
761f08c3bdfSopenharmony_ci
762f08c3bdfSopenharmony_cicleanup:
763f08c3bdfSopenharmony_ci	if (shared_page) {
764f08c3bdfSopenharmony_ci		if (shared_mode == IPV_SHARED)
765f08c3bdfSopenharmony_ci			shmdt(shared_page);
766f08c3bdfSopenharmony_ci		else
767f08c3bdfSopenharmony_ci			munmap_reserve(shared_page, PS);
768f08c3bdfSopenharmony_ci	}
769f08c3bdfSopenharmony_ci	if (shm_id >= 0 && shmctl(shm_id, IPC_RMID, NULL) < 0)
770f08c3bdfSopenharmony_ci		err("shmctl IPC_RMID");
771f08c3bdfSopenharmony_ci	if (sem_id >= 0 && semctl(sem_id, 0, IPC_RMID) < 0)
772f08c3bdfSopenharmony_ci		err("semctl IPC_RMID");
773f08c3bdfSopenharmony_ci	return;
774f08c3bdfSopenharmony_ci
775f08c3bdfSopenharmony_cichild_error:
776f08c3bdfSopenharmony_ci	printf("XXX child process was terminated unexpectedly\n");
777f08c3bdfSopenharmony_ci	failure++;
778f08c3bdfSopenharmony_ci	goto cleanup;
779f08c3bdfSopenharmony_ci}
780f08c3bdfSopenharmony_ci
781f08c3bdfSopenharmony_cistatic void mmap_shared(void)
782f08c3bdfSopenharmony_ci{
783f08c3bdfSopenharmony_ci	do_shared(MMAP_SHARED);
784f08c3bdfSopenharmony_ci}
785f08c3bdfSopenharmony_ci
786f08c3bdfSopenharmony_cistatic void ipv_shared(void)
787f08c3bdfSopenharmony_ci{
788f08c3bdfSopenharmony_ci	do_shared(IPV_SHARED);
789f08c3bdfSopenharmony_ci}
790f08c3bdfSopenharmony_ci
791f08c3bdfSopenharmony_cistatic void anonymous_hugepage(void)
792f08c3bdfSopenharmony_ci{
793f08c3bdfSopenharmony_ci	char *page;
794f08c3bdfSopenharmony_ci	/* Hugepage isn't supported. */
795f08c3bdfSopenharmony_ci	if (!HPS)
796f08c3bdfSopenharmony_ci		return;
797f08c3bdfSopenharmony_ci	test_hugepage = 1;
798f08c3bdfSopenharmony_ci	page = alloc_anonymous_hugepage(HPS, 1);
799f08c3bdfSopenharmony_ci	/* prefault */
800f08c3bdfSopenharmony_ci	page[0] = 'a';
801f08c3bdfSopenharmony_ci	testmem("anonymous hugepage", page, MWRITE);
802f08c3bdfSopenharmony_ci	free_anonymous_hugepage(page, HPS);
803f08c3bdfSopenharmony_ci	test_hugepage = 0;
804f08c3bdfSopenharmony_ci}
805f08c3bdfSopenharmony_ci
806f08c3bdfSopenharmony_cistatic void file_backed_hugepage(void)
807f08c3bdfSopenharmony_ci{
808f08c3bdfSopenharmony_ci	char *page;
809f08c3bdfSopenharmony_ci	char buf[PATHBUFLEN];
810f08c3bdfSopenharmony_ci	int fd;
811f08c3bdfSopenharmony_ci	/* Hugepage isn't supported. */
812f08c3bdfSopenharmony_ci	if (!HPS)
813f08c3bdfSopenharmony_ci		return;
814f08c3bdfSopenharmony_ci	test_hugepage = 1;
815f08c3bdfSopenharmony_ci	snprintf(buf, PATHBUFLEN, "%s/test%d", hugetlbfsdir, tmpcount++);
816f08c3bdfSopenharmony_ci	page = alloc_filebacked_hugepage(buf, HPS, 0, &fd);
817f08c3bdfSopenharmony_ci	/* prefault */
818f08c3bdfSopenharmony_ci	page[0] = 'a';
819f08c3bdfSopenharmony_ci	testmem("file backed hugepage", page, MWRITE);
820f08c3bdfSopenharmony_ci	free_filebacked_hugepage(page, HPS, fd, buf);
821f08c3bdfSopenharmony_ci	test_hugepage = 0;
822f08c3bdfSopenharmony_ci}
823f08c3bdfSopenharmony_ci
824f08c3bdfSopenharmony_cistatic void shm_hugepage(void)
825f08c3bdfSopenharmony_ci{
826f08c3bdfSopenharmony_ci	char *page;
827f08c3bdfSopenharmony_ci	/* Hugepage isn't supported. */
828f08c3bdfSopenharmony_ci	if (!HPS)
829f08c3bdfSopenharmony_ci		return;
830f08c3bdfSopenharmony_ci	test_hugepage = 1;
831f08c3bdfSopenharmony_ci	page = alloc_shm_hugepage(&tmpcount, HPS);
832f08c3bdfSopenharmony_ci	/* prefault */
833f08c3bdfSopenharmony_ci	page[0] = 'a';
834f08c3bdfSopenharmony_ci	testmem("shared memory hugepage", page, MWRITE);
835f08c3bdfSopenharmony_ci	free_shm_hugepage(tmpcount, page);
836f08c3bdfSopenharmony_ci	tmpcount++;
837f08c3bdfSopenharmony_ci	test_hugepage = 0;
838f08c3bdfSopenharmony_ci}
839f08c3bdfSopenharmony_ci
840f08c3bdfSopenharmony_cistruct testcase {
841f08c3bdfSopenharmony_ci	void (*f)(void);
842f08c3bdfSopenharmony_ci	char *name;
843f08c3bdfSopenharmony_ci	int survivable;
844f08c3bdfSopenharmony_ci} cases[] = {
845f08c3bdfSopenharmony_ci	{ dirty_anonymous, "dirty anonymous" },
846f08c3bdfSopenharmony_ci	{ dirty_anonymous_unmap, "dirty anonymous unmap" },
847f08c3bdfSopenharmony_ci	{ mlocked_anonymous, "mlocked anonymous" },
848f08c3bdfSopenharmony_ci	{ file_clean, "file clean", 1 },
849f08c3bdfSopenharmony_ci	{ file_dirty, "file dirty" },
850f08c3bdfSopenharmony_ci	{ file_hole, "file hole" },
851f08c3bdfSopenharmony_ci	{ file_clean_mlocked, "file clean mlocked", 1 },
852f08c3bdfSopenharmony_ci	{ file_dirty_mlocked, "file dirty mlocked"},
853f08c3bdfSopenharmony_ci	{ nonlinear, "nonlinear" },
854f08c3bdfSopenharmony_ci	{ mmap_shared, "mmap shared" },
855f08c3bdfSopenharmony_ci	{ ipv_shared, "ipv shared" },
856f08c3bdfSopenharmony_ci	{ anonymous_hugepage, "anonymous hugepage" },
857f08c3bdfSopenharmony_ci	{ file_backed_hugepage, "file backed hugepage" },
858f08c3bdfSopenharmony_ci	{ shm_hugepage, "shared memory hugepage" },
859f08c3bdfSopenharmony_ci	{},	/* dummy 1 for sniper */
860f08c3bdfSopenharmony_ci	{},	/* dummy 2 for sniper */
861f08c3bdfSopenharmony_ci	{}
862f08c3bdfSopenharmony_ci};
863f08c3bdfSopenharmony_ci
864f08c3bdfSopenharmony_cistruct testcase snipercases[] = {
865f08c3bdfSopenharmony_ci	{ under_io_dirty, "under io dirty" },
866f08c3bdfSopenharmony_ci	{ under_io_clean, "under io clean" },
867f08c3bdfSopenharmony_ci};
868f08c3bdfSopenharmony_ci
869f08c3bdfSopenharmony_civoid usage(void)
870f08c3bdfSopenharmony_ci{
871f08c3bdfSopenharmony_ci	fprintf(stderr, "Usage: tinjpage [--sniper]\n"
872f08c3bdfSopenharmony_ci			"Test hwpoison injection on pages in various states\n"
873f08c3bdfSopenharmony_ci			"--mce-inject    Use mce-inject for injection\n"
874f08c3bdfSopenharmony_ci			"--sniper  Enable racy sniper tests (likely broken)\n");
875f08c3bdfSopenharmony_ci	exit(1);
876f08c3bdfSopenharmony_ci}
877f08c3bdfSopenharmony_ci
878f08c3bdfSopenharmony_civoid handle_opts(char **av)
879f08c3bdfSopenharmony_ci{
880f08c3bdfSopenharmony_ci	while (*++av) {
881f08c3bdfSopenharmony_ci		if (!strcmp(*av, "--sniper")) {
882f08c3bdfSopenharmony_ci			struct testcase *t;
883f08c3bdfSopenharmony_ci			for (t = cases; t->f; t++)
884f08c3bdfSopenharmony_ci				;
885f08c3bdfSopenharmony_ci			*t++ = snipercases[0];
886f08c3bdfSopenharmony_ci			*t++ = snipercases[1];
887f08c3bdfSopenharmony_ci		}
888f08c3bdfSopenharmony_ci		else if (!strcmp(*av, "--mce-inject")) {
889f08c3bdfSopenharmony_ci			inject = inject_mce_inject;
890f08c3bdfSopenharmony_ci		} else
891f08c3bdfSopenharmony_ci			usage();
892f08c3bdfSopenharmony_ci	}
893f08c3bdfSopenharmony_ci}
894f08c3bdfSopenharmony_ci
895f08c3bdfSopenharmony_ciint main(int ac, char **av)
896f08c3bdfSopenharmony_ci{
897f08c3bdfSopenharmony_ci	if (av[1])
898f08c3bdfSopenharmony_ci		handle_opts(av);
899f08c3bdfSopenharmony_ci
900f08c3bdfSopenharmony_ci	PS = getpagesize();
901f08c3bdfSopenharmony_ci	if (hugetlbfs_root(hugetlbfsdir))
902f08c3bdfSopenharmony_ci		HPS = gethugepagesize();
903f08c3bdfSopenharmony_ci
904f08c3bdfSopenharmony_ci	/* don't kill me at poison time, but possibly at page fault time */
905f08c3bdfSopenharmony_ci	early_kill = 0;
906f08c3bdfSopenharmony_ci	system("sysctl -w vm.memory_failure_early_kill=0");
907f08c3bdfSopenharmony_ci
908f08c3bdfSopenharmony_ci	struct sigaction sa = {
909f08c3bdfSopenharmony_ci		.sa_sigaction = sighandler,
910f08c3bdfSopenharmony_ci		.sa_flags = SA_SIGINFO
911f08c3bdfSopenharmony_ci	};
912f08c3bdfSopenharmony_ci
913f08c3bdfSopenharmony_ci	struct testcase *t;
914f08c3bdfSopenharmony_ci	/* catch signals */
915f08c3bdfSopenharmony_ci	sigaction(SIGBUS, &sa, NULL);
916f08c3bdfSopenharmony_ci	for (t = cases; t->f; t++) {
917f08c3bdfSopenharmony_ci		printf("---- testing %s\n", t->name);
918f08c3bdfSopenharmony_ci		t->f();
919f08c3bdfSopenharmony_ci	}
920f08c3bdfSopenharmony_ci
921f08c3bdfSopenharmony_ci	/* suicide version */
922f08c3bdfSopenharmony_ci	for (t = cases; t->f; t++) {
923f08c3bdfSopenharmony_ci		printf("---- testing %s in child\n", t->name);
924f08c3bdfSopenharmony_ci		pid_t child = fork();
925f08c3bdfSopenharmony_ci		if (child == 0) {
926f08c3bdfSopenharmony_ci			signal(SIGBUS, SIG_DFL);
927f08c3bdfSopenharmony_ci			t->f();
928f08c3bdfSopenharmony_ci			if (t->survivable)
929f08c3bdfSopenharmony_ci				_exit(2);
930f08c3bdfSopenharmony_ci			write(1, t->name, strlen(t->name));
931f08c3bdfSopenharmony_ci			write(1, PAIR(" didn't kill itself?\n"));
932f08c3bdfSopenharmony_ci			_exit(1);
933f08c3bdfSopenharmony_ci		} else {
934f08c3bdfSopenharmony_ci			siginfo_t sig;
935f08c3bdfSopenharmony_ci			if (waitid(P_PID, child, &sig, WEXITED) < 0)
936f08c3bdfSopenharmony_ci				perror("waitid");
937f08c3bdfSopenharmony_ci			else {
938f08c3bdfSopenharmony_ci				if (t->survivable) {
939f08c3bdfSopenharmony_ci					if (sig.si_code != CLD_EXITED) {
940f08c3bdfSopenharmony_ci						printf("XXX: %s: child not survived\n", t->name);
941f08c3bdfSopenharmony_ci						failure++;
942f08c3bdfSopenharmony_ci					}
943f08c3bdfSopenharmony_ci				} else {
944f08c3bdfSopenharmony_ci					if (sig.si_code != CLD_KILLED || sig.si_status != SIGBUS) {
945f08c3bdfSopenharmony_ci						printf("XXX: %s: child not killed by SIGBUS\n", t->name);
946f08c3bdfSopenharmony_ci						failure++;
947f08c3bdfSopenharmony_ci					}
948f08c3bdfSopenharmony_ci				}
949f08c3bdfSopenharmony_ci			}
950f08c3bdfSopenharmony_ci		}
951f08c3bdfSopenharmony_ci	}
952f08c3bdfSopenharmony_ci
953f08c3bdfSopenharmony_ci	/* early kill version */
954f08c3bdfSopenharmony_ci	early_kill = 1;
955f08c3bdfSopenharmony_ci	system("sysctl -w vm.memory_failure_early_kill=1");
956f08c3bdfSopenharmony_ci
957f08c3bdfSopenharmony_ci	sigaction(SIGBUS, &sa, NULL);
958f08c3bdfSopenharmony_ci	for (t = cases; t->f; t++) {
959f08c3bdfSopenharmony_ci		printf("---- testing %s (early kill)\n", t->name);
960f08c3bdfSopenharmony_ci		t->f();
961f08c3bdfSopenharmony_ci	}
962f08c3bdfSopenharmony_ci
963f08c3bdfSopenharmony_ci	if (failure > 0) {
964f08c3bdfSopenharmony_ci		printf("FAILURE -- %d cases broken!\n", failure);
965f08c3bdfSopenharmony_ci		return 1;
966f08c3bdfSopenharmony_ci	}
967f08c3bdfSopenharmony_ci	printf("SUCCESS\n");
968f08c3bdfSopenharmony_ci	return 0;
969f08c3bdfSopenharmony_ci}
970