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