30d018209296f717abb2a1bfe3c0d9a11026b964
[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 <errno.h>
6 #include <stdio.h>
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <inttypes.h>
13 #include <sys/mman.h>
14 #include <sys/queue.h>
15
16 #include <rte_fbarray.h>
17 #include <rte_memory.h>
18 #include <rte_eal.h>
19 #include <rte_eal_memconfig.h>
20 #include <rte_errno.h>
21 #include <rte_log.h>
22
23 #include "eal_memalloc.h"
24 #include "eal_private.h"
25 #include "eal_internal_cfg.h"
26
27 /*
28  * Try to mmap *size bytes in /dev/zero. If it is successful, return the
29  * pointer to the mmap'd area and keep *size unmodified. Else, retry
30  * with a smaller zone: decrease *size by hugepage_sz until it reaches
31  * 0. In this case, return NULL. Note: this function returns an address
32  * which is a multiple of hugepage size.
33  */
34
35 #define MEMSEG_LIST_FMT "memseg-%" PRIu64 "k-%i-%i"
36
37 static void *next_baseaddr;
38 static uint64_t system_page_sz;
39
40 void *
41 eal_get_virtual_area(void *requested_addr, size_t *size,
42                 size_t page_sz, int flags, int mmap_flags)
43 {
44         bool addr_is_hint, allow_shrink, unmap, no_align;
45         uint64_t map_sz;
46         void *mapped_addr, *aligned_addr;
47
48         if (system_page_sz == 0)
49                 system_page_sz = sysconf(_SC_PAGESIZE);
50
51         mmap_flags |= MAP_PRIVATE | MAP_ANONYMOUS;
52
53         RTE_LOG(DEBUG, EAL, "Ask a virtual area of 0x%zx bytes\n", *size);
54
55         addr_is_hint = (flags & EAL_VIRTUAL_AREA_ADDR_IS_HINT) > 0;
56         allow_shrink = (flags & EAL_VIRTUAL_AREA_ALLOW_SHRINK) > 0;
57         unmap = (flags & EAL_VIRTUAL_AREA_UNMAP) > 0;
58
59         if (next_baseaddr == NULL && internal_config.base_virtaddr != 0 &&
60                         rte_eal_process_type() == RTE_PROC_PRIMARY)
61                 next_baseaddr = (void *) internal_config.base_virtaddr;
62
63         if (requested_addr == NULL && next_baseaddr != NULL) {
64                 requested_addr = next_baseaddr;
65                 requested_addr = RTE_PTR_ALIGN(requested_addr, page_sz);
66                 addr_is_hint = true;
67         }
68
69         /* we don't need alignment of resulting pointer in the following cases:
70          *
71          * 1. page size is equal to system size
72          * 2. we have a requested address, and it is page-aligned, and we will
73          *    be discarding the address if we get a different one.
74          *
75          * for all other cases, alignment is potentially necessary.
76          */
77         no_align = (requested_addr != NULL &&
78                 requested_addr == RTE_PTR_ALIGN(requested_addr, page_sz) &&
79                 !addr_is_hint) ||
80                 page_sz == system_page_sz;
81
82         do {
83                 map_sz = no_align ? *size : *size + page_sz;
84                 if (map_sz > SIZE_MAX) {
85                         RTE_LOG(ERR, EAL, "Map size too big\n");
86                         rte_errno = E2BIG;
87                         return NULL;
88                 }
89
90                 mapped_addr = mmap(requested_addr, (size_t)map_sz, PROT_READ,
91                                 mmap_flags, -1, 0);
92                 if (mapped_addr == MAP_FAILED && allow_shrink)
93                         *size -= page_sz;
94         } while (allow_shrink && mapped_addr == MAP_FAILED && *size > 0);
95
96         /* align resulting address - if map failed, we will ignore the value
97          * anyway, so no need to add additional checks.
98          */
99         aligned_addr = no_align ? mapped_addr :
100                         RTE_PTR_ALIGN(mapped_addr, page_sz);
101
102         if (*size == 0) {
103                 RTE_LOG(ERR, EAL, "Cannot get a virtual area of any size: %s\n",
104                         strerror(errno));
105                 rte_errno = errno;
106                 return NULL;
107         } else if (mapped_addr == MAP_FAILED) {
108                 RTE_LOG(ERR, EAL, "Cannot get a virtual area: %s\n",
109                         strerror(errno));
110                 /* pass errno up the call chain */
111                 rte_errno = errno;
112                 return NULL;
113         } else if (requested_addr != NULL && !addr_is_hint &&
114                         aligned_addr != requested_addr) {
115                 RTE_LOG(ERR, EAL, "Cannot get a virtual area at requested address: %p (got %p)\n",
116                         requested_addr, aligned_addr);
117                 munmap(mapped_addr, map_sz);
118                 rte_errno = EADDRNOTAVAIL;
119                 return NULL;
120         } else if (requested_addr != NULL && addr_is_hint &&
121                         aligned_addr != requested_addr) {
122                 RTE_LOG(WARNING, EAL, "WARNING! Base virtual address hint (%p != %p) not respected!\n",
123                         requested_addr, aligned_addr);
124                 RTE_LOG(WARNING, EAL, "   This may cause issues with mapping memory into secondary processes\n");
125         } else if (next_baseaddr != NULL) {
126                 next_baseaddr = RTE_PTR_ADD(aligned_addr, *size);
127         }
128
129         RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
130                 aligned_addr, *size);
131
132         if (unmap) {
133                 munmap(mapped_addr, map_sz);
134         } else if (!no_align) {
135                 void *map_end, *aligned_end;
136                 size_t before_len, after_len;
137
138                 /* when we reserve space with alignment, we add alignment to
139                  * mapping size. On 32-bit, if 1GB alignment was requested, this
140                  * would waste 1GB of address space, which is a luxury we cannot
141                  * afford. so, if alignment was performed, check if any unneeded
142                  * address space can be unmapped back.
143                  */
144
145                 map_end = RTE_PTR_ADD(mapped_addr, (size_t)map_sz);
146                 aligned_end = RTE_PTR_ADD(aligned_addr, *size);
147
148                 /* unmap space before aligned mmap address */
149                 before_len = RTE_PTR_DIFF(aligned_addr, mapped_addr);
150                 if (before_len > 0)
151                         munmap(mapped_addr, before_len);
152
153                 /* unmap space after aligned end mmap address */
154                 after_len = RTE_PTR_DIFF(map_end, aligned_end);
155                 if (after_len > 0)
156                         munmap(aligned_end, after_len);
157         }
158
159         return aligned_addr;
160 }
161
162 static struct rte_memseg *
163 virt2memseg(const void *addr, const struct rte_memseg_list *msl)
164 {
165         const struct rte_fbarray *arr;
166         void *start, *end;
167         int ms_idx;
168
169         if (msl == NULL)
170                 return NULL;
171
172         /* a memseg list was specified, check if it's the right one */
173         start = msl->base_va;
174         end = RTE_PTR_ADD(start, msl->len);
175
176         if (addr < start || addr >= end)
177                 return NULL;
178
179         /* now, calculate index */
180         arr = &msl->memseg_arr;
181         ms_idx = RTE_PTR_DIFF(addr, msl->base_va) / msl->page_sz;
182         return rte_fbarray_get(arr, ms_idx);
183 }
184
185 static struct rte_memseg_list *
186 virt2memseg_list(const void *addr)
187 {
188         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
189         struct rte_memseg_list *msl;
190         int msl_idx;
191
192         for (msl_idx = 0; msl_idx < RTE_MAX_MEMSEG_LISTS; msl_idx++) {
193                 void *start, *end;
194                 msl = &mcfg->memsegs[msl_idx];
195
196                 start = msl->base_va;
197                 end = RTE_PTR_ADD(start, msl->len);
198                 if (addr >= start && addr < end)
199                         break;
200         }
201         /* if we didn't find our memseg list */
202         if (msl_idx == RTE_MAX_MEMSEG_LISTS)
203                 return NULL;
204         return msl;
205 }
206
207 __rte_experimental struct rte_memseg_list *
208 rte_mem_virt2memseg_list(const void *addr)
209 {
210         return virt2memseg_list(addr);
211 }
212
213 struct virtiova {
214         rte_iova_t iova;
215         void *virt;
216 };
217 static int
218 find_virt(const struct rte_memseg_list *msl __rte_unused,
219                 const struct rte_memseg *ms, void *arg)
220 {
221         struct virtiova *vi = arg;
222         if (vi->iova >= ms->iova && vi->iova < (ms->iova + ms->len)) {
223                 size_t offset = vi->iova - ms->iova;
224                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
225                 /* stop the walk */
226                 return 1;
227         }
228         return 0;
229 }
230 static int
231 find_virt_legacy(const struct rte_memseg_list *msl __rte_unused,
232                 const struct rte_memseg *ms, size_t len, void *arg)
233 {
234         struct virtiova *vi = arg;
235         if (vi->iova >= ms->iova && vi->iova < (ms->iova + len)) {
236                 size_t offset = vi->iova - ms->iova;
237                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
238                 /* stop the walk */
239                 return 1;
240         }
241         return 0;
242 }
243
244 __rte_experimental void *
245 rte_mem_iova2virt(rte_iova_t iova)
246 {
247         struct virtiova vi;
248
249         memset(&vi, 0, sizeof(vi));
250
251         vi.iova = iova;
252         /* for legacy mem, we can get away with scanning VA-contiguous segments,
253          * as we know they are PA-contiguous as well
254          */
255         if (internal_config.legacy_mem)
256                 rte_memseg_contig_walk(find_virt_legacy, &vi);
257         else
258                 rte_memseg_walk(find_virt, &vi);
259
260         return vi.virt;
261 }
262
263 __rte_experimental struct rte_memseg *
264 rte_mem_virt2memseg(const void *addr, const struct rte_memseg_list *msl)
265 {
266         return virt2memseg(addr, msl != NULL ? msl :
267                         rte_mem_virt2memseg_list(addr));
268 }
269
270 static int
271 physmem_size(const struct rte_memseg_list *msl, void *arg)
272 {
273         uint64_t *total_len = arg;
274
275         *total_len += msl->memseg_arr.count * msl->page_sz;
276
277         return 0;
278 }
279
280 /* get the total size of memory */
281 uint64_t
282 rte_eal_get_physmem_size(void)
283 {
284         uint64_t total_len = 0;
285
286         rte_memseg_list_walk(physmem_size, &total_len);
287
288         return total_len;
289 }
290
291 static int
292 dump_memseg(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
293                 void *arg)
294 {
295         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
296         int msl_idx, ms_idx, fd;
297         FILE *f = arg;
298
299         msl_idx = msl - mcfg->memsegs;
300         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
301                 return -1;
302
303         ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
304         if (ms_idx < 0)
305                 return -1;
306
307         fd = eal_memalloc_get_seg_fd(msl_idx, ms_idx);
308         fprintf(f, "Segment %i-%i: IOVA:0x%"PRIx64", len:%zu, "
309                         "virt:%p, socket_id:%"PRId32", "
310                         "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
311                         "nrank:%"PRIx32" fd:%i\n",
312                         msl_idx, ms_idx,
313                         ms->iova,
314                         ms->len,
315                         ms->addr,
316                         ms->socket_id,
317                         ms->hugepage_sz,
318                         ms->nchannel,
319                         ms->nrank,
320                         fd);
321
322         return 0;
323 }
324
325 /*
326  * Defining here because declared in rte_memory.h, but the actual implementation
327  * is in eal_common_memalloc.c, like all other memalloc internals.
328  */
329 int __rte_experimental
330 rte_mem_event_callback_register(const char *name, rte_mem_event_callback_t clb,
331                 void *arg)
332 {
333         /* FreeBSD boots with legacy mem enabled by default */
334         if (internal_config.legacy_mem) {
335                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
336                 rte_errno = ENOTSUP;
337                 return -1;
338         }
339         return eal_memalloc_mem_event_callback_register(name, clb, arg);
340 }
341
342 int __rte_experimental
343 rte_mem_event_callback_unregister(const char *name, void *arg)
344 {
345         /* FreeBSD boots with legacy mem enabled by default */
346         if (internal_config.legacy_mem) {
347                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
348                 rte_errno = ENOTSUP;
349                 return -1;
350         }
351         return eal_memalloc_mem_event_callback_unregister(name, arg);
352 }
353
354 int __rte_experimental
355 rte_mem_alloc_validator_register(const char *name,
356                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
357 {
358         /* FreeBSD boots with legacy mem enabled by default */
359         if (internal_config.legacy_mem) {
360                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
361                 rte_errno = ENOTSUP;
362                 return -1;
363         }
364         return eal_memalloc_mem_alloc_validator_register(name, clb, socket_id,
365                         limit);
366 }
367
368 int __rte_experimental
369 rte_mem_alloc_validator_unregister(const char *name, int socket_id)
370 {
371         /* FreeBSD boots with legacy mem enabled by default */
372         if (internal_config.legacy_mem) {
373                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
374                 rte_errno = ENOTSUP;
375                 return -1;
376         }
377         return eal_memalloc_mem_alloc_validator_unregister(name, socket_id);
378 }
379
380 /* Dump the physical memory layout on console */
381 void
382 rte_dump_physmem_layout(FILE *f)
383 {
384         rte_memseg_walk(dump_memseg, f);
385 }
386
387 /* return the number of memory channels */
388 unsigned rte_memory_get_nchannel(void)
389 {
390         return rte_eal_get_configuration()->mem_config->nchannel;
391 }
392
393 /* return the number of memory rank */
394 unsigned rte_memory_get_nrank(void)
395 {
396         return rte_eal_get_configuration()->mem_config->nrank;
397 }
398
399 static int
400 rte_eal_memdevice_init(void)
401 {
402         struct rte_config *config;
403
404         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
405                 return 0;
406
407         config = rte_eal_get_configuration();
408         config->mem_config->nchannel = internal_config.force_nchannel;
409         config->mem_config->nrank = internal_config.force_nrank;
410
411         return 0;
412 }
413
414 /* Lock page in physical memory and prevent from swapping. */
415 int
416 rte_mem_lock_page(const void *virt)
417 {
418         unsigned long virtual = (unsigned long)virt;
419         int page_size = getpagesize();
420         unsigned long aligned = (virtual & ~(page_size - 1));
421         return mlock((void *)aligned, page_size);
422 }
423
424 int __rte_experimental
425 rte_memseg_contig_walk_thread_unsafe(rte_memseg_contig_walk_t func, void *arg)
426 {
427         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
428         int i, ms_idx, ret = 0;
429
430         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
431                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
432                 const struct rte_memseg *ms;
433                 struct rte_fbarray *arr;
434
435                 if (msl->memseg_arr.count == 0)
436                         continue;
437
438                 arr = &msl->memseg_arr;
439
440                 ms_idx = rte_fbarray_find_next_used(arr, 0);
441                 while (ms_idx >= 0) {
442                         int n_segs;
443                         size_t len;
444
445                         ms = rte_fbarray_get(arr, ms_idx);
446
447                         /* find how many more segments there are, starting with
448                          * this one.
449                          */
450                         n_segs = rte_fbarray_find_contig_used(arr, ms_idx);
451                         len = n_segs * msl->page_sz;
452
453                         ret = func(msl, ms, len, arg);
454                         if (ret)
455                                 return ret;
456                         ms_idx = rte_fbarray_find_next_used(arr,
457                                         ms_idx + n_segs);
458                 }
459         }
460         return 0;
461 }
462
463 int __rte_experimental
464 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
465 {
466         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
467         int ret = 0;
468
469         /* do not allow allocations/frees/init while we iterate */
470         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
471         ret = rte_memseg_contig_walk_thread_unsafe(func, arg);
472         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
473
474         return ret;
475 }
476
477 int __rte_experimental
478 rte_memseg_walk_thread_unsafe(rte_memseg_walk_t func, void *arg)
479 {
480         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
481         int i, ms_idx, ret = 0;
482
483         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
484                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
485                 const struct rte_memseg *ms;
486                 struct rte_fbarray *arr;
487
488                 if (msl->memseg_arr.count == 0)
489                         continue;
490
491                 arr = &msl->memseg_arr;
492
493                 ms_idx = rte_fbarray_find_next_used(arr, 0);
494                 while (ms_idx >= 0) {
495                         ms = rte_fbarray_get(arr, ms_idx);
496                         ret = func(msl, ms, arg);
497                         if (ret)
498                                 return ret;
499                         ms_idx = rte_fbarray_find_next_used(arr, ms_idx + 1);
500                 }
501         }
502         return 0;
503 }
504
505 int __rte_experimental
506 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
507 {
508         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
509         int ret = 0;
510
511         /* do not allow allocations/frees/init while we iterate */
512         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
513         ret = rte_memseg_walk_thread_unsafe(func, arg);
514         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
515
516         return ret;
517 }
518
519 int __rte_experimental
520 rte_memseg_list_walk_thread_unsafe(rte_memseg_list_walk_t func, void *arg)
521 {
522         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
523         int i, ret = 0;
524
525         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
526                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
527
528                 if (msl->base_va == NULL)
529                         continue;
530
531                 ret = func(msl, arg);
532                 if (ret)
533                         return ret;
534         }
535         return 0;
536 }
537
538 int __rte_experimental
539 rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)
540 {
541         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
542         int ret = 0;
543
544         /* do not allow allocations/frees/init while we iterate */
545         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
546         ret = rte_memseg_list_walk_thread_unsafe(func, arg);
547         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
548
549         return ret;
550 }
551
552 int __rte_experimental
553 rte_memseg_get_fd_thread_unsafe(const struct rte_memseg *ms)
554 {
555         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
556         struct rte_memseg_list *msl;
557         struct rte_fbarray *arr;
558         int msl_idx, seg_idx, ret;
559
560         if (ms == NULL) {
561                 rte_errno = EINVAL;
562                 return -1;
563         }
564
565         msl = rte_mem_virt2memseg_list(ms->addr);
566         if (msl == NULL) {
567                 rte_errno = EINVAL;
568                 return -1;
569         }
570         arr = &msl->memseg_arr;
571
572         msl_idx = msl - mcfg->memsegs;
573         seg_idx = rte_fbarray_find_idx(arr, ms);
574
575         if (!rte_fbarray_is_used(arr, seg_idx)) {
576                 rte_errno = ENOENT;
577                 return -1;
578         }
579
580         ret = eal_memalloc_get_seg_fd(msl_idx, seg_idx);
581         if (ret < 0) {
582                 rte_errno = -ret;
583                 ret = -1;
584         }
585         return ret;
586 }
587
588 int __rte_experimental
589 rte_memseg_get_fd(const struct rte_memseg *ms)
590 {
591         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
592         int ret;
593
594         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
595         ret = rte_memseg_get_fd_thread_unsafe(ms);
596         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
597
598         return ret;
599 }
600
601 int __rte_experimental
602 rte_memseg_get_fd_offset_thread_unsafe(const struct rte_memseg *ms,
603                 size_t *offset)
604 {
605         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
606         struct rte_memseg_list *msl;
607         struct rte_fbarray *arr;
608         int msl_idx, seg_idx, ret;
609
610         if (ms == NULL || offset == NULL) {
611                 rte_errno = EINVAL;
612                 return -1;
613         }
614
615         msl = rte_mem_virt2memseg_list(ms->addr);
616         if (msl == NULL) {
617                 rte_errno = EINVAL;
618                 return -1;
619         }
620         arr = &msl->memseg_arr;
621
622         msl_idx = msl - mcfg->memsegs;
623         seg_idx = rte_fbarray_find_idx(arr, ms);
624
625         if (!rte_fbarray_is_used(arr, seg_idx)) {
626                 rte_errno = ENOENT;
627                 return -1;
628         }
629
630         ret = eal_memalloc_get_seg_fd_offset(msl_idx, seg_idx, offset);
631         if (ret < 0) {
632                 rte_errno = -ret;
633                 ret = -1;
634         }
635         return ret;
636 }
637
638 int __rte_experimental
639 rte_memseg_get_fd_offset(const struct rte_memseg *ms, size_t *offset)
640 {
641         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
642         int ret;
643
644         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
645         ret = rte_memseg_get_fd_offset_thread_unsafe(ms, offset);
646         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
647
648         return ret;
649 }
650
651 /* init memory subsystem */
652 int
653 rte_eal_memory_init(void)
654 {
655         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
656         int retval;
657         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
658
659         if (!mcfg)
660                 return -1;
661
662         /* lock mem hotplug here, to prevent races while we init */
663         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
664
665         if (rte_eal_memseg_init() < 0)
666                 goto fail;
667
668         if (eal_memalloc_init() < 0)
669                 goto fail;
670
671         retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
672                         rte_eal_hugepage_init() :
673                         rte_eal_hugepage_attach();
674         if (retval < 0)
675                 goto fail;
676
677         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
678                 goto fail;
679
680         return 0;
681 fail:
682         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
683         return -1;
684 }