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