1 /*
2 * Test program for memory error handling for hugepages
3 * Author: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
4 */
5 #define _GNU_SOURCE 1
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <unistd.h>
12 #include <getopt.h>
13 #include <sys/mman.h>
14 #include <sys/ipc.h>
15 #include <sys/shm.h>
16 #include <sys/sem.h>
17 #include <sys/types.h>
18 #include <sys/prctl.h>
19 #include <sys/wait.h>
20 #include "hugepage.h"
21
22 #define FILE_BASE "test"
23
24 #define HPAGE_SIZE (2UL*1024*1024)
25 #define BUF_SIZE 256
26 #define PROTECTION (PROT_READ | PROT_WRITE)
27
28 #ifndef SHM_HUGETLB
29 #define SHM_HUGETLB 04000
30 #endif
31
32 /* Control early_kill/late_kill */
33 #define PR_MCE_KILL 33
34 #define PR_MCE_KILL_CLEAR 0
35 #define PR_MCE_KILL_SET 1
36 #define PR_MCE_KILL_LATE 0
37 #define PR_MCE_KILL_EARLY 1
38 #define PR_MCE_KILL_DEFAULT 2
39 #define PR_MCE_KILL_GET 34
40
41 #define MADV_HWPOISON 100
42 #define MADV_SOFT_OFFLINE 101
43
44 int PS; /* Page size */
45 int file_size; /* Memory allocation size (hugepage unit) */
46 /* Error injection position (page offset from the first hugepage head) */
47 int corrupt_page;
48 char filename[BUF_SIZE] = "/test";
49 char filepath[BUF_SIZE];
50
51 #define DEB printf("DEBUG [%d:%s:%d]\n", getpid(), __FILE__, __LINE__);
52
usage(void)53 static void usage(void)
54 {
55 printf(
56 "./thugetlb [-m memory] [-o offset] [-f file] [-xOeSAaFpch] hugetlbfs_directory\n"
57 " -m|--memory size(hugepage unit) Size of hugetlbfs file\n"
58 " -o|--offset offset(page unit) Position of error injection\n"
59 " -x|--inject Error injection switch\n"
60 " -O|--offline Soft offline switch\n"
61 " -e|--early-kill Set PR_MCE_KILL_EARLY\n"
62 " -S|--shm Use shmem with SHM_HUGETLB\n"
63 " -A|--anonymous Use MAP_ANONYMOUS\n"
64 " -a|--avoid-touch Avoid touching error page\n"
65 " -F|--fork\n"
66 " -p|--private\n"
67 " -c|--cow\n"
68 " -f|--filename string\n"
69 " -h|--help\n"
70 "\n"
71 );
72 }
73
74 /*
75 * semaphore get/put wrapper
76 */
get_semaphore(int sem_id,struct sembuf * sembuffer)77 int get_semaphore(int sem_id, struct sembuf *sembuffer)
78 {
79 sembuffer->sem_num = 0;
80 sembuffer->sem_op = -1;
81 sembuffer->sem_flg = SEM_UNDO;
82 return semop(sem_id, sembuffer, 1);
83 }
84
put_semaphore(int sem_id,struct sembuf * sembuffer)85 int put_semaphore(int sem_id, struct sembuf *sembuffer)
86 {
87 sembuffer->sem_num = 0;
88 sembuffer->sem_op = 1;
89 sembuffer->sem_flg = SEM_UNDO;
90 return semop(sem_id, sembuffer, 1);
91 }
92
93 static struct option opts[] = {
94 { "memory" , 1, NULL, 'm' },
95 { "offset" , 1, NULL, 'o' },
96 { "inject" , 0, NULL, 'x' },
97 { "offline" , 0, NULL, 'O' },
98 { "early_kill" , 0, NULL, 'e' },
99 { "shm" , 0, NULL, 'S' },
100 { "anonymous" , 0, NULL, 'A' },
101 { "avoid-touch" , 0, NULL, 'a' },
102 { "fork" , 0, NULL, 'F' },
103 { "private" , 0, NULL, 'p' },
104 { "cow" , 0, NULL, 'c' },
105 { "filename" , 1, NULL, 'f' },
106 { "help" , 0, NULL, 'h' },
107 { NULL , 0, NULL, 0 }
108 };
109
main(int argc,char * argv[])110 int main(int argc, char *argv[])
111 {
112 void *addr;
113 int i;
114 int ret;
115 int fd = 0;
116 int semid;
117 int semaphore;
118 int inject = 0;
119 int madvise_code = MADV_HWPOISON;
120 int early_kill = 0;
121 int avoid_touch = 0;
122 int anonflag = 0;
123 int shmflag = 0;
124 int shmkey = 0;
125 int forkflag = 0;
126 int privateflag = 0;
127 int cowflag = 0;
128 char c;
129 pid_t pid = 0;
130 void *expected_addr = NULL;
131 struct sembuf sembuffer;
132
133 PS = getpagesize();
134 HPS = HPAGE_SIZE;
135 file_size = 1;
136 corrupt_page = -1;
137
138 if (argc == 1) {
139 usage();
140 exit(EXIT_FAILURE);
141 }
142
143 while ((c = getopt_long(argc, argv,
144 "m:o:xOeSAaFpcf:h", opts, NULL)) != -1) {
145 switch (c) {
146 case 'm':
147 file_size = strtol(optarg, NULL, 10);
148 break;
149 case 'o':
150 corrupt_page = strtol(optarg, NULL, 10);
151 break;
152 case 'x':
153 inject = 1;
154 break;
155 case 'O':
156 madvise_code = MADV_SOFT_OFFLINE;
157 break;
158 case 'e':
159 early_kill = 1;
160 break;
161 case 'S':
162 shmflag = 1;
163 break;
164 case 'A':
165 anonflag = 1;
166 break;
167 case 'a':
168 avoid_touch = 1;
169 break;
170 case 'F':
171 forkflag = 1;
172 break;
173 case 'p':
174 privateflag = 1;
175 break;
176 case 'c':
177 cowflag = 1;
178 break;
179 case 'f':
180 strcat(filename, optarg);
181 shmkey = strtol(optarg, NULL, 10);
182 break;
183 case 'h':
184 usage();
185 exit(EXIT_SUCCESS);
186 default:
187 usage();
188 exit(EXIT_FAILURE);
189 }
190 }
191
192 if (inject && corrupt_page * PS > file_size * HPAGE_SIZE)
193 errmsg("Target page is out of range.\n");
194
195 if (avoid_touch && corrupt_page == -1)
196 errmsg("Avoid which page?\n");
197
198 /* Construct file name */
199 if (access(argv[argc - 1], F_OK) == -1) {
200 usage();
201 exit(EXIT_FAILURE);
202 } else {
203 strcpy(filepath, argv[argc - 1]);
204 strcat(filepath, filename);
205 }
206
207 if (shmflag) {
208 addr = alloc_shm_hugepage(&shmkey, file_size * HPAGE_SIZE);
209 if (!addr)
210 errmsg("Failed in alloc_shm_hugepage()");
211 } else if (anonflag) {
212 addr = alloc_anonymous_hugepage(file_size * HPAGE_SIZE,
213 privateflag);
214 if (!addr)
215 errmsg("Failed in alloc_anonymous_hugepage()");
216 } else {
217 addr = alloc_filebacked_hugepage(filepath,
218 file_size * HPAGE_SIZE,
219 privateflag, &fd);
220 if (!addr)
221 errmsg("Failed in alloc_filebacked_hugepage()");
222 }
223
224 if (corrupt_page != -1 && avoid_touch)
225 expected_addr = (void *)(addr + corrupt_page / 512 * HPAGE_SIZE);
226
227 if (forkflag) {
228 semid = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT);
229 if (semid == -1) {
230 perror("semget");
231 goto cleanout;
232 }
233 semaphore = semctl(semid, 0, SETVAL, 1);
234 if (semaphore == -1) {
235 perror("semctl");
236 goto cleanout;
237 }
238 if (get_semaphore(semid, &sembuffer)) {
239 perror("get_semaphore");
240 goto cleanout;
241 }
242 }
243
244 write_hugepage(addr, file_size, 0);
245 read_hugepage(addr, file_size, 0);
246
247 if (early_kill)
248 prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY,
249 NULL, NULL);
250
251 /*
252 * Intended order:
253 * 1. Child COWs
254 * 2. Parent madvise()s
255 * 3. Child exit()s
256 */
257 if (forkflag) {
258 pid = fork();
259 if (!pid) {
260 /* Semaphore is already held */
261 if (cowflag) {
262 write_hugepage(addr, file_size, 0);
263 read_hugepage(addr, file_size, 0);
264 }
265 if (put_semaphore(semid, &sembuffer))
266 err("put_semaphore");
267 usleep(1000);
268 /* Wait for madvise() to be done */
269 if (get_semaphore(semid, &sembuffer))
270 err("put_semaphore");
271 if (put_semaphore(semid, &sembuffer))
272 err("put_semaphore");
273 return 0;
274 }
275 }
276
277 /* Wait for COW */
278 if (forkflag && get_semaphore(semid, &sembuffer)) {
279 perror("get_semaphore");
280 goto cleanout;
281 }
282
283 if (inject && corrupt_page != -1) {
284 ret = madvise(addr + corrupt_page * PS, PS, madvise_code);
285 if (ret) {
286 printf("madivise return %d :", ret);
287 perror("madvise");
288 goto cleanout;
289 }
290 }
291
292 if (forkflag && put_semaphore(semid, &sembuffer)) {
293 perror("put_semaphore");
294 goto cleanout;
295 }
296
297 if (madvise_code != MADV_SOFT_OFFLINE);
298 write_hugepage(addr, file_size, expected_addr);
299 read_hugepage(addr, file_size, expected_addr);
300
301 if (forkflag) {
302 if (wait(&i) == -1)
303 err("wait");
304 if (semctl(semid, 0, IPC_RMID) == -1)
305 err("semctl(IPC_RMID)");
306 }
307 cleanout:
308 if (shmflag) {
309 if (free_shm_hugepage(shmkey, addr) == -1)
310 exit(2);
311 } else if (anonflag) {
312 if (free_anonymous_hugepage(addr, file_size * HPAGE_SIZE) == -1)
313 exit(2);
314 } else {
315 if (free_filebacked_hugepage(addr, file_size * HPAGE_SIZE,
316 fd, filepath) == -1)
317 exit(2);
318 }
319
320 return 0;
321 }
322