mem: allow memseg lists to be marked as external
[dpdk.git] / lib / librte_eal / common / malloc_heap.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 #include <stdint.h>
5 #include <stddef.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <stdarg.h>
9 #include <errno.h>
10 #include <sys/queue.h>
11
12 #include <rte_memory.h>
13 #include <rte_errno.h>
14 #include <rte_eal.h>
15 #include <rte_eal_memconfig.h>
16 #include <rte_launch.h>
17 #include <rte_per_lcore.h>
18 #include <rte_lcore.h>
19 #include <rte_common.h>
20 #include <rte_string_fns.h>
21 #include <rte_spinlock.h>
22 #include <rte_memcpy.h>
23 #include <rte_atomic.h>
24 #include <rte_fbarray.h>
25
26 #include "eal_internal_cfg.h"
27 #include "eal_memalloc.h"
28 #include "malloc_elem.h"
29 #include "malloc_heap.h"
30 #include "malloc_mp.h"
31
32 static unsigned
33 check_hugepage_sz(unsigned flags, uint64_t hugepage_sz)
34 {
35         unsigned check_flag = 0;
36
37         if (!(flags & ~RTE_MEMZONE_SIZE_HINT_ONLY))
38                 return 1;
39
40         switch (hugepage_sz) {
41         case RTE_PGSIZE_256K:
42                 check_flag = RTE_MEMZONE_256KB;
43                 break;
44         case RTE_PGSIZE_2M:
45                 check_flag = RTE_MEMZONE_2MB;
46                 break;
47         case RTE_PGSIZE_16M:
48                 check_flag = RTE_MEMZONE_16MB;
49                 break;
50         case RTE_PGSIZE_256M:
51                 check_flag = RTE_MEMZONE_256MB;
52                 break;
53         case RTE_PGSIZE_512M:
54                 check_flag = RTE_MEMZONE_512MB;
55                 break;
56         case RTE_PGSIZE_1G:
57                 check_flag = RTE_MEMZONE_1GB;
58                 break;
59         case RTE_PGSIZE_4G:
60                 check_flag = RTE_MEMZONE_4GB;
61                 break;
62         case RTE_PGSIZE_16G:
63                 check_flag = RTE_MEMZONE_16GB;
64         }
65
66         return check_flag & flags;
67 }
68
69 /*
70  * Expand the heap with a memory area.
71  */
72 static struct malloc_elem *
73 malloc_heap_add_memory(struct malloc_heap *heap, struct rte_memseg_list *msl,
74                 void *start, size_t len)
75 {
76         struct malloc_elem *elem = start;
77
78         malloc_elem_init(elem, heap, msl, len);
79
80         malloc_elem_insert(elem);
81
82         elem = malloc_elem_join_adjacent_free(elem);
83
84         malloc_elem_free_list_insert(elem);
85
86         return elem;
87 }
88
89 static int
90 malloc_add_seg(const struct rte_memseg_list *msl,
91                 const struct rte_memseg *ms, size_t len, void *arg __rte_unused)
92 {
93         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
94         struct rte_memseg_list *found_msl;
95         struct malloc_heap *heap;
96         int msl_idx;
97
98         if (msl->external)
99                 return 0;
100
101         heap = &mcfg->malloc_heaps[msl->socket_id];
102
103         /* msl is const, so find it */
104         msl_idx = msl - mcfg->memsegs;
105
106         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
107                 return -1;
108
109         found_msl = &mcfg->memsegs[msl_idx];
110
111         malloc_heap_add_memory(heap, found_msl, ms->addr, len);
112
113         heap->total_size += len;
114
115         RTE_LOG(DEBUG, EAL, "Added %zuM to heap on socket %i\n", len >> 20,
116                         msl->socket_id);
117         return 0;
118 }
119
120 /*
121  * Iterates through the freelist for a heap to find a free element
122  * which can store data of the required size and with the requested alignment.
123  * If size is 0, find the biggest available elem.
124  * Returns null on failure, or pointer to element on success.
125  */
126 static struct malloc_elem *
127 find_suitable_element(struct malloc_heap *heap, size_t size,
128                 unsigned int flags, size_t align, size_t bound, bool contig)
129 {
130         size_t idx;
131         struct malloc_elem *elem, *alt_elem = NULL;
132
133         for (idx = malloc_elem_free_list_index(size);
134                         idx < RTE_HEAP_NUM_FREELISTS; idx++) {
135                 for (elem = LIST_FIRST(&heap->free_head[idx]);
136                                 !!elem; elem = LIST_NEXT(elem, free_list)) {
137                         if (malloc_elem_can_hold(elem, size, align, bound,
138                                         contig)) {
139                                 if (check_hugepage_sz(flags,
140                                                 elem->msl->page_sz))
141                                         return elem;
142                                 if (alt_elem == NULL)
143                                         alt_elem = elem;
144                         }
145                 }
146         }
147
148         if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
149                 return alt_elem;
150
151         return NULL;
152 }
153
154 /*
155  * Iterates through the freelist for a heap to find a free element with the
156  * biggest size and requested alignment. Will also set size to whatever element
157  * size that was found.
158  * Returns null on failure, or pointer to element on success.
159  */
160 static struct malloc_elem *
161 find_biggest_element(struct malloc_heap *heap, size_t *size,
162                 unsigned int flags, size_t align, bool contig)
163 {
164         struct malloc_elem *elem, *max_elem = NULL;
165         size_t idx, max_size = 0;
166
167         for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
168                 for (elem = LIST_FIRST(&heap->free_head[idx]);
169                                 !!elem; elem = LIST_NEXT(elem, free_list)) {
170                         size_t cur_size;
171                         if (!check_hugepage_sz(flags, elem->msl->page_sz))
172                                 continue;
173                         if (contig) {
174                                 cur_size =
175                                         malloc_elem_find_max_iova_contig(elem,
176                                                         align);
177                         } else {
178                                 void *data_start = RTE_PTR_ADD(elem,
179                                                 MALLOC_ELEM_HEADER_LEN);
180                                 void *data_end = RTE_PTR_ADD(elem, elem->size -
181                                                 MALLOC_ELEM_TRAILER_LEN);
182                                 void *aligned = RTE_PTR_ALIGN_CEIL(data_start,
183                                                 align);
184                                 /* check if aligned data start is beyond end */
185                                 if (aligned >= data_end)
186                                         continue;
187                                 cur_size = RTE_PTR_DIFF(data_end, aligned);
188                         }
189                         if (cur_size > max_size) {
190                                 max_size = cur_size;
191                                 max_elem = elem;
192                         }
193                 }
194         }
195
196         *size = max_size;
197         return max_elem;
198 }
199
200 /*
201  * Main function to allocate a block of memory from the heap.
202  * It locks the free list, scans it, and adds a new memseg if the
203  * scan fails. Once the new memseg is added, it re-scans and should return
204  * the new element after releasing the lock.
205  */
206 static void *
207 heap_alloc(struct malloc_heap *heap, const char *type __rte_unused, size_t size,
208                 unsigned int flags, size_t align, size_t bound, bool contig)
209 {
210         struct malloc_elem *elem;
211
212         size = RTE_CACHE_LINE_ROUNDUP(size);
213         align = RTE_CACHE_LINE_ROUNDUP(align);
214
215         elem = find_suitable_element(heap, size, flags, align, bound, contig);
216         if (elem != NULL) {
217                 elem = malloc_elem_alloc(elem, size, align, bound, contig);
218
219                 /* increase heap's count of allocated elements */
220                 heap->alloc_count++;
221         }
222
223         return elem == NULL ? NULL : (void *)(&elem[1]);
224 }
225
226 static void *
227 heap_alloc_biggest(struct malloc_heap *heap, const char *type __rte_unused,
228                 unsigned int flags, size_t align, bool contig)
229 {
230         struct malloc_elem *elem;
231         size_t size;
232
233         align = RTE_CACHE_LINE_ROUNDUP(align);
234
235         elem = find_biggest_element(heap, &size, flags, align, contig);
236         if (elem != NULL) {
237                 elem = malloc_elem_alloc(elem, size, align, 0, contig);
238
239                 /* increase heap's count of allocated elements */
240                 heap->alloc_count++;
241         }
242
243         return elem == NULL ? NULL : (void *)(&elem[1]);
244 }
245
246 /* this function is exposed in malloc_mp.h */
247 void
248 rollback_expand_heap(struct rte_memseg **ms, int n_segs,
249                 struct malloc_elem *elem, void *map_addr, size_t map_len)
250 {
251         if (elem != NULL) {
252                 malloc_elem_free_list_remove(elem);
253                 malloc_elem_hide_region(elem, map_addr, map_len);
254         }
255
256         eal_memalloc_free_seg_bulk(ms, n_segs);
257 }
258
259 /* this function is exposed in malloc_mp.h */
260 struct malloc_elem *
261 alloc_pages_on_heap(struct malloc_heap *heap, uint64_t pg_sz, size_t elt_size,
262                 int socket, unsigned int flags, size_t align, size_t bound,
263                 bool contig, struct rte_memseg **ms, int n_segs)
264 {
265         struct rte_memseg_list *msl;
266         struct malloc_elem *elem = NULL;
267         size_t alloc_sz;
268         int allocd_pages;
269         void *ret, *map_addr;
270
271         alloc_sz = (size_t)pg_sz * n_segs;
272
273         /* first, check if we're allowed to allocate this memory */
274         if (eal_memalloc_mem_alloc_validate(socket,
275                         heap->total_size + alloc_sz) < 0) {
276                 RTE_LOG(DEBUG, EAL, "User has disallowed allocation\n");
277                 return NULL;
278         }
279
280         allocd_pages = eal_memalloc_alloc_seg_bulk(ms, n_segs, pg_sz,
281                         socket, true);
282
283         /* make sure we've allocated our pages... */
284         if (allocd_pages < 0)
285                 return NULL;
286
287         map_addr = ms[0]->addr;
288         msl = rte_mem_virt2memseg_list(map_addr);
289
290         /* check if we wanted contiguous memory but didn't get it */
291         if (contig && !eal_memalloc_is_contig(msl, map_addr, alloc_sz)) {
292                 RTE_LOG(DEBUG, EAL, "%s(): couldn't allocate physically contiguous space\n",
293                                 __func__);
294                 goto fail;
295         }
296
297         /* add newly minted memsegs to malloc heap */
298         elem = malloc_heap_add_memory(heap, msl, map_addr, alloc_sz);
299
300         /* try once more, as now we have allocated new memory */
301         ret = find_suitable_element(heap, elt_size, flags, align, bound,
302                         contig);
303
304         if (ret == NULL)
305                 goto fail;
306
307         return elem;
308
309 fail:
310         rollback_expand_heap(ms, n_segs, elem, map_addr, alloc_sz);
311         return NULL;
312 }
313
314 static int
315 try_expand_heap_primary(struct malloc_heap *heap, uint64_t pg_sz,
316                 size_t elt_size, int socket, unsigned int flags, size_t align,
317                 size_t bound, bool contig)
318 {
319         struct malloc_elem *elem;
320         struct rte_memseg **ms;
321         void *map_addr;
322         size_t alloc_sz;
323         int n_segs;
324         bool callback_triggered = false;
325
326         alloc_sz = RTE_ALIGN_CEIL(align + elt_size +
327                         MALLOC_ELEM_TRAILER_LEN, pg_sz);
328         n_segs = alloc_sz / pg_sz;
329
330         /* we can't know in advance how many pages we'll need, so we malloc */
331         ms = malloc(sizeof(*ms) * n_segs);
332         if (ms == NULL)
333                 return -1;
334         memset(ms, 0, sizeof(*ms) * n_segs);
335
336         elem = alloc_pages_on_heap(heap, pg_sz, elt_size, socket, flags, align,
337                         bound, contig, ms, n_segs);
338
339         if (elem == NULL)
340                 goto free_ms;
341
342         map_addr = ms[0]->addr;
343
344         /* notify user about changes in memory map */
345         eal_memalloc_mem_event_notify(RTE_MEM_EVENT_ALLOC, map_addr, alloc_sz);
346
347         /* notify other processes that this has happened */
348         if (request_sync()) {
349                 /* we couldn't ensure all processes have mapped memory,
350                  * so free it back and notify everyone that it's been
351                  * freed back.
352                  *
353                  * technically, we could've avoided adding memory addresses to
354                  * the map, but that would've led to inconsistent behavior
355                  * between primary and secondary processes, as those get
356                  * callbacks during sync. therefore, force primary process to
357                  * do alloc-and-rollback syncs as well.
358                  */
359                 callback_triggered = true;
360                 goto free_elem;
361         }
362         heap->total_size += alloc_sz;
363
364         RTE_LOG(DEBUG, EAL, "Heap on socket %d was expanded by %zdMB\n",
365                 socket, alloc_sz >> 20ULL);
366
367         free(ms);
368
369         return 0;
370
371 free_elem:
372         if (callback_triggered)
373                 eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE,
374                                 map_addr, alloc_sz);
375
376         rollback_expand_heap(ms, n_segs, elem, map_addr, alloc_sz);
377
378         request_sync();
379 free_ms:
380         free(ms);
381
382         return -1;
383 }
384
385 static int
386 try_expand_heap_secondary(struct malloc_heap *heap, uint64_t pg_sz,
387                 size_t elt_size, int socket, unsigned int flags, size_t align,
388                 size_t bound, bool contig)
389 {
390         struct malloc_mp_req req;
391         int req_result;
392
393         memset(&req, 0, sizeof(req));
394
395         req.t = REQ_TYPE_ALLOC;
396         req.alloc_req.align = align;
397         req.alloc_req.bound = bound;
398         req.alloc_req.contig = contig;
399         req.alloc_req.flags = flags;
400         req.alloc_req.elt_size = elt_size;
401         req.alloc_req.page_sz = pg_sz;
402         req.alloc_req.socket = socket;
403         req.alloc_req.heap = heap; /* it's in shared memory */
404
405         req_result = request_to_primary(&req);
406
407         if (req_result != 0)
408                 return -1;
409
410         if (req.result != REQ_RESULT_SUCCESS)
411                 return -1;
412
413         return 0;
414 }
415
416 static int
417 try_expand_heap(struct malloc_heap *heap, uint64_t pg_sz, size_t elt_size,
418                 int socket, unsigned int flags, size_t align, size_t bound,
419                 bool contig)
420 {
421         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
422         int ret;
423
424         rte_rwlock_write_lock(&mcfg->memory_hotplug_lock);
425
426         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
427                 ret = try_expand_heap_primary(heap, pg_sz, elt_size, socket,
428                                 flags, align, bound, contig);
429         } else {
430                 ret = try_expand_heap_secondary(heap, pg_sz, elt_size, socket,
431                                 flags, align, bound, contig);
432         }
433
434         rte_rwlock_write_unlock(&mcfg->memory_hotplug_lock);
435         return ret;
436 }
437
438 static int
439 compare_pagesz(const void *a, const void *b)
440 {
441         const struct rte_memseg_list * const*mpa = a;
442         const struct rte_memseg_list * const*mpb = b;
443         const struct rte_memseg_list *msla = *mpa;
444         const struct rte_memseg_list *mslb = *mpb;
445         uint64_t pg_sz_a = msla->page_sz;
446         uint64_t pg_sz_b = mslb->page_sz;
447
448         if (pg_sz_a < pg_sz_b)
449                 return -1;
450         if (pg_sz_a > pg_sz_b)
451                 return 1;
452         return 0;
453 }
454
455 static int
456 alloc_more_mem_on_socket(struct malloc_heap *heap, size_t size, int socket,
457                 unsigned int flags, size_t align, size_t bound, bool contig)
458 {
459         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
460         struct rte_memseg_list *requested_msls[RTE_MAX_MEMSEG_LISTS];
461         struct rte_memseg_list *other_msls[RTE_MAX_MEMSEG_LISTS];
462         uint64_t requested_pg_sz[RTE_MAX_MEMSEG_LISTS];
463         uint64_t other_pg_sz[RTE_MAX_MEMSEG_LISTS];
464         uint64_t prev_pg_sz;
465         int i, n_other_msls, n_other_pg_sz, n_requested_msls, n_requested_pg_sz;
466         bool size_hint = (flags & RTE_MEMZONE_SIZE_HINT_ONLY) > 0;
467         unsigned int size_flags = flags & ~RTE_MEMZONE_SIZE_HINT_ONLY;
468         void *ret;
469
470         memset(requested_msls, 0, sizeof(requested_msls));
471         memset(other_msls, 0, sizeof(other_msls));
472         memset(requested_pg_sz, 0, sizeof(requested_pg_sz));
473         memset(other_pg_sz, 0, sizeof(other_pg_sz));
474
475         /*
476          * go through memseg list and take note of all the page sizes available,
477          * and if any of them were specifically requested by the user.
478          */
479         n_requested_msls = 0;
480         n_other_msls = 0;
481         for (i = 0; i < RTE_MAX_MEMSEG_LISTS; i++) {
482                 struct rte_memseg_list *msl = &mcfg->memsegs[i];
483
484                 if (msl->socket_id != socket)
485                         continue;
486
487                 if (msl->base_va == NULL)
488                         continue;
489
490                 /* if pages of specific size were requested */
491                 if (size_flags != 0 && check_hugepage_sz(size_flags,
492                                 msl->page_sz))
493                         requested_msls[n_requested_msls++] = msl;
494                 else if (size_flags == 0 || size_hint)
495                         other_msls[n_other_msls++] = msl;
496         }
497
498         /* sort the lists, smallest first */
499         qsort(requested_msls, n_requested_msls, sizeof(requested_msls[0]),
500                         compare_pagesz);
501         qsort(other_msls, n_other_msls, sizeof(other_msls[0]),
502                         compare_pagesz);
503
504         /* now, extract page sizes we are supposed to try */
505         prev_pg_sz = 0;
506         n_requested_pg_sz = 0;
507         for (i = 0; i < n_requested_msls; i++) {
508                 uint64_t pg_sz = requested_msls[i]->page_sz;
509
510                 if (prev_pg_sz != pg_sz) {
511                         requested_pg_sz[n_requested_pg_sz++] = pg_sz;
512                         prev_pg_sz = pg_sz;
513                 }
514         }
515         prev_pg_sz = 0;
516         n_other_pg_sz = 0;
517         for (i = 0; i < n_other_msls; i++) {
518                 uint64_t pg_sz = other_msls[i]->page_sz;
519
520                 if (prev_pg_sz != pg_sz) {
521                         other_pg_sz[n_other_pg_sz++] = pg_sz;
522                         prev_pg_sz = pg_sz;
523                 }
524         }
525
526         /* finally, try allocating memory of specified page sizes, starting from
527          * the smallest sizes
528          */
529         for (i = 0; i < n_requested_pg_sz; i++) {
530                 uint64_t pg_sz = requested_pg_sz[i];
531
532                 /*
533                  * do not pass the size hint here, as user expects other page
534                  * sizes first, before resorting to best effort allocation.
535                  */
536                 if (!try_expand_heap(heap, pg_sz, size, socket, size_flags,
537                                 align, bound, contig))
538                         return 0;
539         }
540         if (n_other_pg_sz == 0)
541                 return -1;
542
543         /* now, check if we can reserve anything with size hint */
544         ret = find_suitable_element(heap, size, flags, align, bound, contig);
545         if (ret != NULL)
546                 return 0;
547
548         /*
549          * we still couldn't reserve memory, so try expanding heap with other
550          * page sizes, if there are any
551          */
552         for (i = 0; i < n_other_pg_sz; i++) {
553                 uint64_t pg_sz = other_pg_sz[i];
554
555                 if (!try_expand_heap(heap, pg_sz, size, socket, flags,
556                                 align, bound, contig))
557                         return 0;
558         }
559         return -1;
560 }
561
562 /* this will try lower page sizes first */
563 static void *
564 heap_alloc_on_socket(const char *type, size_t size, int socket,
565                 unsigned int flags, size_t align, size_t bound, bool contig)
566 {
567         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
568         struct malloc_heap *heap = &mcfg->malloc_heaps[socket];
569         unsigned int size_flags = flags & ~RTE_MEMZONE_SIZE_HINT_ONLY;
570         void *ret;
571
572         rte_spinlock_lock(&(heap->lock));
573
574         align = align == 0 ? 1 : align;
575
576         /* for legacy mode, try once and with all flags */
577         if (internal_config.legacy_mem) {
578                 ret = heap_alloc(heap, type, size, flags, align, bound, contig);
579                 goto alloc_unlock;
580         }
581
582         /*
583          * we do not pass the size hint here, because even if allocation fails,
584          * we may still be able to allocate memory from appropriate page sizes,
585          * we just need to request more memory first.
586          */
587         ret = heap_alloc(heap, type, size, size_flags, align, bound, contig);
588         if (ret != NULL)
589                 goto alloc_unlock;
590
591         if (!alloc_more_mem_on_socket(heap, size, socket, flags, align, bound,
592                         contig)) {
593                 ret = heap_alloc(heap, type, size, flags, align, bound, contig);
594
595                 /* this should have succeeded */
596                 if (ret == NULL)
597                         RTE_LOG(ERR, EAL, "Error allocating from heap\n");
598         }
599 alloc_unlock:
600         rte_spinlock_unlock(&(heap->lock));
601         return ret;
602 }
603
604 void *
605 malloc_heap_alloc(const char *type, size_t size, int socket_arg,
606                 unsigned int flags, size_t align, size_t bound, bool contig)
607 {
608         int socket, i, cur_socket;
609         void *ret;
610
611         /* return NULL if size is 0 or alignment is not power-of-2 */
612         if (size == 0 || (align && !rte_is_power_of_2(align)))
613                 return NULL;
614
615         if (!rte_eal_has_hugepages())
616                 socket_arg = SOCKET_ID_ANY;
617
618         if (socket_arg == SOCKET_ID_ANY)
619                 socket = malloc_get_numa_socket();
620         else
621                 socket = socket_arg;
622
623         /* Check socket parameter */
624         if (socket >= RTE_MAX_NUMA_NODES)
625                 return NULL;
626
627         ret = heap_alloc_on_socket(type, size, socket, flags, align, bound,
628                         contig);
629         if (ret != NULL || socket_arg != SOCKET_ID_ANY)
630                 return ret;
631
632         /* try other heaps */
633         for (i = 0; i < (int) rte_socket_count(); i++) {
634                 cur_socket = rte_socket_id_by_idx(i);
635                 if (cur_socket == socket)
636                         continue;
637                 ret = heap_alloc_on_socket(type, size, cur_socket, flags,
638                                 align, bound, contig);
639                 if (ret != NULL)
640                         return ret;
641         }
642         return NULL;
643 }
644
645 static void *
646 heap_alloc_biggest_on_socket(const char *type, int socket, unsigned int flags,
647                 size_t align, bool contig)
648 {
649         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
650         struct malloc_heap *heap = &mcfg->malloc_heaps[socket];
651         void *ret;
652
653         rte_spinlock_lock(&(heap->lock));
654
655         align = align == 0 ? 1 : align;
656
657         ret = heap_alloc_biggest(heap, type, flags, align, contig);
658
659         rte_spinlock_unlock(&(heap->lock));
660
661         return ret;
662 }
663
664 void *
665 malloc_heap_alloc_biggest(const char *type, int socket_arg, unsigned int flags,
666                 size_t align, bool contig)
667 {
668         int socket, i, cur_socket;
669         void *ret;
670
671         /* return NULL if align is not power-of-2 */
672         if ((align && !rte_is_power_of_2(align)))
673                 return NULL;
674
675         if (!rte_eal_has_hugepages())
676                 socket_arg = SOCKET_ID_ANY;
677
678         if (socket_arg == SOCKET_ID_ANY)
679                 socket = malloc_get_numa_socket();
680         else
681                 socket = socket_arg;
682
683         /* Check socket parameter */
684         if (socket >= RTE_MAX_NUMA_NODES)
685                 return NULL;
686
687         ret = heap_alloc_biggest_on_socket(type, socket, flags, align,
688                         contig);
689         if (ret != NULL || socket_arg != SOCKET_ID_ANY)
690                 return ret;
691
692         /* try other heaps */
693         for (i = 0; i < (int) rte_socket_count(); i++) {
694                 cur_socket = rte_socket_id_by_idx(i);
695                 if (cur_socket == socket)
696                         continue;
697                 ret = heap_alloc_biggest_on_socket(type, cur_socket, flags,
698                                 align, contig);
699                 if (ret != NULL)
700                         return ret;
701         }
702         return NULL;
703 }
704
705 /* this function is exposed in malloc_mp.h */
706 int
707 malloc_heap_free_pages(void *aligned_start, size_t aligned_len)
708 {
709         int n_segs, seg_idx, max_seg_idx;
710         struct rte_memseg_list *msl;
711         size_t page_sz;
712
713         msl = rte_mem_virt2memseg_list(aligned_start);
714         if (msl == NULL)
715                 return -1;
716
717         page_sz = (size_t)msl->page_sz;
718         n_segs = aligned_len / page_sz;
719         seg_idx = RTE_PTR_DIFF(aligned_start, msl->base_va) / page_sz;
720         max_seg_idx = seg_idx + n_segs;
721
722         for (; seg_idx < max_seg_idx; seg_idx++) {
723                 struct rte_memseg *ms;
724
725                 ms = rte_fbarray_get(&msl->memseg_arr, seg_idx);
726                 eal_memalloc_free_seg(ms);
727         }
728         return 0;
729 }
730
731 int
732 malloc_heap_free(struct malloc_elem *elem)
733 {
734         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
735         struct malloc_heap *heap;
736         void *start, *aligned_start, *end, *aligned_end;
737         size_t len, aligned_len, page_sz;
738         struct rte_memseg_list *msl;
739         unsigned int i, n_segs, before_space, after_space;
740         int ret;
741
742         if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
743                 return -1;
744
745         /* elem may be merged with previous element, so keep heap address */
746         heap = elem->heap;
747         msl = elem->msl;
748         page_sz = (size_t)msl->page_sz;
749
750         rte_spinlock_lock(&(heap->lock));
751
752         /* mark element as free */
753         elem->state = ELEM_FREE;
754
755         elem = malloc_elem_free(elem);
756
757         /* anything after this is a bonus */
758         ret = 0;
759
760         /* ...of which we can't avail if we are in legacy mode, or if this is an
761          * externally allocated segment.
762          */
763         if (internal_config.legacy_mem || msl->external)
764                 goto free_unlock;
765
766         /* check if we can free any memory back to the system */
767         if (elem->size < page_sz)
768                 goto free_unlock;
769
770         /* probably, but let's make sure, as we may not be using up full page */
771         start = elem;
772         len = elem->size;
773         aligned_start = RTE_PTR_ALIGN_CEIL(start, page_sz);
774         end = RTE_PTR_ADD(elem, len);
775         aligned_end = RTE_PTR_ALIGN_FLOOR(end, page_sz);
776
777         aligned_len = RTE_PTR_DIFF(aligned_end, aligned_start);
778
779         /* can't free anything */
780         if (aligned_len < page_sz)
781                 goto free_unlock;
782
783         /* we can free something. however, some of these pages may be marked as
784          * unfreeable, so also check that as well
785          */
786         n_segs = aligned_len / page_sz;
787         for (i = 0; i < n_segs; i++) {
788                 const struct rte_memseg *tmp =
789                                 rte_mem_virt2memseg(aligned_start, msl);
790
791                 if (tmp->flags & RTE_MEMSEG_FLAG_DO_NOT_FREE) {
792                         /* this is an unfreeable segment, so move start */
793                         aligned_start = RTE_PTR_ADD(tmp->addr, tmp->len);
794                 }
795         }
796
797         /* recalculate length and number of segments */
798         aligned_len = RTE_PTR_DIFF(aligned_end, aligned_start);
799         n_segs = aligned_len / page_sz;
800
801         /* check if we can still free some pages */
802         if (n_segs == 0)
803                 goto free_unlock;
804
805         /* We're not done yet. We also have to check if by freeing space we will
806          * be leaving free elements that are too small to store new elements.
807          * Check if we have enough space in the beginning and at the end, or if
808          * start/end are exactly page aligned.
809          */
810         before_space = RTE_PTR_DIFF(aligned_start, elem);
811         after_space = RTE_PTR_DIFF(end, aligned_end);
812         if (before_space != 0 &&
813                         before_space < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
814                 /* There is not enough space before start, but we may be able to
815                  * move the start forward by one page.
816                  */
817                 if (n_segs == 1)
818                         goto free_unlock;
819
820                 /* move start */
821                 aligned_start = RTE_PTR_ADD(aligned_start, page_sz);
822                 aligned_len -= page_sz;
823                 n_segs--;
824         }
825         if (after_space != 0 && after_space <
826                         MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
827                 /* There is not enough space after end, but we may be able to
828                  * move the end backwards by one page.
829                  */
830                 if (n_segs == 1)
831                         goto free_unlock;
832
833                 /* move end */
834                 aligned_end = RTE_PTR_SUB(aligned_end, page_sz);
835                 aligned_len -= page_sz;
836                 n_segs--;
837         }
838
839         /* now we can finally free us some pages */
840
841         rte_rwlock_write_lock(&mcfg->memory_hotplug_lock);
842
843         /*
844          * we allow secondary processes to clear the heap of this allocated
845          * memory because it is safe to do so, as even if notifications about
846          * unmapped pages don't make it to other processes, heap is shared
847          * across all processes, and will become empty of this memory anyway,
848          * and nothing can allocate it back unless primary process will be able
849          * to deliver allocation message to every single running process.
850          */
851
852         malloc_elem_free_list_remove(elem);
853
854         malloc_elem_hide_region(elem, (void *) aligned_start, aligned_len);
855
856         heap->total_size -= aligned_len;
857
858         if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
859                 /* notify user about changes in memory map */
860                 eal_memalloc_mem_event_notify(RTE_MEM_EVENT_FREE,
861                                 aligned_start, aligned_len);
862
863                 /* don't care if any of this fails */
864                 malloc_heap_free_pages(aligned_start, aligned_len);
865
866                 request_sync();
867         } else {
868                 struct malloc_mp_req req;
869
870                 memset(&req, 0, sizeof(req));
871
872                 req.t = REQ_TYPE_FREE;
873                 req.free_req.addr = aligned_start;
874                 req.free_req.len = aligned_len;
875
876                 /*
877                  * we request primary to deallocate pages, but we don't do it
878                  * in this thread. instead, we notify primary that we would like
879                  * to deallocate pages, and this process will receive another
880                  * request (in parallel) that will do it for us on another
881                  * thread.
882                  *
883                  * we also don't really care if this succeeds - the data is
884                  * already removed from the heap, so it is, for all intents and
885                  * purposes, hidden from the rest of DPDK even if some other
886                  * process (including this one) may have these pages mapped.
887                  *
888                  * notifications about deallocated memory happen during sync.
889                  */
890                 request_to_primary(&req);
891         }
892
893         RTE_LOG(DEBUG, EAL, "Heap on socket %d was shrunk by %zdMB\n",
894                 msl->socket_id, aligned_len >> 20ULL);
895
896         rte_rwlock_write_unlock(&mcfg->memory_hotplug_lock);
897 free_unlock:
898         rte_spinlock_unlock(&(heap->lock));
899         return ret;
900 }
901
902 int
903 malloc_heap_resize(struct malloc_elem *elem, size_t size)
904 {
905         int ret;
906
907         if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
908                 return -1;
909
910         rte_spinlock_lock(&(elem->heap->lock));
911
912         ret = malloc_elem_resize(elem, size);
913
914         rte_spinlock_unlock(&(elem->heap->lock));
915
916         return ret;
917 }
918
919 /*
920  * Function to retrieve data for heap on given socket
921  */
922 int
923 malloc_heap_get_stats(struct malloc_heap *heap,
924                 struct rte_malloc_socket_stats *socket_stats)
925 {
926         size_t idx;
927         struct malloc_elem *elem;
928
929         rte_spinlock_lock(&heap->lock);
930
931         /* Initialise variables for heap */
932         socket_stats->free_count = 0;
933         socket_stats->heap_freesz_bytes = 0;
934         socket_stats->greatest_free_size = 0;
935
936         /* Iterate through free list */
937         for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
938                 for (elem = LIST_FIRST(&heap->free_head[idx]);
939                         !!elem; elem = LIST_NEXT(elem, free_list))
940                 {
941                         socket_stats->free_count++;
942                         socket_stats->heap_freesz_bytes += elem->size;
943                         if (elem->size > socket_stats->greatest_free_size)
944                                 socket_stats->greatest_free_size = elem->size;
945                 }
946         }
947         /* Get stats on overall heap and allocated memory on this heap */
948         socket_stats->heap_totalsz_bytes = heap->total_size;
949         socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
950                         socket_stats->heap_freesz_bytes);
951         socket_stats->alloc_count = heap->alloc_count;
952
953         rte_spinlock_unlock(&heap->lock);
954         return 0;
955 }
956
957 /*
958  * Function to retrieve data for heap on given socket
959  */
960 void
961 malloc_heap_dump(struct malloc_heap *heap, FILE *f)
962 {
963         struct malloc_elem *elem;
964
965         rte_spinlock_lock(&heap->lock);
966
967         fprintf(f, "Heap size: 0x%zx\n", heap->total_size);
968         fprintf(f, "Heap alloc count: %u\n", heap->alloc_count);
969
970         elem = heap->first;
971         while (elem) {
972                 malloc_elem_dump(elem, f);
973                 elem = elem->next;
974         }
975
976         rte_spinlock_unlock(&heap->lock);
977 }
978
979 int
980 rte_eal_malloc_heap_init(void)
981 {
982         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
983
984         if (register_mp_requests()) {
985                 RTE_LOG(ERR, EAL, "Couldn't register malloc multiprocess actions\n");
986                 rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
987                 return -1;
988         }
989
990         /* unlock mem hotplug here. it's safe for primary as no requests can
991          * even come before primary itself is fully initialized, and secondaries
992          * do not need to initialize the heap.
993          */
994         rte_rwlock_read_unlock(&mcfg->memory_hotplug_lock);
995
996         /* secondary process does not need to initialize anything */
997         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
998                 return 0;
999
1000         /* add all IOVA-contiguous areas to the heap */
1001         return rte_memseg_contig_walk(malloc_add_seg, NULL);
1002 }