mem: make segment preallocation OS-specific
[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         /* if requested address is not aligned by page size, or if requested
70          * address is NULL, add page size to requested length as we may get an
71          * address that's aligned by system page size, which can be smaller than
72          * our requested page size. additionally, we shouldn't try to align if
73          * system page size is the same as requested page size.
74          */
75         no_align = (requested_addr != NULL &&
76                 ((uintptr_t)requested_addr & (page_sz - 1))) ||
77                 page_sz == system_page_sz;
78
79         do {
80                 map_sz = no_align ? *size : *size + page_sz;
81                 if (map_sz > SIZE_MAX) {
82                         RTE_LOG(ERR, EAL, "Map size too big\n");
83                         rte_errno = E2BIG;
84                         return NULL;
85                 }
86
87                 mapped_addr = mmap(requested_addr, (size_t)map_sz, PROT_READ,
88                                 mmap_flags, -1, 0);
89                 if (mapped_addr == MAP_FAILED && allow_shrink)
90                         *size -= page_sz;
91         } while (allow_shrink && mapped_addr == MAP_FAILED && *size > 0);
92
93         /* align resulting address - if map failed, we will ignore the value
94          * anyway, so no need to add additional checks.
95          */
96         aligned_addr = no_align ? mapped_addr :
97                         RTE_PTR_ALIGN(mapped_addr, page_sz);
98
99         if (*size == 0) {
100                 RTE_LOG(ERR, EAL, "Cannot get a virtual area of any size: %s\n",
101                         strerror(errno));
102                 rte_errno = errno;
103                 return NULL;
104         } else if (mapped_addr == MAP_FAILED) {
105                 RTE_LOG(ERR, EAL, "Cannot get a virtual area: %s\n",
106                         strerror(errno));
107                 /* pass errno up the call chain */
108                 rte_errno = errno;
109                 return NULL;
110         } else if (requested_addr != NULL && !addr_is_hint &&
111                         aligned_addr != requested_addr) {
112                 RTE_LOG(ERR, EAL, "Cannot get a virtual area at requested address: %p (got %p)\n",
113                         requested_addr, aligned_addr);
114                 munmap(mapped_addr, map_sz);
115                 rte_errno = EADDRNOTAVAIL;
116                 return NULL;
117         } else if (requested_addr != NULL && addr_is_hint &&
118                         aligned_addr != requested_addr) {
119                 RTE_LOG(WARNING, EAL, "WARNING! Base virtual address hint (%p != %p) not respected!\n",
120                         requested_addr, aligned_addr);
121                 RTE_LOG(WARNING, EAL, "   This may cause issues with mapping memory into secondary processes\n");
122         } else if (next_baseaddr != NULL) {
123                 next_baseaddr = RTE_PTR_ADD(aligned_addr, *size);
124         }
125
126         RTE_LOG(DEBUG, EAL, "Virtual area found at %p (size = 0x%zx)\n",
127                 aligned_addr, *size);
128
129         if (unmap) {
130                 munmap(mapped_addr, map_sz);
131         } else if (!no_align) {
132                 void *map_end, *aligned_end;
133                 size_t before_len, after_len;
134
135                 /* when we reserve space with alignment, we add alignment to
136                  * mapping size. On 32-bit, if 1GB alignment was requested, this
137                  * would waste 1GB of address space, which is a luxury we cannot
138                  * afford. so, if alignment was performed, check if any unneeded
139                  * address space can be unmapped back.
140                  */
141
142                 map_end = RTE_PTR_ADD(mapped_addr, (size_t)map_sz);
143                 aligned_end = RTE_PTR_ADD(aligned_addr, *size);
144
145                 /* unmap space before aligned mmap address */
146                 before_len = RTE_PTR_DIFF(aligned_addr, mapped_addr);
147                 if (before_len > 0)
148                         munmap(mapped_addr, before_len);
149
150                 /* unmap space after aligned end mmap address */
151                 after_len = RTE_PTR_DIFF(map_end, aligned_end);
152                 if (after_len > 0)
153                         munmap(aligned_end, after_len);
154         }
155
156         return aligned_addr;
157 }
158
159 static struct rte_memseg *
160 virt2memseg(const void *addr, const struct rte_memseg_list *msl)
161 {
162         const struct rte_fbarray *arr;
163         void *start, *end;
164         int ms_idx;
165
166         if (msl == NULL)
167                 return NULL;
168
169         /* a memseg list was specified, check if it's the right one */
170         start = msl->base_va;
171         end = RTE_PTR_ADD(start, (size_t)msl->page_sz * msl->memseg_arr.len);
172
173         if (addr < start || addr >= end)
174                 return NULL;
175
176         /* now, calculate index */
177         arr = &msl->memseg_arr;
178         ms_idx = RTE_PTR_DIFF(addr, msl->base_va) / msl->page_sz;
179         return rte_fbarray_get(arr, ms_idx);
180 }
181
182 static struct rte_memseg_list *
183 virt2memseg_list(const void *addr)
184 {
185         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
186         struct rte_memseg_list *msl;
187         int msl_idx;
188
189         for (msl_idx = 0; msl_idx < RTE_MAX_MEMSEG_LISTS; msl_idx++) {
190                 void *start, *end;
191                 msl = &mcfg->memsegs[msl_idx];
192
193                 start = msl->base_va;
194                 end = RTE_PTR_ADD(start,
195                                 (size_t)msl->page_sz * msl->memseg_arr.len);
196                 if (addr >= start && addr < end)
197                         break;
198         }
199         /* if we didn't find our memseg list */
200         if (msl_idx == RTE_MAX_MEMSEG_LISTS)
201                 return NULL;
202         return msl;
203 }
204
205 __rte_experimental struct rte_memseg_list *
206 rte_mem_virt2memseg_list(const void *addr)
207 {
208         return virt2memseg_list(addr);
209 }
210
211 struct virtiova {
212         rte_iova_t iova;
213         void *virt;
214 };
215 static int
216 find_virt(const struct rte_memseg_list *msl __rte_unused,
217                 const struct rte_memseg *ms, void *arg)
218 {
219         struct virtiova *vi = arg;
220         if (vi->iova >= ms->iova && vi->iova < (ms->iova + ms->len)) {
221                 size_t offset = vi->iova - ms->iova;
222                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
223                 /* stop the walk */
224                 return 1;
225         }
226         return 0;
227 }
228 static int
229 find_virt_legacy(const struct rte_memseg_list *msl __rte_unused,
230                 const struct rte_memseg *ms, size_t len, void *arg)
231 {
232         struct virtiova *vi = arg;
233         if (vi->iova >= ms->iova && vi->iova < (ms->iova + len)) {
234                 size_t offset = vi->iova - ms->iova;
235                 vi->virt = RTE_PTR_ADD(ms->addr, offset);
236                 /* stop the walk */
237                 return 1;
238         }
239         return 0;
240 }
241
242 __rte_experimental void *
243 rte_mem_iova2virt(rte_iova_t iova)
244 {
245         struct virtiova vi;
246
247         memset(&vi, 0, sizeof(vi));
248
249         vi.iova = iova;
250         /* for legacy mem, we can get away with scanning VA-contiguous segments,
251          * as we know they are PA-contiguous as well
252          */
253         if (internal_config.legacy_mem)
254                 rte_memseg_contig_walk(find_virt_legacy, &vi);
255         else
256                 rte_memseg_walk(find_virt, &vi);
257
258         return vi.virt;
259 }
260
261 __rte_experimental struct rte_memseg *
262 rte_mem_virt2memseg(const void *addr, const struct rte_memseg_list *msl)
263 {
264         return virt2memseg(addr, msl != NULL ? msl :
265                         rte_mem_virt2memseg_list(addr));
266 }
267
268 static int
269 physmem_size(const struct rte_memseg_list *msl, void *arg)
270 {
271         uint64_t *total_len = arg;
272
273         *total_len += msl->memseg_arr.count * msl->page_sz;
274
275         return 0;
276 }
277
278 /* get the total size of memory */
279 uint64_t
280 rte_eal_get_physmem_size(void)
281 {
282         uint64_t total_len = 0;
283
284         rte_memseg_list_walk(physmem_size, &total_len);
285
286         return total_len;
287 }
288
289 static int
290 dump_memseg(const struct rte_memseg_list *msl, const struct rte_memseg *ms,
291                 void *arg)
292 {
293         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
294         int msl_idx, ms_idx;
295         FILE *f = arg;
296
297         msl_idx = msl - mcfg->memsegs;
298         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
299                 return -1;
300
301         ms_idx = rte_fbarray_find_idx(&msl->memseg_arr, ms);
302         if (ms_idx < 0)
303                 return -1;
304
305         fprintf(f, "Segment %i-%i: IOVA:0x%"PRIx64", len:%zu, "
306                         "virt:%p, socket_id:%"PRId32", "
307                         "hugepage_sz:%"PRIu64", nchannel:%"PRIx32", "
308                         "nrank:%"PRIx32"\n",
309                         msl_idx, ms_idx,
310                         ms->iova,
311                         ms->len,
312                         ms->addr,
313                         ms->socket_id,
314                         ms->hugepage_sz,
315                         ms->nchannel,
316                         ms->nrank);
317
318         return 0;
319 }
320
321 /*
322  * Defining here because declared in rte_memory.h, but the actual implementation
323  * is in eal_common_memalloc.c, like all other memalloc internals.
324  */
325 int __rte_experimental
326 rte_mem_event_callback_register(const char *name, rte_mem_event_callback_t clb,
327                 void *arg)
328 {
329         /* FreeBSD boots with legacy mem enabled by default */
330         if (internal_config.legacy_mem) {
331                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
332                 rte_errno = ENOTSUP;
333                 return -1;
334         }
335         return eal_memalloc_mem_event_callback_register(name, clb, arg);
336 }
337
338 int __rte_experimental
339 rte_mem_event_callback_unregister(const char *name, void *arg)
340 {
341         /* FreeBSD boots with legacy mem enabled by default */
342         if (internal_config.legacy_mem) {
343                 RTE_LOG(DEBUG, EAL, "Registering mem event callbacks not supported\n");
344                 rte_errno = ENOTSUP;
345                 return -1;
346         }
347         return eal_memalloc_mem_event_callback_unregister(name, arg);
348 }
349
350 int __rte_experimental
351 rte_mem_alloc_validator_register(const char *name,
352                 rte_mem_alloc_validator_t clb, int socket_id, size_t limit)
353 {
354         /* FreeBSD boots with legacy mem enabled by default */
355         if (internal_config.legacy_mem) {
356                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
357                 rte_errno = ENOTSUP;
358                 return -1;
359         }
360         return eal_memalloc_mem_alloc_validator_register(name, clb, socket_id,
361                         limit);
362 }
363
364 int __rte_experimental
365 rte_mem_alloc_validator_unregister(const char *name, int socket_id)
366 {
367         /* FreeBSD boots with legacy mem enabled by default */
368         if (internal_config.legacy_mem) {
369                 RTE_LOG(DEBUG, EAL, "Registering mem alloc validators not supported\n");
370                 rte_errno = ENOTSUP;
371                 return -1;
372         }
373         return eal_memalloc_mem_alloc_validator_unregister(name, socket_id);
374 }
375
376 /* Dump the physical memory layout on console */
377 void
378 rte_dump_physmem_layout(FILE *f)
379 {
380         rte_memseg_walk(dump_memseg, f);
381 }
382
383 /* return the number of memory channels */
384 unsigned rte_memory_get_nchannel(void)
385 {
386         return rte_eal_get_configuration()->mem_config->nchannel;
387 }
388
389 /* return the number of memory rank */
390 unsigned rte_memory_get_nrank(void)
391 {
392         return rte_eal_get_configuration()->mem_config->nrank;
393 }
394
395 static int
396 rte_eal_memdevice_init(void)
397 {
398         struct rte_config *config;
399
400         if (rte_eal_process_type() == RTE_PROC_SECONDARY)
401                 return 0;
402
403         config = rte_eal_get_configuration();
404         config->mem_config->nchannel = internal_config.force_nchannel;
405         config->mem_config->nrank = internal_config.force_nrank;
406
407         return 0;
408 }
409
410 /* Lock page in physical memory and prevent from swapping. */
411 int
412 rte_mem_lock_page(const void *virt)
413 {
414         unsigned long virtual = (unsigned long)virt;
415         int page_size = getpagesize();
416         unsigned long aligned = (virtual & ~(page_size - 1));
417         return mlock((void *)aligned, page_size);
418 }
419
420 int __rte_experimental
421 rte_memseg_contig_walk(rte_memseg_contig_walk_t func, void *arg)
422 {
423         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
424         int i, ms_idx, ret = 0;
425
426         /* do not allow allocations/frees/init while we iterate */
427         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
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 < 0) {
454                                 ret = -1;
455                                 goto out;
456                         } else if (ret > 0) {
457                                 ret = 1;
458                                 goto out;
459                         }
460                         ms_idx = rte_fbarray_find_next_used(arr,
461                                         ms_idx + n_segs);
462                 }
463         }
464 out:
465         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
466         return ret;
467 }
468
469 int __rte_experimental
470 rte_memseg_walk(rte_memseg_walk_t func, void *arg)
471 {
472         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
473         int i, ms_idx, ret = 0;
474
475         /* do not allow allocations/frees/init while we iterate */
476         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
477
478         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
479                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
480                 const struct rte_memseg *ms;
481                 struct rte_fbarray *arr;
482
483                 if (msl->memseg_arr.count == 0)
484                         continue;
485
486                 arr = &msl->memseg_arr;
487
488                 ms_idx = rte_fbarray_find_next_used(arr, 0);
489                 while (ms_idx >= 0) {
490                         ms = rte_fbarray_get(arr, ms_idx);
491                         ret = func(msl, ms, arg);
492                         if (ret < 0) {
493                                 ret = -1;
494                                 goto out;
495                         } else if (ret > 0) {
496                                 ret = 1;
497                                 goto out;
498                         }
499                         ms_idx = rte_fbarray_find_next_used(arr, ms_idx + 1);
500                 }
501         }
502 out:
503         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
504         return ret;
505 }
506
507 int __rte_experimental
508 rte_memseg_list_walk(rte_memseg_list_walk_t func, void *arg)
509 {
510         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
511         int i, ret = 0;
512
513         /* do not allow allocations/frees/init while we iterate */
514         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
515
516         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
517                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
518
519                 if (msl->base_va == NULL)
520                         continue;
521
522                 ret = func(msl, arg);
523                 if (ret < 0) {
524                         ret = -1;
525                         goto out;
526                 }
527                 if (ret > 0) {
528                         ret = 1;
529                         goto out;
530                 }
531         }
532 out:
533         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
534         return ret;
535 }
536
537 /* init memory subsystem */
538 int
539 rte_eal_memory_init(void)
540 {
541         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
542         int retval;
543         RTE_LOG(DEBUG, EAL, "Setting up physically contiguous memory...\n");
544
545         if (!mcfg)
546                 return -1;
547
548         /* lock mem hotplug here, to prevent races while we init */
549         rte_rwlock_read_lock(&mcfg->memory_hotplug_lock);
550
551         if (rte_eal_memseg_init() < 0)
552                 goto fail;
553
554         if (eal_memalloc_init() < 0)
555                 goto fail;
556
557         retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
558                         rte_eal_hugepage_init() :
559                         rte_eal_hugepage_attach();
560         if (retval < 0)
561                 goto fail;
562
563         if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
564                 goto fail;
565
566         return 0;
567 fail:
568         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
569         return -1;
570 }