generic: test MADV_POPULATE_READ with IO errors
[xfstests-dev.git] / src / t_mmap_cow_memory_failure.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved. */
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <libgen.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <semaphore.h>
10 #include <sys/mman.h>
11 #include <sys/wait.h>
12 #include <sys/sem.h>
13 #include <time.h>
14 #include <unistd.h>
15
16 sem_t *sem;
17
18 void sigbus_handler(int signal)
19 {
20         printf("Process is killed by signal: %d\n", signal);
21         sem_post(sem);
22 }
23
24 void mmap_read_file(char *filename, off_t offset, size_t size)
25 {
26         int fd;
27         char *map, *dummy;
28         struct timespec ts;
29
30         fd = open(filename, O_RDWR);
31         map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
32         dummy = malloc(size);
33
34         /* make sure page fault happens */
35         memcpy(dummy, map, size);
36
37         /* ready */
38         sem_post(sem);
39
40         usleep(200000);
41
42         clock_gettime(CLOCK_REALTIME, &ts);
43         ts.tv_sec += 3;
44         /* wait for injection done */
45         sem_timedwait(sem, &ts);
46
47         free(dummy);
48         munmap(map, size);
49         close(fd);
50 }
51
52 void mmap_read_file_then_poison(char *filename, off_t offset, size_t size,
53                 off_t poisonOffset, size_t poisonSize)
54 {
55         int fd, error;
56         char *map, *dummy;
57
58         /* wait for parent preparation done */
59         sem_wait(sem);
60
61         fd = open(filename, O_RDWR);
62         map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
63         dummy = malloc(size);
64
65         /* make sure page fault happens */
66         memcpy(dummy, map, size);
67
68         printf("Inject poison...\n");
69         error = madvise(map + poisonOffset, poisonSize, MADV_HWPOISON);
70         if (error)
71                 printf("madvise() has fault: %d, errno: %d\n", error, errno);
72
73         free(dummy);
74         munmap(map, size);
75         close(fd);
76 }
77
78 int main(int argc, char *argv[])
79 {
80         char *pReadFile = NULL, *pPoisonFile = NULL;
81         size_t mmapSize, poisonSize;
82         off_t mmapOffset = 0, poisonOffset = 0;
83         long pagesize = sysconf(_SC_PAGESIZE);
84         int c;
85         pid_t pid;
86
87         if (pagesize < 1) {
88                 fprintf(stderr, "sysconf(_SC_PAGESIZE): failed to get page size\n");
89                 abort();
90         }
91
92         /* default mmap / poison size, in unit of System Page Size */
93         mmapSize = poisonSize = pagesize;
94
95         while ((c = getopt(argc, argv, "o::s::O::S::R:P:")) != -1) {
96                 switch (c) {
97                 /* mmap offset */
98                 case 'o':
99                         mmapOffset = atoi(optarg) * pagesize;
100                         break;
101                 /* mmap size */
102                 case 's':
103                         mmapSize = atoi(optarg) * pagesize;
104                         break;
105                 /* madvice offset */
106                 case 'O':
107                         poisonOffset = atoi(optarg) * pagesize;
108                         break;
109                 /* madvice size */
110                 case 'S':
111                         poisonSize = atoi(optarg) * pagesize;
112                         break;
113                 /* filename for mmap read */
114                 case 'R':
115                         pReadFile = optarg;
116                         break;
117                 /* filename for poison read */
118                 case 'P':
119                         pPoisonFile = optarg;
120                         break;
121                 default:
122                         printf("Unknown option: %c\n", c);
123                         exit(1);
124                 }
125         }
126
127         if (!pReadFile || !pPoisonFile) {
128                 printf("Usage: \n"
129                        "  %s [-o mmapOffset] [-s mmapSize] [-O mmapOffset] [-S mmapSize] -R readFile -P poisonFile\n"
130                        "  (offset and size are both in unit of System Page Size: %ld)\n",
131                                 basename(argv[0]), pagesize);
132                 exit(0);
133         }
134         if (poisonSize < mmapSize)
135                 mmapSize = poisonSize;
136
137         /* fork and mmap files */
138         pid = fork();
139         if (pid == 0) {
140                 /* handle SIGBUS */
141                 signal(SIGBUS, sigbus_handler);
142                 sem = sem_open("sync", O_CREAT, 0666, 0);
143
144                 /* mread & do memory failure on poison file */
145                 mmap_read_file_then_poison(pPoisonFile, mmapOffset, mmapSize,
146                                 poisonOffset, poisonSize);
147
148                 sem_close(sem);
149         } else {
150                 sem = sem_open("sync", O_CREAT, 0666, 0);
151
152                 /* mread read file, wait for child process to be killed */
153                 mmap_read_file(pReadFile, mmapOffset, mmapSize);
154                 sem_close(sem);
155         }
156         exit(0);
157 }