1 | /*
|
---|
2 | * libhugetlbfs - Easy use of Linux hugepages
|
---|
3 | * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public License
|
---|
7 | * as published by the Free Software Foundation; either version 2.1 of
|
---|
8 | * the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library is distributed in the hope that it will be useful, but
|
---|
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
18 | */
|
---|
19 |
|
---|
20 | #define _LARGEFILE64_SOURCE
|
---|
21 | #define _GNU_SOURCE
|
---|
22 |
|
---|
23 | #include <stdio.h>
|
---|
24 | #include <stdlib.h>
|
---|
25 | #include <limits.h>
|
---|
26 | #include <string.h>
|
---|
27 | #include <errno.h>
|
---|
28 | #include <ctype.h>
|
---|
29 | #include <unistd.h>
|
---|
30 | #include <signal.h>
|
---|
31 | #include <sys/types.h>
|
---|
32 | //#include <sys/vfs.h>
|
---|
33 | #include <sys/ipc.h>
|
---|
34 | //#include <sys/shm.h>
|
---|
35 | #include <sys/stat.h>
|
---|
36 | //#include <sys/sysinfo.h>
|
---|
37 | #include <sys/types.h>
|
---|
38 | #include <sys/mman.h>
|
---|
39 | #include <fcntl.h>
|
---|
40 |
|
---|
41 | //#include "hugetlbfs.h"
|
---|
42 | #include "hugetests.h"
|
---|
43 |
|
---|
44 | #define HUGETLBFS_MAGIC 0x958458f6
|
---|
45 | #define BUF_SZ 1024
|
---|
46 | #define MEMINFO_SZ 2048
|
---|
47 |
|
---|
48 | int verbose_test = 1;
|
---|
49 | char *test_name;
|
---|
50 |
|
---|
51 | void check_must_be_root(void)
|
---|
52 | {
|
---|
53 | uid_t uid = getuid();
|
---|
54 | if (uid != 0)
|
---|
55 | CONFIG("Must be root");
|
---|
56 | }
|
---|
57 |
|
---|
58 |
|
---|
59 | #if 0
|
---|
60 | void check_hugetlb_shm_group(void)
|
---|
61 | {
|
---|
62 | int fd;
|
---|
63 | ssize_t ret;
|
---|
64 | char gid_buffer[64] = {0};
|
---|
65 | gid_t hugetlb_shm_group;
|
---|
66 | gid_t gid = getgid();
|
---|
67 | uid_t uid = getuid();
|
---|
68 |
|
---|
69 | /* root is an exception */
|
---|
70 | if (uid == 0)
|
---|
71 | return;
|
---|
72 |
|
---|
73 | fd = open("/proc/sys/vm/hugetlb_shm_group", O_RDONLY);
|
---|
74 | if (fd < 0)
|
---|
75 | ERROR("Unable to open /proc/sys/vm/hugetlb_shm_group: %s",
|
---|
76 | strerror(errno));
|
---|
77 | ret = read(fd, &gid_buffer, sizeof(gid_buffer));
|
---|
78 | if (ret < 0)
|
---|
79 | ERROR("Unable to read /proc/sys/vm/hugetlb_shm_group: %s",
|
---|
80 | strerror(errno));
|
---|
81 | hugetlb_shm_group = atoi(gid_buffer);
|
---|
82 | close(fd);
|
---|
83 | if (hugetlb_shm_group != gid)
|
---|
84 | CONFIG("Do not have permission to use SHM_HUGETLB");
|
---|
85 | }
|
---|
86 |
|
---|
87 | #define SYSFS_CPU_ONLINE_FMT "/sys/devices/system/cpu/cpu%d/online"
|
---|
88 |
|
---|
89 | void check_online_cpus(int online_cpus[], int nr_cpus_needed)
|
---|
90 | {
|
---|
91 | char cpu_state, path_buf[64];
|
---|
92 | int total_cpus, cpu_idx, fd, ret, i;
|
---|
93 |
|
---|
94 | total_cpus = get_nprocs_conf();
|
---|
95 | cpu_idx = 0;
|
---|
96 |
|
---|
97 | if (get_nprocs() < nr_cpus_needed)
|
---|
98 | CONFIG("Atleast online %d cpus are required", nr_cpus_needed);
|
---|
99 |
|
---|
100 | for (i = 0; i < total_cpus && cpu_idx < nr_cpus_needed; i++) {
|
---|
101 | errno = 0;
|
---|
102 | sprintf(path_buf, SYSFS_CPU_ONLINE_FMT, i);
|
---|
103 | fd = open(path_buf, O_RDONLY);
|
---|
104 | if (fd < 0) {
|
---|
105 | /* If 'online' is absent, the cpu cannot be offlined */
|
---|
106 | if (errno == ENOENT) {
|
---|
107 | online_cpus[cpu_idx] = i;
|
---|
108 | cpu_idx++;
|
---|
109 | continue;
|
---|
110 | } else {
|
---|
111 | FAIL("Unable to open %s: %s", path_buf,
|
---|
112 | strerror(errno));
|
---|
113 | }
|
---|
114 | }
|
---|
115 |
|
---|
116 | ret = read(fd, &cpu_state, 1);
|
---|
117 | if (ret < 1)
|
---|
118 | FAIL("Unable to read %s: %s", path_buf,
|
---|
119 | strerror(errno));
|
---|
120 |
|
---|
121 | if (cpu_state == '1') {
|
---|
122 | online_cpus[cpu_idx] = i;
|
---|
123 | cpu_idx++;
|
---|
124 | }
|
---|
125 |
|
---|
126 | close(fd);
|
---|
127 | }
|
---|
128 |
|
---|
129 | if (cpu_idx < nr_cpus_needed)
|
---|
130 | CONFIG("Atleast %d online cpus were not found", nr_cpus_needed);
|
---|
131 | }
|
---|
132 | #endif
|
---|
133 |
|
---|
134 | void __attribute__((weak)) cleanup(void)
|
---|
135 | {
|
---|
136 | }
|
---|
137 |
|
---|
138 | #if 0
|
---|
139 | static void segv_handler(int signum, siginfo_t *si, void *uc)
|
---|
140 | {
|
---|
141 | FAIL("Segmentation fault");
|
---|
142 | }
|
---|
143 | #endif
|
---|
144 |
|
---|
145 | static void sigint_handler(int signum, siginfo_t *si, void *uc)
|
---|
146 | {
|
---|
147 | cleanup();
|
---|
148 | fprintf(stderr, "%s: %s (pid=%d)\n", test_name,
|
---|
149 | strsignal(signum), getpid());
|
---|
150 | exit(RC_BUG);
|
---|
151 | }
|
---|
152 |
|
---|
153 | void test_init(int argc, char *argv[])
|
---|
154 | {
|
---|
155 | int err;
|
---|
156 | struct sigaction sa_int = {
|
---|
157 | .sa_sigaction = sigint_handler,
|
---|
158 | };
|
---|
159 |
|
---|
160 | test_name = argv[0];
|
---|
161 |
|
---|
162 | err = sigaction(SIGINT, &sa_int, NULL);
|
---|
163 | if (err)
|
---|
164 | FAIL("Can't install SIGINT handler: %s", strerror(errno));
|
---|
165 |
|
---|
166 | if (getenv("QUIET_TEST"))
|
---|
167 | verbose_test = 0;
|
---|
168 |
|
---|
169 | verbose_printf("Starting testcase \"%s\", pid %d\n",
|
---|
170 | test_name, getpid());
|
---|
171 | }
|
---|
172 |
|
---|
173 |
|
---|
174 | #if 0
|
---|
175 |
|
---|
176 | #define MAPS_BUF_SZ 4096
|
---|
177 |
|
---|
178 | static int read_maps(unsigned long addr, char *buf)
|
---|
179 | {
|
---|
180 | FILE *f;
|
---|
181 | char line[MAPS_BUF_SZ];
|
---|
182 | char *tmp;
|
---|
183 |
|
---|
184 | f = fopen("/proc/self/maps", "r");
|
---|
185 | if (!f) {
|
---|
186 | ERROR("Failed to open /proc/self/maps: %s\n", strerror(errno));
|
---|
187 | return -1;
|
---|
188 | }
|
---|
189 |
|
---|
190 | while (1) {
|
---|
191 | unsigned long start, end, off, ino;
|
---|
192 | int ret;
|
---|
193 |
|
---|
194 | tmp = fgets(line, MAPS_BUF_SZ, f);
|
---|
195 | if (!tmp)
|
---|
196 | break;
|
---|
197 |
|
---|
198 | buf[0] = '\0';
|
---|
199 | ret = sscanf(line, "%lx-%lx %*s %lx %*s %ld %255s",
|
---|
200 | &start, &end, &off, &ino,
|
---|
201 | buf);
|
---|
202 | if ((ret < 4) || (ret > 5)) {
|
---|
203 | ERROR("Couldn't parse /proc/self/maps line: %s\n",
|
---|
204 | line);
|
---|
205 | fclose(f);
|
---|
206 | return -1;
|
---|
207 | }
|
---|
208 |
|
---|
209 | if ((start <= addr) && (addr < end)) {
|
---|
210 | fclose(f);
|
---|
211 | return 1;
|
---|
212 | }
|
---|
213 | }
|
---|
214 |
|
---|
215 | fclose(f);
|
---|
216 | return 0;
|
---|
217 | }
|
---|
218 |
|
---|
219 | int range_is_mapped(unsigned long low, unsigned long high)
|
---|
220 | {
|
---|
221 | FILE *f;
|
---|
222 | char line[MAPS_BUF_SZ];
|
---|
223 | char *tmp;
|
---|
224 |
|
---|
225 | f = fopen("/proc/self/maps", "r");
|
---|
226 | if (!f) {
|
---|
227 | ERROR("Failed to open /proc/self/maps: %s\n", strerror(errno));
|
---|
228 | return -1;
|
---|
229 | }
|
---|
230 |
|
---|
231 | while (1) {
|
---|
232 | unsigned long start, end;
|
---|
233 | int ret;
|
---|
234 |
|
---|
235 | tmp = fgets(line, MAPS_BUF_SZ, f);
|
---|
236 | if (!tmp)
|
---|
237 | break;
|
---|
238 |
|
---|
239 | ret = sscanf(line, "%lx-%lx", &start, &end);
|
---|
240 | if (ret != 2) {
|
---|
241 | ERROR("Couldn't parse /proc/self/maps line: %s\n",
|
---|
242 | line);
|
---|
243 | fclose(f);
|
---|
244 | return -1;
|
---|
245 | }
|
---|
246 |
|
---|
247 | if ((start >= low) && (start < high)) {
|
---|
248 | fclose(f);
|
---|
249 | return 1;
|
---|
250 | }
|
---|
251 | if ((end >= low) && (end < high)) {
|
---|
252 | fclose(f);
|
---|
253 | return 1;
|
---|
254 | }
|
---|
255 |
|
---|
256 | }
|
---|
257 |
|
---|
258 | fclose(f);
|
---|
259 | return 0;
|
---|
260 | }
|
---|
261 |
|
---|
262 | /*
|
---|
263 | * With the inclusion of MAP_HUGETLB it is now possible to have huge pages
|
---|
264 | * without using hugetlbfs, so not all huge page regions will show with the
|
---|
265 | * test that reads /proc/self/maps. Instead we ask /proc/self/smaps for
|
---|
266 | * the KernelPageSize. On success we return the page size (in bytes) for the
|
---|
267 | * mapping that contains addr, on failure we return 0
|
---|
268 | */
|
---|
269 | unsigned long long get_mapping_page_size(void *p)
|
---|
270 | {
|
---|
271 | FILE *f;
|
---|
272 | char line[MAPS_BUF_SZ];
|
---|
273 | char *tmp;
|
---|
274 | unsigned long addr = (unsigned long)p;
|
---|
275 |
|
---|
276 | f = fopen("/proc/self/smaps", "r");
|
---|
277 | if (!f) {
|
---|
278 | ERROR("Unable to open /proc/self/smaps\n");
|
---|
279 | return 0;
|
---|
280 | }
|
---|
281 |
|
---|
282 | while ((tmp = fgets(line, MAPS_BUF_SZ, f))) {
|
---|
283 | unsigned long start, end, dummy;
|
---|
284 | char map_name[256];
|
---|
285 | char buf[64];
|
---|
286 | int ret;
|
---|
287 |
|
---|
288 | ret = sscanf(line, "%lx-%lx %s %lx %s %ld %s", &start, &end,
|
---|
289 | buf, &dummy, buf, &dummy, map_name);
|
---|
290 | if (ret < 7 || start > addr || end <= addr)
|
---|
291 | continue;
|
---|
292 |
|
---|
293 | while ((tmp = fgets(line, MAPS_BUF_SZ, f))) {
|
---|
294 | unsigned long long page_size;
|
---|
295 |
|
---|
296 | ret = sscanf(line, "KernelPageSize: %lld kB",
|
---|
297 | &page_size);
|
---|
298 | if (ret == 0 )
|
---|
299 | continue;
|
---|
300 | if (ret < 1 || page_size <= 0) {
|
---|
301 | ERROR("Cannot parse /proc/self/smaps\n");
|
---|
302 | page_size = 0;
|
---|
303 | }
|
---|
304 |
|
---|
305 | fclose(f);
|
---|
306 | /* page_size is reported in kB, we return B */
|
---|
307 | return page_size * 1024;
|
---|
308 | }
|
---|
309 | }
|
---|
310 |
|
---|
311 | /* We couldn't find an entry for this addr in smaps */
|
---|
312 | fclose(f);
|
---|
313 | return 0;
|
---|
314 | }
|
---|
315 |
|
---|
316 | /* We define this function standalone, rather than in terms of
|
---|
317 | * hugetlbfs_test_path() so that we can use it without -lhugetlbfs for
|
---|
318 | * testing PRELOAD */
|
---|
319 | int test_addr_huge(void *p)
|
---|
320 | {
|
---|
321 | char name[256];
|
---|
322 | char *dirend;
|
---|
323 | int ret;
|
---|
324 | struct statfs64 sb;
|
---|
325 |
|
---|
326 | ret = read_maps((unsigned long)p, name);
|
---|
327 | if (ret < 0)
|
---|
328 | return ret;
|
---|
329 | if (ret == 0) {
|
---|
330 | verbose_printf("Couldn't find address %p in /proc/self/maps\n",
|
---|
331 | p);
|
---|
332 | return -1;
|
---|
333 | }
|
---|
334 |
|
---|
335 | /* looks like a filename? */
|
---|
336 | if (name[0] != '/')
|
---|
337 | return 0;
|
---|
338 |
|
---|
339 | /* Truncate the filename portion */
|
---|
340 |
|
---|
341 | dirend = strrchr(name, '/');
|
---|
342 | if (dirend && dirend > name) {
|
---|
343 | *dirend = '\0';
|
---|
344 | }
|
---|
345 |
|
---|
346 | ret = statfs64(name, &sb);
|
---|
347 | if (ret)
|
---|
348 | return -1;
|
---|
349 |
|
---|
350 | return (sb.f_type == HUGETLBFS_MAGIC);
|
---|
351 | }
|
---|
352 |
|
---|
353 | ino_t get_addr_inode(void *p)
|
---|
354 | {
|
---|
355 | char name[256];
|
---|
356 | int ret;
|
---|
357 | struct stat sb;
|
---|
358 |
|
---|
359 | ret = read_maps((unsigned long)p, name);
|
---|
360 | if (ret < 0)
|
---|
361 | return ret;
|
---|
362 | if (ret == 0) {
|
---|
363 | ERROR("Couldn't find address %p in /proc/self/maps\n", p);
|
---|
364 | return -1;
|
---|
365 | }
|
---|
366 |
|
---|
367 | /* Don't care about non-filenames */
|
---|
368 | if (name[0] != '/')
|
---|
369 | return 0;
|
---|
370 |
|
---|
371 | /* Truncate the filename portion */
|
---|
372 |
|
---|
373 | ret = stat(name, &sb);
|
---|
374 | if (ret < 0) {
|
---|
375 | /* Don't care about unlinked files */
|
---|
376 | if (errno == ENOENT)
|
---|
377 | return 0;
|
---|
378 | ERROR("stat failed: %s\n", strerror(errno));
|
---|
379 | return -1;
|
---|
380 | }
|
---|
381 |
|
---|
382 | return sb.st_ino;
|
---|
383 | }
|
---|
384 |
|
---|
385 |
|
---|
386 | int remove_shmid(int shmid)
|
---|
387 | {
|
---|
388 | if (shmid >= 0) {
|
---|
389 | if (shmctl(shmid, IPC_RMID, NULL) != 0) {
|
---|
390 | ERROR("shmctl(%x, IPC_RMID) failed (%s)\n",
|
---|
391 | shmid, strerror(errno));
|
---|
392 | return -1;
|
---|
393 | }
|
---|
394 | }
|
---|
395 | return 0;
|
---|
396 | }
|
---|
397 | #endif
|
---|
398 |
|
---|
399 | #if 1
|
---|
400 | int hugetlbfs_unlinked_fd(int size)
|
---|
401 | {
|
---|
402 | const char *path;
|
---|
403 | char name[PATH_MAX+1];
|
---|
404 | int fd;
|
---|
405 |
|
---|
406 | path = "/tmp";
|
---|
407 | if (!path)
|
---|
408 | return -1;
|
---|
409 |
|
---|
410 | name[sizeof(name)-1] = '\0';
|
---|
411 |
|
---|
412 | strcpy(name, path);
|
---|
413 | strncat(name, "/libhugetlbfs.tmp.XXXXXX", sizeof(name)-1);
|
---|
414 | /* FIXME: deal with overflows */
|
---|
415 |
|
---|
416 | fd = mkstemp(name);
|
---|
417 |
|
---|
418 | if (fd < 0) {
|
---|
419 | ERROR("mkstemp() failed: %s\n", strerror(errno));
|
---|
420 | return -1;
|
---|
421 | }
|
---|
422 |
|
---|
423 | ftruncate(fd, size);
|
---|
424 |
|
---|
425 | unlink(name);
|
---|
426 |
|
---|
427 | return fd;
|
---|
428 | }
|
---|
429 |
|
---|
430 |
|
---|
431 | #endif
|
---|