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