• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010-2017  Red Hat, Inc.
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  * overcommit hugetlbfs and check the statistics.
14  *
15  * Description:
16  *
17  * hugetlbfs allows to overcommit hugepages and there are tunables in
18  * sysfs and procfs. The test here want to ensure it is possible to
19  * overcommit by either mmap or shared memory. Also ensure those
20  * reservation can be read/write, and several statistics work correctly.
21  *
22  * First, it resets nr_hugepages and nr_overcommit_hugepages. Then, set
23  * both to a specify value - N, and allocate N + %50 x N hugepages.
24  * Finally, it reads and writes every page. There are command options to
25  * choose either to manage hugepages from sysfs or procfs, and reserve
26  * them by mmap or shmget.
27  */
28 
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include "hugetlb.h"
33 #include "tst_safe_sysv_ipc.h"
34 #include "tst_test.h"
35 
36 #define PROTECTION		(PROT_READ | PROT_WRITE)
37 #define PATH_MEMINFO		"/proc/meminfo"
38 
39 static char path_sys_sz[BUFSIZ];
40 static char path_sys_sz_over[BUFSIZ];
41 static char path_sys_sz_free[BUFSIZ];
42 static char path_sys_sz_resv[BUFSIZ];
43 static char path_sys_sz_surp[BUFSIZ];
44 static char path_sys_sz_huge[BUFSIZ];
45 
46 #define PATH_PROC_VM		"/proc/sys/vm/"
47 #define PATH_PROC_OVER		PATH_PROC_VM "nr_overcommit_hugepages"
48 #define PATH_PROC_HUGE		PATH_PROC_VM "nr_hugepages"
49 #define PATH_SHMMAX		"/proc/sys/kernel/shmmax"
50 
51 /* Only ia64 requires this */
52 #ifdef __ia64__
53 #define ADDR (void *)(0x8000000000000000UL)
54 #define FLAGS (MAP_SHARED | MAP_FIXED)
55 #define SHMAT_FLAGS (SHM_RND)
56 #else
57 #define ADDR (void *)(0x0UL)
58 #define FLAGS (MAP_SHARED)
59 #define SHMAT_FLAGS (0)
60 #endif
61 
62 #ifndef SHM_HUGETLB
63 #define SHM_HUGETLB 04000
64 #endif
65 
66 #define MOUNT_DIR "hugemmap05"
67 #define TEST_FILE MOUNT_DIR "/file"
68 
69 static unsigned long long shmmax;
70 static char *path, *pathover;
71 static int key = -1, shmid = -1, fd = -1;
72 static int mounted, restore_shmmax, restore_nr_hgpgs, restore_overcomm_hgpgs;
73 static long hugepagesize, nr_hugepages, nr_overcommit_hugepages;
74 static long size = 128, length = 384;
75 
76 char *opt_sysfs;
77 char *opt_alloc;
78 char *opt_shmid;
79 static struct tst_option options[] = {
80 	{"s",  &opt_sysfs, "-s        Setup hugepages from sysfs"},
81 	{"m",  &opt_shmid, "-m        Reserve hugepages by shmget"},
82 	{"a:", &opt_alloc, "-a        Number of overcommint hugepages"},
83 	{NULL, NULL, NULL}
84 };
85 
86 static void check_wr_bytes(void *addr);
87 static int checkproc(long act_val, char *string, long exp_val);
88 static int checksys(char *path, char *pattern, long exp_val);
89 static void init_sys_sz_paths(void);
90 
test_overcommit(void)91 static void test_overcommit(void)
92 {
93 	void *addr = NULL, *shmaddr = NULL;
94 
95 	if (opt_shmid) {
96 		shmid = SAFE_SHMGET(key, (length / 2 * hugepagesize),
97 				 SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
98 	} else {
99 		fd = SAFE_OPEN(TEST_FILE, O_CREAT | O_RDWR, 0755);
100 		addr = SAFE_MMAP(ADDR, (length / 2 * hugepagesize),
101 				 PROTECTION, FLAGS, fd, 0);
102 	}
103 
104 	if (opt_sysfs) {
105 		tst_res(TINFO, "check sysfs before allocation.");
106 		if (checksys(path_sys_sz_huge, "HugePages_Total", length / 2))
107 			return;
108 		if (checksys(path_sys_sz_free, "HugePages_Free", length / 2))
109 			return;
110 		if (checksys(path_sys_sz_surp, "HugePages_Surp",
111 			     length / 2 - size))
112 			return;
113 		if (checksys(path_sys_sz_resv, "HugePages_Rsvd", length / 2))
114 			return;
115 	} else {
116 		tst_res(TINFO, "check /proc/meminfo before allocation.");
117 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Total:"),
118 			      "HugePages_Total", length / 2))
119 			return;
120 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Free:"),
121 			      "HugePages_Free", length / 2))
122 			return;
123 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Surp:"),
124 			      "HugePages_Surp", length / 2 - size))
125 			return;
126 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Rsvd:"),
127 			      "HugePages_Rsvd", length / 2))
128 			return;
129 	}
130 
131 	if (opt_shmid) {
132 		tst_res(TINFO, "shmid: 0x%x", shmid);
133 		shmaddr = SAFE_SHMAT(shmid, ADDR, SHMAT_FLAGS);
134 		check_wr_bytes(shmaddr);
135 	} else {
136 		check_wr_bytes(addr);
137 	}
138 
139 	if (opt_sysfs) {
140 		tst_res(TINFO, "check sysfs.");
141 		if (checksys(path_sys_sz_huge, "HugePages_Total", length / 2))
142 			return;
143 		if (checksys(path_sys_sz_free, "HugePages_Free", 0))
144 			return;
145 		if (checksys(path_sys_sz_surp, "HugePages_Surp",
146 			     length / 2 - size))
147 			return;
148 		if (checksys(path_sys_sz_resv, "HugePages_Rsvd", 0))
149 			return;
150 	} else {
151 		tst_res(TINFO, "check /proc/meminfo.");
152 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Total:"),
153 			      "HugePages_Total", length / 2))
154 			return;
155 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Free:"),
156 			      "HugePages_Free", 0))
157 			return;
158 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Surp:"),
159 			      "HugePages_Surp", length / 2 - size))
160 			return;
161 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Rsvd:"),
162 			      "HugePages_Rsvd", 0))
163 			return;
164 	}
165 
166 	if (opt_shmid) {
167 		SAFE_SHMDT(shmaddr);
168 		SAFE_SHMCTL(shmid, IPC_RMID, NULL);
169 	} else {
170 		SAFE_MUNMAP(addr, (length / 2 * hugepagesize));
171 		SAFE_CLOSE(fd);
172 		SAFE_UNLINK(TEST_FILE);
173 	}
174 
175 	tst_res(TPASS, "hugepages overcommit test pass");
176 }
177 
cleanup(void)178 static void cleanup(void)
179 {
180 	if (opt_shmid && shmid != -1)
181 		SAFE_SHMCTL(shmid, IPC_RMID, NULL);
182 
183 	if (!opt_shmid && fd != -1) {
184 		SAFE_CLOSE(fd);
185 		SAFE_UNLINK(TEST_FILE);
186 	}
187 
188 	if (mounted)
189 		tst_umount(MOUNT_DIR);
190 
191 	if (restore_nr_hgpgs) {
192 		tst_res(TINFO, "restore nr_hugepages to %ld.", nr_hugepages);
193 		SAFE_FILE_PRINTF(path, "%ld", nr_hugepages);
194 	}
195 
196 	if (restore_shmmax)
197 		SAFE_FILE_PRINTF(PATH_SHMMAX, "%llu", shmmax);
198 
199 	if (restore_overcomm_hgpgs) {
200 		tst_res(TINFO, "restore nr_overcommit_hugepages to %ld.",
201 			nr_overcommit_hugepages);
202 		SAFE_FILE_PRINTF(pathover, "%ld", nr_overcommit_hugepages);
203 	}
204 }
205 
setup(void)206 static void setup(void)
207 {
208 	check_hugepage();
209 	hugepagesize = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
210 	init_sys_sz_paths();
211 
212 	if (opt_sysfs) {
213 		path = path_sys_sz_huge;
214 		pathover = path_sys_sz_over;
215 	} else {
216 		path = PATH_PROC_HUGE;
217 		pathover = PATH_PROC_OVER;
218 	}
219 
220 	if (opt_alloc) {
221 		size = atoi(opt_alloc);
222 		length = (size + size * 0.5) * 2;
223 	}
224 
225 	if (opt_shmid) {
226 		SAFE_FILE_SCANF(PATH_SHMMAX, "%llu", &shmmax);
227 		if (shmmax < (unsigned long long)(length / 2 * hugepagesize)) {
228 			restore_shmmax = 1;
229 			SAFE_FILE_PRINTF(PATH_SHMMAX, "%ld",
230 					(length / 2 * hugepagesize));
231 		}
232 	}
233 
234 	SAFE_FILE_SCANF(path, "%ld", &nr_hugepages);
235 	tst_res(TINFO, "original nr_hugepages is %ld", nr_hugepages);
236 
237 	/* Reset. */
238 	SAFE_FILE_PRINTF(path, "%ld", size);
239 	restore_nr_hgpgs = 1;
240 
241 	if (access(pathover, F_OK)) {
242 		tst_brk(TCONF, "file %s does not exist in the system",
243 			pathover);
244 	}
245 
246 	SAFE_FILE_SCANF(pathover, "%ld", &nr_overcommit_hugepages);
247 	tst_res(TINFO, "original nr_overcommit_hugepages is %ld",
248 		nr_overcommit_hugepages);
249 
250 	/* Reset. */
251 	SAFE_FILE_PRINTF(pathover, "%ld", size);
252 	restore_overcomm_hgpgs = 1;
253 
254 	SAFE_MKDIR(MOUNT_DIR, 0700);
255 	SAFE_MOUNT(NULL, MOUNT_DIR, "hugetlbfs", 0, NULL);
256 	mounted = 1;
257 
258 	if (opt_shmid) {
259 		/* Use /proc/meminfo to generate an IPC key. */
260 		key = ftok(PATH_MEMINFO, strlen(PATH_MEMINFO));
261 		if (key == -1)
262 			tst_brk(TBROK | TERRNO, "ftok");
263 	}
264 }
265 
check_wr_bytes(void * addr)266 static void check_wr_bytes(void *addr)
267 {
268 	long i;
269 
270 	memset((char *)addr, '\a', (length / 2 * hugepagesize));
271 
272 	tst_res(TINFO, "First hex is %x", *((unsigned int *)addr));
273 	for (i = 0; i < (length / 2 * hugepagesize); i++) {
274 		if (((char *)addr)[i] != '\a') {
275 			tst_res(TFAIL, "mismatch at %ld", i);
276 			break;
277 		}
278 	}
279 }
280 
checksys(char * path,char * string,long exp_val)281 static int checksys(char *path, char *string, long exp_val)
282 {
283 	long act_val;
284 
285 	SAFE_FILE_SCANF(path, "%ld", &act_val);
286 	tst_res(TINFO, "%s is %ld.", string, act_val);
287 	if (act_val != exp_val) {
288 		tst_res(TFAIL, "%s is not %ld but %ld.", string, exp_val,
289 			act_val);
290 		return 1;
291 	}
292 	return 0;
293 }
294 
checkproc(long act_val,char * pattern,long exp_val)295 static int checkproc(long act_val, char *pattern, long exp_val)
296 {
297 	tst_res(TINFO, "%s is %ld.", pattern, act_val);
298 	if (act_val != exp_val) {
299 		tst_res(TFAIL, "%s is not %ld but %ld.",
300 			pattern, exp_val, act_val);
301 		return 1;
302 	}
303 	return 0;
304 }
305 
init_sys_sz_paths(void)306 static void init_sys_sz_paths(void)
307 {
308 	sprintf(path_sys_sz, "/sys/kernel/mm/hugepages/hugepages-%ldkB",
309 		hugepagesize / 1024);
310 	sprintf(path_sys_sz_over, "%s/nr_overcommit_hugepages", path_sys_sz);
311 	sprintf(path_sys_sz_free, "%s/free_hugepages", path_sys_sz);
312 	sprintf(path_sys_sz_resv, "%s/resv_hugepages", path_sys_sz);
313 	sprintf(path_sys_sz_surp, "%s/surplus_hugepages", path_sys_sz);
314 	sprintf(path_sys_sz_huge, "%s/nr_hugepages", path_sys_sz);
315 }
316 
317 static struct tst_test test = {
318 	.needs_root = 1,
319 	.needs_tmpdir = 1,
320 	.options = options,
321 	.setup = setup,
322 	.cleanup = cleanup,
323 	.test_all = test_overcommit,
324 };
325