19ea47570b78b5769fedb3d48d647ed303316675
[dpdk.git] / lib / librte_eal / common / eal_common_memory.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <inttypes.h>
14 #include <sys/mman.h>
15 #include <sys/queue.h>
16
17 #include <rte_fbarray.h>
18 #include <rte_memory.h>
19 #include <rte_eal.h>
20 #include <rte_eal_memconfig.h>
21 #include <rte_errno.h>
22 #include <rte_log.h>
23
24 #include "eal_memalloc.h"
25 #include "eal_private.h"
26 #include "eal_internal_cfg.h"
27 #include "eal_memcfg.h"
28 #include "malloc_heap.h"
29
30 /*
31  * Try to mmap *size bytes in /dev/zero. If it is successful, return the
32  * pointer to the mmap'd area and keep *size unmodified. Else, retry
33  * with a smaller zone: decrease *size by hugepage_sz until it reaches
34  * 0. In this case, return NULL. Note: this function returns an address
35  * which is a multiple of hugepage size.
36  */
37
38 #define MEMSEG_LIST_FMT "memseg-%" PRIu64 "k-%i-%i"
39
40 static void *next_baseaddr;
41 static uint64_t system_page_sz;
42
43 #ifdef RTE_ARCH_64
44 /*
45  * Linux kernel uses a really high address as starting address for serving
46  * mmaps calls. If there exists addressing limitations and IOVA mode is VA,
47  * this starting address is likely too high for those devices. However, it
48  * is possible to use a lower address in the process virtual address space
49  * as with 64 bits there is a lot of available space.
50  *
51  * Current known limitations are 39 or 40 bits. Setting the starting address
52  * at 4GB implies there are 508GB or 1020GB for mapping the available
53  * hugepages. This is likely enough for most systems, although a device with
54  * addressing limitations should call rte_mem_check_dma_mask for ensuring all
55  * memory is within supported range.
56  */
57 static uint64_t baseaddr = 0x100000000;
58 #endif
59
60 #define MAX_MMAP_WITH_DEFINED_ADDR_TRIES 5
61 void *
62 eal_get_virtual_area(void *requested_addr, size_t *size,
63                 size_t page_sz, int flags, int mmap_flags)
64 {
65         bool addr_is_hint, allow_shrink, unmap, no_align;
66         uint64_t map_sz;
67         void *mapped_addr, *aligned_addr;
68         uint8_t try = 0;
69
70         if (system_page_sz == 0)
71                 system_page_sz = sysconf(_SC_PAGESIZE);
72
73         mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
74
75         RTE_LOG(DEBUG, EAL, "Ask a virtual area of 0x%zx bytes\n", *size);
76
77         addr_is_hint = (flags & EAL_VIRTUAL_AREA_ADDR_IS_HINT) > 0;
78         allow_shrink = (flags & EAL_VIRTUAL_AREA_ALLOW_SHRINK) > 0;
79         unmap = (flags & EAL_VIRTUAL_AREA_UNMAP) > 0;
80
81         if (next_baseaddr == NULL && internal_config.base_virtaddr != 0 &&
82                         rte_eal_process_type() == RTE_PROC_PRIMARY)
83                 next_baseaddr = (void *) internal_config.base_virtaddr;
84
85 #ifdef RTE_ARCH_64
86         if (next_baseaddr == NULL && internal_config.base_virtaddr == 0 &&
87                         rte_eal_process_type() == RTE_PROC_PRIMARY)
88                 next_baseaddr = (void *) baseaddr;
89 #endif
90         if (requested_addr == NULL && next_baseaddr != NULL) {
91                 requested_addr = next_baseaddr;
92                 requested_addr = RTE_PTR_ALIGN(requested_addr, page_sz);
93                 addr_is_hint = true;
94         }
95
96         /* we don't need alignment of resulting pointer in the following cases:
97          *
98          * 1. page size is equal to system size
99          * 2. we have a requested address, and it is page-aligned, and we will
100          *    be discarding the address if we get a different one.
101          *
102          * for all other cases, alignment is potentially necessary.
103          */
104         no_align = (requested_addr != NULL &&
105                 requested_addr == RTE_PTR_ALIGN(requested_addr, page_sz) &&
106                 !addr_is_hint) ||
107                 page_sz == system_page_sz;
108
109         do {
110                 map_sz = no_align ? *size : *size + page_sz;
111                 if (map_sz > SIZE_MAX) {
112                         RTE_LOG(ERR, EAL, "Map size too big\n");
113                         rte_errno = E2BIG;
114                         return NULL;
115                 }
116
117                 mapped_addr = mmap(requested_addr, (size_t)map_sz, PROT_READ,
118                                 mmap_flags, -1, 0);
119                 if (mapped_addr == MAP_FAILED && allow_shrink)
120                         *size -= page_sz;
121
122                 if (mapped_addr != MAP_FAILED && addr_is_hint &&
123                     mapped_addr != requested_addr) {
124                         try++;
125                         next_baseaddr = RTE_PTR_ADD(next_baseaddr, page_sz);
126                         if (try <= MAX_MMAP_WITH_DEFINED_ADDR_TRIES) {
127                                 /* hint was not used. Try with another offset */
128                                 munmap(mapped_addr, map_sz);
129                                 mapped_addr = MAP_FAILED;
130                                 requested_addr = next_baseaddr;
131                         }
132                 }
133         } while ((allow_shrink || addr_is_hint) &&
134                  mapped_addr == MAP_FAILED && *size > 0);
135
136         /* align resulting address - if map failed, we will ignore the value
137          * anyway, so no need to add additional checks.
138          */
139         aligned_addr = no_align ? mapped_addr :
140                         RTE_PTR_ALIGN(mapped_addr, page_sz);
141
142         if (*size == 0) {
143                 RTE_LOG(ERR, EAL, "Cannot get a virtual area of any size: %s\n",
144                         strerror(errno));
145                 rte_errno = errno;
146                 return NULL;
147         } else if (mapped_addr == MAP_FAILED) {
148                 RTE_LOG(ERR, EAL, "Cannot get a virtual area: %s\n",
149                         strerror(errno));
150                 /* pass errno up the call chain */
151                 rte_errno = errno;
152                 return NULL;
153         } else if (requested_addr != NULL && !addr_is_hint &&
154                         aligned_addr != requested_addr) {
155                 RTE_LOG(ERR, EAL, "Cannot get a virtual area at requested address: %p (got %p)\n",
156                         requested_addr, aligned_addr);
157                 munmap(mapped_addr, map_sz);
158                 rte_errno = EADDRNOTAVAIL;
159                 return NULL;
160         } else if (requested_addr != NULL && addr_is_hint &&
161                         aligned_addr != requested_addr) {
162                 RTE_LOG(WARNING, EAL, "WARNING! Base virtual address hint (%p != %p) not respected!\n",
163                         requested_addr, aligned_addr);
164                 RTE_LOG(WARNING, EAL, "   This may cause issues with mapping memory into secondary processes\n");
165         } else if (next_baseaddr != NULL) {
166                 next_baseaddr = RTE_PTR_ADD(aligned_addr, *size);
167         }
168
169         RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
170                 aligned_addr, *size);
171
172         if (unmap) {
173                 munmap(mapped_addr, map_sz);
174         } else if (!no_align) {
175                 void *map_end, *aligned_end;
176                 size_t before_len, after_len;
177
178                 /* when we reserve space with alignment, we add alignment to
179                  * mapping size. On 32-bit, if 1GB alignment was requested, this
180                  * would waste 1GB of address space, which is a luxury we cannot
181                  * afford. so, if alignment was performed, check if any unneeded
182                  * address space can be unmapped back.
183                  */
184
185                 map_end = RTE_PTR_ADD(mapped_addr, (size_t)map_sz);
186                 aligned_end = RTE_PTR_ADD(aligned_addr, *size);
187
188                 /* unmap space before aligned mmap address */
189                 before_len = RTE_PTR_DIFF(aligned_addr, mapped_addr);
190                 if (before_len > 0)
191                         munmap(mapped_addr, before_len);
192
193                 /* unmap space after aligned end mmap address */
194                 after_len = RTE_PTR_DIFF(map_end, aligned_end);
195                 if (after_len > 0)
196                         munmap(aligned_end, after_len);
197         }
198
199         return aligned_addr;
200 }
201
202 static struct rte_memseg *
203 virt2memseg(const void *addr, const struct rte_memseg_list *msl)
204 {
205         const struct rte_fbarray *arr;
206         void *start, *end;
207         int ms_idx;
208
209         if (msl == NULL)
210                 return NULL;
211
212         /* a memseg list was specified, check if it's the right one */
213         start = msl->base_va;
214         end = RTE_PTR_ADD(start, msl->len);
215
216         if (addr < start || addr >= end)
217                 return NULL;
218
219         /* now, calculate index */
220         arr = &msl->memseg_arr;
221         ms_idx = RTE_PTR_DIFF(addr, msl->base_va) / msl->page_sz;
222         return rte_fbarray_get(arr, ms_idx);
223 }
224
225 static struct rte_memseg_list *
226 virt2memseg_list(const void *addr)
227 {
228         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
229         struct rte_memseg_list *msl;
230         int msl_idx;
231
232         for (msl_idx = 0; msl_idx < RTE_MAX_MEMSEG_LISTS; msl_idx++) {
233                 void *start, *end;
234                 msl = &mcfg->memsegs[msl_idx];
235
236                 start = msl->base_va;
237                 end = RTE_PTR_ADD(start, msl->len);
238                 if (addr >= start && addr < end)
239                         break;
240         }
241         /* if we didn't find our memseg list */
242         if (msl_idx == RTE_MAX_MEMSEG_LISTS)
243                 return NULL;
244         return msl;
245 }
246
247 struct rte_memseg_list *
248 rte_mem_virt2memseg_list(const void *addr)
249 {
250         return virt2memseg_list(addr);
251 }
252
253 struct virtiova {
254         rte_iova_t iova;
255         void *virt;
256 };
257 static int
258 find_virt(const struct rte_memseg_list *msl __rte_unused,
259                 const struct rte_memseg *ms, void *arg)
260 {
261         struct virtiova *vi = arg;
262         if (vi->iova >= ms->iova && vi->iova < (ms->iova + ms->len)) {
263                 size_t offset = vi->iova - ms->iova;
264                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
265                 /* stop the walk */
266                 return 1;
267         }
268         return 0;
269 }
270 static int
271 find_virt_legacy(const struct rte_memseg_list *msl __rte_unused,
272                 const struct rte_memseg *ms, size_t len, void *arg)
273 {
274         struct virtiova *vi = arg;
275         if (vi->iova >= ms->iova && vi->iova < (ms->iova + len)) {
276                 size_t offset = vi->iova - ms->iova;
277                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
278                 /* stop the walk */
279                 return 1;
280         }
281         return 0;
282 }
283
284 void *
285 rte_mem_iova2virt(rte_iova_t iova)
286 {
287         struct virtiova vi;
288
289         memset(&vi, 0, sizeof(vi));
290
291         vi.iova = iova;
292         /* for legacy mem, we can get away with scanning VA-contiguous segments,
293          * as we know they are PA-contiguous as well
294          */
295         if (internal_config.legacy_mem)
296                 rte_memseg_contig_walk(find_virt_legacy, &vi);
297         else
298                 rte_memseg_walk(find_virt, &vi);
299
300         return vi.virt;
301 }
302
303 struct rte_memseg *
304 rte_mem_virt2memseg(const void *addr, const struct rte_memseg_list *msl)
305 {
306         return virt2memseg(addr, msl != NULL ? msl :
307                         rte_mem_virt2memseg_list(addr));
308 }
309
310 static int
311 physmem_size(const struct rte_memseg_list *msl, void *arg)
312 {
313         uint64_t *total_len = arg;
314
315         if (msl->external)
316                 return 0;
317
318         *total_len += msl->memseg_arr.count * msl->page_sz;
319
320         return 0;
321 }
322
323 /* get the total size of memory */
324 uint64_t
325 rte_eal_get_physmem_size(void)
326 {
327         uint64_t total_len = 0;
328
329         rte_memseg_list_walk(physmem_size, &total_len);
330
331         return total_len;
332 }
333
334 static int
335 dump_memseg(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
336                 void *arg)
337 {
338         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
339         int msl_idx, ms_idx, fd;
340         FILE *f = arg;
341
342         msl_idx = msl - mcfg->memsegs;
343         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
344                 return -1;
345
346         ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
347         if (ms_idx < 0)
348                 return -1;
349
350         fd = eal_memalloc_get_seg_fd(msl_idx, ms_idx);
351         fprintf(f, "Segment %i-%i: IOVA:0x%"PRIx64", len:%zu, "
352                         "virt:%p, socket_id:%"PRId32", "
353                         "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
354                         "nrank:%"PRIx32" fd:%i\n",
355                         msl_idx, ms_idx,
356                         ms->iova,
357                         ms->len,
358                         ms->addr,
359                         ms->socket_id,
360                         ms->hugepage_sz,
361                         ms->nchannel,
362                         ms->nrank,
363                         fd);
364
365         return 0;
366 }
367
368 /*
369  * Defining here because declared in rte_memory.h, but the actual implementation
370  * is in eal_common_memalloc.c, like all other memalloc internals.
371  */
372 int
373 rte_mem_event_callback_register(const char *name, rte_mem_event_callback_t clb,
374                 void *arg)
375 {
376         /* FreeBSD boots with legacy mem enabled by default */
377         if (internal_config.legacy_mem) {
378                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
379                 rte_errno = ENOTSUP;
380                 return -1;
381         }
382         return eal_memalloc_mem_event_callback_register(name, clb, arg);
383 }
384
385 int
386 rte_mem_event_callback_unregister(const char *name, void *arg)
387 {
388         /* FreeBSD boots with legacy mem enabled by default */
389         if (internal_config.legacy_mem) {
390                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
391                 rte_errno = ENOTSUP;
392                 return -1;
393         }
394         return eal_memalloc_mem_event_callback_unregister(name, arg);
395 }
396
397 int
398 rte_mem_alloc_validator_register(const char *name,
399                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
400 {
401         /* FreeBSD boots with legacy mem enabled by default */
402         if (internal_config.legacy_mem) {
403                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
404                 rte_errno = ENOTSUP;
405                 return -1;
406         }
407         return eal_memalloc_mem_alloc_validator_register(name, clb, socket_id,
408                         limit);
409 }
410
411 int
412 rte_mem_alloc_validator_unregister(const char *name, int socket_id)
413 {
414         /* FreeBSD boots with legacy mem enabled by default */
415         if (internal_config.legacy_mem) {
416                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
417                 rte_errno = ENOTSUP;
418                 return -1;
419         }
420         return eal_memalloc_mem_alloc_validator_unregister(name, socket_id);
421 }
422
423 /* Dump the physical memory layout on console */
424 void
425 rte_dump_physmem_layout(FILE *f)
426 {
427         rte_memseg_walk(dump_memseg, f);
428 }
429
430 static int
431 check_iova(const struct rte_memseg_list *msl __rte_unused,
432                 const struct rte_memseg *ms, void *arg)
433 {
434         uint64_t *mask = arg;
435         rte_iova_t iova;
436
437         /* higher address within segment */
438         iova = (ms->iova + ms->len) - 1;
439         if (!(iova & *mask))
440                 return 0;
441
442         RTE_LOG(DEBUG, EAL, "memseg iova %"PRIx64", len %zx, out of range\n",
443                             ms->iova, ms->len);
444
445         RTE_LOG(DEBUG, EAL, "\tusing dma mask %"PRIx64"\n", *mask);
446         return 1;
447 }
448
449 #define MAX_DMA_MASK_BITS 63
450
451 /* check memseg iovas are within the required range based on dma mask */
452 static int
453 check_dma_mask(uint8_t maskbits, bool thread_unsafe)
454 {
455         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
456         uint64_t mask;
457         int ret;
458
459         /* Sanity check. We only check width can be managed with 64 bits
460          * variables. Indeed any higher value is likely wrong. */
461         if (maskbits > MAX_DMA_MASK_BITS) {
462                 RTE_LOG(ERR, EAL, "wrong dma mask size %u (Max: %u)\n",
463                                    maskbits, MAX_DMA_MASK_BITS);
464                 return -1;
465         }
466
467         /* create dma mask */
468         mask = ~((1ULL << maskbits) - 1);
469
470         if (thread_unsafe)
471                 ret = rte_memseg_walk_thread_unsafe(check_iova, &mask);
472         else
473                 ret = rte_memseg_walk(check_iova, &mask);
474
475         if (ret)
476                 /*
477                  * Dma mask precludes hugepage usage.
478                  * This device can not be used and we do not need to keep
479                  * the dma mask.
480                  */
481                 return 1;
482
483         /*
484          * we need to keep the more restricted maskbit for checking
485          * potential dynamic memory allocation in the future.
486          */
487         mcfg->dma_maskbits = mcfg->dma_maskbits == 0 ? maskbits :
488                              RTE_MIN(mcfg->dma_maskbits, maskbits);
489
490         return 0;
491 }
492
493 int
494 rte_mem_check_dma_mask(uint8_t maskbits)
495 {
496         return check_dma_mask(maskbits, false);
497 }
498
499 int
500 rte_mem_check_dma_mask_thread_unsafe(uint8_t maskbits)
501 {
502         return check_dma_mask(maskbits, true);
503 }
504
505 /*
506  * Set dma mask to use when memory initialization is done.
507  *
508  * This function should ONLY be used by code executed before the memory
509  * initialization. PMDs should use rte_mem_check_dma_mask if addressing
510  * limitations by the device.
511  */
512 void
513 rte_mem_set_dma_mask(uint8_t maskbits)
514 {
515         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
516
517         mcfg->dma_maskbits = mcfg->dma_maskbits == 0 ? maskbits :
518                              RTE_MIN(mcfg->dma_maskbits, maskbits);
519 }
520
521 /* return the number of memory channels */
522 unsigned rte_memory_get_nchannel(void)
523 {
524         return rte_eal_get_configuration()->mem_config->nchannel;
525 }
526
527 /* return the number of memory rank */
528 unsigned rte_memory_get_nrank(void)
529 {
530         return rte_eal_get_configuration()->mem_config->nrank;
531 }
532
533 static int
534 rte_eal_memdevice_init(void)
535 {
536         struct rte_config *config;
537
538         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
539                 return 0;
540
541         config = rte_eal_get_configuration();
542         config->mem_config->nchannel = internal_config.force_nchannel;
543         config->mem_config->nrank = internal_config.force_nrank;
544
545         return 0;
546 }
547
548 /* Lock page in physical memory and prevent from swapping. */
549 int
550 rte_mem_lock_page(const void *virt)
551 {
552         unsigned long virtual = (unsigned long)virt;
553         int page_size = getpagesize();
554         unsigned long aligned = (virtual & ~(page_size - 1));
555         return mlock((void *)aligned, page_size);
556 }
557
558 int
559 rte_memseg_contig_walk_thread_unsafe(rte_memseg_contig_walk_t func, void *arg)
560 {
561         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
562         int i, ms_idx, ret = 0;
563
564         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
565                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
566                 const struct rte_memseg *ms;
567                 struct rte_fbarray *arr;
568
569                 if (msl->memseg_arr.count == 0)
570                         continue;
571
572                 arr = &msl->memseg_arr;
573
574                 ms_idx = rte_fbarray_find_next_used(arr, 0);
575                 while (ms_idx >= 0) {
576                         int n_segs;
577                         size_t len;
578
579                         ms = rte_fbarray_get(arr, ms_idx);
580
581                         /* find how many more segments there are, starting with
582                          * this one.
583                          */
584                         n_segs = rte_fbarray_find_contig_used(arr, ms_idx);
585                         len = n_segs * msl->page_sz;
586
587                         ret = func(msl, ms, len, arg);
588                         if (ret)
589                                 return ret;
590                         ms_idx = rte_fbarray_find_next_used(arr,
591                                         ms_idx + n_segs);
592                 }
593         }
594         return 0;
595 }
596
597 int
598 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
599 {
600         int ret = 0;
601
602         /* do not allow allocations/frees/init while we iterate */
603         rte_mcfg_mem_read_lock();
604         ret = rte_memseg_contig_walk_thread_unsafe(func, arg);
605         rte_mcfg_mem_read_unlock();
606
607         return ret;
608 }
609
610 int
611 rte_memseg_walk_thread_unsafe(rte_memseg_walk_t func, void *arg)
612 {
613         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
614         int i, ms_idx, ret = 0;
615
616         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
617                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
618                 const struct rte_memseg *ms;
619                 struct rte_fbarray *arr;
620
621                 if (msl->memseg_arr.count == 0)
622                         continue;
623
624                 arr = &msl->memseg_arr;
625
626                 ms_idx = rte_fbarray_find_next_used(arr, 0);
627                 while (ms_idx >= 0) {
628                         ms = rte_fbarray_get(arr, ms_idx);
629                         ret = func(msl, ms, arg);
630                         if (ret)
631                                 return ret;
632                         ms_idx = rte_fbarray_find_next_used(arr, ms_idx + 1);
633                 }
634         }
635         return 0;
636 }
637
638 int
639 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
640 {
641         int ret = 0;
642
643         /* do not allow allocations/frees/init while we iterate */
644         rte_mcfg_mem_read_lock();
645         ret = rte_memseg_walk_thread_unsafe(func, arg);
646         rte_mcfg_mem_read_unlock();
647
648         return ret;
649 }
650
651 int
652 rte_memseg_list_walk_thread_unsafe(rte_memseg_list_walk_t func, void *arg)
653 {
654         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
655         int i, ret = 0;
656
657         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
658                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
659
660                 if (msl->base_va == NULL)
661                         continue;
662
663                 ret = func(msl, arg);
664                 if (ret)
665                         return ret;
666         }
667         return 0;
668 }
669
670 int
671 rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)
672 {
673         int ret = 0;
674
675         /* do not allow allocations/frees/init while we iterate */
676         rte_mcfg_mem_read_lock();
677         ret = rte_memseg_list_walk_thread_unsafe(func, arg);
678         rte_mcfg_mem_read_unlock();
679
680         return ret;
681 }
682
683 int
684 rte_memseg_get_fd_thread_unsafe(const struct rte_memseg *ms)
685 {
686         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
687         struct rte_memseg_list *msl;
688         struct rte_fbarray *arr;
689         int msl_idx, seg_idx, ret;
690
691         if (ms == NULL) {
692                 rte_errno = EINVAL;
693                 return -1;
694         }
695
696         msl = rte_mem_virt2memseg_list(ms->addr);
697         if (msl == NULL) {
698                 rte_errno = EINVAL;
699                 return -1;
700         }
701         arr = &msl->memseg_arr;
702
703         msl_idx = msl - mcfg->memsegs;
704         seg_idx = rte_fbarray_find_idx(arr, ms);
705
706         if (!rte_fbarray_is_used(arr, seg_idx)) {
707                 rte_errno = ENOENT;
708                 return -1;
709         }
710
711         /* segment fd API is not supported for external segments */
712         if (msl->external) {
713                 rte_errno = ENOTSUP;
714                 return -1;
715         }
716
717         ret = eal_memalloc_get_seg_fd(msl_idx, seg_idx);
718         if (ret < 0) {
719                 rte_errno = -ret;
720                 ret = -1;
721         }
722         return ret;
723 }
724
725 int
726 rte_memseg_get_fd(const struct rte_memseg *ms)
727 {
728         int ret;
729
730         rte_mcfg_mem_read_lock();
731         ret = rte_memseg_get_fd_thread_unsafe(ms);
732         rte_mcfg_mem_read_unlock();
733
734         return ret;
735 }
736
737 int
738 rte_memseg_get_fd_offset_thread_unsafe(const struct rte_memseg *ms,
739                 size_t *offset)
740 {
741         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
742         struct rte_memseg_list *msl;
743         struct rte_fbarray *arr;
744         int msl_idx, seg_idx, ret;
745
746         if (ms == NULL || offset == NULL) {
747                 rte_errno = EINVAL;
748                 return -1;
749         }
750
751         msl = rte_mem_virt2memseg_list(ms->addr);
752         if (msl == NULL) {
753                 rte_errno = EINVAL;
754                 return -1;
755         }
756         arr = &msl->memseg_arr;
757
758         msl_idx = msl - mcfg->memsegs;
759         seg_idx = rte_fbarray_find_idx(arr, ms);
760
761         if (!rte_fbarray_is_used(arr, seg_idx)) {
762                 rte_errno = ENOENT;
763                 return -1;
764         }
765
766         /* segment fd API is not supported for external segments */
767         if (msl->external) {
768                 rte_errno = ENOTSUP;
769                 return -1;
770         }
771
772         ret = eal_memalloc_get_seg_fd_offset(msl_idx, seg_idx, offset);
773         if (ret < 0) {
774                 rte_errno = -ret;
775                 ret = -1;
776         }
777         return ret;
778 }
779
780 int
781 rte_memseg_get_fd_offset(const struct rte_memseg *ms, size_t *offset)
782 {
783         int ret;
784
785         rte_mcfg_mem_read_lock();
786         ret = rte_memseg_get_fd_offset_thread_unsafe(ms, offset);
787         rte_mcfg_mem_read_unlock();
788
789         return ret;
790 }
791
792 int
793 rte_extmem_register(void *va_addr, size_t len, rte_iova_t iova_addrs[],
794                 unsigned int n_pages, size_t page_sz)
795 {
796         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
797         unsigned int socket_id, n;
798         int ret = 0;
799
800         if (va_addr == NULL || page_sz == 0 || len == 0 ||
801                         !rte_is_power_of_2(page_sz) ||
802                         RTE_ALIGN(len, page_sz) != len ||
803                         ((len / page_sz) != n_pages && iova_addrs != NULL) ||
804                         !rte_is_aligned(va_addr, page_sz)) {
805                 rte_errno = EINVAL;
806                 return -1;
807         }
808         rte_mcfg_mem_write_lock();
809
810         /* make sure the segment doesn't already exist */
811         if (malloc_heap_find_external_seg(va_addr, len) != NULL) {
812                 rte_errno = EEXIST;
813                 ret = -1;
814                 goto unlock;
815         }
816
817         /* get next available socket ID */
818         socket_id = mcfg->next_socket_id;
819         if (socket_id > INT32_MAX) {
820                 RTE_LOG(ERR, EAL, "Cannot assign new socket ID's\n");
821                 rte_errno = ENOSPC;
822                 ret = -1;
823                 goto unlock;
824         }
825
826         /* we can create a new memseg */
827         n = len / page_sz;
828         if (malloc_heap_create_external_seg(va_addr, iova_addrs, n,
829                         page_sz, "extmem", socket_id) == NULL) {
830                 ret = -1;
831                 goto unlock;
832         }
833
834         /* memseg list successfully created - increment next socket ID */
835         mcfg->next_socket_id++;
836 unlock:
837         rte_mcfg_mem_write_unlock();
838         return ret;
839 }
840
841 int
842 rte_extmem_unregister(void *va_addr, size_t len)
843 {
844         struct rte_memseg_list *msl;
845         int ret = 0;
846
847         if (va_addr == NULL || len == 0) {
848                 rte_errno = EINVAL;
849                 return -1;
850         }
851         rte_mcfg_mem_write_lock();
852
853         /* find our segment */
854         msl = malloc_heap_find_external_seg(va_addr, len);
855         if (msl == NULL) {
856                 rte_errno = ENOENT;
857                 ret = -1;
858                 goto unlock;
859         }
860
861         ret = malloc_heap_destroy_external_seg(msl);
862 unlock:
863         rte_mcfg_mem_write_unlock();
864         return ret;
865 }
866
867 static int
868 sync_memory(void *va_addr, size_t len, bool attach)
869 {
870         struct rte_memseg_list *msl;
871         int ret = 0;
872
873         if (va_addr == NULL || len == 0) {
874                 rte_errno = EINVAL;
875                 return -1;
876         }
877         rte_mcfg_mem_write_lock();
878
879         /* find our segment */
880         msl = malloc_heap_find_external_seg(va_addr, len);
881         if (msl == NULL) {
882                 rte_errno = ENOENT;
883                 ret = -1;
884                 goto unlock;
885         }
886         if (attach)
887                 ret = rte_fbarray_attach(&msl->memseg_arr);
888         else
889                 ret = rte_fbarray_detach(&msl->memseg_arr);
890
891 unlock:
892         rte_mcfg_mem_write_unlock();
893         return ret;
894 }
895
896 int
897 rte_extmem_attach(void *va_addr, size_t len)
898 {
899         return sync_memory(va_addr, len, true);
900 }
901
902 int
903 rte_extmem_detach(void *va_addr, size_t len)
904 {
905         return sync_memory(va_addr, len, false);
906 }
907
908 /* init memory subsystem */
909 int
910 rte_eal_memory_init(void)
911 {
912         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
913         int retval;
914         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
915
916         if (!mcfg)
917                 return -1;
918
919         /* lock mem hotplug here, to prevent races while we init */
920         rte_mcfg_mem_read_lock();
921
922         if (rte_eal_memseg_init() < 0)
923                 goto fail;
924
925         if (eal_memalloc_init() < 0)
926                 goto fail;
927
928         retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
929                         rte_eal_hugepage_init() :
930                         rte_eal_hugepage_attach();
931         if (retval < 0)
932                 goto fail;
933
934         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
935                 goto fail;
936
937         return 0;
938 fail:
939         rte_mcfg_mem_read_unlock();
940         return -1;
941 }