• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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