New upstream version 18.08
[deb_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, (size_t)msl->page_sz * msl->memseg_arr.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,
198                                 (size_t)msl->page_sz * msl->memseg_arr.len);
199                 if (addr >= start && addr < end)
200                         break;
201         }
202         /* if we didn't find our memseg list */
203         if (msl_idx == RTE_MAX_MEMSEG_LISTS)
204                 return NULL;
205         return msl;
206 }
207
208 __rte_experimental struct rte_memseg_list *
209 rte_mem_virt2memseg_list(const void *addr)
210 {
211         return virt2memseg_list(addr);
212 }
213
214 struct virtiova {
215         rte_iova_t iova;
216         void *virt;
217 };
218 static int
219 find_virt(const struct rte_memseg_list *msl __rte_unused,
220                 const struct rte_memseg *ms, void *arg)
221 {
222         struct virtiova *vi = arg;
223         if (vi->iova >= ms->iova && vi->iova < (ms->iova + ms->len)) {
224                 size_t offset = vi->iova - ms->iova;
225                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
226                 /* stop the walk */
227                 return 1;
228         }
229         return 0;
230 }
231 static int
232 find_virt_legacy(const struct rte_memseg_list *msl __rte_unused,
233                 const struct rte_memseg *ms, size_t len, void *arg)
234 {
235         struct virtiova *vi = arg;
236         if (vi->iova >= ms->iova && vi->iova < (ms->iova + len)) {
237                 size_t offset = vi->iova - ms->iova;
238                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
239                 /* stop the walk */
240                 return 1;
241         }
242         return 0;
243 }
244
245 __rte_experimental void *
246 rte_mem_iova2virt(rte_iova_t iova)
247 {
248         struct virtiova vi;
249
250         memset(&vi, 0, sizeof(vi));
251
252         vi.iova = iova;
253         /* for legacy mem, we can get away with scanning VA-contiguous segments,
254          * as we know they are PA-contiguous as well
255          */
256         if (internal_config.legacy_mem)
257                 rte_memseg_contig_walk(find_virt_legacy, &vi);
258         else
259                 rte_memseg_walk(find_virt, &vi);
260
261         return vi.virt;
262 }
263
264 __rte_experimental struct rte_memseg *
265 rte_mem_virt2memseg(const void *addr, const struct rte_memseg_list *msl)
266 {
267         return virt2memseg(addr, msl != NULL ? msl :
268                         rte_mem_virt2memseg_list(addr));
269 }
270
271 static int
272 physmem_size(const struct rte_memseg_list *msl, void *arg)
273 {
274         uint64_t *total_len = arg;
275
276         *total_len += msl->memseg_arr.count * msl->page_sz;
277
278         return 0;
279 }
280
281 /* get the total size of memory */
282 uint64_t
283 rte_eal_get_physmem_size(void)
284 {
285         uint64_t total_len = 0;
286
287         rte_memseg_list_walk(physmem_size, &total_len);
288
289         return total_len;
290 }
291
292 static int
293 dump_memseg(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
294                 void *arg)
295 {
296         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
297         int msl_idx, ms_idx;
298         FILE *f = arg;
299
300         msl_idx = msl - mcfg->memsegs;
301         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
302                 return -1;
303
304         ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
305         if (ms_idx < 0)
306                 return -1;
307
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"\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
321         return 0;
322 }
323
324 /*
325  * Defining here because declared in rte_memory.h, but the actual implementation
326  * is in eal_common_memalloc.c, like all other memalloc internals.
327  */
328 int __rte_experimental
329 rte_mem_event_callback_register(const char *name, rte_mem_event_callback_t clb,
330                 void *arg)
331 {
332         /* FreeBSD boots with legacy mem enabled by default */
333         if (internal_config.legacy_mem) {
334                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
335                 rte_errno = ENOTSUP;
336                 return -1;
337         }
338         return eal_memalloc_mem_event_callback_register(name, clb, arg);
339 }
340
341 int __rte_experimental
342 rte_mem_event_callback_unregister(const char *name, void *arg)
343 {
344         /* FreeBSD boots with legacy mem enabled by default */
345         if (internal_config.legacy_mem) {
346                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
347                 rte_errno = ENOTSUP;
348                 return -1;
349         }
350         return eal_memalloc_mem_event_callback_unregister(name, arg);
351 }
352
353 int __rte_experimental
354 rte_mem_alloc_validator_register(const char *name,
355                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
356 {
357         /* FreeBSD boots with legacy mem enabled by default */
358         if (internal_config.legacy_mem) {
359                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
360                 rte_errno = ENOTSUP;
361                 return -1;
362         }
363         return eal_memalloc_mem_alloc_validator_register(name, clb, socket_id,
364                         limit);
365 }
366
367 int __rte_experimental
368 rte_mem_alloc_validator_unregister(const char *name, int socket_id)
369 {
370         /* FreeBSD boots with legacy mem enabled by default */
371         if (internal_config.legacy_mem) {
372                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
373                 rte_errno = ENOTSUP;
374                 return -1;
375         }
376         return eal_memalloc_mem_alloc_validator_unregister(name, socket_id);
377 }
378
379 /* Dump the physical memory layout on console */
380 void
381 rte_dump_physmem_layout(FILE *f)
382 {
383         rte_memseg_walk(dump_memseg, f);
384 }
385
386 /* return the number of memory channels */
387 unsigned rte_memory_get_nchannel(void)
388 {
389         return rte_eal_get_configuration()->mem_config->nchannel;
390 }
391
392 /* return the number of memory rank */
393 unsigned rte_memory_get_nrank(void)
394 {
395         return rte_eal_get_configuration()->mem_config->nrank;
396 }
397
398 static int
399 rte_eal_memdevice_init(void)
400 {
401         struct rte_config *config;
402
403         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
404                 return 0;
405
406         config = rte_eal_get_configuration();
407         config->mem_config->nchannel = internal_config.force_nchannel;
408         config->mem_config->nrank = internal_config.force_nrank;
409
410         return 0;
411 }
412
413 /* Lock page in physical memory and prevent from swapping. */
414 int
415 rte_mem_lock_page(const void *virt)
416 {
417         unsigned long virtual = (unsigned long)virt;
418         int page_size = getpagesize();
419         unsigned long aligned = (virtual & ~(page_size - 1));
420         return mlock((void *)aligned, page_size);
421 }
422
423 int __rte_experimental
424 rte_memseg_contig_walk_thread_unsafe(rte_memseg_contig_walk_t func, void *arg)
425 {
426         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
427         int i, ms_idx, ret = 0;
428
429         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
430                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
431                 const struct rte_memseg *ms;
432                 struct rte_fbarray *arr;
433
434                 if (msl->memseg_arr.count == 0)
435                         continue;
436
437                 arr = &msl->memseg_arr;
438
439                 ms_idx = rte_fbarray_find_next_used(arr, 0);
440                 while (ms_idx >= 0) {
441                         int n_segs;
442                         size_t len;
443
444                         ms = rte_fbarray_get(arr, ms_idx);
445
446                         /* find how many more segments there are, starting with
447                          * this one.
448                          */
449                         n_segs = rte_fbarray_find_contig_used(arr, ms_idx);
450                         len = n_segs * msl->page_sz;
451
452                         ret = func(msl, ms, len, arg);
453                         if (ret)
454                                 return ret;
455                         ms_idx = rte_fbarray_find_next_used(arr,
456                                         ms_idx + n_segs);
457                 }
458         }
459         return 0;
460 }
461
462 int __rte_experimental
463 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
464 {
465         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
466         int ret = 0;
467
468         /* do not allow allocations/frees/init while we iterate */
469         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
470         ret = rte_memseg_contig_walk_thread_unsafe(func, arg);
471         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
472
473         return ret;
474 }
475
476 int __rte_experimental
477 rte_memseg_walk_thread_unsafe(rte_memseg_walk_t func, void *arg)
478 {
479         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
480         int i, ms_idx, ret = 0;
481
482         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
483                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
484                 const struct rte_memseg *ms;
485                 struct rte_fbarray *arr;
486
487                 if (msl->memseg_arr.count == 0)
488                         continue;
489
490                 arr = &msl->memseg_arr;
491
492                 ms_idx = rte_fbarray_find_next_used(arr, 0);
493                 while (ms_idx >= 0) {
494                         ms = rte_fbarray_get(arr, ms_idx);
495                         ret = func(msl, ms, arg);
496                         if (ret)
497                                 return ret;
498                         ms_idx = rte_fbarray_find_next_used(arr, ms_idx + 1);
499                 }
500         }
501         return 0;
502 }
503
504 int __rte_experimental
505 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
506 {
507         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
508         int ret = 0;
509
510         /* do not allow allocations/frees/init while we iterate */
511         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
512         ret = rte_memseg_walk_thread_unsafe(func, arg);
513         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
514
515         return ret;
516 }
517
518 int __rte_experimental
519 rte_memseg_list_walk_thread_unsafe(rte_memseg_list_walk_t func, void *arg)
520 {
521         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
522         int i, ret = 0;
523
524         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
525                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
526
527                 if (msl->base_va == NULL)
528                         continue;
529
530                 ret = func(msl, arg);
531                 if (ret)
532                         return ret;
533         }
534         return 0;
535 }
536
537 int __rte_experimental
538 rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)
539 {
540         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
541         int ret = 0;
542
543         /* do not allow allocations/frees/init while we iterate */
544         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
545         ret = rte_memseg_list_walk_thread_unsafe(func, arg);
546         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
547
548         return ret;
549 }
550
551 /* init memory subsystem */
552 int
553 rte_eal_memory_init(void)
554 {
555         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
556         int retval;
557         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
558
559         if (!mcfg)
560                 return -1;
561
562         /* lock mem hotplug here, to prevent races while we init */
563         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
564
565         if (rte_eal_memseg_init() < 0)
566                 goto fail;
567
568         if (eal_memalloc_init() < 0)
569                 goto fail;
570
571         retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
572                         rte_eal_hugepage_init() :
573                         rte_eal_hugepage_attach();
574         if (retval < 0)
575                 goto fail;
576
577         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
578                 goto fail;
579
580         return 0;
581 fail:
582         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
583         return -1;
584 }