• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Test program for Linux poison memory error recovery.
3  * This program is extended from tinjpage with a multi-process model.
4  *
5  * This injects poison into various mapping cases and triggers the poison
6  * handling.  Requires special injection support in the kernel.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation; version
11  * 2.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should find a copy of v2 of the GNU General Public License somewhere
19  * on your Linux system; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  * Authors: Andi Kleen, Fengguang Wu, Haicheng Li
23  *
24  */
25 #define _GNU_SOURCE 1
26 #include <stdio.h>
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <setjmp.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <stdarg.h>
34 #include <getopt.h>
35 #include <limits.h>
36 
37 #include <sys/mman.h>
38 #include <sys/fcntl.h>
39 #include <sys/types.h>
40 #include <sys/shm.h>
41 #include <sys/sem.h>
42 #include <sys/wait.h>
43 #include <sys/stat.h>
44 
45 #define MADV_POISON 100
46 
47 #define PAGE_SIZE 4 * 1024
48 #define SHM_SIZE 1		// in page_size.
49 #define SHM_MODE 0600
50 #define FILE_SIZE 1 * 1024 * 1024 * 1024
51 #define LOG_BUFLEN 100
52 
53 #define INSTANCE_NUM 10000
54 
55 #define TEST_PASS 1
56 #define TEST_FAIL 0
57 
58 static int PS = PAGE_SIZE;
59 static int instance = 0;	// index of the child process.
60 static int testid = 0;		// test index of the child process.
61 static int test_types = 0;	// totoal test types.
62 static int t_shm = -1;		// index of shm test case.
63 static int failure = 0;		// result of child process.
64 static int unexpected = 0;	// result of child process.
65 static int early_kill = 0;
66 struct test {
67 	int id;
68 	int result;
69 };
70 struct shm {
71 	int id;
72 	int ready;
73 	int done;
74 };
75 struct ipc {
76 	struct test test[INSTANCE_NUM];
77 	struct shm shm;
78 };
79 static int ipc_entry;
80 static int *shmptr = NULL;
81 
82 static pid_t g_pid[INSTANCE_NUM] = { 0 };
83 static int shm_size = SHM_SIZE;
84 static int child_num = INSTANCE_NUM;
85 static int shm_child_num = 0;
86 static char log_file[PATH_MAX];
87 static FILE *log_fd = NULL;
88 static char result_file[PATH_MAX];
89 static FILE *result_fd = NULL;
90 static char tmp_dir[PATH_MAX] = { '\0' };
91 static int clean_env = 0;
92 
93 static int semid_ready = 0;
94 
95 static pid_t mypid;
96 
97 union semun {
98 	int val;
99 	struct semid_ds *buf;
100 	unsigned short int *array;
101 	struct semid_info *__buf;
102 };
103 
104 enum rmode {
105 	MREAD = 0,
106 	MWRITE = 1,
107 	MREAD_OK = 2,
108 	MWRITE_OK = 3,
109 	MNOTHING = -1,
110 };
111 
112 static struct option opts[] = {
113 	{"clean", 0, 0, 'C'},
114 	{"help", 0, 0, 'h'},
115 	{"instance", 0, 0, 'i'},
116 	{"log", 0, 0, 'l'},
117 	{"result", 0, 0, 'r'},
118 	{"shmsize", 0, 0, 's'},
119 	{"tmpdir", 0, 0, 't'},
120 	{"", 0, 0, '\0'}
121 };
122 
help(void)123 static void help(void)
124 {
125 	printf("Usage: page-poisoning [OPTION]...\n"
126 	       "Stress test for Linux HWPOISON Page Recovery with multiple processes.\n"
127 	       "\n"
128 	       "Mandatory arguments to long options are mandatory for short options too.\n"
129 	       "  -C, --clean                record log and result in clean files.\n"
130 	       "  -h                         print this page\n"
131 	       "  -i, --child_num=NUM        spawn NUM processes to do test (default NUM = %d)\n"
132 	       "  -l, --log=LOG              record logs to file LOG.\n"
133 	       "  -r, --result=RESULT        record test result to file RESULT.\n"
134 	       "  -s, --shmsize=SIZE         each shared memory segment is SIZE-page based.\n"
135 	       "  -t, --tmpdir=DIR           create temporary files in DIR.\n\n",
136 	       INSTANCE_NUM);
137 }
138 
139 static void err(const char *fmt, ...);
mylog(const char * fmt,...)140 static void mylog(const char *fmt, ...)
141 {
142 	char buf[LOG_BUFLEN] = { '\0' };
143 	va_list args;
144 	if (!log_fd)
145 		err("no log file there\n");
146 
147 	va_start(args, fmt);
148 	vsprintf(buf, fmt, args);
149 	printf("[pid %d] %s", mypid, buf);
150 	fprintf(log_fd, "[pid %d] %s", mypid, buf);
151 	fflush(log_fd);
152 	va_end(args);
153 }
154 
result(const char * fmt,...)155 static void result(const char *fmt, ...)
156 {
157 	char buf[LOG_BUFLEN] = { '\0' };
158 	va_list args;
159 	if (!result_fd)
160 		err("no result file there\n");
161 
162 	va_start(args, fmt);
163 	vsprintf(buf, fmt, args);
164 	fprintf(result_fd, "[pid %d] %s", mypid, buf);
165 	fflush(result_fd);
166 	if (log_fd)
167 		mylog("%s", buf);
168 	va_end(args);
169 }
170 
err(const char * fmt,...)171 static void err(const char *fmt, ...)
172 {
173 	char buf[LOG_BUFLEN] = { '\0' };
174 	va_list args;
175 	va_start(args, fmt);
176 
177 	vsprintf(buf, fmt, args);
178 	if (result_fd)
179 		result("error: %s :%s\n", buf, strerror(errno));
180 	else
181 		perror(buf);
182 	va_end(args);
183 	exit(1);
184 }
185 
checked_mmap(void * start,size_t length,int prot,int flags,int fd,off_t offset)186 static void *checked_mmap(void *start, size_t length, int prot, int flags,
187 		   int fd, off_t offset)
188 {
189 	void *map = mmap(start, length, prot, flags, fd, offset);
190 	if (map == (void *)-1L)
191 		err("mmap");
192 	return map;
193 }
194 
munmap_reserve(void * page,int size)195 static void munmap_reserve(void *page, int size)
196 {
197 	munmap(page, size);
198 	mmap(page, size, PROT_NONE, MAP_PRIVATE | MAP_FIXED, 0, 0);
199 }
200 
xmalloc(size_t s)201 static void *xmalloc(size_t s)
202 {
203 	void *p = malloc(s);
204 	if (!p)
205 		exit(ENOMEM);
206 	return p;
207 }
208 
209 static int recovercount;
210 static sigjmp_buf recover_ctx;
211 static sigjmp_buf early_recover_ctx;
212 static void *expected_addr;
213 
sighandler(int sig,siginfo_t * si,void * arg)214 static void sighandler(int sig, siginfo_t * si, void *arg)
215 {
216 	mylog("signal %d code %d addr %p\n", sig, si->si_code, si->si_addr);
217 	if (si->si_addr != expected_addr) {
218 		result("failed: Unexpected address in signal %p (expected %p)\n",
219 		       si->si_addr, expected_addr);
220 		failure++;
221 	}
222 
223 	if (--recovercount == 0) {
224 		result("failed: I seem to be in a signal loop. bailing out.\n");
225 		exit(1);
226 	}
227 
228 	if (si->si_code == 4)
229 		siglongjmp(recover_ctx, 1);
230 	else
231 		siglongjmp(early_recover_ctx, 1);
232 }
233 
poison(char * msg,char * page,enum rmode mode)234 static void poison(char *msg, char *page, enum rmode mode)
235 {
236 	expected_addr = page;
237 	recovercount = 5;
238 
239 	if (sigsetjmp(early_recover_ctx, 1) == 0) {
240 		if (madvise(page, PS, MADV_POISON) != 0) {
241 			if (errno == EINVAL) {
242 				result("failed: Kernel doesn't support poison injection\n");
243 				exit(0);
244 			}
245 			err("error: madvise: %s", strerror(errno));
246 			return;
247 		}
248 
249 		if (early_kill && (mode == MWRITE || mode == MREAD)) {
250 			result("failed: %s: process is not early killed\n",
251 			       msg);
252 			failure++;
253 		}
254 
255 		return;
256 	}
257 
258 	if (early_kill) {
259 		if (mode == MREAD_OK || mode == MWRITE_OK) {
260 			result("failed: %s: killed\n", msg);
261 			failure++;
262 		} else
263 			mylog("pass: recovered\n");
264 	}
265 }
266 
recover(char * msg,char * page,enum rmode mode)267 static void recover(char *msg, char *page, enum rmode mode)
268 {
269 	expected_addr = page;
270 	recovercount = 5;
271 
272 	if (sigsetjmp(recover_ctx, 1) == 0) {
273 		switch (mode) {
274 		case MWRITE:
275 			mylog("writing 2\n");
276 			*page = 2;
277 			break;
278 		case MWRITE_OK:
279 			mylog("writing 4\n");
280 			*page = 4;
281 			return;
282 		case MREAD:
283 			mylog("reading %x\n", *(unsigned char *)page);
284 			break;
285 		case MREAD_OK:
286 			mylog("reading %x\n", *(unsigned char *)page);
287 			return;
288 		case MNOTHING:
289 			return;
290 		}
291 		/* signal or kill should have happened */
292 		result("failed: %s: page is not poisoned after injection\n", msg);
293 		failure++;
294 		return;
295 	}
296 	if (mode == MREAD_OK || mode == MWRITE_OK) {
297 		result("failed: %s: killed\n", msg);
298 		failure++;
299 	} else
300 		mylog("pass: recovered\n");
301 }
302 
testmem(char * msg,char * page,enum rmode mode)303 static void testmem(char *msg, char *page, enum rmode mode)
304 {
305 	mylog("%s poisoning page %p\n", msg, page);
306 	poison(msg, page, mode);
307 	recover(msg, page, mode);
308 }
309 
expecterr(char * msg,int err)310 static void expecterr(char *msg, int err)
311 {
312 	if (err) {
313 		mylog("pass: expected error %d on %s\n", errno, msg);
314 	} else {
315 		result("failed: unexpected no error on %s\n", msg);
316 		failure++;
317 	}
318 }
319 
320 /*
321  * Any optional error is really a deficiency in the kernel VFS error reporting
322  * and should be eventually fixed and turned into a expecterr
323  */
optionalerr(char * msg,int err)324 static void optionalerr(char *msg, int err)
325 {
326 	if (err) {
327 		mylog("pass: expected error %d on %s\n", errno, msg);
328 	} else {
329 		mylog("LATER: expected likely incorrect no error on %s\n", msg);
330 		unexpected++;
331 	}
332 }
333 
playfile(char * buf)334 static int playfile(char *buf)
335 {
336 	int fd;
337 	if (buf[0] == 0)
338 		snprintf(buf, PATH_MAX, "%s/dirty%d", tmp_dir, mypid);
339 	fd = open(buf, O_CREAT | O_RDWR | O_TRUNC, 0600);
340 	if (fd < 0)
341 		err("opening temporary file: %s", buf);
342 
343 	const int NPAGES = 5;
344 	char *tmp = xmalloc(PS * NPAGES);
345 	int i;
346 	for (i = 0; i < PS * NPAGES; i++)
347 		tmp[i] = i;
348 	write(fd, tmp, PS * NPAGES);
349 
350 	lseek(fd, 0, SEEK_SET);
351 	free(tmp);
352 	return fd;
353 }
354 
dirty_anonymous(void)355 static void dirty_anonymous(void)
356 {
357 	struct ipc *ipc;
358 	char *page;
359 
360 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
361 		err("shmat error\n");
362 	ipc->test[instance].id = testid;
363 	page = checked_mmap(NULL, PS, PROT_READ | PROT_WRITE,
364 			    MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, 0, 0);
365 	testmem("dirty", page, MWRITE);
366 	if (!failure)
367 		ipc->test[instance].result = TEST_PASS;
368 	shmdt(ipc);
369 }
370 
dirty_anonymous_unmap(void)371 static void dirty_anonymous_unmap(void)
372 {
373 	struct ipc *ipc;
374 	char *page;
375 
376 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
377 		err("shmat error\n");
378 	ipc->test[instance].id = testid;
379 	page = checked_mmap(NULL, PS, PROT_READ | PROT_WRITE,
380 			    MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, 0, 0);
381 	testmem("dirty", page, MWRITE);
382 	munmap_reserve(page, PS);
383 	if (!failure)
384 		ipc->test[instance].result = TEST_PASS;
385 	shmdt(ipc);
386 }
387 
mlocked_anonymous(void)388 static void mlocked_anonymous(void)
389 {
390 	struct ipc *ipc;
391 	char *page;
392 
393 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
394 		err("shmat error\n");
395 	ipc->test[instance].id = testid;
396 	page = checked_mmap(NULL, PS, PROT_READ | PROT_WRITE,
397 			    MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, 0, 0);
398 	testmem("mlocked", page, MWRITE);
399 	if (!failure)
400 		ipc->test[instance].result = TEST_PASS;
401 	shmdt(ipc);
402 }
403 
do_file_clean(int flags,char * name)404 static void do_file_clean(int flags, char *name)
405 {
406 	char *page;
407 	char fn[PATH_MAX];
408 	snprintf(fn, PATH_MAX, "%s/clean%d", tmp_dir, mypid);
409 	int fd = open(fn, O_RDWR | O_TRUNC | O_CREAT, 0600);
410 	if (fd < 0)
411 		err("opening temporary file: %s", fn);
412 	write(fd, fn, 4);
413 	page = checked_mmap(NULL, PS, PROT_READ | PROT_WRITE, MAP_SHARED | flags,
414 			    fd, 0);
415 	fsync(fd);
416 	close(fd);
417 	testmem(name, page, MREAD_OK);
418 	/* reread page from disk */
419 	mylog("reading %x\n", *(unsigned char *)page);
420 	testmem(name, page, MWRITE_OK);
421 }
422 
file_clean(void)423 static void file_clean(void)
424 {
425 	struct ipc *ipc;
426 
427 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
428 		err("shmat error\n");
429 	ipc->test[instance].id = testid;
430 	do_file_clean(0, "file clean");
431 	if (!failure)
432 		ipc->test[instance].result = TEST_PASS;
433 	shmdt(ipc);
434 }
435 
file_clean_mlocked(void)436 static void file_clean_mlocked(void)
437 {
438 	struct ipc *ipc;
439 
440 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
441 		err("shmat error\n");
442 	ipc->test[instance].id = testid;
443 	do_file_clean(MAP_LOCKED, "file clean mlocked");
444 	if (!failure)
445 		ipc->test[instance].result = TEST_PASS;
446 	shmdt(ipc);
447 }
448 
ndesc(char * buf,char * name,char * add)449 static char *ndesc(char *buf, char *name, char *add)
450 {
451 	snprintf(buf, 100, "%s %s", name, add);
452 	return buf;
453 }
454 
do_file_dirty(int flags,char * name)455 static void do_file_dirty(int flags, char *name)
456 {
457 	char nbuf[100];
458 	char *page;
459 	char fn[PATH_MAX];
460 	fn[0] = 0;
461 	int fd = playfile(fn);
462 
463 	page = checked_mmap(NULL, PS, PROT_READ,
464 			    MAP_SHARED | MAP_POPULATE | flags, fd, 0);
465 	testmem(ndesc(nbuf, name, "initial"), page, MREAD);
466 	expecterr("msync expect error", msync(page, PS, MS_SYNC) < 0);
467 	close(fd);
468 	munmap_reserve(page, PS);
469 
470 	fd = open(fn, O_RDONLY);
471 	if (fd < 0)
472 		err("reopening temp file");
473 	page = checked_mmap(NULL, PS, PROT_READ, MAP_SHARED | MAP_POPULATE | flags,
474 			    fd, 0);
475 	recover(ndesc(nbuf, name, "populated"), page, MREAD_OK);
476 	close(fd);
477 	munmap_reserve(page, PS);
478 
479 	fd = open(fn, O_RDONLY);
480 	if (fd < 0)
481 		err("reopening temp file");
482 	page = checked_mmap(NULL, PS, PROT_READ, MAP_SHARED | flags, fd, 0);
483 	recover(ndesc(nbuf, name, "fault"), page, MREAD_OK);
484 	close(fd);
485 	munmap_reserve(page, PS);
486 
487 	fd = open(fn, O_RDWR);
488 	char buf[128];
489 	/* the earlier close has eaten the error */
490 	optionalerr("explicit read after poison", read(fd, buf, sizeof buf) < 0);
491 	optionalerr("explicit write after poison", write(fd, "foobar", 6) < 0);
492 	optionalerr("fsync expect error", fsync(fd) < 0);
493 	close(fd);
494 
495 	/* should unlink return an error here? */
496 	if (unlink(fn) < 0)
497 		perror("unlink");
498 }
499 
file_dirty(void)500 static void file_dirty(void)
501 {
502 	struct ipc *ipc;
503 
504 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
505 		err("shmat error\n");
506 	ipc->test[instance].id = testid;
507 	do_file_dirty(0, "file dirty");
508 	if (!failure)
509 		ipc->test[instance].result = TEST_PASS;
510 	shmdt(ipc);
511 }
512 
file_dirty_mlocked(void)513 static void file_dirty_mlocked(void)
514 {
515 	struct ipc *ipc;
516 
517 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
518 		err("shmat error\n");
519 	ipc->test[instance].id = testid;
520 	do_file_dirty(MAP_LOCKED, "file dirty mlocked");
521 	if (!failure)
522 		ipc->test[instance].result = TEST_PASS;
523 	shmdt(ipc);
524 }
525 
request_sem(int id,int num)526 static void request_sem(int id, int num)
527 {
528 	struct sembuf sb;
529 
530 	sb.sem_num = num;
531 	sb.sem_op = -1;
532 	sb.sem_flg = 0;
533 
534 	semop(id, &sb, 1);
535 }
536 
waiton_sem(int id,int num)537 static void waiton_sem(int id, int num)
538 {
539 	struct sembuf sb;
540 
541 	sb.sem_num = num;
542 	sb.sem_flg = 0;
543 
544 	sb.sem_op = -1;
545 	semop(id, &sb, 1);
546 	sb.sem_op = 0;
547 	semop(id, &sb, 1);
548 }
549 
release_sem(int id,int num)550 static void release_sem(int id, int num)
551 {
552 	struct sembuf sb;
553 
554 	sb.sem_num = num;
555 	sb.sem_op = 1;
556 	sb.sem_flg = 0;
557 
558 	semop(id, &sb, 1);
559 }
560 
clean_anonymous(void)561 static void clean_anonymous(void)
562 {
563 	char *page;
564 	page = checked_mmap(NULL, PS, PROT_READ | PROT_WRITE,
565 			    MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
566 	testmem("clean", page, MWRITE_OK);
567 }
568 
anon_clean(void)569 static void anon_clean(void)
570 {
571 	struct ipc *ipc;
572 
573 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
574 		err("shmat error\n");
575 	ipc->test[instance].id = testid;
576 	clean_anonymous();
577 	if (!failure)
578 		ipc->test[instance].result = TEST_PASS;
579 	shmdt(ipc);
580 }
581 
582 /* TBD
583 static void survival(void)
584 {
585 	struct ipc *ipc;
586 	char page;
587 
588 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
589 		err("shmat error\n");
590 	ipc->test[instance].id = testid;
591 	testmem("survial", &page, MNOTHING);
592 	if (!failure)
593 		ipc->test[instance].result = TEST_PASS;
594 	shmdt(ipc);
595 }
596 */
597 
shm_test(void)598 static void shm_test(void)
599 {
600 	struct ipc *ipc;
601 
602 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
603 		err("shmat error\n");
604 	ipc->test[instance].id = testid;
605 
606 	request_sem(semid_ready, 0);
607 	if (!ipc->shm.ready) {
608 		if ((ipc->shm.id = shmget(IPC_PRIVATE, shm_size * PS,
609 					  SHM_MODE)) < 0)
610 			err("shmget error\n");
611 		ipc->shm.ready = 1;
612 	}
613 	if ((shmptr = shmat(ipc->shm.id, 0, 0)) == (void *)-1) {
614 		err("shmat error\n");
615 	} else
616 		*shmptr = mypid;
617 	release_sem(semid_ready, 0);
618 
619 	waiton_sem(semid_ready, 1);
620 
621 	request_sem(semid_ready, 0);
622 	if (!ipc->shm.done) {
623 		ipc->shm.done = 1;
624 		testmem("shm dirty", (char *)shmptr, MWRITE);
625 	} else
626 		recover("shm dirty", (char *)shmptr, MREAD);
627 	release_sem(semid_ready, 0);
628 
629 	if (!failure)
630 		ipc->test[instance].result = TEST_PASS;
631 	shmdt(shmptr);
632 	shmdt(ipc);
633 }
634 
setup_ipc(void)635 static void setup_ipc(void)
636 {
637 	int size;
638 	union semun sunion;
639 	struct ipc *ipc;
640 
641 	size = sizeof(struct ipc);
642 
643 	if ((ipc_entry = shmget(IPC_PRIVATE, size, SHM_MODE)) < 0)
644 		err("shmget error\n");
645 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
646 		err("shmat error\n");
647 	memset(ipc, 0, sizeof(struct ipc));
648 	ipc->shm.id = -1;
649 	shmdt(ipc);
650 
651 	semid_ready = semget(IPC_PRIVATE, 2, SHM_R | SHM_W);
652 	sunion.val = 1;
653 	semctl(semid_ready, 0, SETVAL, sunion);
654 	if (t_shm != -1) {
655 		if (((child_num - 1) % test_types) >= t_shm)
656 			shm_child_num = (child_num - 1) / test_types + 1;
657 		else
658 			shm_child_num = (child_num - 1) / test_types;
659 	}
660 	if (shm_child_num) {
661 		sunion.val = shm_child_num;
662 		semctl(semid_ready, 1, SETVAL, sunion);
663 		mylog("there are %d shm_child\n", shm_child_num);
664 	}
665 }
666 
free_ipc(void)667 static void free_ipc(void)
668 {
669 	struct ipc *ipc;
670 
671 	semctl(semid_ready, 0, IPC_RMID);
672 	if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
673 		err("shmat error\n");
674 	if (ipc->shm.id != -1)
675 		shmctl(ipc->shm.id, IPC_RMID, 0);
676 	shmdt(ipc);
677 	shmctl(ipc_entry, IPC_RMID, 0);
678 }
679 
cleanup(void)680 static void cleanup(void)
681 {
682 	int i;
683 	for (i = 0; i < instance; i++)
684 		kill(g_pid[i], 9);	//kill the suviving child.
685 	free_ipc();
686 }
687 
688 struct testcase {
689 	void (*f) (void);
690 	char *name;
691 	int survivable;
692 } cases[] = {
693 	{
694 	shm_test, "shared memory test", 0}, {
695 	anon_clean, "anonymous clean", 1}, {
696 	dirty_anonymous, "anonymous dirty", 0}, {
697 	dirty_anonymous_unmap, "anonymous dirty unmap", 0}, {
698 	mlocked_anonymous, "anonymous dirty mlocked", 0}, {
699 	file_clean, "file clean", 1}, {
700 	file_dirty, "file dirty", 0}, {
701 	file_clean_mlocked, "file clean mlocked", 1}, {
702 	file_dirty_mlocked, "file dirty mlocked", 0},
703 //      { survival, "survival", 0 },
704 	{
705 	NULL, NULL, 0}
706 };
707 
run_test(int children)708 static int run_test(int children)
709 {
710 	pid_t pid = -1;
711 	int i = 0, rc = 0;
712 	siginfo_t sig;
713 	struct ipc *ipc;
714 
715 	for (i = 0; i < children; i++) {
716 		pid = fork();
717 		if (pid < 0) {
718 			err("fork %d\n", i);
719 			break;
720 		} else if (pid == 0) {
721 			int j = instance % test_types;
722 			mypid = getpid();
723 			testid = j;
724 			cases[j].f();
725 			exit(0);
726 		} else {
727 			g_pid[i] = pid;
728 			++instance;
729 			fflush(stdout);
730 		}
731 	}
732 
733 	mylog("have spawned %d processes\n", instance);
734 	if (instance) {
735 		if ((ipc = shmat(ipc_entry, 0, 0)) == (void *)-1)
736 			err("shmat error\n");
737 
738 		for (i = 0; i < instance; i++) {
739 			int t = ipc->test[i].id;
740 
741 			mylog("wait for Pid %d\n", g_pid[i]);
742 			waitid(P_PID, g_pid[i], &sig, WEXITED);
743 			if (ipc->test[i].result == TEST_PASS)
744 				result("Ins %d: Pid %d: pass - %s\n", i,
745 				      g_pid[i], cases[t].name);
746 			else {
747 				result("Ins %d: Pid %d: failed - %s\n", i,
748 				       g_pid[i], cases[t].name);
749 				failure++;
750 			}
751 		}
752 		shmdt(ipc);
753 	}
754 
755 	if (!failure)
756 		result("\t!!! Page Poisoning Test got PASS. !!!\n\n");
757 	else {
758 		result("\t!!! Page Poisoning Test is FAILED (%d failures found). !!!\n\n",
759 		         failure);
760 		rc = 1;
761 	}
762 
763 	return rc;
764 }
765 
setup_log(void)766 static void setup_log(void)
767 {
768 	int rc = 0;
769 	if (clean_env)
770 		log_fd = fopen(log_file, "w");
771 	else
772 		log_fd = fopen(log_file, "a");
773 	if (!log_fd)
774 		err("cannot open log file: %s\n", log_file);
775 
776 	if (clean_env)
777 		result_fd = fopen(result_file, "w");
778 	else
779 		result_fd = fopen(result_file, "a");
780 	if (!result_fd)
781 		err("cannot open log file: %s\n", result_file);
782 
783 	if (tmp_dir[0] != '\0') {
784 		rc = mkdir(tmp_dir, 0777);
785 		if (rc && errno != EEXIST)
786 			err("cannot create tmp dir: %s: %s\n", tmp_dir,
787 			    strerror(errno));
788 	}
789 }
790 
free_log(void)791 static void free_log(void)
792 {
793 	fclose(log_fd);
794 	fclose(result_fd);
795 }
796 
main_sighandler(int sig,siginfo_t * si,void * arg)797 static void main_sighandler(int sig, siginfo_t * si, void *arg)
798 {
799 	mylog("receive signal to get terminated\n");
800 	cleanup();
801 	exit(1);
802 }
803 
setup_sig(void)804 static void setup_sig(void)
805 {
806 	struct sigaction sa = {
807 		.sa_sigaction = main_sighandler,
808 		.sa_flags = SA_SIGINFO
809 	};
810 	struct sigaction sa_bus = {
811 		.sa_sigaction = sighandler,
812 		.sa_flags = SA_SIGINFO
813 	};
814 
815 	sigaction(SIGINT, &sa, NULL);
816 	sigaction(SIGKILL, &sa, NULL);
817 	sigaction(SIGTERM, &sa, NULL);
818 	sigaction(SIGBUS, &sa_bus, NULL);
819 }
820 
setup_test(void)821 static void setup_test(void)
822 {
823 	struct testcase *t;
824 	/* catch signals */
825 	for (t = cases; t->f; t++)
826 		if (t->f == shm_test)
827 			t_shm = (t - cases);
828 	test_types = t - cases;
829 }
830 
main(int argc,char ** argv)831 int main(int argc, char **argv)
832 {
833 	int rc = 0, c, opt_index;
834 
835 	snprintf(log_file, sizeof(log_file), "page-poisoning.log");
836 	snprintf(result_file, sizeof(result_file), "page-poisoning.result");
837 	snprintf(tmp_dir, sizeof(tmp_dir), "./tmp");
838 	while (1) {
839 		c = getopt_long(argc, argv, "Chi:l:r:s:t:", opts, &opt_index);
840 		if (c == -1)
841 			break;
842 		switch (c) {
843 		case 'C':
844 			clean_env = 1;
845 			break;
846 		case 'h':
847 			help();
848 			return 0;
849 		case 'i':
850 			child_num = strtol(optarg, NULL, 0);
851 			if (child_num > INSTANCE_NUM)
852 				child_num = INSTANCE_NUM;
853 			break;
854 		case 'l':
855 			snprintf(log_file, sizeof(log_file), "%s", optarg);
856 			break;
857 		case 'r':
858 			snprintf(result_file, sizeof(result_file), "%s",
859 				 optarg);
860 			break;
861 		case 's':
862 			shm_size = strtol(optarg, NULL, 0);
863 			if (shm_size < SHM_SIZE)
864 				shm_size = SHM_SIZE;
865 			break;
866 		case 't':
867 			snprintf(tmp_dir, sizeof(tmp_dir), "%s", optarg);
868 			break;
869 		default:
870 			help();
871 			return 0;
872 		}
873 	}
874 
875 	if (!early_kill)
876 		system("sysctl -w vm.memory_failure_early_kill=0");
877 	mypid = getpid();
878 	setup_log();
879 	setup_test();
880 	if (!child_num) {
881 		mylog("end without test executed since child_num = 0\n");
882 		return rc;
883 	}
884 
885 	mylog("start page-poisoning test\n");
886 	PS = getpagesize();
887 	setup_ipc();
888 	setup_sig();
889 	rc = run_test(child_num);
890 	free_ipc();
891 	mylog("page-poisoning test done!\n");
892 	free_log();
893 
894 	return rc;
895 }
896