memzone: replace memzone array with fbarray
[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_eal.h>
14 #include <rte_eal_memconfig.h>
15 #include <rte_launch.h>
16 #include <rte_per_lcore.h>
17 #include <rte_lcore.h>
18 #include <rte_common.h>
19 #include <rte_string_fns.h>
20 #include <rte_spinlock.h>
21 #include <rte_memcpy.h>
22 #include <rte_atomic.h>
23
24 #include "eal_internal_cfg.h"
25 #include "malloc_elem.h"
26 #include "malloc_heap.h"
27
28 static unsigned
29 check_hugepage_sz(unsigned flags, uint64_t hugepage_sz)
30 {
31         unsigned check_flag = 0;
32
33         if (!(flags & ~RTE_MEMZONE_SIZE_HINT_ONLY))
34                 return 1;
35
36         switch (hugepage_sz) {
37         case RTE_PGSIZE_256K:
38                 check_flag = RTE_MEMZONE_256KB;
39                 break;
40         case RTE_PGSIZE_2M:
41                 check_flag = RTE_MEMZONE_2MB;
42                 break;
43         case RTE_PGSIZE_16M:
44                 check_flag = RTE_MEMZONE_16MB;
45                 break;
46         case RTE_PGSIZE_256M:
47                 check_flag = RTE_MEMZONE_256MB;
48                 break;
49         case RTE_PGSIZE_512M:
50                 check_flag = RTE_MEMZONE_512MB;
51                 break;
52         case RTE_PGSIZE_1G:
53                 check_flag = RTE_MEMZONE_1GB;
54                 break;
55         case RTE_PGSIZE_4G:
56                 check_flag = RTE_MEMZONE_4GB;
57                 break;
58         case RTE_PGSIZE_16G:
59                 check_flag = RTE_MEMZONE_16GB;
60         }
61
62         return check_flag & flags;
63 }
64
65 /*
66  * Expand the heap with a memory area.
67  */
68 static struct malloc_elem *
69 malloc_heap_add_memory(struct malloc_heap *heap, struct rte_memseg_list *msl,
70                 void *start, size_t len)
71 {
72         struct malloc_elem *elem = start;
73
74         malloc_elem_init(elem, heap, msl, len);
75
76         malloc_elem_insert(elem);
77
78         elem = malloc_elem_join_adjacent_free(elem);
79
80         malloc_elem_free_list_insert(elem);
81
82         heap->total_size += len;
83
84         return elem;
85 }
86
87 static int
88 malloc_add_seg(const struct rte_memseg_list *msl,
89                 const struct rte_memseg *ms, size_t len, void *arg __rte_unused)
90 {
91         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
92         struct rte_memseg_list *found_msl;
93         struct malloc_heap *heap;
94         int msl_idx;
95
96         heap = &mcfg->malloc_heaps[msl->socket_id];
97
98         /* msl is const, so find it */
99         msl_idx = msl - mcfg->memsegs;
100         found_msl = &mcfg->memsegs[msl_idx];
101
102         if (msl_idx < 0 || msl_idx >= RTE_MAX_MEMSEG_LISTS)
103                 return -1;
104
105         malloc_heap_add_memory(heap, found_msl, ms->addr, len);
106
107         RTE_LOG(DEBUG, EAL, "Added %zuM to heap on socket %i\n", len >> 20,
108                         msl->socket_id);
109         return 0;
110 }
111
112 /*
113  * Iterates through the freelist for a heap to find a free element
114  * which can store data of the required size and with the requested alignment.
115  * If size is 0, find the biggest available elem.
116  * Returns null on failure, or pointer to element on success.
117  */
118 static struct malloc_elem *
119 find_suitable_element(struct malloc_heap *heap, size_t size,
120                 unsigned int flags, size_t align, size_t bound, bool contig)
121 {
122         size_t idx;
123         struct malloc_elem *elem, *alt_elem = NULL;
124
125         for (idx = malloc_elem_free_list_index(size);
126                         idx < RTE_HEAP_NUM_FREELISTS; idx++) {
127                 for (elem = LIST_FIRST(&heap->free_head[idx]);
128                                 !!elem; elem = LIST_NEXT(elem, free_list)) {
129                         if (malloc_elem_can_hold(elem, size, align, bound,
130                                         contig)) {
131                                 if (check_hugepage_sz(flags,
132                                                 elem->msl->page_sz))
133                                         return elem;
134                                 if (alt_elem == NULL)
135                                         alt_elem = elem;
136                         }
137                 }
138         }
139
140         if ((alt_elem != NULL) && (flags & RTE_MEMZONE_SIZE_HINT_ONLY))
141                 return alt_elem;
142
143         return NULL;
144 }
145
146 /*
147  * Main function to allocate a block of memory from the heap.
148  * It locks the free list, scans it, and adds a new memseg if the
149  * scan fails. Once the new memseg is added, it re-scans and should return
150  * the new element after releasing the lock.
151  */
152 void *
153 malloc_heap_alloc(struct malloc_heap *heap,
154                 const char *type __attribute__((unused)), size_t size, unsigned flags,
155                 size_t align, size_t bound, bool contig)
156 {
157         struct malloc_elem *elem;
158
159         size = RTE_CACHE_LINE_ROUNDUP(size);
160         align = RTE_CACHE_LINE_ROUNDUP(align);
161
162         rte_spinlock_lock(&heap->lock);
163
164         elem = find_suitable_element(heap, size, flags, align, bound, contig);
165         if (elem != NULL) {
166                 elem = malloc_elem_alloc(elem, size, align, bound, contig);
167                 /* increase heap's count of allocated elements */
168                 heap->alloc_count++;
169         }
170         rte_spinlock_unlock(&heap->lock);
171
172         return elem == NULL ? NULL : (void *)(&elem[1]);
173 }
174
175 int
176 malloc_heap_free(struct malloc_elem *elem)
177 {
178         struct malloc_heap *heap;
179         struct malloc_elem *ret;
180
181         if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
182                 return -1;
183
184         /* elem may be merged with previous element, so keep heap address */
185         heap = elem->heap;
186
187         rte_spinlock_lock(&(heap->lock));
188
189         ret = malloc_elem_free(elem);
190
191         rte_spinlock_unlock(&(heap->lock));
192
193         return ret != NULL ? 0 : -1;
194 }
195
196 int
197 malloc_heap_resize(struct malloc_elem *elem, size_t size)
198 {
199         int ret;
200
201         if (!malloc_elem_cookies_ok(elem) || elem->state != ELEM_BUSY)
202                 return -1;
203
204         rte_spinlock_lock(&(elem->heap->lock));
205
206         ret = malloc_elem_resize(elem, size);
207
208         rte_spinlock_unlock(&(elem->heap->lock));
209
210         return ret;
211 }
212
213 /*
214  * Function to retrieve data for heap on given socket
215  */
216 int
217 malloc_heap_get_stats(struct malloc_heap *heap,
218                 struct rte_malloc_socket_stats *socket_stats)
219 {
220         size_t idx;
221         struct malloc_elem *elem;
222
223         rte_spinlock_lock(&heap->lock);
224
225         /* Initialise variables for heap */
226         socket_stats->free_count = 0;
227         socket_stats->heap_freesz_bytes = 0;
228         socket_stats->greatest_free_size = 0;
229
230         /* Iterate through free list */
231         for (idx = 0; idx < RTE_HEAP_NUM_FREELISTS; idx++) {
232                 for (elem = LIST_FIRST(&heap->free_head[idx]);
233                         !!elem; elem = LIST_NEXT(elem, free_list))
234                 {
235                         socket_stats->free_count++;
236                         socket_stats->heap_freesz_bytes += elem->size;
237                         if (elem->size > socket_stats->greatest_free_size)
238                                 socket_stats->greatest_free_size = elem->size;
239                 }
240         }
241         /* Get stats on overall heap and allocated memory on this heap */
242         socket_stats->heap_totalsz_bytes = heap->total_size;
243         socket_stats->heap_allocsz_bytes = (socket_stats->heap_totalsz_bytes -
244                         socket_stats->heap_freesz_bytes);
245         socket_stats->alloc_count = heap->alloc_count;
246
247         rte_spinlock_unlock(&heap->lock);
248         return 0;
249 }
250
251 /*
252  * Function to retrieve data for heap on given socket
253  */
254 void
255 malloc_heap_dump(struct malloc_heap *heap, FILE *f)
256 {
257         struct malloc_elem *elem;
258
259         rte_spinlock_lock(&heap->lock);
260
261         fprintf(f, "Heap size: 0x%zx\n", heap->total_size);
262         fprintf(f, "Heap alloc count: %u\n", heap->alloc_count);
263
264         elem = heap->first;
265         while (elem) {
266                 malloc_elem_dump(elem, f);
267                 elem = elem->next;
268         }
269
270         rte_spinlock_unlock(&heap->lock);
271 }
272
273 int
274 rte_eal_malloc_heap_init(void)
275 {
276         struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
277
278         if (mcfg == NULL)
279                 return -1;
280
281         /* secondary process does not need to initialize anything */
282         if (rte_eal_process_type() != RTE_PROC_PRIMARY)
283                 return 0;
284
285         /* add all IOVA-contiguous areas to the heap */
286         return rte_memseg_contig_walk(malloc_add_seg, NULL);
287 }