Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2017-2018 Intel Corporation
3 : : */
4 : :
5 : : #include <errno.h>
6 : : #include <stdbool.h>
7 : : #include <stdlib.h>
8 : : #include <stdio.h>
9 : : #include <stdint.h>
10 : : #include <inttypes.h>
11 : : #include <string.h>
12 : : #include <sys/mman.h>
13 : : #include <sys/stat.h>
14 : : #include <sys/file.h>
15 : : #include <unistd.h>
16 : : #include <limits.h>
17 : : #include <fcntl.h>
18 : : #include <signal.h>
19 : : #include <setjmp.h>
20 : : #include <linux/memfd.h>
21 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
22 : : #include <numa.h>
23 : : #include <numaif.h>
24 : : #endif
25 : : #include <linux/falloc.h>
26 : : #include <linux/mman.h> /* for hugetlb-related mmap flags */
27 : :
28 : : #include <rte_common.h>
29 : : #include <rte_log.h>
30 : : #include <rte_eal.h>
31 : : #include <rte_memory.h>
32 : : #include <rte_cycles.h>
33 : :
34 : : #include "eal_filesystem.h"
35 : : #include "eal_internal_cfg.h"
36 : : #include "eal_memalloc.h"
37 : : #include "eal_memcfg.h"
38 : : #include "eal_private.h"
39 : :
40 : : const int anonymous_hugepages_supported =
41 : : #ifdef MAP_HUGE_SHIFT
42 : : 1;
43 : : #define RTE_MAP_HUGE_SHIFT MAP_HUGE_SHIFT
44 : : #else
45 : : 0;
46 : : #define RTE_MAP_HUGE_SHIFT 26
47 : : #endif
48 : :
49 : : /*
50 : : * not all kernel version support fallocate on hugetlbfs, so fall back to
51 : : * ftruncate and disallow deallocation if fallocate is not supported.
52 : : */
53 : : static int fallocate_supported = -1; /* unknown */
54 : :
55 : : /*
56 : : * we have two modes - single file segments, and file-per-page mode.
57 : : *
58 : : * for single-file segments, we use memseg_list_fd to store the segment fd,
59 : : * while the fds[] will not be allocated, and len will be set to 0.
60 : : *
61 : : * for file-per-page mode, each page will have its own fd, so 'memseg_list_fd'
62 : : * will be invalid (set to -1), and we'll use 'fds' to keep track of page fd's.
63 : : *
64 : : * we cannot know how many pages a system will have in advance, but we do know
65 : : * that they come in lists, and we know lengths of these lists. so, simply store
66 : : * a malloc'd array of fd's indexed by list and segment index.
67 : : *
68 : : * they will be initialized at startup, and filled as we allocate/deallocate
69 : : * segments.
70 : : */
71 : : static struct {
72 : : int *fds; /**< dynamically allocated array of segment lock fd's */
73 : : int memseg_list_fd; /**< memseg list fd */
74 : : int len; /**< total length of the array */
75 : : int count; /**< entries used in an array */
76 : : } fd_list[RTE_MAX_MEMSEG_LISTS];
77 : :
78 : : /** local copy of a memory map, used to synchronize memory hotplug in MP */
79 : : static struct rte_memseg_list local_memsegs[RTE_MAX_MEMSEG_LISTS];
80 : :
81 : : static sigjmp_buf huge_jmpenv;
82 : :
83 : 0 : static void huge_sigbus_handler(int signo __rte_unused)
84 : : {
85 : 0 : siglongjmp(huge_jmpenv, 1);
86 : : }
87 : :
88 : : /* Put setjmp into a wrap method to avoid compiling error. Any non-volatile,
89 : : * non-static local variable in the stack frame calling sigsetjmp might be
90 : : * clobbered by a call to longjmp.
91 : : */
92 : 1082 : static int huge_wrap_sigsetjmp(void)
93 : : {
94 : 1082 : return sigsetjmp(huge_jmpenv, 1);
95 : : }
96 : :
97 : : static struct sigaction huge_action_old;
98 : : static int huge_need_recover;
99 : :
100 : : static void
101 : 1082 : huge_register_sigbus(void)
102 : : {
103 : : sigset_t mask;
104 : : struct sigaction action;
105 : :
106 : 1082 : sigemptyset(&mask);
107 : 1082 : sigaddset(&mask, SIGBUS);
108 : 1082 : action.sa_flags = 0;
109 : 1082 : action.sa_mask = mask;
110 : 1082 : action.sa_handler = huge_sigbus_handler;
111 : :
112 : 1082 : huge_need_recover = !sigaction(SIGBUS, &action, &huge_action_old);
113 : 1082 : }
114 : :
115 : : static void
116 : : huge_recover_sigbus(void)
117 : : {
118 [ + - ]: 1081 : if (huge_need_recover) {
119 : 1082 : sigaction(SIGBUS, &huge_action_old, NULL);
120 : 1082 : huge_need_recover = 0;
121 : : }
122 : : }
123 : :
124 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
125 : : static bool
126 : 1588 : check_numa(void)
127 : : {
128 : : bool ret = true;
129 : : /* Check if kernel supports NUMA. */
130 [ - + ]: 1588 : if (numa_available() != 0) {
131 : 0 : EAL_LOG(DEBUG, "NUMA is not supported.");
132 : : ret = false;
133 : : }
134 : 1588 : return ret;
135 : : }
136 : :
137 : : static void
138 : 506 : prepare_numa(int *oldpolicy, struct bitmask *oldmask, int socket_id)
139 : : {
140 : 506 : EAL_LOG(DEBUG, "Trying to obtain current memory policy.");
141 [ - + ]: 506 : if (get_mempolicy(oldpolicy, oldmask->maskp,
142 : 506 : oldmask->size + 1, 0, 0) < 0) {
143 : 0 : EAL_LOG(ERR,
144 : : "Failed to get current mempolicy: %s. "
145 : : "Assuming MPOL_DEFAULT.", strerror(errno));
146 : 0 : *oldpolicy = MPOL_DEFAULT;
147 : : }
148 : 506 : EAL_LOG(DEBUG,
149 : : "Setting policy MPOL_PREFERRED for socket %d",
150 : : socket_id);
151 : 506 : numa_set_preferred(socket_id);
152 : 506 : }
153 : :
154 : : static void
155 : 506 : restore_numa(int *oldpolicy, struct bitmask *oldmask)
156 : : {
157 : 506 : EAL_LOG(DEBUG,
158 : : "Restoring previous memory policy: %d", *oldpolicy);
159 [ + - ]: 506 : if (*oldpolicy == MPOL_DEFAULT) {
160 : 506 : numa_set_localalloc();
161 [ # # ]: 0 : } else if (set_mempolicy(*oldpolicy, oldmask->maskp,
162 : 0 : oldmask->size + 1) < 0) {
163 : 0 : EAL_LOG(ERR, "Failed to restore mempolicy: %s",
164 : : strerror(errno));
165 : 0 : numa_set_localalloc();
166 : : }
167 : : numa_free_cpumask(oldmask);
168 : 506 : }
169 : : #endif
170 : :
171 : : /*
172 : : * uses fstat to report the size of a file on disk
173 : : */
174 : : static off_t
175 : : get_file_size(int fd)
176 : : {
177 : : struct stat st;
178 [ # # ]: 0 : if (fstat(fd, &st) < 0)
179 : : return 0;
180 : 0 : return st.st_size;
181 : : }
182 : :
183 : : static int
184 [ + - ]: 54 : pagesz_flags(uint64_t page_sz)
185 : : {
186 : : /* as per mmap() manpage, all page sizes are log2 of page size
187 : : * shifted by MAP_HUGE_SHIFT
188 : : */
189 : 54 : int log2 = rte_log2_u64(page_sz);
190 : 54 : return log2 << RTE_MAP_HUGE_SHIFT;
191 : : }
192 : :
193 : : /* returns 1 on successful lock, 0 on unsuccessful lock, -1 on error */
194 : 2045 : static int lock(int fd, int type)
195 : : {
196 : : int ret;
197 : :
198 : : /* flock may be interrupted */
199 : : do {
200 : 2045 : ret = flock(fd, type | LOCK_NB);
201 [ - + - - ]: 2045 : } while (ret && errno == EINTR);
202 : :
203 [ - + - - ]: 2045 : if (ret && errno == EWOULDBLOCK) {
204 : : /* couldn't lock */
205 : : return 0;
206 [ - + ]: 2045 : } else if (ret) {
207 : 0 : EAL_LOG(ERR, "%s(): error calling flock(): %s",
208 : : __func__, strerror(errno));
209 : 0 : return -1;
210 : : }
211 : : /* lock was successful */
212 : : return 1;
213 : : }
214 : :
215 : : static int
216 : 54 : get_seg_memfd(struct hugepage_info *hi __rte_unused,
217 : : unsigned int list_idx __rte_unused,
218 : : unsigned int seg_idx __rte_unused)
219 : : {
220 : : int fd;
221 : : char segname[250]; /* as per manpage, limit is 249 bytes plus null */
222 : :
223 : 54 : int flags = MFD_HUGETLB | pagesz_flags(hi->hugepage_sz);
224 : : const struct internal_config *internal_conf =
225 : 54 : eal_get_internal_configuration();
226 : :
227 [ - + ]: 54 : if (internal_conf->single_file_segments) {
228 : 0 : fd = fd_list[list_idx].memseg_list_fd;
229 : :
230 [ # # ]: 0 : if (fd < 0) {
231 : : snprintf(segname, sizeof(segname), "seg_%i", list_idx);
232 : 0 : fd = memfd_create(segname, flags);
233 [ # # ]: 0 : if (fd < 0) {
234 : 0 : EAL_LOG(DEBUG, "%s(): memfd create failed: %s",
235 : : __func__, strerror(errno));
236 : 0 : return -1;
237 : : }
238 : 0 : fd_list[list_idx].memseg_list_fd = fd;
239 : : }
240 : : } else {
241 : 54 : fd = fd_list[list_idx].fds[seg_idx];
242 : :
243 [ + + ]: 54 : if (fd < 0) {
244 : : snprintf(segname, sizeof(segname), "seg_%i-%i",
245 : : list_idx, seg_idx);
246 : 27 : fd = memfd_create(segname, flags);
247 [ - + ]: 27 : if (fd < 0) {
248 : 0 : EAL_LOG(DEBUG, "%s(): memfd create failed: %s",
249 : : __func__, strerror(errno));
250 : 0 : return -1;
251 : : }
252 : 27 : fd_list[list_idx].fds[seg_idx] = fd;
253 : : }
254 : : }
255 : : return fd;
256 : : }
257 : :
258 : : static int
259 : 2115 : get_seg_fd(char *path, int buflen, struct hugepage_info *hi,
260 : : unsigned int list_idx, unsigned int seg_idx,
261 : : bool *dirty)
262 : : {
263 : : int fd;
264 : : int *out_fd;
265 : : const char *huge_path;
266 : : struct stat st;
267 : : int ret;
268 : : const struct internal_config *internal_conf =
269 : 2115 : eal_get_internal_configuration();
270 : :
271 [ + + ]: 2115 : if (dirty != NULL)
272 : 1082 : *dirty = false;
273 : :
274 : : /* for in-memory mode, we only make it here when we're sure we support
275 : : * memfd, and this is a special case.
276 : : */
277 [ + + ]: 2115 : if (internal_conf->in_memory)
278 : 54 : return get_seg_memfd(hi, list_idx, seg_idx);
279 : :
280 [ + + ]: 2061 : if (internal_conf->single_file_segments) {
281 : 18 : out_fd = &fd_list[list_idx].memseg_list_fd;
282 : 18 : huge_path = eal_get_hugefile_path(path, buflen, hi->hugedir, list_idx);
283 : : } else {
284 : 2043 : out_fd = &fd_list[list_idx].fds[seg_idx];
285 : 2043 : huge_path = eal_get_hugefile_path(path, buflen, hi->hugedir,
286 : 2043 : list_idx * RTE_MAX_MEMSEG_PER_LIST + seg_idx);
287 : : }
288 [ - + ]: 2061 : if (huge_path == NULL) {
289 : 0 : EAL_LOG(DEBUG, "%s(): hugefile path truncated: '%s'",
290 : : __func__, path);
291 : 0 : return -1;
292 : : }
293 : :
294 : 2061 : fd = *out_fd;
295 [ + + ]: 2061 : if (fd >= 0)
296 : : return fd;
297 : :
298 : : /*
299 : : * There is no TOCTOU between stat() and unlink()/open()
300 : : * because the hugepage directory is locked.
301 : : */
302 : 1047 : ret = stat(path, &st);
303 [ + + - + ]: 1047 : if (ret < 0 && errno != ENOENT) {
304 : 0 : EAL_LOG(DEBUG, "%s(): stat() for '%s' failed: %s",
305 : : __func__, path, strerror(errno));
306 : 0 : return -1;
307 : : }
308 [ + + ]: 1047 : if (!internal_conf->hugepage_file.unlink_existing && ret == 0 &&
309 [ - + ]: 9 : dirty != NULL)
310 : 0 : *dirty = true;
311 : :
312 : : /*
313 : : * The kernel clears a hugepage only when it is mapped
314 : : * from a particular file for the first time.
315 : : * If the file already exists, the old content will be mapped.
316 : : * If the memory manager assumes all mapped pages to be clean,
317 : : * the file must be removed and created anew.
318 : : * Otherwise, the primary caller must be notified
319 : : * that mapped pages will be dirty
320 : : * (secondary callers receive the segment state from the primary one).
321 : : * When multiple hugepages are mapped from the same file,
322 : : * whether they will be dirty depends on the part that is mapped.
323 : : */
324 [ + + ]: 1047 : if (!internal_conf->single_file_segments &&
325 [ + + + + ]: 2083 : internal_conf->hugepage_file.unlink_existing &&
326 [ - + ]: 2046 : rte_eal_process_type() == RTE_PROC_PRIMARY &&
327 : : ret == 0) {
328 : : /* coverity[toctou] */
329 [ # # ]: 0 : if (unlink(path) < 0) {
330 : 0 : EAL_LOG(DEBUG, "%s(): could not remove '%s': %s",
331 : : __func__, path, strerror(errno));
332 : 0 : return -1;
333 : : }
334 : : }
335 : :
336 : : /* coverity[toctou] */
337 : : fd = open(path, O_CREAT | O_RDWR, 0600);
338 [ - + ]: 1047 : if (fd < 0) {
339 : 0 : EAL_LOG(ERR, "%s(): open '%s' failed: %s",
340 : : __func__, path, strerror(errno));
341 : 0 : return -1;
342 : : }
343 : : /* take out a read lock */
344 [ - + ]: 1047 : if (lock(fd, LOCK_SH) < 0) {
345 : 0 : EAL_LOG(ERR, "%s(): lock '%s' failed: %s",
346 : : __func__, path, strerror(errno));
347 : 0 : close(fd);
348 : 0 : return -1;
349 : : }
350 : 1047 : *out_fd = fd;
351 : 1047 : return fd;
352 : : }
353 : :
354 : : static int
355 : 0 : resize_hugefile_in_memory(int fd, uint64_t fa_offset,
356 : : uint64_t page_sz, bool grow)
357 : : {
358 [ # # ]: 0 : int flags = grow ? 0 : FALLOC_FL_PUNCH_HOLE |
359 : : FALLOC_FL_KEEP_SIZE;
360 : : int ret;
361 : :
362 : : /* grow or shrink the file */
363 : 0 : ret = fallocate(fd, flags, fa_offset, page_sz);
364 : :
365 [ # # ]: 0 : if (ret < 0) {
366 : 0 : EAL_LOG(DEBUG, "%s(): fallocate() failed: %s",
367 : : __func__,
368 : : strerror(errno));
369 : 0 : return -1;
370 : : }
371 : : return 0;
372 : : }
373 : :
374 : : static int
375 : 18 : resize_hugefile_in_filesystem(int fd, uint64_t fa_offset, uint64_t page_sz,
376 : : bool grow, bool *dirty)
377 : : {
378 : : const struct internal_config *internal_conf =
379 : 18 : eal_get_internal_configuration();
380 : : bool again = false;
381 : :
382 : : do {
383 [ - + ]: 18 : if (fallocate_supported == 0) {
384 : : /* we cannot deallocate memory if fallocate() is not
385 : : * supported, and hugepage file is already locked at
386 : : * creation, so no further synchronization needed.
387 : : */
388 : :
389 [ # # ]: 0 : if (!grow) {
390 : 0 : EAL_LOG(DEBUG, "%s(): fallocate not supported, not freeing page back to the system",
391 : : __func__);
392 : 0 : return -1;
393 : : }
394 : 0 : uint64_t new_size = fa_offset + page_sz;
395 : 0 : uint64_t cur_size = get_file_size(fd);
396 : :
397 : : /* fallocate isn't supported, fall back to ftruncate */
398 [ # # ]: 0 : if (dirty != NULL)
399 : 0 : *dirty = new_size <= cur_size;
400 [ # # # # ]: 0 : if (new_size > cur_size &&
401 : 0 : ftruncate(fd, new_size) < 0) {
402 : 0 : EAL_LOG(DEBUG, "%s(): ftruncate() failed: %s",
403 : : __func__, strerror(errno));
404 : 0 : return -1;
405 : : }
406 : : } else {
407 [ + + ]: 18 : int flags = grow ? 0 : FALLOC_FL_PUNCH_HOLE |
408 : : FALLOC_FL_KEEP_SIZE;
409 : : int ret;
410 : :
411 : : /*
412 : : * technically, it is perfectly safe for both primary
413 : : * and secondary to grow and shrink the page files:
414 : : * growing the file repeatedly has no effect because
415 : : * a page can only be allocated once, while mmap ensures
416 : : * that secondaries hold on to the page even after the
417 : : * page itself is removed from the filesystem.
418 : : *
419 : : * however, leaving growing/shrinking to the primary
420 : : * tends to expose bugs in fdlist page count handling,
421 : : * so leave this here just in case.
422 : : */
423 [ + - ]: 18 : if (rte_eal_process_type() != RTE_PROC_PRIMARY)
424 : : return 0;
425 : :
426 : : /* grow or shrink the file */
427 : 18 : ret = fallocate(fd, flags, fa_offset, page_sz);
428 : :
429 [ - + ]: 18 : if (ret < 0) {
430 [ # # ]: 0 : if (fallocate_supported == -1 &&
431 [ # # ]: 0 : errno == ENOTSUP) {
432 : 0 : EAL_LOG(ERR, "%s(): fallocate() not supported, hugepage deallocation will be disabled",
433 : : __func__);
434 : : again = true;
435 : 0 : fallocate_supported = 0;
436 : : } else {
437 : 0 : EAL_LOG(DEBUG, "%s(): fallocate() failed: %s",
438 : : __func__,
439 : : strerror(errno));
440 : 0 : return -1;
441 : : }
442 : : } else {
443 : 18 : fallocate_supported = 1;
444 : : /*
445 : : * It is unknown which portions of an existing
446 : : * hugepage file were allocated previously,
447 : : * so all pages within the file are considered
448 : : * dirty, unless the file is a fresh one.
449 : : */
450 [ + + ]: 18 : if (dirty != NULL)
451 : 9 : *dirty &= !internal_conf->hugepage_file.unlink_existing;
452 : : }
453 : : }
454 [ - + ]: 18 : } while (again);
455 : :
456 : : return 0;
457 : : }
458 : :
459 : : static void
460 : 1 : close_hugefile(int fd, char *path, int list_idx)
461 : : {
462 : : const struct internal_config *internal_conf =
463 : 1 : eal_get_internal_configuration();
464 : : /*
465 : : * primary process must unlink the file, but only when not in in-memory
466 : : * mode (as in that case there is no file to unlink).
467 : : */
468 [ + - + - ]: 2 : if (!internal_conf->in_memory &&
469 [ - + ]: 2 : rte_eal_process_type() == RTE_PROC_PRIMARY &&
470 : 1 : unlink(path))
471 : 0 : EAL_LOG(ERR, "%s(): unlinking '%s' failed: %s",
472 : : __func__, path, strerror(errno));
473 : :
474 : 1 : close(fd);
475 : 1 : fd_list[list_idx].memseg_list_fd = -1;
476 : 1 : }
477 : :
478 : : static int
479 : 18 : resize_hugefile(int fd, uint64_t fa_offset, uint64_t page_sz, bool grow,
480 : : bool *dirty)
481 : : {
482 : : /* in-memory mode is a special case, because we can be sure that
483 : : * fallocate() is supported.
484 : : */
485 : : const struct internal_config *internal_conf =
486 : 18 : eal_get_internal_configuration();
487 : :
488 [ - + ]: 18 : if (internal_conf->in_memory) {
489 [ # # ]: 0 : if (dirty != NULL)
490 : 0 : *dirty = false;
491 : 0 : return resize_hugefile_in_memory(fd, fa_offset,
492 : : page_sz, grow);
493 : : }
494 : :
495 : 18 : return resize_hugefile_in_filesystem(fd, fa_offset, page_sz,
496 : : grow, dirty);
497 : : }
498 : :
499 : : static int
500 : 1082 : alloc_seg(struct rte_memseg *ms, void *addr, int socket_id,
501 : : struct hugepage_info *hi, unsigned int list_idx,
502 : : unsigned int seg_idx)
503 : : {
504 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
505 : 1082 : int cur_socket_id = 0;
506 : : #endif
507 : : uint64_t map_offset;
508 : : rte_iova_t iova;
509 : : void *va;
510 : : char path[PATH_MAX];
511 : : int ret = 0;
512 : : int fd;
513 : : bool dirty;
514 : : size_t alloc_sz;
515 : : int flags;
516 : : void *new_addr;
517 : : const struct internal_config *internal_conf =
518 : 1082 : eal_get_internal_configuration();
519 : :
520 : 1082 : alloc_sz = hi->hugepage_sz;
521 : :
522 : : /* these are checked at init, but code analyzers don't know that */
523 : 1082 : if (internal_conf->in_memory && !anonymous_hugepages_supported) {
524 : : EAL_LOG(ERR, "Anonymous hugepages not supported, in-memory mode cannot allocate memory");
525 : : return -1;
526 : : }
527 : :
528 : : /* use memfd for in-memory mode */
529 : : int mmap_flags;
530 : :
531 : : /* takes out a read lock on segment or segment list */
532 : 1082 : fd = get_seg_fd(path, sizeof(path), hi, list_idx, seg_idx, &dirty);
533 [ - + ]: 1082 : if (fd < 0) {
534 : 0 : EAL_LOG(ERR, "Couldn't get fd on hugepage file");
535 : 0 : return -1;
536 : : }
537 : :
538 [ + + ]: 1082 : if (internal_conf->single_file_segments) {
539 : 9 : map_offset = seg_idx * alloc_sz;
540 : 9 : ret = resize_hugefile(fd, map_offset, alloc_sz, true, &dirty);
541 [ - + ]: 9 : if (ret < 0)
542 : 0 : goto resized;
543 : :
544 : 9 : fd_list[list_idx].count++;
545 : : } else {
546 : : map_offset = 0;
547 [ - + ]: 1073 : if (ftruncate(fd, alloc_sz) < 0) {
548 : 0 : EAL_LOG(DEBUG, "%s(): ftruncate() failed: %s", __func__, strerror(errno));
549 : 0 : goto resized;
550 : : }
551 [ + + ]: 1073 : if (internal_conf->hugepage_file.unlink_before_mapping &&
552 [ - + ]: 27 : !internal_conf->in_memory) {
553 [ # # ]: 0 : if (unlink(path)) {
554 : 0 : EAL_LOG(DEBUG, "%s(): unlink() failed: %s",
555 : : __func__, strerror(errno));
556 : 0 : goto resized;
557 : : }
558 : : }
559 : : }
560 : : mmap_flags = MAP_SHARED | MAP_POPULATE | MAP_FIXED;
561 : :
562 : 1082 : huge_register_sigbus();
563 : :
564 : : /*
565 : : * map the segment, and populate page tables, the kernel fills
566 : : * this segment with zeros if it's a new page.
567 : : */
568 : 1082 : va = mmap(addr, alloc_sz, PROT_READ | PROT_WRITE, mmap_flags, fd,
569 : : map_offset);
570 : :
571 [ - + ]: 1082 : if (va == MAP_FAILED) {
572 : 0 : EAL_LOG(DEBUG, "%s(): mmap() failed: %s", __func__,
573 : : strerror(errno));
574 : : /* mmap failed, but the previous region might have been
575 : : * unmapped anyway. try to remap it
576 : : */
577 : 0 : goto unmapped;
578 : : }
579 [ - + ]: 1082 : if (va != addr) {
580 : 0 : EAL_LOG(DEBUG, "%s(): wrong mmap() address", __func__);
581 : 0 : munmap(va, alloc_sz);
582 : 0 : goto resized;
583 : : }
584 : :
585 : : /* In linux, hugetlb limitations, like cgroup, are
586 : : * enforced at fault time instead of mmap(), even
587 : : * with the option of MAP_POPULATE. Kernel will send
588 : : * a SIGBUS signal. To avoid to be killed, save stack
589 : : * environment here, if SIGBUS happens, we can jump
590 : : * back here.
591 : : */
592 [ - + ]: 1082 : if (huge_wrap_sigsetjmp()) {
593 : 0 : EAL_LOG(DEBUG, "SIGBUS: Cannot mmap more hugepages of size %uMB",
594 : : (unsigned int)(alloc_sz >> 20));
595 : 0 : goto mapped;
596 : : }
597 : :
598 : : /* we need to trigger a write to the page to enforce page fault and
599 : : * ensure that page is accessible to us, but we can't overwrite value
600 : : * that is already there, so read the old value, and write itback.
601 : : * kernel populates the page with zeroes initially.
602 : : */
603 : 1082 : *(volatile int *)addr = *(volatile int *)addr;
604 : :
605 : 1082 : iova = rte_mem_virt2iova(addr);
606 [ - + ]: 1082 : if (iova == RTE_BAD_PHYS_ADDR) {
607 : 0 : EAL_LOG(DEBUG, "%s(): can't get IOVA addr",
608 : : __func__);
609 : 0 : goto mapped;
610 : : }
611 : :
612 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
613 : : /*
614 : : * If the kernel has been built without NUMA support, get_mempolicy()
615 : : * will return an error. If check_numa() returns false, memory
616 : : * allocation is not NUMA aware and the socket_id should not be
617 : : * checked.
618 : : */
619 [ + - ]: 1082 : if (check_numa()) {
620 : 1082 : ret = get_mempolicy(&cur_socket_id, NULL, 0, addr,
621 : : MPOL_F_NODE | MPOL_F_ADDR);
622 [ - + ]: 1082 : if (ret < 0) {
623 : 0 : EAL_LOG(DEBUG, "%s(): get_mempolicy: %s",
624 : : __func__, strerror(errno));
625 : 0 : goto mapped;
626 [ + + ]: 1082 : } else if (cur_socket_id != socket_id) {
627 : 1 : EAL_LOG(DEBUG,
628 : : "%s(): allocation happened on wrong socket (wanted %d, got %d)",
629 : : __func__, socket_id, cur_socket_id);
630 : 1 : goto mapped;
631 : : }
632 : : }
633 : : #else
634 : : if (rte_socket_count() > 1)
635 : : EAL_LOG(DEBUG, "%s(): not checking hugepage NUMA node.",
636 : : __func__);
637 : : #endif
638 : :
639 : : huge_recover_sigbus();
640 : :
641 : 1081 : ms->addr = addr;
642 : 1081 : ms->hugepage_sz = alloc_sz;
643 : 1081 : ms->len = alloc_sz;
644 : 1081 : ms->nchannel = rte_memory_get_nchannel();
645 : 1081 : ms->nrank = rte_memory_get_nrank();
646 : 1081 : ms->iova = iova;
647 : 1081 : ms->socket_id = socket_id;
648 [ + - ]: 1081 : ms->flags = dirty ? RTE_MEMSEG_FLAG_DIRTY : 0;
649 : :
650 : 1081 : return 0;
651 : :
652 : 1 : mapped:
653 : 1 : munmap(addr, alloc_sz);
654 [ + - ]: 1 : unmapped:
655 : : huge_recover_sigbus();
656 : : flags = EAL_RESERVE_FORCE_ADDRESS;
657 : 1 : new_addr = eal_get_virtual_area(addr, &alloc_sz, alloc_sz, 0, flags);
658 [ - + ]: 1 : if (new_addr != addr) {
659 [ # # ]: 0 : if (new_addr != NULL)
660 : 0 : munmap(new_addr, alloc_sz);
661 : : /* we're leaving a hole in our virtual address space. if
662 : : * somebody else maps this hole now, we could accidentally
663 : : * override it in the future.
664 : : */
665 : 0 : EAL_LOG(CRIT, "Can't mmap holes in our virtual address space");
666 : : }
667 : : /* roll back the ref count */
668 [ + - ]: 1 : if (internal_conf->single_file_segments)
669 : 0 : fd_list[list_idx].count--;
670 : 1 : resized:
671 : : /* some codepaths will return negative fd, so exit early */
672 : : if (fd < 0)
673 : : return -1;
674 : :
675 [ - + ]: 1 : if (internal_conf->single_file_segments) {
676 : 0 : resize_hugefile(fd, map_offset, alloc_sz, false, NULL);
677 : : /* ignore failure, can't make it any worse */
678 : :
679 : : /* if refcount is at zero, close the file */
680 [ # # ]: 0 : if (fd_list[list_idx].count == 0)
681 : 0 : close_hugefile(fd, path, list_idx);
682 : : } else {
683 : : /* only remove file if we can take out a write lock */
684 [ + - ]: 1 : if (!internal_conf->hugepage_file.unlink_before_mapping &&
685 [ + - + - ]: 2 : internal_conf->in_memory == 0 &&
686 : 1 : lock(fd, LOCK_EX) == 1)
687 : 1 : unlink(path);
688 : 1 : close(fd);
689 : 1 : fd_list[list_idx].fds[seg_idx] = -1;
690 : : }
691 : : return -1;
692 : : }
693 : :
694 : : static int
695 : 1033 : free_seg(struct rte_memseg *ms, struct hugepage_info *hi,
696 : : unsigned int list_idx, unsigned int seg_idx)
697 : : {
698 : : uint64_t map_offset;
699 : : char path[PATH_MAX];
700 : : int fd, ret = 0;
701 : : const struct internal_config *internal_conf =
702 : 1033 : eal_get_internal_configuration();
703 : :
704 : : /* erase page data */
705 : 1033 : memset(ms->addr, 0, ms->len);
706 : :
707 [ - + ]: 1033 : if (mmap(ms->addr, ms->len, PROT_NONE,
708 : : MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) ==
709 : : MAP_FAILED) {
710 : 0 : EAL_LOG(DEBUG, "couldn't unmap page");
711 : 0 : return -1;
712 : : }
713 : :
714 : 1033 : eal_mem_set_dump(ms->addr, ms->len, false);
715 : :
716 : : /* if we are not in single file segments mode, we're going to unmap the
717 : : * segment and thus drop the lock on original fd, but hugepage dir is
718 : : * now locked so we can take out another one without races.
719 : : */
720 : 1033 : fd = get_seg_fd(path, sizeof(path), hi, list_idx, seg_idx, NULL);
721 [ + - ]: 1033 : if (fd < 0)
722 : : return -1;
723 : :
724 [ + + ]: 1033 : if (internal_conf->single_file_segments) {
725 : 9 : map_offset = seg_idx * ms->len;
726 [ + - ]: 9 : if (resize_hugefile(fd, map_offset, ms->len, false, NULL))
727 : : return -1;
728 : :
729 [ + + ]: 9 : if (--(fd_list[list_idx].count) == 0)
730 : 1 : close_hugefile(fd, path, list_idx);
731 : :
732 : : ret = 0;
733 : : } else {
734 : : /* if we're able to take out a write lock, we're the last one
735 : : * holding onto this page.
736 : : */
737 [ + + ]: 1024 : if (!internal_conf->in_memory &&
738 [ + - ]: 997 : internal_conf->hugepage_file.unlink_existing &&
739 [ + - ]: 997 : !internal_conf->hugepage_file.unlink_before_mapping) {
740 : 997 : ret = lock(fd, LOCK_EX);
741 [ + - ]: 997 : if (ret >= 0) {
742 : : /* no one else is using this page */
743 [ + - ]: 997 : if (ret == 1)
744 : 997 : unlink(path);
745 : : }
746 : : }
747 : : /* closing fd will drop the lock */
748 : 1024 : close(fd);
749 : 1024 : fd_list[list_idx].fds[seg_idx] = -1;
750 : : }
751 : :
752 : : memset(ms, 0, sizeof(*ms));
753 : :
754 [ + - ]: 1033 : return ret < 0 ? -1 : 0;
755 : : }
756 : :
757 : : struct alloc_walk_param {
758 : : struct hugepage_info *hi;
759 : : struct rte_memseg **ms;
760 : : size_t page_sz;
761 : : unsigned int segs_allocated;
762 : : unsigned int n_segs;
763 : : int socket;
764 : : bool exact;
765 : : };
766 : : static int
767 : 542 : alloc_seg_walk(const struct rte_memseg_list *msl, void *arg)
768 : : {
769 : 542 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
770 : : struct alloc_walk_param *wa = arg;
771 : : struct rte_memseg_list *cur_msl;
772 : : size_t page_sz;
773 : : int cur_idx, start_idx, j, dir_fd = -1;
774 : : unsigned int msl_idx, need, i;
775 : : const struct internal_config *internal_conf =
776 : 542 : eal_get_internal_configuration();
777 : :
778 [ + + ]: 542 : if (msl->page_sz != wa->page_sz)
779 : : return 0;
780 [ + + ]: 530 : if (msl->socket_id != wa->socket)
781 : : return 0;
782 : :
783 : : page_sz = (size_t)msl->page_sz;
784 : :
785 : 506 : msl_idx = msl - mcfg->memsegs;
786 : : cur_msl = &mcfg->memsegs[msl_idx];
787 : :
788 : 506 : need = wa->n_segs;
789 : :
790 : : /* try finding space in memseg list */
791 [ + + ]: 506 : if (wa->exact) {
792 : : /* if we require exact number of pages in a list, find them */
793 : 494 : cur_idx = rte_fbarray_find_next_n_free(&cur_msl->memseg_arr, 0,
794 : : need);
795 [ + - ]: 494 : if (cur_idx < 0)
796 : : return 0;
797 : : start_idx = cur_idx;
798 : : } else {
799 : : int cur_len;
800 : :
801 : : /* we don't require exact number of pages, so we're going to go
802 : : * for best-effort allocation. that means finding the biggest
803 : : * unused block, and going with that.
804 : : */
805 : 12 : cur_idx = rte_fbarray_find_biggest_free(&cur_msl->memseg_arr,
806 : : 0);
807 [ + - ]: 12 : if (cur_idx < 0)
808 : : return 0;
809 : : start_idx = cur_idx;
810 : : /* adjust the size to possibly be smaller than original
811 : : * request, but do not allow it to be bigger.
812 : : */
813 : 12 : cur_len = rte_fbarray_find_contig_free(&cur_msl->memseg_arr,
814 : : cur_idx);
815 : 12 : need = RTE_MIN(need, (unsigned int)cur_len);
816 : : }
817 : :
818 : : /* do not allow any page allocations during the time we're allocating,
819 : : * because file creation and locking operations are not atomic,
820 : : * and we might be the first or the last ones to use a particular page,
821 : : * so we need to ensure atomicity of every operation.
822 : : *
823 : : * during init, we already hold a write lock, so don't try to take out
824 : : * another one.
825 : : */
826 [ + + + - ]: 506 : if (wa->hi->lock_descriptor == -1 && !internal_conf->in_memory) {
827 : 494 : dir_fd = open(wa->hi->hugedir, O_RDONLY);
828 [ - + ]: 494 : if (dir_fd < 0) {
829 : 0 : EAL_LOG(ERR, "%s(): Cannot open '%s': %s",
830 : : __func__, wa->hi->hugedir, strerror(errno));
831 : 0 : return -1;
832 : : }
833 : : /* blocking writelock */
834 [ - + ]: 494 : if (flock(dir_fd, LOCK_EX)) {
835 : 0 : EAL_LOG(ERR, "%s(): Cannot lock '%s': %s",
836 : : __func__, wa->hi->hugedir, strerror(errno));
837 : 0 : close(dir_fd);
838 : 0 : return -1;
839 : : }
840 : : }
841 : :
842 [ + + ]: 1560 : for (i = 0; i < need; i++, cur_idx++) {
843 : : struct rte_memseg *cur;
844 : : void *map_addr;
845 : :
846 : 1054 : cur = rte_fbarray_get(&cur_msl->memseg_arr, cur_idx);
847 : 1054 : map_addr = RTE_PTR_ADD(cur_msl->base_va,
848 : : cur_idx * page_sz);
849 : :
850 [ - + ]: 1054 : if (alloc_seg(cur, map_addr, wa->socket, wa->hi,
851 : : msl_idx, cur_idx)) {
852 : 0 : EAL_LOG(DEBUG, "attempted to allocate %i segments, but only %i were allocated",
853 : : need, i);
854 : :
855 : : /* if exact number wasn't requested, stop */
856 [ # # ]: 0 : if (!wa->exact)
857 : 0 : goto out;
858 : :
859 : : /* clean up */
860 [ # # ]: 0 : for (j = start_idx; j < cur_idx; j++) {
861 : : struct rte_memseg *tmp;
862 : : struct rte_fbarray *arr =
863 : : &cur_msl->memseg_arr;
864 : :
865 : 0 : tmp = rte_fbarray_get(arr, j);
866 : 0 : rte_fbarray_set_free(arr, j);
867 : :
868 : : /* free_seg may attempt to create a file, which
869 : : * may fail.
870 : : */
871 [ # # ]: 0 : if (free_seg(tmp, wa->hi, msl_idx, j))
872 : 0 : EAL_LOG(DEBUG, "Cannot free page");
873 : : }
874 : : /* clear the list */
875 [ # # ]: 0 : if (wa->ms)
876 : 0 : memset(wa->ms, 0, sizeof(*wa->ms) * wa->n_segs);
877 : :
878 [ # # ]: 0 : if (dir_fd >= 0)
879 : 0 : close(dir_fd);
880 : 0 : return -1;
881 : : }
882 [ + - ]: 1054 : if (wa->ms)
883 : 1054 : wa->ms[i] = cur;
884 : :
885 : 1054 : rte_fbarray_set_used(&cur_msl->memseg_arr, cur_idx);
886 : : }
887 : 506 : out:
888 : 506 : wa->segs_allocated = i;
889 [ + - ]: 506 : if (i > 0)
890 : 506 : cur_msl->version++;
891 [ + + ]: 506 : if (dir_fd >= 0)
892 : 494 : close(dir_fd);
893 : : /* if we didn't allocate any segments, move on to the next list */
894 : 506 : return i > 0;
895 : : }
896 : :
897 : : struct free_walk_param {
898 : : struct hugepage_info *hi;
899 : : struct rte_memseg *ms;
900 : : };
901 : : static int
902 : 1161 : free_seg_walk(const struct rte_memseg_list *msl, void *arg)
903 : : {
904 : 1161 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
905 : : struct rte_memseg_list *found_msl;
906 : : struct free_walk_param *wa = arg;
907 : : uintptr_t start_addr, end_addr;
908 : : int msl_idx, seg_idx, ret, dir_fd = -1;
909 : : const struct internal_config *internal_conf =
910 : 1161 : eal_get_internal_configuration();
911 : :
912 : 1161 : start_addr = (uintptr_t) msl->base_va;
913 : 1161 : end_addr = start_addr + msl->len;
914 : :
915 [ + - + + ]: 1161 : if ((uintptr_t)wa->ms->addr < start_addr ||
916 : : (uintptr_t)wa->ms->addr >= end_addr)
917 : : return 0;
918 : :
919 : 1033 : msl_idx = msl - mcfg->memsegs;
920 : 1033 : seg_idx = RTE_PTR_DIFF(wa->ms->addr, start_addr) / msl->page_sz;
921 : :
922 : : /* msl is const */
923 : : found_msl = &mcfg->memsegs[msl_idx];
924 : :
925 : : /* do not allow any page allocations during the time we're freeing,
926 : : * because file creation and locking operations are not atomic,
927 : : * and we might be the first or the last ones to use a particular page,
928 : : * so we need to ensure atomicity of every operation.
929 : : *
930 : : * during init, we already hold a write lock, so don't try to take out
931 : : * another one.
932 : : */
933 [ + - + + ]: 1033 : if (wa->hi->lock_descriptor == -1 && !internal_conf->in_memory) {
934 : 1006 : dir_fd = open(wa->hi->hugedir, O_RDONLY);
935 [ - + ]: 1006 : if (dir_fd < 0) {
936 : 0 : EAL_LOG(ERR, "%s(): Cannot open '%s': %s",
937 : : __func__, wa->hi->hugedir, strerror(errno));
938 : 0 : return -1;
939 : : }
940 : : /* blocking writelock */
941 [ - + ]: 1006 : if (flock(dir_fd, LOCK_EX)) {
942 : 0 : EAL_LOG(ERR, "%s(): Cannot lock '%s': %s",
943 : : __func__, wa->hi->hugedir, strerror(errno));
944 : 0 : close(dir_fd);
945 : 0 : return -1;
946 : : }
947 : : }
948 : :
949 : 1033 : found_msl->version++;
950 : :
951 : 1033 : rte_fbarray_set_free(&found_msl->memseg_arr, seg_idx);
952 : :
953 : 1033 : ret = free_seg(wa->ms, wa->hi, msl_idx, seg_idx);
954 : :
955 [ + + ]: 1033 : if (dir_fd >= 0)
956 : 1006 : close(dir_fd);
957 : :
958 [ - + ]: 1033 : if (ret < 0)
959 : 0 : return -1;
960 : :
961 : : return 1;
962 : : }
963 : :
964 : : int
965 : 506 : eal_memalloc_alloc_seg_bulk(struct rte_memseg **ms, int n_segs, size_t page_sz,
966 : : int socket, bool exact)
967 : : {
968 : : int i, ret = -1;
969 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
970 : : bool have_numa = false;
971 : : int oldpolicy;
972 : : struct bitmask *oldmask;
973 : : #endif
974 : : struct alloc_walk_param wa;
975 : : struct hugepage_info *hi = NULL;
976 : : struct internal_config *internal_conf =
977 : 506 : eal_get_internal_configuration();
978 : :
979 : : memset(&wa, 0, sizeof(wa));
980 : :
981 : : /* dynamic allocation not supported in legacy mode */
982 [ + - ]: 506 : if (internal_conf->legacy_mem)
983 : : return -1;
984 : :
985 [ + - ]: 509 : for (i = 0; i < (int) RTE_DIM(internal_conf->hugepage_info); i++) {
986 : 509 : if (page_sz ==
987 [ + + ]: 509 : internal_conf->hugepage_info[i].hugepage_sz) {
988 : 506 : hi = &internal_conf->hugepage_info[i];
989 : 506 : break;
990 : : }
991 : : }
992 [ - + ]: 506 : if (!hi) {
993 : 0 : EAL_LOG(ERR, "%s(): can't find relevant hugepage_info entry",
994 : : __func__);
995 : 0 : return -1;
996 : : }
997 : :
998 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
999 [ + - ]: 506 : if (check_numa()) {
1000 : 506 : oldmask = numa_allocate_nodemask();
1001 : 506 : prepare_numa(&oldpolicy, oldmask, socket);
1002 : : have_numa = true;
1003 : : }
1004 : : #endif
1005 : :
1006 : 506 : wa.exact = exact;
1007 : 506 : wa.hi = hi;
1008 : 506 : wa.ms = ms;
1009 : 506 : wa.n_segs = n_segs;
1010 : 506 : wa.page_sz = page_sz;
1011 : 506 : wa.socket = socket;
1012 : 506 : wa.segs_allocated = 0;
1013 : :
1014 : : /* memalloc is locked, so it's safe to use thread-unsafe version */
1015 : 506 : ret = rte_memseg_list_walk_thread_unsafe(alloc_seg_walk, &wa);
1016 [ - + ]: 506 : if (ret == 0) {
1017 : 0 : EAL_LOG(DEBUG, "%s(): couldn't find suitable memseg_list",
1018 : : __func__);
1019 : : ret = -1;
1020 [ + - ]: 506 : } else if (ret > 0) {
1021 : 506 : ret = (int)wa.segs_allocated;
1022 : : }
1023 : :
1024 : : #ifdef RTE_EAL_NUMA_AWARE_HUGEPAGES
1025 [ + - ]: 506 : if (have_numa)
1026 : 506 : restore_numa(&oldpolicy, oldmask);
1027 : : #endif
1028 : : return ret;
1029 : : }
1030 : :
1031 : : struct rte_memseg *
1032 : 0 : eal_memalloc_alloc_seg(size_t page_sz, int socket)
1033 : : {
1034 : : struct rte_memseg *ms;
1035 [ # # ]: 0 : if (eal_memalloc_alloc_seg_bulk(&ms, 1, page_sz, socket, true) < 0)
1036 : : return NULL;
1037 : : /* return pointer to newly allocated memseg */
1038 : 0 : return ms;
1039 : : }
1040 : :
1041 : : int
1042 : 1033 : eal_memalloc_free_seg_bulk(struct rte_memseg **ms, int n_segs)
1043 : : {
1044 : : int seg, ret = 0;
1045 : : struct internal_config *internal_conf =
1046 : 1033 : eal_get_internal_configuration();
1047 : :
1048 : : /* dynamic free not supported in legacy mode */
1049 [ + - ]: 1033 : if (internal_conf->legacy_mem)
1050 : : return -1;
1051 : :
1052 [ + + ]: 2066 : for (seg = 0; seg < n_segs; seg++) {
1053 : 1033 : struct rte_memseg *cur = ms[seg];
1054 : : struct hugepage_info *hi = NULL;
1055 : : struct free_walk_param wa;
1056 : : int i, walk_res;
1057 : :
1058 : : /* if this page is marked as unfreeable, fail */
1059 [ - + ]: 1033 : if (cur->flags & RTE_MEMSEG_FLAG_DO_NOT_FREE) {
1060 : 0 : EAL_LOG(DEBUG, "Page is not allowed to be freed");
1061 : : ret = -1;
1062 : 1033 : continue;
1063 : : }
1064 : :
1065 : : memset(&wa, 0, sizeof(wa));
1066 : :
1067 [ + - ]: 1060 : for (i = 0; i < (int)RTE_DIM(internal_conf->hugepage_info);
1068 : 27 : i++) {
1069 : 1060 : hi = &internal_conf->hugepage_info[i];
1070 [ + + ]: 1060 : if (cur->hugepage_sz == hi->hugepage_sz)
1071 : : break;
1072 : : }
1073 [ - + ]: 1033 : if (i == (int)RTE_DIM(internal_conf->hugepage_info)) {
1074 : 0 : EAL_LOG(ERR, "Can't find relevant hugepage_info entry");
1075 : : ret = -1;
1076 : 0 : continue;
1077 : : }
1078 : :
1079 : 1033 : wa.ms = cur;
1080 : 1033 : wa.hi = hi;
1081 : :
1082 : : /* memalloc is locked, so it's safe to use thread-unsafe version
1083 : : */
1084 : 1033 : walk_res = rte_memseg_list_walk_thread_unsafe(free_seg_walk,
1085 : : &wa);
1086 [ + - ]: 1033 : if (walk_res == 1)
1087 : 1033 : continue;
1088 [ # # ]: 0 : if (walk_res == 0)
1089 : 0 : EAL_LOG(ERR, "Couldn't find memseg list");
1090 : : ret = -1;
1091 : : }
1092 : : return ret;
1093 : : }
1094 : :
1095 : : int
1096 : 1033 : eal_memalloc_free_seg(struct rte_memseg *ms)
1097 : : {
1098 : : const struct internal_config *internal_conf =
1099 : 1033 : eal_get_internal_configuration();
1100 : :
1101 : : /* dynamic free not supported in legacy mode */
1102 [ + - ]: 1033 : if (internal_conf->legacy_mem)
1103 : : return -1;
1104 : :
1105 : 1033 : return eal_memalloc_free_seg_bulk(&ms, 1);
1106 : : }
1107 : :
1108 : : static int
1109 : 27 : sync_chunk(struct rte_memseg_list *primary_msl,
1110 : : struct rte_memseg_list *local_msl, struct hugepage_info *hi,
1111 : : unsigned int msl_idx, bool used, int start, int end)
1112 : : {
1113 : : struct rte_fbarray *l_arr, *p_arr;
1114 : : int i, ret, chunk_len, diff_len;
1115 : :
1116 : 27 : l_arr = &local_msl->memseg_arr;
1117 : 27 : p_arr = &primary_msl->memseg_arr;
1118 : :
1119 : : /* we need to aggregate allocations/deallocations into bigger chunks,
1120 : : * as we don't want to spam the user with per-page callbacks.
1121 : : *
1122 : : * to avoid any potential issues, we also want to trigger
1123 : : * deallocation callbacks *before* we actually deallocate
1124 : : * memory, so that the user application could wrap up its use
1125 : : * before it goes away.
1126 : : */
1127 : :
1128 : 27 : chunk_len = end - start;
1129 : :
1130 : : /* find how many contiguous pages we can map/unmap for this chunk */
1131 : : diff_len = used ?
1132 [ + - ]: 27 : rte_fbarray_find_contig_free(l_arr, start) :
1133 : 0 : rte_fbarray_find_contig_used(l_arr, start);
1134 : :
1135 : : /* has to be at least one page */
1136 [ + - ]: 27 : if (diff_len < 1)
1137 : : return -1;
1138 : :
1139 : 27 : diff_len = RTE_MIN(chunk_len, diff_len);
1140 : :
1141 : : /* if we are freeing memory, notify the application */
1142 [ - + ]: 27 : if (!used) {
1143 : : struct rte_memseg *ms;
1144 : : void *start_va;
1145 : : size_t len, page_sz;
1146 : :
1147 : 0 : ms = rte_fbarray_get(l_arr, start);
1148 : 0 : start_va = ms->addr;
1149 : 0 : page_sz = (size_t)primary_msl->page_sz;
1150 : 0 : len = page_sz * diff_len;
1151 : :
1152 : 0 : eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE,
1153 : : start_va, len);
1154 : : }
1155 : :
1156 [ + + ]: 54 : for (i = 0; i < diff_len; i++) {
1157 : : struct rte_memseg *p_ms, *l_ms;
1158 : 28 : int seg_idx = start + i;
1159 : :
1160 : 28 : l_ms = rte_fbarray_get(l_arr, seg_idx);
1161 : 28 : p_ms = rte_fbarray_get(p_arr, seg_idx);
1162 : :
1163 [ + - ]: 28 : if (l_ms == NULL || p_ms == NULL)
1164 : : return -1;
1165 : :
1166 [ + - ]: 28 : if (used) {
1167 : 28 : ret = alloc_seg(l_ms, p_ms->addr,
1168 : : p_ms->socket_id, hi,
1169 : : msl_idx, seg_idx);
1170 [ + + ]: 28 : if (ret < 0)
1171 : : return -1;
1172 : 27 : rte_fbarray_set_used(l_arr, seg_idx);
1173 : : } else {
1174 : 0 : ret = free_seg(l_ms, hi, msl_idx, seg_idx);
1175 : 0 : rte_fbarray_set_free(l_arr, seg_idx);
1176 [ # # ]: 0 : if (ret < 0)
1177 : : return -1;
1178 : : }
1179 : : }
1180 : :
1181 : : /* if we just allocated memory, notify the application */
1182 [ + - ]: 26 : if (used) {
1183 : : struct rte_memseg *ms;
1184 : : void *start_va;
1185 : : size_t len, page_sz;
1186 : :
1187 : 26 : ms = rte_fbarray_get(l_arr, start);
1188 : 26 : start_va = ms->addr;
1189 : 26 : page_sz = (size_t)primary_msl->page_sz;
1190 : 26 : len = page_sz * diff_len;
1191 : :
1192 : 26 : eal_memalloc_mem_event_notify(RTE_MEM_EVENT_ALLOC,
1193 : : start_va, len);
1194 : : }
1195 : :
1196 : : /* calculate how much we can advance until next chunk */
1197 : : diff_len = used ?
1198 [ + - ]: 26 : rte_fbarray_find_contig_used(l_arr, start) :
1199 : 0 : rte_fbarray_find_contig_free(l_arr, start);
1200 : 26 : ret = RTE_MIN(chunk_len, diff_len);
1201 : :
1202 : 26 : return ret;
1203 : : }
1204 : :
1205 : : static int
1206 : 55 : sync_status(struct rte_memseg_list *primary_msl,
1207 : : struct rte_memseg_list *local_msl, struct hugepage_info *hi,
1208 : : unsigned int msl_idx, bool used)
1209 : : {
1210 : : struct rte_fbarray *l_arr, *p_arr;
1211 : : int p_idx, l_chunk_len, p_chunk_len, ret;
1212 : : int start, end;
1213 : :
1214 : : /* this is a little bit tricky, but the basic idea is - walk both lists
1215 : : * and spot any places where there are discrepancies. walking both lists
1216 : : * and noting discrepancies in a single go is a hard problem, so we do
1217 : : * it in two passes - first we spot any places where allocated segments
1218 : : * mismatch (i.e. ensure that everything that's allocated in the primary
1219 : : * is also allocated in the secondary), and then we do it by looking at
1220 : : * free segments instead.
1221 : : *
1222 : : * we also need to aggregate changes into chunks, as we have to call
1223 : : * callbacks per allocation, not per page.
1224 : : */
1225 : 55 : l_arr = &local_msl->memseg_arr;
1226 : 55 : p_arr = &primary_msl->memseg_arr;
1227 : :
1228 [ + + ]: 55 : if (used)
1229 : 28 : p_idx = rte_fbarray_find_next_used(p_arr, 0);
1230 : : else
1231 : 27 : p_idx = rte_fbarray_find_next_free(p_arr, 0);
1232 : :
1233 [ + + ]: 108 : while (p_idx >= 0) {
1234 : : int next_chunk_search_idx;
1235 : :
1236 [ + + ]: 54 : if (used) {
1237 : 27 : p_chunk_len = rte_fbarray_find_contig_used(p_arr,
1238 : : p_idx);
1239 : 27 : l_chunk_len = rte_fbarray_find_contig_used(l_arr,
1240 : : p_idx);
1241 : : } else {
1242 : 27 : p_chunk_len = rte_fbarray_find_contig_free(p_arr,
1243 : : p_idx);
1244 : 27 : l_chunk_len = rte_fbarray_find_contig_free(l_arr,
1245 : : p_idx);
1246 : : }
1247 : : /* best case scenario - no differences (or bigger, which will be
1248 : : * fixed during next iteration), look for next chunk
1249 : : */
1250 [ + + ]: 54 : if (l_chunk_len >= p_chunk_len) {
1251 : 27 : next_chunk_search_idx = p_idx + p_chunk_len;
1252 : 27 : goto next_chunk;
1253 : : }
1254 : :
1255 : : /* if both chunks start at the same point, skip parts we know
1256 : : * are identical, and sync the rest. each call to sync_chunk
1257 : : * will only sync contiguous segments, so we need to call this
1258 : : * until we are sure there are no more differences in this
1259 : : * chunk.
1260 : : */
1261 : 27 : start = p_idx + l_chunk_len;
1262 : 27 : end = p_idx + p_chunk_len;
1263 : : do {
1264 : 27 : ret = sync_chunk(primary_msl, local_msl, hi, msl_idx,
1265 : : used, start, end);
1266 : 27 : start += ret;
1267 [ - + ]: 27 : } while (start < end && ret >= 0);
1268 : : /* if ret is negative, something went wrong */
1269 [ + + ]: 27 : if (ret < 0)
1270 : : return -1;
1271 : :
1272 : : next_chunk_search_idx = p_idx + p_chunk_len;
1273 : 53 : next_chunk:
1274 : : /* skip to end of this chunk */
1275 [ + + ]: 53 : if (used) {
1276 : 26 : p_idx = rte_fbarray_find_next_used(p_arr,
1277 : : next_chunk_search_idx);
1278 : : } else {
1279 : 27 : p_idx = rte_fbarray_find_next_free(p_arr,
1280 : : next_chunk_search_idx);
1281 : : }
1282 : : }
1283 : : return 0;
1284 : : }
1285 : :
1286 : : static int
1287 : 28 : sync_existing(struct rte_memseg_list *primary_msl,
1288 : : struct rte_memseg_list *local_msl, struct hugepage_info *hi,
1289 : : unsigned int msl_idx)
1290 : : {
1291 : : int ret, dir_fd;
1292 : :
1293 : : /* do not allow any page allocations during the time we're allocating,
1294 : : * because file creation and locking operations are not atomic,
1295 : : * and we might be the first or the last ones to use a particular page,
1296 : : * so we need to ensure atomicity of every operation.
1297 : : */
1298 : 28 : dir_fd = open(hi->hugedir, O_RDONLY);
1299 [ - + ]: 28 : if (dir_fd < 0) {
1300 : 0 : EAL_LOG(ERR, "%s(): Cannot open '%s': %s", __func__,
1301 : : hi->hugedir, strerror(errno));
1302 : 0 : return -1;
1303 : : }
1304 : : /* blocking writelock */
1305 [ - + ]: 28 : if (flock(dir_fd, LOCK_EX)) {
1306 : 0 : EAL_LOG(ERR, "%s(): Cannot lock '%s': %s", __func__,
1307 : : hi->hugedir, strerror(errno));
1308 : 0 : close(dir_fd);
1309 : 0 : return -1;
1310 : : }
1311 : :
1312 : : /* ensure all allocated space is the same in both lists */
1313 : 28 : ret = sync_status(primary_msl, local_msl, hi, msl_idx, true);
1314 [ + + ]: 28 : if (ret < 0)
1315 : 1 : goto fail;
1316 : :
1317 : : /* ensure all unallocated space is the same in both lists */
1318 : 27 : ret = sync_status(primary_msl, local_msl, hi, msl_idx, false);
1319 [ - + ]: 27 : if (ret < 0)
1320 : 0 : goto fail;
1321 : :
1322 : : /* update version number */
1323 : 27 : local_msl->version = primary_msl->version;
1324 : :
1325 : 27 : close(dir_fd);
1326 : :
1327 : 27 : return 0;
1328 : 1 : fail:
1329 : 1 : close(dir_fd);
1330 : 1 : return -1;
1331 : : }
1332 : :
1333 : : static int
1334 : 213 : sync_walk(const struct rte_memseg_list *msl, void *arg __rte_unused)
1335 : : {
1336 : 213 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1337 : : struct rte_memseg_list *primary_msl, *local_msl;
1338 : : struct hugepage_info *hi = NULL;
1339 : : unsigned int i;
1340 : : int msl_idx;
1341 : : struct internal_config *internal_conf =
1342 : 213 : eal_get_internal_configuration();
1343 : :
1344 [ + - ]: 213 : if (msl->external)
1345 : : return 0;
1346 : :
1347 : 213 : msl_idx = msl - mcfg->memsegs;
1348 : 213 : primary_msl = &mcfg->memsegs[msl_idx];
1349 : 213 : local_msl = &local_memsegs[msl_idx];
1350 : :
1351 [ + - ]: 213 : for (i = 0; i < RTE_DIM(internal_conf->hugepage_info); i++) {
1352 : 213 : uint64_t cur_sz =
1353 : : internal_conf->hugepage_info[i].hugepage_sz;
1354 : 213 : uint64_t msl_sz = primary_msl->page_sz;
1355 [ + - ]: 213 : if (msl_sz == cur_sz) {
1356 : 213 : hi = &internal_conf->hugepage_info[i];
1357 : 213 : break;
1358 : : }
1359 : : }
1360 [ - + ]: 213 : if (!hi) {
1361 : 0 : EAL_LOG(ERR, "Can't find relevant hugepage_info entry");
1362 : 0 : return -1;
1363 : : }
1364 : :
1365 : : /* if versions don't match, synchronize everything */
1366 [ + + + + ]: 241 : if (local_msl->version != primary_msl->version &&
1367 : 28 : sync_existing(primary_msl, local_msl, hi, msl_idx))
1368 : 1 : return -1;
1369 : : return 0;
1370 : : }
1371 : :
1372 : :
1373 : : int
1374 : 27 : eal_memalloc_sync_with_primary(void)
1375 : : {
1376 : : /* nothing to be done in primary */
1377 [ + - ]: 27 : if (rte_eal_process_type() == RTE_PROC_PRIMARY)
1378 : : return 0;
1379 : :
1380 : : /* memalloc is locked, so it's safe to call thread-unsafe version */
1381 [ + + ]: 27 : if (rte_memseg_list_walk_thread_unsafe(sync_walk, NULL))
1382 : 1 : return -1;
1383 : : return 0;
1384 : : }
1385 : :
1386 : : static int
1387 : 208 : secondary_msl_create_walk(const struct rte_memseg_list *msl,
1388 : : void *arg __rte_unused)
1389 : : {
1390 : 208 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1391 : : struct rte_memseg_list *primary_msl, *local_msl;
1392 : : char name[RTE_FBARRAY_NAME_LEN];
1393 : : int msl_idx, ret;
1394 : : uint64_t tsc;
1395 : : pid_t pid;
1396 : :
1397 [ + - ]: 208 : if (msl->external)
1398 : : return 0;
1399 : :
1400 : 208 : msl_idx = msl - mcfg->memsegs;
1401 : : primary_msl = &mcfg->memsegs[msl_idx];
1402 : : local_msl = &local_memsegs[msl_idx];
1403 : :
1404 : : /*
1405 : : * Create distinct fbarrays for each secondary using TSC for uniqueness,
1406 : : * since PID is not unique across containers (different PID namespaces).
1407 : : * The worst case name length is:
1408 : : * Base name: "memseg-1048576k-99-99" ~21 chars
1409 : : * Suffix "_<pid>_<16hex>" +24
1410 : : * Total = ~45 < RTE_FBARRAY_NAME_LEN 64
1411 : : */
1412 : : tsc = rte_get_tsc_cycles();
1413 : 208 : pid = getpid();
1414 : : ret = snprintf(name, sizeof(name), "%s_%d_%"PRIx64,
1415 [ - + ]: 208 : primary_msl->memseg_arr.name, pid, tsc);
1416 [ - + ]: 208 : if (ret >= (int)sizeof(name)) {
1417 : 0 : EAL_LOG(ERR, "fbarray name \"%s_%d_%"PRIx64"\" is too long",
1418 : : primary_msl->memseg_arr.name, pid, tsc);
1419 : 0 : return -1;
1420 : : }
1421 : :
1422 : 208 : ret = rte_fbarray_init(&local_msl->memseg_arr, name,
1423 : : primary_msl->memseg_arr.len,
1424 : : primary_msl->memseg_arr.elt_sz);
1425 [ - + ]: 208 : if (ret < 0) {
1426 : 0 : EAL_LOG(ERR, "Cannot initialize local memory map");
1427 : 0 : return -1;
1428 : : }
1429 : 208 : local_msl->base_va = primary_msl->base_va;
1430 : 208 : local_msl->len = primary_msl->len;
1431 : :
1432 : 208 : return 0;
1433 : : }
1434 : :
1435 : : static int
1436 : 209 : secondary_msl_destroy_walk(const struct rte_memseg_list *msl,
1437 : : void *arg __rte_unused)
1438 : : {
1439 : 209 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1440 : : struct rte_memseg_list *local_msl;
1441 : : int msl_idx, ret;
1442 : :
1443 [ + - ]: 209 : if (msl->external)
1444 : : return 0;
1445 : :
1446 : 209 : msl_idx = msl - mcfg->memsegs;
1447 : : local_msl = &local_memsegs[msl_idx];
1448 : :
1449 : 209 : ret = rte_fbarray_destroy(&local_msl->memseg_arr);
1450 [ + + ]: 209 : if (ret < 0) {
1451 : 1 : EAL_LOG(ERR, "Cannot destroy local memory map");
1452 : 1 : return -1;
1453 : : }
1454 : 208 : local_msl->base_va = NULL;
1455 : 208 : local_msl->len = 0;
1456 : :
1457 : 208 : return 0;
1458 : : }
1459 : :
1460 : : static int
1461 : 692 : alloc_list(int list_idx, int len)
1462 : : {
1463 : : int *data;
1464 : : int i;
1465 : : const struct internal_config *internal_conf =
1466 : 692 : eal_get_internal_configuration();
1467 : :
1468 : : /* single-file segments mode does not need fd list */
1469 [ + + ]: 692 : if (!internal_conf->single_file_segments) {
1470 : : /* ensure we have space to store fd per each possible segment */
1471 : 684 : data = malloc(sizeof(int) * len);
1472 [ - + ]: 684 : if (data == NULL) {
1473 : 0 : EAL_LOG(ERR, "Unable to allocate space for file descriptors");
1474 : 0 : return -1;
1475 : : }
1476 : : /* set all fd's as invalid */
1477 [ + + ]: 5506092 : for (i = 0; i < len; i++)
1478 : 5505408 : data[i] = -1;
1479 : 684 : fd_list[list_idx].fds = data;
1480 : 684 : fd_list[list_idx].len = len;
1481 : : } else {
1482 : 8 : fd_list[list_idx].fds = NULL;
1483 : 8 : fd_list[list_idx].len = 0;
1484 : : }
1485 : :
1486 : 692 : fd_list[list_idx].count = 0;
1487 : 692 : fd_list[list_idx].memseg_list_fd = -1;
1488 : :
1489 : 692 : return 0;
1490 : : }
1491 : :
1492 : : static int
1493 : 764 : destroy_list(int list_idx)
1494 : : {
1495 : : const struct internal_config *internal_conf =
1496 : 764 : eal_get_internal_configuration();
1497 : :
1498 : : /* single-file segments mode does not need fd list */
1499 [ + + ]: 764 : if (!internal_conf->single_file_segments) {
1500 : 635 : int *fds = fd_list[list_idx].fds;
1501 : : int i;
1502 : : /* go through each fd and ensure it's closed */
1503 [ + + ]: 5194363 : for (i = 0; i < fd_list[list_idx].len; i++) {
1504 [ + + ]: 5193728 : if (fds[i] >= 0) {
1505 : 66 : close(fds[i]);
1506 : 66 : fds[i] = -1;
1507 : : }
1508 : : }
1509 : 635 : free(fds);
1510 : 635 : fd_list[list_idx].fds = NULL;
1511 : 635 : fd_list[list_idx].len = 0;
1512 [ + + ]: 129 : } else if (fd_list[list_idx].memseg_list_fd >= 0) {
1513 : 121 : close(fd_list[list_idx].memseg_list_fd);
1514 : 121 : fd_list[list_idx].count = 0;
1515 : 121 : fd_list[list_idx].memseg_list_fd = -1;
1516 : : }
1517 : 764 : return 0;
1518 : : }
1519 : :
1520 : : static int
1521 : 692 : fd_list_create_walk(const struct rte_memseg_list *msl,
1522 : : void *arg __rte_unused)
1523 : : {
1524 : 692 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1525 : : unsigned int len;
1526 : : int msl_idx;
1527 : :
1528 [ + - ]: 692 : if (msl->external)
1529 : : return 0;
1530 : :
1531 : 692 : msl_idx = msl - mcfg->memsegs;
1532 : 692 : len = msl->memseg_arr.len;
1533 : :
1534 : 692 : return alloc_list(msl_idx, len);
1535 : : }
1536 : :
1537 : : static int
1538 : 764 : fd_list_destroy_walk(const struct rte_memseg_list *msl, void *arg __rte_unused)
1539 : : {
1540 : 764 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1541 : : int msl_idx;
1542 : :
1543 [ + - ]: 764 : if (msl->external)
1544 : : return 0;
1545 : :
1546 : 764 : msl_idx = msl - mcfg->memsegs;
1547 : :
1548 : 764 : return destroy_list(msl_idx);
1549 : : }
1550 : :
1551 : : int
1552 : 18 : eal_memalloc_set_seg_fd(int list_idx, int seg_idx, int fd)
1553 : : {
1554 : 18 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1555 : : const struct internal_config *internal_conf =
1556 : 18 : eal_get_internal_configuration();
1557 : :
1558 : : /* single file segments mode doesn't support individual segment fd's */
1559 [ + - ]: 18 : if (internal_conf->single_file_segments)
1560 : : return -ENOTSUP;
1561 : :
1562 : : /* if list is not allocated, allocate it */
1563 [ - + ]: 18 : if (fd_list[list_idx].len == 0) {
1564 : 0 : int len = mcfg->memsegs[list_idx].memseg_arr.len;
1565 : :
1566 [ # # ]: 0 : if (alloc_list(list_idx, len) < 0)
1567 : : return -ENOMEM;
1568 : : }
1569 : 18 : fd_list[list_idx].fds[seg_idx] = fd;
1570 : :
1571 : 18 : return 0;
1572 : : }
1573 : :
1574 : : int
1575 : 121 : eal_memalloc_set_seg_list_fd(int list_idx, int fd)
1576 : : {
1577 : : const struct internal_config *internal_conf =
1578 : 121 : eal_get_internal_configuration();
1579 : :
1580 : : /* non-single file segment mode doesn't support segment list fd's */
1581 [ + - ]: 121 : if (!internal_conf->single_file_segments)
1582 : : return -ENOTSUP;
1583 : :
1584 : 121 : fd_list[list_idx].memseg_list_fd = fd;
1585 : :
1586 : 121 : return 0;
1587 : : }
1588 : :
1589 : : int
1590 : 2 : eal_memalloc_get_seg_fd(int list_idx, int seg_idx)
1591 : : {
1592 : : int fd;
1593 : : const struct internal_config *internal_conf =
1594 : 2 : eal_get_internal_configuration();
1595 : :
1596 [ - + ]: 2 : if (internal_conf->single_file_segments) {
1597 : 0 : fd = fd_list[list_idx].memseg_list_fd;
1598 [ + - ]: 2 : } else if (fd_list[list_idx].len == 0) {
1599 : : /* list not initialized */
1600 : : fd = -1;
1601 : : } else {
1602 : 2 : fd = fd_list[list_idx].fds[seg_idx];
1603 : : }
1604 [ - + ]: 2 : if (fd < 0)
1605 : 0 : return -ENODEV;
1606 : : return fd;
1607 : : }
1608 : :
1609 : : int
1610 : 1 : eal_memalloc_get_seg_fd_offset(int list_idx, int seg_idx, size_t *offset)
1611 : : {
1612 : 1 : struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
1613 : : const struct internal_config *internal_conf =
1614 : 1 : eal_get_internal_configuration();
1615 : :
1616 [ - + ]: 1 : if (internal_conf->single_file_segments) {
1617 : 0 : size_t pgsz = mcfg->memsegs[list_idx].page_sz;
1618 : :
1619 : : /* segment not active? */
1620 [ # # ]: 0 : if (fd_list[list_idx].memseg_list_fd < 0)
1621 : : return -ENOENT;
1622 : 0 : *offset = pgsz * seg_idx;
1623 : : } else {
1624 : : /* fd_list not initialized? */
1625 [ + - ]: 1 : if (fd_list[list_idx].len == 0)
1626 : : return -ENODEV;
1627 : :
1628 : : /* segment not active? */
1629 [ + - ]: 1 : if (fd_list[list_idx].fds[seg_idx] < 0)
1630 : : return -ENOENT;
1631 : 1 : *offset = 0;
1632 : : }
1633 : : return 0;
1634 : : }
1635 : :
1636 : : int
1637 : 266 : eal_memalloc_cleanup(void)
1638 : : {
1639 : : /* close all remaining fd's - these are per-process, so it's safe */
1640 [ + - ]: 266 : if (rte_memseg_list_walk_thread_unsafe(fd_list_destroy_walk, NULL))
1641 : : return -1;
1642 : :
1643 : : /* destroy the shadow page table if we're a secondary process */
1644 [ + + ]: 266 : if (rte_eal_process_type() == RTE_PROC_PRIMARY)
1645 : : return 0;
1646 : :
1647 [ + + ]: 28 : if (rte_memseg_list_walk_thread_unsafe(secondary_msl_destroy_walk,
1648 : : NULL))
1649 : 1 : return -1;
1650 : :
1651 : : return 0;
1652 : : }
1653 : :
1654 : : int
1655 : 206 : eal_memalloc_init(void)
1656 : : {
1657 : : const struct internal_config *internal_conf =
1658 : 206 : eal_get_internal_configuration();
1659 : :
1660 [ + + ]: 206 : if (rte_eal_process_type() == RTE_PROC_SECONDARY)
1661 : : /* memory_hotplug_lock is held during initialization, so it's
1662 : : * safe to call thread-unsafe version.
1663 : : */
1664 [ + - ]: 26 : if (rte_memseg_list_walk_thread_unsafe(secondary_msl_create_walk, NULL) < 0)
1665 : : return -1;
1666 [ + + ]: 206 : if (rte_eal_process_type() == RTE_PROC_PRIMARY &&
1667 [ + + ]: 180 : internal_conf->in_memory) {
1668 : 3 : EAL_LOG(DEBUG, "Using memfd for anonymous memory");
1669 : : /* this cannot ever happen but better safe than sorry */
1670 : : if (!anonymous_hugepages_supported) {
1671 : : EAL_LOG(ERR, "Using anonymous memory is not supported");
1672 : : return -1;
1673 : : }
1674 : : /* safety net, should be impossible to configure */
1675 [ + - ]: 3 : if (internal_conf->hugepage_file.unlink_before_mapping &&
1676 [ - + ]: 3 : !internal_conf->hugepage_file.unlink_existing) {
1677 : 0 : EAL_LOG(ERR, "Unlinking existing hugepage files is prohibited, cannot unlink them before mapping.");
1678 : 0 : return -1;
1679 : : }
1680 : : }
1681 : :
1682 : : /* initialize all of the fd lists */
1683 [ - + ]: 206 : if (rte_memseg_list_walk_thread_unsafe(fd_list_create_walk, NULL))
1684 : 0 : return -1;
1685 : : return 0;
1686 : : }
|