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