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