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