mem_avl.c (23784B)
1 2 /* 3 * 4 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 5 * 6 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 7 * 8 * Generic Memory Manager using AVL Tree 9 * 10 * NOTE: These routines are NOT thread-safe. If multiple threads could 11 * access an AVL tree, external locks must be used to mutually exclude 12 * each call to the shr_avl routines, including shr_avl_traverse(). 13 */ 14 #include <shared/bsl.h> 15 16 #include <sal/types.h> 17 #include <shared/alloc.h> 18 #include <sal/core/libc.h> 19 #include <shared/mem_avl.h> 20 #include <assert.h> 21 22 #define MEM_AVL_DEBUG 23 #ifdef MEM_AVL_DEBUG 24 #include <soc/cm.h> 25 #endif 26 /* 27 * 28 * The memory management scheme works as follows: 29 * 30 * Structure: 31 * AVL Tree: 32 * Each node in the tree describes a free block of memory. 33 * Said node has the number of memory units contained in the block, 34 * as well as the starting address (within the block) of the 35 * block. 36 * Doubly Linked List: 37 * The nodes within the aforementioned tree have previous and next 38 * pointers to allow for a DLL. This DLL is used to coalesce 39 * adjacent free blocks, reducing fragmentation, and allowing for 40 * larger blocks to be allocated. 41 * 42 * Initialization: 43 * We start with a single node avl tree. This is deemed the 'free' 44 * block tree. The node has the entire memory to be managed and is 45 * the head of our doubly linked list. 46 * 47 * Allocation: 48 * We execute a best fit search of the free tree. 49 * The node returned to us is removed from the tree. If the node 50 * returned to us is larger than our need, we split off the 51 * unused portion and add said portion back into the tree. 52 * We mark the allocated portion used, insert the remainder 53 * into the DLL (the allocated node was already in the DLL). 54 * We then return the memory address to the user. 55 * 56 * ReAlloc: 57 * If the realloc size is identical, we simply return the same 58 * address to the user. 59 * Otherwise, we free the associated block, (with possible 60 * coalescence), and allocate a fresh block. 61 * One item of note, this implementation of realloc does not 62 * copy the original contents of the memory to the new location. 63 * 64 * Free: 65 * Find the node (within the DLL) associated with this block of 66 * memory. If the previous block is free also, coalesce the two 67 * blocks into one. If the next block is free, coalesce that block 68 * into this one. Mark the memory as free in the DLL, and add it to 69 * the free tree. 70 * 71 * LANGUAGE: 72 * 73 * C 74 * 75 * AUTHORS: 76 * 77 * Travis B. Sawyer, Sean Campbell 78 * 79 * CREATION DATE: 80 * 81 * 21-June-2005 82 * Rewritten to use AVL free tree on: 83 * 27-July-2005 84 */ 85 86 /* 87 * Internally used functions 88 */ 89 static void shr_mem_avl_list_tree(shr_avl_entry_t *a, int m); 90 static int _shr_mem_avl_remove(shr_mem_avl_t *mem_avl, 91 shr_mem_avl_entry_pt entry); 92 static int _shr_mem_avl_compare(void *user_data, 93 shr_avl_datum_t *a, 94 shr_avl_datum_t *b); 95 static int _shr_mem_avl_dll_insert(shr_mem_avl_t* mem_avl, 96 shr_mem_avl_entry_pt entry); 97 static int _shr_mem_avl_dll_remove(shr_mem_avl_t *mem_avl, 98 shr_mem_avl_entry_pt entry); 99 static shr_mem_avl_entry_pt _shr_mem_avl_dll_search(shr_mem_avl_t *mem_avl, 100 unsigned int addr, 101 unsigned int loose); 102 103 static void _shr_mem_avl_find_free_count(shr_avl_entry_t* entry, 104 int size, 105 int *count); 106 107 int shr_mem_avl_create(shr_mem_avl_t **mem_avl_ptr, 108 int mem_size, 109 int mem_base, 110 int max_blocks) 111 { 112 int status = 0; 113 shr_mem_avl_entry_t *entry; 114 shr_mem_avl_t *mem_avl; 115 116 mem_avl = sal_alloc(sizeof(shr_mem_avl_t), "mem_avl"); 117 if (mem_avl == NULL) { 118 return -1; 119 } 120 121 sal_memset(mem_avl, 0, sizeof(*mem_avl)); 122 123 *mem_avl_ptr = mem_avl; 124 125 /* create the AVL tree. 126 Datum is shr_mem_avl_entry_s */ 127 status = shr_avl_create(&mem_avl->tree, NULL, 128 sizeof(shr_mem_avl_entry_t), max_blocks); 129 if (status != 0) { 130 return status; 131 } 132 133 /* Initialize and create a single entry with the whole mem */ 134 entry = sal_alloc(sizeof(shr_mem_avl_entry_t), "mem_avl"); 135 136 if (entry == NULL) { 137 return -1; 138 } 139 140 sal_memset(entry, 0, sizeof(*entry)); 141 142 /* Initialize doubly linked list (for block coalesence) */ 143 entry->size = mem_size; 144 entry->addr = mem_base; 145 entry->next = NULL; 146 entry->prev = NULL; 147 entry->self = entry; 148 149 mem_avl->mem_list = entry; 150 151 /* insert the entry into the avl tree */ 152 status = shr_avl_insert(mem_avl->tree, _shr_mem_avl_compare, 153 (shr_avl_datum_t*)((mem_avl->mem_list))); 154 155 return( status ); 156 } 157 158 static int _shr_mem_avl_compare(void *user_data, shr_avl_datum_t *a, 159 shr_avl_datum_t *b) 160 { 161 int size_diff; 162 163 COMPILER_REFERENCE(user_data); 164 165 size_diff = (((shr_mem_avl_entry_pt)(a))->size - 166 ((shr_mem_avl_entry_pt)(b))->size); 167 168 if (!size_diff) { 169 /* both entries are of same size...check the address. If both have 170 * same address (which means its the same entry) return 0 else 1 171 */ 172 if (((shr_mem_avl_entry_pt)(a))->addr == 173 ((shr_mem_avl_entry_pt)(b))->addr) { 174 return 0; 175 } 176 if (((shr_mem_avl_entry_pt)(a))->addr > 177 ((shr_mem_avl_entry_pt)(b))->addr) { 178 return 1; 179 } 180 return -1; 181 } 182 183 return size_diff; 184 } 185 int shr_mem_avl_destroy(shr_mem_avl_t *mem_avl) 186 { 187 int status, rv = 0; 188 shr_mem_avl_entry_pt entry; 189 shr_mem_avl_entry_pt next; 190 191 /* destroy the avl tree. Frees the space for datum's also. */ 192 status = shr_avl_destroy(mem_avl->tree); 193 if (status != 0) { 194 rv = -1; 195 } 196 mem_avl->tree = NULL; 197 198 /* 199 * Now walk the dll and free each entry 200 */ 201 entry = mem_avl->mem_list; 202 203 while (NULL != entry) { 204 next = entry->next; 205 sal_free(entry); 206 entry = next; 207 } 208 mem_avl->mem_list = NULL; 209 210 return rv; 211 } 212 213 static int _shr_mem_avl_avl_search_compare(shr_avl_datum_t *a, 214 shr_avl_datum_t *b) 215 { 216 return (((shr_mem_avl_entry_pt)(a))->size - 217 ((shr_mem_avl_entry_pt)(b))->size); 218 } 219 220 /* performs bestfit search of the avl tree to find the free block */ 221 static shr_avl_datum_t* _shr_mem_avl_avl_search(shr_avl_t *tree, 222 shr_avl_datum_t *datum) 223 { 224 int cmp_rv; 225 shr_avl_datum_t *result = NULL; 226 shr_avl_t sub_tree; 227 228 if ((!tree) || (!tree->root)) { 229 return NULL; 230 } 231 232 cmp_rv = _shr_mem_avl_avl_search_compare(&tree->root->datum, datum); 233 234 if (cmp_rv > 0) { 235 /* search left sub-tree */ 236 if (tree->root->left != NULL) { 237 sub_tree.root = tree->root->left; 238 result = _shr_mem_avl_avl_search(&sub_tree, datum); 239 if (result != NULL) { 240 cmp_rv = _shr_mem_avl_avl_search_compare(result, datum); 241 if (cmp_rv < 0) { 242 result = &tree->root->datum; 243 } 244 } 245 } else { 246 result = &tree->root->datum; 247 } 248 } 249 250 if (cmp_rv == 0) { 251 result = &tree->root->datum; 252 } 253 254 if (cmp_rv < 0) { 255 /* search right sub-tree */ 256 if (tree->root->right != NULL) { 257 sub_tree.root = tree->root->right; 258 result = _shr_mem_avl_avl_search(&sub_tree, datum); 259 if (result != NULL) { 260 cmp_rv = _shr_mem_avl_avl_search_compare(result, datum); 261 if (cmp_rv < 0) { 262 result = &tree->root->datum; 263 } 264 } 265 } else { 266 result = &tree->root->datum; 267 } 268 } 269 270 return result; 271 } 272 273 int shr_mem_avl_malloc(shr_mem_avl_t *mem_avl, int size, unsigned int *addr) 274 { 275 shr_mem_avl_entry_t entry; 276 shr_mem_avl_entry_pt curr_entry; 277 shr_mem_avl_entry_pt split_entry; 278 void *pTmpVoid; 279 #ifndef NDEBUG 280 int nStatus; 281 #endif /* !NDEBUG*/ 282 shr_avl_datum_t *pDatumSearch, *pDatum; 283 284 assert(mem_avl != 0); 285 assert(addr); 286 287 if (0 == size) { 288 /* 289 * What does malloc do here? 290 */ 291 return( -1 ); 292 } 293 294 /* 295 * Need to search the tree to find the best fit. 296 */ 297 sal_memset(&entry, 0, sizeof(entry)); 298 entry.size = size; 299 pDatumSearch = (shr_avl_datum_t *) &entry; 300 301 pDatum = _shr_mem_avl_avl_search(mem_avl->tree, pDatumSearch); 302 if (NULL == pDatum) { 303 /* OOM */ 304 return( -1 ); 305 } 306 curr_entry = ((shr_mem_avl_entry_pt)(pDatum))->self; 307 308 if (size > curr_entry->size) { 309 /* OOM */ 310 return( -1 ); 311 } 312 313 /* 314 * Remove the node from the free tree 315 */ 316 /* 317 * ENHANCEME: do something with the status from avl_remove 318 */ 319 #ifndef NDEBUG 320 nStatus = 321 #endif /* !NDEBUG*/ 322 _shr_mem_avl_remove(mem_avl, curr_entry); 323 324 assert(nStatus >= 0); 325 326 /* 327 * We have a 'free' block of memory. It may be larger than we need, 328 * if it is, we need to split off the extra and put that back into 329 * the free tree 330 */ 331 if (size < curr_entry->size) { 332 /* 333 * The search returned a free block that is greater than 334 * what we need. We need to split off the unused lines, 335 * and add that back to the free tree. 336 */ 337 pTmpVoid = NULL; 338 pTmpVoid = sal_alloc(sizeof(*split_entry), "mem_avl"); 339 if (NULL == pTmpVoid) { 340 /* OOSM */ 341 return( -1 ); 342 } 343 344 split_entry = (shr_mem_avl_entry_pt)pTmpVoid; 345 346 sal_memset(split_entry, 0, sizeof(*split_entry)); 347 split_entry->size = curr_entry->size - size; 348 split_entry->addr = curr_entry->addr + entry.size; 349 split_entry->next = NULL; 350 split_entry->prev = NULL; 351 split_entry->self = split_entry; 352 353 /* 354 * Since we're splitting, we have to update the 'allocated' node's 355 * size appropriately 356 */ 357 curr_entry->size = size; 358 359 360 /* 361 * ENHANCEME: do something with the status from avl_insert 362 */ 363 #ifndef NDEBUG 364 nStatus = 365 #endif /*!NDEBUG*/ 366 shr_avl_insert(mem_avl->tree, _shr_mem_avl_compare, 367 (shr_avl_datum_t*)split_entry); 368 assert(nStatus >= 0); 369 _shr_mem_avl_dll_insert(mem_avl, split_entry); 370 } 371 372 /* 373 * Now take care of the block we're interested in 374 */ 375 *addr = curr_entry->addr; 376 curr_entry->used = 1; 377 378 return( 0 ); 379 } 380 381 int shr_mem_avl_realloc(shr_mem_avl_t *mem_avl, int size, unsigned int addr) 382 { 383 shr_mem_avl_entry_pt curr_entry; 384 shr_mem_avl_entry_pt split_entry; 385 void *pTmpVoid; 386 #ifndef NDEBUG 387 int nStatus; 388 #endif /* !NDEBUG*/ 389 390 assert(mem_avl != 0); 391 392 if (0 == size) { 393 /* 394 * What does realloc do here? 395 */ 396 return( -1 ); 397 } 398 399 /* use loose search to find target block */ 400 curr_entry = _shr_mem_avl_dll_search(mem_avl, addr, 1); 401 if (NULL == curr_entry) { 402 /* 403 * This is bad, we couldn't find a 404 * block containing this address 405 */ 406 return( -1 ); 407 } 408 409 /* verify size and availability */ 410 if ( ((curr_entry->addr + curr_entry->size - 1) < (addr + size - 1)) || 411 (curr_entry->used) ) { 412 return( -1 ); 413 } 414 415 /* 416 * Remove the node from the free tree 417 */ 418 /* 419 * ENHANCEME: do something with the status from avl_remove 420 */ 421 #ifndef NDEBUG 422 nStatus = 423 #endif /* !NDEBUG*/ 424 _shr_mem_avl_remove(mem_avl, curr_entry); 425 assert(nStatus >= 0); 426 427 /* 428 * We have a 'free' block of memory. The starting address of this 429 * block may not matched the target. If not, split off the first 430 * portian and place it back into the free tree 431 */ 432 if (addr > curr_entry->addr) { 433 /* 434 * The search returned a free block that contains the 435 * desired block. The start address is not a match so we 436 * need to split off the portion prior to the target 437 * address and add it back to the free tree. 438 */ 439 pTmpVoid = NULL; 440 pTmpVoid = sal_alloc(sizeof(*split_entry), "mem_avl"); 441 if (NULL == pTmpVoid) { 442 /* OOSM */ 443 return( -1 ); 444 } 445 446 split_entry = (shr_mem_avl_entry_pt)pTmpVoid; 447 448 sal_memset(split_entry, 0, sizeof(*split_entry)); 449 split_entry->size = addr - curr_entry->addr; 450 split_entry->addr = curr_entry->addr; 451 split_entry->next = NULL; 452 split_entry->prev = NULL; 453 split_entry->self = split_entry; 454 455 /* 456 * Since we're splitting, we have to update the 'allocated' node's 457 * size and address appropriately 458 */ 459 curr_entry->addr = addr; 460 curr_entry->size = curr_entry->size - split_entry->size; 461 462 463 /* 464 * ENHANCEME: do something with the status from avl_insert 465 */ 466 #ifndef NDEBUG 467 nStatus = 468 #endif /* !NDEBUG*/ 469 shr_avl_insert(mem_avl->tree, _shr_mem_avl_compare, 470 (shr_avl_datum_t*)split_entry); 471 assert(nStatus >= 0); 472 _shr_mem_avl_dll_insert(mem_avl, split_entry); 473 } 474 475 if (size < curr_entry->size) { 476 /* 477 * The search returned a free block that is greater than 478 * what we need. We need to split off the unused lines, 479 * and add that back to the free tree. 480 */ 481 pTmpVoid = NULL; 482 pTmpVoid = sal_alloc(sizeof(*split_entry), "mem_avl"); 483 if (NULL == pTmpVoid) { 484 /* OOSM */ 485 return( -1 ); 486 } 487 488 split_entry = (shr_mem_avl_entry_pt)pTmpVoid; 489 490 sal_memset(split_entry, 0, sizeof(*split_entry)); 491 split_entry->size = curr_entry->size - size; 492 split_entry->addr = curr_entry->addr + size; 493 split_entry->next = NULL; 494 split_entry->prev = NULL; 495 split_entry->self = split_entry; 496 497 /* 498 * Since we're splitting, we have to update the 'allocated' node's 499 * size appropriately 500 */ 501 curr_entry->size = size; 502 503 504 /* 505 * ENHANCEME: do something with the status from avl_insert 506 */ 507 #ifndef NDEBUG 508 nStatus = 509 #endif /* !NDEBUG*/ 510 shr_avl_insert(mem_avl->tree, _shr_mem_avl_compare, 511 (shr_avl_datum_t*)split_entry); 512 assert( nStatus >= 0); 513 _shr_mem_avl_dll_insert(mem_avl, split_entry); 514 } 515 516 /* 517 * Now take care of the block we're interested in 518 */ 519 curr_entry->used = 1; 520 521 return( 0 ); 522 } 523 /* 524 * Doubly Linked List functions 525 */ 526 static shr_mem_avl_entry_pt _shr_mem_avl_dll_search(shr_mem_avl_t *mem_avl, 527 unsigned int addr, 528 unsigned int loose) 529 { 530 shr_mem_avl_entry_pt entry; 531 unsigned int curr_addr; 532 533 assert(mem_avl); 534 535 entry = mem_avl->mem_list; 536 537 while (NULL != entry) { 538 curr_addr = entry->addr; 539 if (curr_addr == addr) { 540 return( entry ); 541 } 542 if ( (loose) && 543 (curr_addr < addr) && 544 ((curr_addr + entry->size - 1) > addr) ) { 545 return( entry ); 546 } 547 548 entry = entry->next; 549 } 550 551 /* 552 * If we got here, we didn't find the appropriate entry 553 */ 554 return( NULL ); 555 } 556 557 static int _shr_mem_avl_dll_insert(shr_mem_avl_t* mem_avl, 558 shr_mem_avl_entry_pt entry) 559 { 560 shr_mem_avl_entry_pt curr_entry; 561 unsigned int curr_addr; 562 unsigned int insert_addr; 563 564 assert(mem_avl); 565 assert(entry); 566 567 curr_entry = mem_avl->mem_list; 568 569 /* 570 * Search for the first entry that has an address that is greater 571 * than our insert address. Once found, back track one entry and 572 * insert the new one before the found 573 * entry. If we don't find said entry, insert it at the tail. 574 * We assume we cannot have the same address twice in the DLL. 575 */ 576 insert_addr = entry->addr; 577 while (NULL != curr_entry) { 578 curr_addr = curr_entry->addr; 579 580 if (curr_addr > insert_addr) { 581 curr_entry = curr_entry->prev; 582 break; 583 } 584 585 if (NULL == curr_entry->next) { 586 break; 587 } 588 curr_entry = curr_entry->next; 589 } 590 591 /* 592 * Insert after the entry we found. 593 * Since this is an ordered list, and the head is initialized 594 * We need not worry about inserting at the head. 595 */ 596 entry->prev = curr_entry; 597 entry->next = curr_entry->next; 598 /* entry->used = 1; */ 599 curr_entry->next = entry; 600 if (NULL != entry->next) { 601 entry->next->prev = entry; 602 } 603 604 return( 0 ); 605 } 606 607 static int _shr_mem_avl_remove(shr_mem_avl_t *mem_avl, 608 shr_mem_avl_entry_pt entry) 609 { 610 int nStatus; 611 612 /* 613 * Calls remove root if necessary 614 */ 615 nStatus = shr_avl_delete(mem_avl->tree, _shr_mem_avl_compare, 616 (shr_avl_datum_t*)entry); 617 618 return( nStatus ); 619 } 620 621 static int _shr_mem_avl_dll_remove(shr_mem_avl_t *mem_avl, 622 shr_mem_avl_entry_pt entry) 623 { 624 shr_mem_avl_entry_pt prev_entry; 625 shr_mem_avl_entry_pt next_entry; 626 627 assert(mem_avl); 628 assert(entry); 629 630 prev_entry = entry->prev; 631 next_entry = entry->next; 632 633 if (NULL != prev_entry) { 634 prev_entry->next = next_entry; 635 } 636 637 if (NULL != next_entry) { 638 next_entry->prev = prev_entry; 639 } 640 641 return( 0 ); 642 } 643 644 int shr_mem_avl_free(shr_mem_avl_t *mem_avl, unsigned int addr) 645 { 646 shr_mem_avl_entry_pt entry; 647 shr_mem_avl_entry_pt prev_entry; 648 shr_mem_avl_entry_pt next_entry; 649 #ifndef NDEBUG 650 int nStatus; 651 #endif /* !NDEBUG*/ 652 assert(mem_avl); 653 654 /* 655 * Search for our DLL entry 656 */ 657 entry = _shr_mem_avl_dll_search(mem_avl, addr, 0); 658 if (NULL == entry) { 659 /* 660 * This is bad, we couldn't find our 661 * allocated block to free 662 */ 663 return( -1 ); 664 } 665 666 if ( !(entry->used) ) { 667 /* 668 * Trying to free a free block? 669 */ 670 return( -1 ); 671 } 672 673 /* 674 * Attempt to coalesce adjacent blocks. 675 * 676 * We have four scenarios: 677 * 1. Previous block is free 678 * 2. Next block is free 679 * 3. Both Previous & Next blocks are free. 680 * 4. Neither Previous & Next blocks are free. 681 */ 682 prev_entry = entry->prev; 683 next_entry = entry->next; 684 685 if (NULL != prev_entry) { 686 /* Previous Block is free */ 687 /* 688 * Since we want to ensure that we don't remove the root node 689 * of the DLL, we coalesce to to previous node 690 */ 691 if ( !(prev_entry->used) ) { 692 _shr_mem_avl_remove(mem_avl, prev_entry); 693 prev_entry->size += entry->size; 694 _shr_mem_avl_dll_remove(mem_avl, entry); 695 sal_free(entry); 696 /* 697 * Setup our pointer to look at the coalesced block 698 */ 699 entry = prev_entry; 700 } 701 } 702 703 if (NULL != next_entry) { 704 /* Next Block is free */ 705 if ( !(next_entry->used) ) { 706 entry->size += next_entry->size; 707 _shr_mem_avl_remove(mem_avl, next_entry); 708 _shr_mem_avl_dll_remove(mem_avl, next_entry); 709 sal_free(next_entry); 710 } 711 } 712 713 /* 714 * Remove the used flag 715 */ 716 entry->used = 0; 717 718 /* 719 * Add to free tree 720 */ 721 /* 722 * ENHANCEME: do something with the status from avl_insert 723 */ 724 #ifndef NDEBUG 725 nStatus = 726 #endif /* !NDEBUG*/ 727 shr_avl_insert(mem_avl->tree, _shr_mem_avl_compare, 728 (shr_avl_datum_t*)entry); 729 assert(nStatus >= 0); 730 731 return( 0); 732 } 733 734 static void shr_mem_avl_list_tree(shr_avl_entry_t* entry, int m) 735 { 736 shr_mem_avl_entry_pt mem_entry; 737 int n=m; 738 if (entry == 0) { 739 return; 740 } 741 mem_entry = (shr_mem_avl_entry_pt)(&entry->datum); 742 if (entry->right) { 743 shr_mem_avl_list_tree(entry->right, m+1); 744 } 745 while (n--) { 746 LOG_CLI((BSL_META(" "))); 747 } 748 LOG_CLI((BSL_META("0x%x (size:0x%x) (0x%x)\n"),mem_entry->addr, 749 mem_entry->size, entry->balance)); 750 if (entry->left) { 751 shr_mem_avl_list_tree(entry->left, m+1); 752 } 753 } 754 755 int shr_mem_avl_list_output( shr_mem_avl_t *mem_avl ) 756 { 757 shr_mem_avl_entry_pt entry; 758 int index; 759 760 assert(mem_avl); 761 762 entry = mem_avl->mem_list; 763 LOG_CLI((BSL_META("Listing Memory Nodes:\n"))); 764 index = 0; 765 while (NULL != entry) { 766 LOG_CLI((BSL_META("%i: Units: 0x%x Addr: 0x%x Used: %i "), 767 index++, 768 entry->size, 769 entry->addr, 770 entry->used)); 771 LOG_CLI((BSL_META("This: %p Prev: %p Self: %p Next %p\n"), 772 (void *)entry, 773 (void *)entry->prev, 774 (void *)entry->self, 775 (void *)entry->next)); 776 entry = entry->next; 777 } 778 779 return( 0 ); 780 781 } 782 783 int shr_mem_avl_free_tree_list( shr_mem_avl_t *mem_avl ) 784 { 785 assert(mem_avl); 786 787 LOG_CLI((BSL_META("Listing Free Tree:\n"))); 788 shr_mem_avl_list_tree(mem_avl->tree->root, 0); 789 790 return( 0 ); 791 792 } 793 794 int shr_mem_avl_check_mem( shr_mem_avl_t *mem_avl, 795 int *edges, 796 int *patches, 797 int *usedCount, 798 int *freeCount) 799 { 800 shr_mem_avl_entry_pt entry; 801 802 assert(mem_avl); 803 804 *edges = 0; 805 *patches = 1; 806 *usedCount = 0; 807 *freeCount = 0; 808 809 /* 810 * Search for edges 811 */ 812 entry = mem_avl->mem_list; 813 while (NULL != entry) { 814 if (NULL != entry->prev) { 815 if ((entry->used) != 816 (entry->prev->used) ) { 817 (*edges)++; 818 } 819 } 820 entry = entry->next; 821 } 822 823 /* 824 * Search for patches, and count used & free at the same time 825 */ 826 entry = mem_avl->mem_list; 827 while (NULL != entry) { 828 if (NULL != entry->next) { 829 if ((entry->used) != 830 (entry->next->used)) { 831 (*patches)++; 832 } 833 } 834 835 if (entry->used == 1) { 836 (*usedCount)++; 837 } 838 else { 839 (*freeCount)++; 840 } 841 842 entry = entry->next; 843 } 844 845 return( 0 ); 846 } 847 848 int shr_mem_avl_free_count(shr_mem_avl_t *mem_avl, int size, int*count) 849 { 850 assert(mem_avl); 851 *count = 0; 852 _shr_mem_avl_find_free_count(mem_avl->tree->root, size, count); 853 return( 0 ); 854 } 855 856 static void _shr_mem_avl_find_free_count(shr_avl_entry_t* entry, int size, int *count) 857 { 858 shr_mem_avl_entry_pt mem_entry; 859 if (entry == 0) { 860 return; 861 } 862 863 mem_entry = (shr_mem_avl_entry_pt)(&entry->datum); 864 if (entry->right) { 865 _shr_mem_avl_find_free_count(entry->right, size, count); 866 } 867 *count = *count + (int)(mem_entry->size / size); 868 if (entry->left) { 869 _shr_mem_avl_find_free_count(entry->left, size, count); 870 } 871 return; 872 }