Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : : #include <inttypes.h>
5 : : #include <stdint.h>
6 : : #include <stddef.h>
7 : : #include <stdio.h>
8 : : #include <string.h>
9 : : #include <sys/queue.h>
10 : :
11 : : #include <rte_memory.h>
12 : : #include <rte_eal.h>
13 : : #include <rte_common.h>
14 : :
15 : : #include "eal_private.h"
16 : : #include "eal_internal_cfg.h"
17 : : #include "eal_memalloc.h"
18 : : #include "malloc_elem.h"
19 : : #include "malloc_heap.h"
20 : :
21 : : /*
22 : : * If debugging is enabled, freed memory is set to poison value
23 : : * to catch buggy programs. Otherwise, freed memory is set to zero
24 : : * to avoid having to zero in zmalloc
25 : : */
26 : : #ifdef RTE_MALLOC_DEBUG
27 : : #define MALLOC_POISON 0x6b
28 : : #else
29 : : #define MALLOC_POISON 0
30 : : #endif
31 : :
32 : : size_t
33 : 0 : malloc_elem_find_max_iova_contig(struct malloc_elem *elem, size_t align)
34 : : {
35 : : void *cur_page, *contig_seg_start, *page_end, *cur_seg_end;
36 : : void *data_start, *data_end;
37 : : rte_iova_t expected_iova;
38 : : struct rte_memseg *ms;
39 : : size_t page_sz, cur, max;
40 : : const struct internal_config *internal_conf =
41 : 0 : eal_get_internal_configuration();
42 : :
43 : 0 : page_sz = (size_t)elem->msl->page_sz;
44 : 0 : data_start = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN);
45 : 0 : data_end = RTE_PTR_ADD(elem, elem->size - MALLOC_ELEM_TRAILER_LEN);
46 : : /* segment must start after header and with specified alignment */
47 : 0 : contig_seg_start = RTE_PTR_ALIGN_CEIL(data_start, align);
48 : :
49 : : /* return if aligned address is already out of malloc element */
50 [ # # ]: 0 : if (contig_seg_start > data_end)
51 : : return 0;
52 : :
53 : : /* if we're in IOVA as VA mode, or if we're in legacy mode with
54 : : * hugepages, all elements are IOVA-contiguous. however, we can only
55 : : * make these assumptions about internal memory - externally allocated
56 : : * segments have to be checked.
57 : : */
58 [ # # # # ]: 0 : if (!elem->msl->external &&
59 : 0 : (rte_eal_iova_mode() == RTE_IOVA_VA ||
60 [ # # # # ]: 0 : (internal_conf->legacy_mem &&
61 : 0 : rte_eal_has_hugepages())))
62 : 0 : return RTE_PTR_DIFF(data_end, contig_seg_start);
63 : :
64 : 0 : cur_page = RTE_PTR_ALIGN_FLOOR(contig_seg_start, page_sz);
65 : 0 : ms = rte_mem_virt2memseg(cur_page, elem->msl);
66 : :
67 : : /* do first iteration outside the loop */
68 : 0 : page_end = RTE_PTR_ADD(cur_page, page_sz);
69 : 0 : cur_seg_end = RTE_MIN(page_end, data_end);
70 : 0 : cur = RTE_PTR_DIFF(cur_seg_end, contig_seg_start) -
71 : : MALLOC_ELEM_TRAILER_LEN;
72 : : max = cur;
73 : 0 : expected_iova = ms->iova + page_sz;
74 : : /* memsegs are contiguous in memory */
75 : 0 : ms++;
76 : :
77 : : cur_page = RTE_PTR_ADD(cur_page, page_sz);
78 : :
79 [ # # ]: 0 : while (cur_page < data_end) {
80 : 0 : page_end = RTE_PTR_ADD(cur_page, page_sz);
81 : 0 : cur_seg_end = RTE_MIN(page_end, data_end);
82 : :
83 : : /* reset start of contiguous segment if unexpected iova */
84 [ # # ]: 0 : if (ms->iova != expected_iova) {
85 : : /* next contiguous segment must start at specified
86 : : * alignment.
87 : : */
88 : 0 : contig_seg_start = RTE_PTR_ALIGN(cur_page, align);
89 : : /* new segment start may be on a different page, so find
90 : : * the page and skip to next iteration to make sure
91 : : * we're not blowing past data end.
92 : : */
93 : 0 : ms = rte_mem_virt2memseg(contig_seg_start, elem->msl);
94 : 0 : cur_page = ms->addr;
95 : : /* don't trigger another recalculation */
96 : 0 : expected_iova = ms->iova;
97 : 0 : continue;
98 : : }
99 : : /* cur_seg_end ends on a page boundary or on data end. if we're
100 : : * looking at data end, then malloc trailer is already included
101 : : * in the calculations. if we're looking at page end, then we
102 : : * know there's more data past this page and thus there's space
103 : : * for malloc element trailer, so don't count it here.
104 : : */
105 : 0 : cur = RTE_PTR_DIFF(cur_seg_end, contig_seg_start);
106 : : /* update max if cur value is bigger */
107 : : if (cur > max)
108 : : max = cur;
109 : :
110 : : /* move to next page */
111 : : cur_page = page_end;
112 : 0 : expected_iova = ms->iova + page_sz;
113 : : /* memsegs are contiguous in memory */
114 : 0 : ms++;
115 : : }
116 : :
117 : : return max;
118 : : }
119 : :
120 : : /*
121 : : * Initialize a general malloc_elem header structure
122 : : */
123 : : void
124 : 132815 : malloc_elem_init(struct malloc_elem *elem, struct malloc_heap *heap,
125 : : struct rte_memseg_list *msl, size_t size,
126 : : struct malloc_elem *orig_elem, size_t orig_size, bool dirty)
127 : : {
128 : 132815 : elem->heap = heap;
129 : 132815 : elem->msl = msl;
130 : 132815 : elem->prev = NULL;
131 : 132815 : elem->next = NULL;
132 : 132815 : memset(&elem->free_list, 0, sizeof(elem->free_list));
133 : 132815 : elem->state = ELEM_FREE;
134 : 132815 : elem->dirty = dirty;
135 : 132815 : elem->size = size;
136 : 132815 : elem->pad = 0;
137 : 132815 : elem->orig_elem = orig_elem;
138 : 132815 : elem->orig_size = orig_size;
139 : : set_header(elem);
140 : : set_trailer(elem);
141 : 132815 : }
142 : :
143 : : void
144 : 628 : malloc_elem_insert(struct malloc_elem *elem)
145 : : {
146 : : struct malloc_elem *prev_elem, *next_elem;
147 : 628 : struct malloc_heap *heap = elem->heap;
148 : :
149 : : /* first and last elements must be both NULL or both non-NULL */
150 [ - + ]: 628 : if ((heap->first == NULL) != (heap->last == NULL)) {
151 : 0 : EAL_LOG(ERR, "Heap is probably corrupt");
152 : 0 : return;
153 : : }
154 : :
155 [ + + + - ]: 628 : if (heap->first == NULL && heap->last == NULL) {
156 : : /* if empty heap */
157 : 161 : heap->first = elem;
158 : 161 : heap->last = elem;
159 : : prev_elem = NULL;
160 : 161 : next_elem = NULL;
161 [ - + ]: 467 : } else if (elem < heap->first) {
162 : : /* if lower than start */
163 : : prev_elem = NULL;
164 : 0 : next_elem = heap->first;
165 : 0 : heap->first = elem;
166 [ + + ]: 467 : } else if (elem > heap->last) {
167 : : /* if higher than end */
168 : 465 : prev_elem = heap->last;
169 : : next_elem = NULL;
170 : 465 : heap->last = elem;
171 : : } else {
172 : : /* the new memory is somewhere between start and end */
173 : : uint64_t dist_from_start, dist_from_end;
174 : :
175 : 2 : dist_from_end = RTE_PTR_DIFF(heap->last, elem);
176 : 2 : dist_from_start = RTE_PTR_DIFF(elem, heap->first);
177 : :
178 : : /* check which is closer, and find closest list entries */
179 [ - + ]: 2 : if (dist_from_start < dist_from_end) {
180 : 0 : prev_elem = heap->first;
181 [ # # ]: 0 : while (prev_elem->next < elem)
182 : 0 : prev_elem = prev_elem->next;
183 : 0 : next_elem = prev_elem->next;
184 : : } else {
185 : 2 : next_elem = heap->last;
186 [ + + ]: 4 : while (next_elem->prev > elem)
187 : 2 : next_elem = next_elem->prev;
188 : 2 : prev_elem = next_elem->prev;
189 : : }
190 : : }
191 : :
192 : : /* insert new element */
193 : 628 : elem->prev = prev_elem;
194 : 628 : elem->next = next_elem;
195 [ + + ]: 628 : if (prev_elem)
196 : 467 : prev_elem->next = elem;
197 [ + + ]: 628 : if (next_elem)
198 : 2 : next_elem->prev = elem;
199 : : }
200 : :
201 : : /*
202 : : * Attempt to find enough physically contiguous memory in this block to store
203 : : * our data. Assume that element has at least enough space to fit in the data,
204 : : * so we just check the page addresses.
205 : : */
206 : : static bool
207 : : elem_check_phys_contig(const struct rte_memseg_list *msl,
208 : : void *start, size_t size)
209 : : {
210 : 4 : return eal_memalloc_is_contig(msl, start, size);
211 : : }
212 : :
213 : : /*
214 : : * calculate the starting point of where data of the requested size
215 : : * and alignment would fit in the current element. If the data doesn't
216 : : * fit, return NULL.
217 : : */
218 : : static void *
219 : 238302 : elem_start_pt(struct malloc_elem *elem, size_t size, unsigned align,
220 : : size_t bound, bool contig)
221 : : {
222 : 238302 : size_t elem_size = elem->size;
223 : :
224 : : /*
225 : : * we're allocating from the end, so adjust the size of element by
226 : : * alignment size.
227 : : */
228 [ + + ]: 238302 : while (elem_size >= size) {
229 : 236472 : const size_t bmask = ~(bound - 1);
230 : 236472 : uintptr_t end_pt = (uintptr_t)elem +
231 : : elem_size - MALLOC_ELEM_TRAILER_LEN;
232 : 236472 : uintptr_t new_data_start = RTE_ALIGN_FLOOR((end_pt - size),
233 : : align);
234 : : uintptr_t new_elem_start;
235 : :
236 : : /* check boundary */
237 [ + + ]: 236472 : if ((new_data_start & bmask) != ((end_pt - 1) & bmask)) {
238 : 6 : end_pt = RTE_ALIGN_FLOOR(end_pt, bound);
239 : 6 : new_data_start = RTE_ALIGN_FLOOR((end_pt - size),
240 : : align);
241 : 6 : end_pt = new_data_start + size;
242 : :
243 [ + - ]: 6 : if (((end_pt - 1) & bmask) != (new_data_start & bmask))
244 : : return NULL;
245 : : }
246 : :
247 : 236472 : new_elem_start = new_data_start - MALLOC_ELEM_HEADER_LEN;
248 : :
249 : : /* if the new start point is before the exist start,
250 : : * it won't fit
251 : : */
252 [ + + ]: 236472 : if (new_elem_start < (uintptr_t)elem)
253 : : return NULL;
254 : :
255 [ + + ]: 236332 : if (contig) {
256 : 4 : size_t new_data_size = end_pt - new_data_start;
257 : :
258 : : /*
259 : : * if physical contiguousness was requested and we
260 : : * couldn't fit all data into one physically contiguous
261 : : * block, try again with lower addresses.
262 : : */
263 [ - + ]: 4 : if (!elem_check_phys_contig(elem->msl,
264 : : (void *)new_data_start,
265 : : new_data_size)) {
266 : 0 : elem_size -= align;
267 : 0 : continue;
268 : : }
269 : : }
270 : 236332 : return (void *)new_elem_start;
271 : : }
272 : : return NULL;
273 : : }
274 : :
275 : : /*
276 : : * use elem_start_pt to determine if we get meet the size and
277 : : * alignment request from the current element
278 : : */
279 : : int
280 : 120682 : malloc_elem_can_hold(struct malloc_elem *elem, size_t size, unsigned align,
281 : : size_t bound, bool contig)
282 : : {
283 : 120682 : return elem_start_pt(elem, size, align, bound, contig) != NULL;
284 : : }
285 : :
286 : : /*
287 : : * split an existing element into two smaller elements at the given
288 : : * split_pt parameter.
289 : : */
290 : : static void
291 : 132187 : split_elem(struct malloc_elem *elem, struct malloc_elem *split_pt)
292 : : {
293 : 132187 : struct malloc_elem *next_elem = elem->next;
294 : 132187 : const size_t old_elem_size = (uintptr_t)split_pt - (uintptr_t)elem;
295 : 132187 : const size_t new_elem_size = elem->size - old_elem_size;
296 : :
297 : 132187 : malloc_elem_init(split_pt, elem->heap, elem->msl, new_elem_size,
298 : 132187 : elem->orig_elem, elem->orig_size, elem->dirty);
299 : 132187 : split_pt->prev = elem;
300 : 132187 : split_pt->next = next_elem;
301 [ + + ]: 132187 : if (next_elem)
302 : 131553 : next_elem->prev = split_pt;
303 : : else
304 : 634 : elem->heap->last = split_pt;
305 : 132187 : elem->next = split_pt;
306 : 132187 : elem->size = old_elem_size;
307 : : set_trailer(elem);
308 [ - + ]: 132187 : if (elem->pad) {
309 : : /* Update inner padding inner element size. */
310 : 0 : elem = RTE_PTR_ADD(elem, elem->pad);
311 : 0 : elem->size = old_elem_size - elem->pad;
312 : : }
313 : 132187 : }
314 : :
315 : : /*
316 : : * our malloc heap is a doubly linked list, so doubly remove our element.
317 : : */
318 : : static void __rte_unused
319 : : remove_elem(struct malloc_elem *elem)
320 : : {
321 : : struct malloc_elem *next, *prev;
322 : 524 : next = elem->next;
323 : 524 : prev = elem->prev;
324 : :
325 [ + + ]: 524 : if (next)
326 : 22 : next->prev = prev;
327 : : else
328 : 502 : elem->heap->last = prev;
329 [ + + ]: 524 : if (prev)
330 : 468 : prev->next = next;
331 : : else
332 : 56 : elem->heap->first = next;
333 : :
334 : 524 : elem->prev = NULL;
335 : 524 : elem->next = NULL;
336 : 524 : }
337 : :
338 : : static int
339 : 39653 : next_elem_is_adjacent(struct malloc_elem *elem)
340 : : {
341 : : const struct internal_config *internal_conf =
342 : 39653 : eal_get_internal_configuration();
343 : :
344 : 79297 : return elem->next == RTE_PTR_ADD(elem, elem->size) &&
345 [ + + + - ]: 39653 : elem->next->msl == elem->msl &&
346 [ - + ]: 39644 : (!internal_conf->match_allocations ||
347 [ # # ]: 0 : elem->orig_elem == elem->next->orig_elem);
348 : : }
349 : :
350 : : static int
351 : 84477 : prev_elem_is_adjacent(struct malloc_elem *elem)
352 : : {
353 : : const struct internal_config *internal_conf =
354 : 84477 : eal_get_internal_configuration();
355 : :
356 : 168943 : return elem == RTE_PTR_ADD(elem->prev, elem->prev->size) &&
357 [ + + + - ]: 84477 : elem->prev->msl == elem->msl &&
358 [ - + ]: 84466 : (!internal_conf->match_allocations ||
359 [ # # ]: 0 : elem->orig_elem == elem->prev->orig_elem);
360 : : }
361 : :
362 : : /*
363 : : * Given an element size, compute its freelist index.
364 : : * We free an element into the freelist containing similarly-sized elements.
365 : : * We try to allocate elements starting with the freelist containing
366 : : * similarly-sized elements, and if necessary, we search freelists
367 : : * containing larger elements.
368 : : *
369 : : * Example element size ranges for a heap with five free lists:
370 : : * heap->free_head[0] - (0 , 2^8)
371 : : * heap->free_head[1] - [2^8 , 2^10)
372 : : * heap->free_head[2] - [2^10 ,2^12)
373 : : * heap->free_head[3] - [2^12, 2^14)
374 : : * heap->free_head[4] - [2^14, MAX_SIZE]
375 : : */
376 : : size_t
377 : 361320 : malloc_elem_free_list_index(size_t size)
378 : : {
379 : : #define MALLOC_MINSIZE_LOG2 8
380 : : #define MALLOC_LOG2_INCREMENT 2
381 : :
382 : : size_t log2;
383 : : size_t index;
384 : :
385 [ + + ]: 361320 : if (size < (1UL << MALLOC_MINSIZE_LOG2))
386 : : return 0;
387 : :
388 : : /* Find next power of 2 > size. */
389 : 344375 : log2 = sizeof(size) * 8 - rte_clz64(size);
390 : :
391 : : /* Compute freelist index, based on log2(size). */
392 : 344375 : index = (log2 - MALLOC_MINSIZE_LOG2 + MALLOC_LOG2_INCREMENT - 1) /
393 : : MALLOC_LOG2_INCREMENT;
394 : :
395 : : return index <= RTE_HEAP_NUM_FREELISTS - 1 ?
396 : 344375 : index : RTE_HEAP_NUM_FREELISTS - 1;
397 : : }
398 : :
399 : : /*
400 : : * Add the specified element to its heap's free list.
401 : : */
402 : : void
403 : 241930 : malloc_elem_free_list_insert(struct malloc_elem *elem)
404 : : {
405 : : size_t idx;
406 : :
407 : 241930 : idx = malloc_elem_free_list_index(elem->size - MALLOC_ELEM_HEADER_LEN);
408 : 241930 : elem->state = ELEM_FREE;
409 [ + + ]: 241930 : LIST_INSERT_HEAD(&elem->heap->free_head[idx], elem, free_list);
410 : 241930 : }
411 : :
412 : : /*
413 : : * Remove the specified element from its heap's free list.
414 : : */
415 : : void
416 : 241780 : malloc_elem_free_list_remove(struct malloc_elem *elem)
417 : : {
418 [ + + ]: 241780 : LIST_REMOVE(elem, free_list);
419 : 241780 : }
420 : :
421 : : /*
422 : : * reserve a block of data in an existing malloc_elem. If the malloc_elem
423 : : * is much larger than the data block requested, we split the element in two.
424 : : * This function is only called from malloc_heap_alloc so parameter checking
425 : : * is not done here, as it's done there previously.
426 : : */
427 : : struct malloc_elem *
428 : 117620 : malloc_elem_alloc(struct malloc_elem *elem, size_t size, unsigned align,
429 : : size_t bound, bool contig)
430 : : {
431 : 117620 : struct malloc_elem *new_elem = elem_start_pt(elem, size, align, bound,
432 : : contig);
433 : 117620 : const size_t old_elem_size = (uintptr_t)new_elem - (uintptr_t)elem;
434 : 117620 : const size_t trailer_size = elem->size - old_elem_size - size -
435 : : MALLOC_ELEM_OVERHEAD;
436 : :
437 : 117620 : malloc_elem_free_list_remove(elem);
438 : :
439 [ + + ]: 117620 : if (trailer_size > MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
440 : : /* split it, too much free space after elem */
441 : 19116 : struct malloc_elem *new_free_elem =
442 : 19116 : RTE_PTR_ADD(new_elem, size + MALLOC_ELEM_OVERHEAD);
443 : :
444 : : asan_clear_split_alloczone(new_free_elem);
445 : :
446 : 19116 : split_elem(elem, new_free_elem);
447 : 19116 : malloc_elem_free_list_insert(new_free_elem);
448 : :
449 [ - + ]: 19116 : if (elem == elem->heap->last)
450 : 0 : elem->heap->last = new_free_elem;
451 : : }
452 : :
453 [ + + ]: 117620 : if (old_elem_size < MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
454 : : /* don't split it, pad the element instead */
455 : 4575 : elem->state = ELEM_BUSY;
456 : 4575 : elem->pad = old_elem_size;
457 : :
458 : : asan_clear_alloczone(elem);
459 : :
460 : : /* put a dummy header in padding, to point to real element header */
461 [ + + ]: 4575 : if (elem->pad > 0) { /* pad will be at least 64-bytes, as everything
462 : : * is cache-line aligned */
463 : 64 : new_elem->pad = elem->pad;
464 : 64 : new_elem->state = ELEM_PAD;
465 : 64 : new_elem->size = elem->size - elem->pad;
466 : : set_header(new_elem);
467 : : }
468 : :
469 : 4575 : return new_elem;
470 : : }
471 : :
472 : : asan_clear_split_alloczone(new_elem);
473 : :
474 : : /* we are going to split the element in two. The original element
475 : : * remains free, and the new element is the one allocated.
476 : : * Re-insert original element, in case its new size makes it
477 : : * belong on a different list.
478 : : */
479 : :
480 : 113045 : split_elem(elem, new_elem);
481 : :
482 : : asan_clear_alloczone(new_elem);
483 : :
484 : 113045 : new_elem->state = ELEM_BUSY;
485 : 113045 : malloc_elem_free_list_insert(elem);
486 : :
487 : 113045 : return new_elem;
488 : : }
489 : :
490 : : /*
491 : : * join two struct malloc_elem together. elem1 and elem2 must
492 : : * be contiguous in memory.
493 : : */
494 : : static inline void
495 : : join_elem(struct malloc_elem *elem1, struct malloc_elem *elem2)
496 : : {
497 : 123636 : struct malloc_elem *next = elem2->next;
498 : 123636 : elem1->size += elem2->size;
499 : 123636 : if (next)
500 : 122830 : next->prev = elem1;
501 : : else
502 : 806 : elem1->heap->last = elem1;
503 : 123636 : elem1->next = next;
504 : 123636 : elem1->dirty |= elem2->dirty;
505 [ - + + + : 123636 : if (elem1->pad) {
- + ]
506 : 11 : struct malloc_elem *inner = RTE_PTR_ADD(elem1, elem1->pad);
507 : 11 : inner->size = elem1->size - elem1->pad;
508 : : }
509 : : }
510 : :
511 : : struct malloc_elem *
512 : 109743 : malloc_elem_join_adjacent_free(struct malloc_elem *elem)
513 : : {
514 : : /*
515 : : * check if next element exists, is adjacent and is free, if so join
516 : : * with it, need to remove from free list.
517 : : */
518 [ + + + + : 149371 : if (elem->next != NULL && elem->next->state == ELEM_FREE &&
+ + ]
519 : 39628 : next_elem_is_adjacent(elem)) {
520 : : void *erase;
521 : : size_t erase_len;
522 : :
523 : : /* we will want to erase the trailer and header */
524 : 39625 : erase = RTE_PTR_SUB(elem->next, MALLOC_ELEM_TRAILER_LEN);
525 : 39625 : erase_len = MALLOC_ELEM_OVERHEAD + elem->next->pad;
526 : :
527 : : /* remove from free list, join to this one */
528 : 39625 : malloc_elem_free_list_remove(elem->next);
529 [ + + ]: 39625 : join_elem(elem, elem->next);
530 : :
531 : : /* erase header, trailer and pad */
532 : : memset(erase, MALLOC_POISON, erase_len);
533 : : }
534 : :
535 : : /*
536 : : * check if prev element exists, is adjacent and is free, if so join
537 : : * with it, need to remove from free list.
538 : : */
539 [ + + + + : 193752 : if (elem->prev != NULL && elem->prev->state == ELEM_FREE &&
+ - ]
540 : 84009 : prev_elem_is_adjacent(elem)) {
541 : : struct malloc_elem *new_elem;
542 : : void *erase;
543 : : size_t erase_len;
544 : :
545 : : /* we will want to erase trailer and header */
546 : : erase = RTE_PTR_SUB(elem, MALLOC_ELEM_TRAILER_LEN);
547 : 84009 : erase_len = MALLOC_ELEM_OVERHEAD + elem->pad;
548 : :
549 : : /* remove from free list, join to this one */
550 : 84009 : malloc_elem_free_list_remove(elem->prev);
551 : :
552 [ + + ]: 84009 : new_elem = elem->prev;
553 : : join_elem(new_elem, elem);
554 : :
555 : : /* erase header, trailer and pad */
556 : : memset(erase, MALLOC_POISON, erase_len);
557 : :
558 : : elem = new_elem;
559 : : }
560 : :
561 : 109743 : return elem;
562 : : }
563 : :
564 : : /*
565 : : * free a malloc_elem block by adding it to the free list. If the
566 : : * blocks either immediately before or immediately after newly freed block
567 : : * are also free, the blocks are merged together.
568 : : */
569 : : struct malloc_elem *
570 : 109115 : malloc_elem_free(struct malloc_elem *elem)
571 : : {
572 : : void *ptr;
573 : : size_t data_len;
574 : :
575 : 109115 : ptr = RTE_PTR_ADD(elem, MALLOC_ELEM_HEADER_LEN);
576 : 109115 : data_len = elem->size - MALLOC_ELEM_OVERHEAD;
577 : :
578 : : /*
579 : : * Consider the element clean for the purposes of joining.
580 : : * If both neighbors are clean or non-existent,
581 : : * the joint element will be clean,
582 : : * which means the memory should be cleared.
583 : : * There is no need to clear the memory if the joint element is dirty.
584 : : */
585 : 109115 : elem->dirty = false;
586 : 109115 : elem = malloc_elem_join_adjacent_free(elem);
587 : :
588 : 109115 : malloc_elem_free_list_insert(elem);
589 : :
590 : 109115 : elem->pad = 0;
591 : :
592 : : /* decrease heap's count of allocated elements */
593 : 109115 : elem->heap->alloc_count--;
594 : :
595 : : #ifndef RTE_MALLOC_DEBUG
596 : : /* Normally clear the memory when needed. */
597 [ + - ]: 109115 : if (!elem->dirty)
598 : : memset(ptr, 0, data_len);
599 : : #else
600 : : /* Always poison the memory in debug mode. */
601 : : memset(ptr, MALLOC_POISON, data_len);
602 : : #endif
603 : :
604 : 109115 : return elem;
605 : : }
606 : :
607 : : /* assume all checks were already done */
608 : : void
609 : 524 : malloc_elem_hide_region(struct malloc_elem *elem, void *start, size_t len)
610 : : {
611 : : struct malloc_elem *hide_start, *hide_end, *prev, *next;
612 : : size_t len_before, len_after;
613 : :
614 : : hide_start = start;
615 : 524 : hide_end = RTE_PTR_ADD(start, len);
616 : :
617 : 524 : prev = elem->prev;
618 : 524 : next = elem->next;
619 : :
620 : : /* we cannot do anything with non-adjacent elements */
621 [ + + + + ]: 524 : if (next && next_elem_is_adjacent(elem)) {
622 : 16 : len_after = RTE_PTR_DIFF(next, hide_end);
623 [ + - ]: 16 : if (len_after >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
624 : : asan_clear_split_alloczone(hide_end);
625 : :
626 : : /* split after */
627 : 16 : split_elem(elem, hide_end);
628 : :
629 : 16 : malloc_elem_free_list_insert(hide_end);
630 [ # # ]: 0 : } else if (len_after > 0) {
631 : 0 : EAL_LOG(ERR, "Unaligned element, heap is probably corrupt");
632 : 0 : return;
633 : : }
634 : : }
635 : :
636 : : /* we cannot do anything with non-adjacent elements */
637 [ + + + + ]: 524 : if (prev && prev_elem_is_adjacent(elem)) {
638 : 457 : len_before = RTE_PTR_DIFF(hide_start, elem);
639 [ + + ]: 457 : if (len_before >= MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE) {
640 : : asan_clear_split_alloczone(hide_start);
641 : :
642 : : /* split before */
643 : 8 : split_elem(elem, hide_start);
644 : :
645 : : prev = elem;
646 : : elem = hide_start;
647 : :
648 : 8 : malloc_elem_free_list_insert(prev);
649 [ - + ]: 449 : } else if (len_before > 0) {
650 : 0 : EAL_LOG(ERR, "Unaligned element, heap is probably corrupt");
651 : 0 : return;
652 : : }
653 : : }
654 : :
655 : : asan_clear_alloczone(elem);
656 : :
657 : : remove_elem(elem);
658 : : }
659 : :
660 : : /*
661 : : * attempt to resize a malloc_elem by expanding into any free space
662 : : * immediately after it in memory.
663 : : */
664 : : int
665 : 17 : malloc_elem_resize(struct malloc_elem *elem, size_t size)
666 : : {
667 : 17 : const size_t new_size = size + elem->pad + MALLOC_ELEM_OVERHEAD;
668 : :
669 : : /* if we request a smaller size, then always return ok */
670 [ + + ]: 17 : if (elem->size >= new_size) {
671 : : asan_clear_alloczone(elem);
672 : : return 0;
673 : : }
674 : :
675 : : /* check if there is a next element, it's free and adjacent */
676 [ + + + + : 19 : if (!elem->next || elem->next->state != ELEM_FREE ||
- + ]
677 : 3 : !next_elem_is_adjacent(elem))
678 : 13 : return -1;
679 [ + + ]: 3 : if (elem->size + elem->next->size < new_size)
680 : : return -1;
681 : :
682 : : /* we now know the element fits, so remove from free list,
683 : : * join the two
684 : : */
685 : 2 : malloc_elem_free_list_remove(elem->next);
686 [ - + ]: 2 : join_elem(elem, elem->next);
687 : :
688 [ + - ]: 2 : if (elem->size - new_size >= MIN_DATA_SIZE + MALLOC_ELEM_OVERHEAD) {
689 : : /* now we have a big block together. Lets cut it down a bit, by splitting */
690 : 2 : struct malloc_elem *split_pt = RTE_PTR_ADD(elem, new_size);
691 : 2 : split_pt = RTE_PTR_ALIGN_CEIL(split_pt, RTE_CACHE_LINE_SIZE);
692 : :
693 : : asan_clear_split_alloczone(split_pt);
694 : :
695 : 2 : split_elem(elem, split_pt);
696 : 2 : malloc_elem_free_list_insert(split_pt);
697 : : }
698 : :
699 : : asan_clear_alloczone(elem);
700 : :
701 : : return 0;
702 : : }
703 : :
704 : : static inline const char *
705 : : elem_state_to_str(enum elem_state state)
706 : : {
707 : 0 : switch (state) {
708 : : case ELEM_PAD:
709 : : return "PAD";
710 : 0 : case ELEM_BUSY:
711 : 0 : return "BUSY";
712 : 0 : case ELEM_FREE:
713 : 0 : return "FREE";
714 : : }
715 : 0 : return "ERROR";
716 : : }
717 : :
718 : : void
719 : 0 : malloc_elem_dump(const struct malloc_elem *elem, FILE *f)
720 : : {
721 : : fprintf(f, "Malloc element at %p (%s)\n", elem,
722 [ # # # # ]: 0 : elem_state_to_str(elem->state));
723 : 0 : fprintf(f, " len: 0x%zx pad: 0x%" PRIx32 "\n", elem->size, elem->pad);
724 : 0 : fprintf(f, " prev: %p next: %p\n", elem->prev, elem->next);
725 : 0 : }
|