1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Hugo Santos, hugosantos@gmail.com 7 */ 8 9 #include <Slab.h> 10 11 #include "slab_private.h" 12 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <KernelExport.h> 17 #include <util/AutoLock.h> 18 #include <util/DoublyLinkedList.h> 19 #include <util/OpenHashTable.h> 20 21 #include <smp.h> 22 #include <vm.h> 23 #include <vm_low_memory.h> 24 25 #include <algorithm> // swap 26 #include <new> 27 28 29 // TODO kMagazineCapacity should be dynamically tuned per cache. 30 31 32 //#define TRACE_SLAB 33 34 #ifdef TRACE_SLAB 35 #define TRACE_CACHE(cache, format, args...) \ 36 dprintf("Cache[%p, %s] " format "\n", cache, cache->name , ##args) 37 #else 38 #define TRACE_CACHE(cache, format, bananas...) do { } while (0) 39 #endif 40 41 42 static const int kMagazineCapacity = 32; 43 static const size_t kCacheColorPeriod = 8; 44 45 struct object_link { 46 struct object_link *next; 47 }; 48 49 struct slab : DoublyLinkedListLinkImpl<slab> { 50 void *pages; 51 size_t count, size; 52 size_t offset; 53 object_link *free; 54 }; 55 56 typedef DoublyLinkedList<slab> SlabList; 57 58 struct object_cache : DoublyLinkedListLinkImpl<object_cache> { 59 char name[32]; 60 benaphore lock; 61 size_t object_size; 62 size_t cache_color_cycle; 63 SlabList empty, partial, full; 64 size_t used_count, empty_count, pressure; 65 66 size_t slab_size; 67 size_t usage, maximum; 68 uint32 flags; 69 70 void *cookie; 71 object_cache_constructor constructor; 72 object_cache_destructor destructor; 73 object_cache_reclaimer reclaimer; 74 75 status_t (*allocate_pages)(object_cache *cache, void **pages, 76 uint32 flags); 77 void (*free_pages)(object_cache *cache, void *pages); 78 79 object_depot depot; 80 81 virtual slab *CreateSlab(uint32 flags) = 0; 82 virtual void ReturnSlab(slab *slab) = 0; 83 virtual slab *ObjectSlab(void *object) const = 0; 84 85 slab *InitSlab(slab *slab, void *pages, size_t byteCount); 86 void UninitSlab(slab *slab); 87 88 virtual status_t PrepareObject(slab *source, void *object) { return B_OK; } 89 virtual void UnprepareObject(slab *source, void *object) {} 90 91 virtual ~object_cache() {} 92 }; 93 94 typedef DoublyLinkedList<object_cache> ObjectCacheList; 95 96 struct SmallObjectCache : object_cache { 97 slab *CreateSlab(uint32 flags); 98 void ReturnSlab(slab *slab); 99 slab *ObjectSlab(void *object) const; 100 }; 101 102 103 struct HashedObjectCache : object_cache { 104 struct Link : HashTableLink<Link> { 105 const void *buffer; 106 slab *parent; 107 }; 108 109 struct Definition { 110 typedef HashedObjectCache ParentType; 111 typedef const void * KeyType; 112 typedef Link ValueType; 113 114 Definition(HashedObjectCache *_parent) : parent(_parent) {} 115 116 size_t HashKey(const void *key) const 117 { 118 return (((const uint8 *)key) - ((const uint8 *)0)) 119 >> parent->lower_boundary; 120 } 121 122 size_t Hash(Link *value) const { return HashKey(value->buffer); } 123 bool Compare(const void *key, Link *value) const 124 { return value->buffer == key; } 125 HashTableLink<Link> *GetLink(Link *value) const { return value; } 126 127 HashedObjectCache *parent; 128 }; 129 130 typedef OpenHashTable<Definition> HashTable; 131 132 HashedObjectCache() 133 : hash_table(this) {} 134 135 slab *CreateSlab(uint32 flags); 136 void ReturnSlab(slab *slab); 137 slab *ObjectSlab(void *object) const; 138 status_t PrepareObject(slab *source, void *object); 139 void UnprepareObject(slab *source, void *object); 140 141 HashTable hash_table; 142 size_t lower_boundary; 143 }; 144 145 146 struct depot_magazine { 147 struct depot_magazine *next; 148 uint16 current_round, round_count; 149 void *rounds[0]; 150 }; 151 152 153 struct depot_cpu_store { 154 benaphore lock; 155 struct depot_magazine *loaded, *previous; 156 }; 157 158 159 static ObjectCacheList sObjectCaches; 160 static benaphore sObjectCacheListLock; 161 162 static uint8 *sInitialBegin, *sInitialLimit, *sInitialPointer; 163 static kernel_args *sKernelArgs; 164 165 166 static status_t object_cache_reserve_internal(object_cache *cache, 167 size_t object_count, uint32 flags); 168 static status_t object_depot_init_locks(object_depot *depot); 169 static depot_magazine *alloc_magazine(); 170 static void free_magazine(depot_magazine *magazine); 171 172 template<typename Type> static Type * 173 _pop(Type* &head) 174 { 175 Type *oldHead = head; 176 head = head->next; 177 return oldHead; 178 } 179 180 181 template<typename Type> static inline void 182 _push(Type* &head, Type *object) 183 { 184 object->next = head; 185 head = object; 186 } 187 188 189 static inline void * 190 link_to_object(object_link *link, size_t objectSize) 191 { 192 return ((uint8 *)link) - (objectSize - sizeof(object_link)); 193 } 194 195 196 static inline object_link * 197 object_to_link(void *object, size_t objectSize) 198 { 199 return (object_link *)(((uint8 *)object) 200 + (objectSize - sizeof(object_link))); 201 } 202 203 204 static inline depot_cpu_store * 205 object_depot_cpu(object_depot *depot) 206 { 207 return &depot->stores[smp_get_current_cpu()]; 208 } 209 210 211 static inline int 212 __fls0(size_t value) 213 { 214 if (value == 0) 215 return -1; 216 217 int bit; 218 for (bit = 0; value != 1; bit++) 219 value >>= 1; 220 return bit; 221 } 222 223 224 static void * 225 internal_alloc(size_t size, uint32 flags) 226 { 227 if (flags & CACHE_DURING_BOOT) { 228 if ((sInitialPointer + size) > sInitialLimit) 229 panic("internal_alloc: ran out of initial space"); 230 231 uint8 *buffer = sInitialPointer; 232 sInitialPointer += size; 233 return buffer; 234 } 235 236 return block_alloc(size); 237 } 238 239 240 static void 241 internal_free(void *_buffer) 242 { 243 uint8 *buffer = (uint8 *)_buffer; 244 245 if (buffer >= sInitialBegin && buffer < sInitialLimit) 246 return; 247 248 block_free(buffer); 249 } 250 251 252 static status_t 253 benaphore_boot_init(benaphore *lock, const char *name, uint32 flags) 254 { 255 if (flags & CACHE_DURING_BOOT) { 256 lock->sem = -1; 257 lock->count = 1; 258 return B_OK; 259 } 260 261 return benaphore_init(lock, name); 262 } 263 264 265 static status_t 266 area_allocate_pages(object_cache *cache, void **pages, uint32 flags) 267 { 268 TRACE_CACHE(cache, "allocate pages (%lu, 0x0%lx)", cache->slab_size, flags); 269 270 uint32 lock = B_FULL_LOCK; 271 if (cache->flags & CACHE_UNLOCKED_PAGES) 272 lock = B_NO_LOCK; 273 274 // if we are allocating, it is because we need the pages immediatly 275 // so we lock them. when moving the slab to the empty list we should 276 // unlock them, and lock them again when getting one from the empty list. 277 area_id areaId = create_area(cache->name, pages, B_ANY_KERNEL_ADDRESS, 278 cache->slab_size, lock, B_READ_AREA | B_WRITE_AREA); 279 if (areaId < 0) 280 return areaId; 281 282 cache->usage += cache->slab_size; 283 284 TRACE_CACHE(cache, " ... = { %ld, %p }", areaId, *pages); 285 286 return B_OK; 287 } 288 289 290 static void 291 area_free_pages(object_cache *cache, void *pages) 292 { 293 area_id id = area_for(pages); 294 295 TRACE_CACHE(cache, "delete pages %p (%ld)", pages, id); 296 297 if (id < B_OK) 298 panic("object cache: freeing unknown area"); 299 300 delete_area(id); 301 302 cache->usage -= cache->slab_size; 303 } 304 305 306 static status_t 307 early_allocate_pages(object_cache *cache, void **pages, uint32 flags) 308 { 309 TRACE_CACHE(cache, "early allocate pages (%lu, 0x0%lx)", cache->slab_size, 310 flags); 311 312 addr_t base = vm_allocate_early(sKernelArgs, cache->slab_size, 313 cache->slab_size, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 314 315 *pages = (void *)base; 316 317 cache->usage += cache->slab_size; 318 319 TRACE_CACHE(cache, " ... = { %p }", *pages); 320 321 return B_OK; 322 } 323 324 325 static void 326 early_free_pages(object_cache *cache, void *pages) 327 { 328 panic("memory pressure on bootup?"); 329 } 330 331 332 static void 333 object_cache_low_memory(void *_self, int32 level) 334 { 335 if (level == B_NO_LOW_MEMORY) 336 return; 337 338 object_cache *cache = (object_cache *)_self; 339 340 // we are calling the reclaimer without the object cache lock 341 // to give the owner a chance to return objects to the slabs. 342 // As we only register the low memory handler after we complete 343 // the init of the object cache, unless there are some funky 344 // memory re-order business going on, we should be ok. 345 346 if (cache->reclaimer) 347 cache->reclaimer(cache->cookie, level); 348 349 BenaphoreLocker _(cache->lock); 350 size_t minimumAllowed; 351 352 switch (level) { 353 case B_LOW_MEMORY_NOTE: 354 minimumAllowed = cache->pressure / 2 + 1; 355 break; 356 357 case B_LOW_MEMORY_WARNING: 358 cache->pressure /= 2; 359 minimumAllowed = 0; 360 break; 361 362 default: 363 cache->pressure = 0; 364 minimumAllowed = 0; 365 break; 366 } 367 368 if (cache->empty_count <= minimumAllowed) 369 return; 370 371 TRACE_CACHE(cache, "cache: memory pressure, will release down to %lu.", 372 minimumAllowed); 373 374 while (cache->empty_count > minimumAllowed) { 375 cache->ReturnSlab(cache->empty.RemoveHead()); 376 cache->empty_count--; 377 } 378 } 379 380 381 static void 382 object_cache_return_object_wrapper(object_depot *depot, void *object) 383 { 384 object_cache *cache = (object_cache *)(((uint8 *)depot) 385 - offsetof(object_cache, depot)); 386 387 object_cache_free(cache, object); 388 } 389 390 391 static status_t 392 object_cache_init(object_cache *cache, const char *name, size_t objectSize, 393 size_t alignment, size_t maximum, uint32 flags, void *cookie, 394 object_cache_constructor constructor, object_cache_destructor destructor, 395 object_cache_reclaimer reclaimer) 396 { 397 strlcpy(cache->name, name, sizeof(cache->name)); 398 399 status_t status = benaphore_boot_init(&cache->lock, name, flags); 400 if (status < B_OK) 401 return status; 402 403 if (objectSize < sizeof(object_link)) 404 objectSize = sizeof(object_link); 405 406 if (alignment > 0 && (objectSize & (alignment - 1))) 407 cache->object_size = objectSize + alignment 408 - (objectSize & (alignment - 1)); 409 else 410 cache->object_size = objectSize; 411 412 TRACE_CACHE(cache, "init %lu, %lu -> %lu", objectSize, alignment, 413 cache->object_size); 414 415 cache->cache_color_cycle = 0; 416 cache->used_count = 0; 417 cache->empty_count = 0; 418 cache->pressure = 0; 419 420 cache->usage = 0; 421 cache->maximum = maximum; 422 423 cache->flags = flags; 424 425 // no gain in using the depot in single cpu setups 426 if (smp_get_num_cpus() == 1) 427 cache->flags |= CACHE_NO_DEPOT; 428 429 if (!(cache->flags & CACHE_NO_DEPOT)) { 430 status_t status = object_depot_init(&cache->depot, flags, 431 object_cache_return_object_wrapper); 432 if (status < B_OK) { 433 benaphore_destroy(&cache->lock); 434 return status; 435 } 436 } 437 438 cache->cookie = cookie; 439 cache->constructor = constructor; 440 cache->destructor = destructor; 441 cache->reclaimer = reclaimer; 442 443 if (cache->flags & CACHE_DURING_BOOT) { 444 cache->allocate_pages = early_allocate_pages; 445 cache->free_pages = early_free_pages; 446 } else { 447 cache->allocate_pages = area_allocate_pages; 448 cache->free_pages = area_free_pages; 449 } 450 451 register_low_memory_handler(object_cache_low_memory, cache, 0); 452 453 BenaphoreLocker _(sObjectCacheListLock); 454 sObjectCaches.Add(cache); 455 456 return B_OK; 457 } 458 459 460 static status_t 461 object_cache_init_locks(object_cache *cache) 462 { 463 status_t status = benaphore_init(&cache->lock, cache->name); 464 if (status < B_OK) 465 return status; 466 467 if (cache->flags & CACHE_NO_DEPOT) 468 return B_OK; 469 470 return object_depot_init_locks(&cache->depot); 471 } 472 473 474 static void 475 object_cache_commit_slab(object_cache *cache, slab *slab) 476 { 477 void *pages = (void *)ROUNDOWN((addr_t)slab->pages, B_PAGE_SIZE); 478 if (create_area(cache->name, &pages, B_EXACT_ADDRESS, cache->slab_size, 479 B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA) < B_OK) 480 panic("failed to create_area()"); 481 } 482 483 484 static void 485 object_cache_commit_pre_pages(object_cache *cache) 486 { 487 SlabList::Iterator it = cache->full.GetIterator(); 488 while (it.HasNext()) 489 object_cache_commit_slab(cache, it.Next()); 490 491 it = cache->partial.GetIterator(); 492 while (it.HasNext()) 493 object_cache_commit_slab(cache, it.Next()); 494 495 it = cache->empty.GetIterator(); 496 while (it.HasNext()) 497 object_cache_commit_slab(cache, it.Next()); 498 499 cache->allocate_pages = area_allocate_pages; 500 cache->free_pages = area_free_pages; 501 } 502 503 504 static void 505 delete_cache(object_cache *cache) 506 { 507 cache->~object_cache(); 508 internal_free(cache); 509 } 510 511 512 static SmallObjectCache * 513 create_small_object_cache(const char *name, size_t object_size, 514 size_t alignment, size_t maximum, uint32 flags, void *cookie, 515 object_cache_constructor constructor, object_cache_destructor destructor, 516 object_cache_reclaimer reclaimer) 517 { 518 void *buffer = internal_alloc(sizeof(SmallObjectCache), flags); 519 if (buffer == NULL) 520 return NULL; 521 522 SmallObjectCache *cache = new (buffer) SmallObjectCache(); 523 524 if (object_cache_init(cache, name, object_size, alignment, maximum, flags, 525 cookie, constructor, destructor, reclaimer) < B_OK) { 526 delete_cache(cache); 527 return NULL; 528 } 529 530 cache->slab_size = B_PAGE_SIZE; 531 532 return cache; 533 } 534 535 536 static HashedObjectCache * 537 create_hashed_object_cache(const char *name, size_t object_size, 538 size_t alignment, size_t maximum, uint32 flags, void *cookie, 539 object_cache_constructor constructor, object_cache_destructor destructor, 540 object_cache_reclaimer reclaimer) 541 { 542 void *buffer = internal_alloc(sizeof(HashedObjectCache), flags); 543 if (buffer == NULL) 544 return NULL; 545 546 HashedObjectCache *cache = new (buffer) HashedObjectCache(); 547 548 if (object_cache_init(cache, name, object_size, alignment, maximum, flags, 549 cookie, constructor, destructor, reclaimer) < B_OK) { 550 delete_cache(cache); 551 return NULL; 552 } 553 554 cache->slab_size = max_c(16 * B_PAGE_SIZE, 8 * object_size); 555 cache->lower_boundary = __fls0(cache->object_size); 556 557 return cache; 558 } 559 560 561 object_cache * 562 create_object_cache(const char *name, size_t object_size, size_t alignment, 563 void *cookie, object_cache_constructor constructor, 564 object_cache_destructor destructor) 565 { 566 return create_object_cache_etc(name, object_size, alignment, 0, 0, cookie, 567 constructor, destructor, NULL); 568 } 569 570 571 object_cache * 572 create_object_cache_etc(const char *name, size_t object_size, size_t alignment, 573 size_t maximum, uint32 flags, void *cookie, 574 object_cache_constructor constructor, object_cache_destructor destructor, 575 object_cache_reclaimer reclaimer) 576 { 577 if (object_size == 0) 578 return NULL; 579 else if (object_size <= 256) 580 return create_small_object_cache(name, object_size, alignment, 581 maximum, flags, cookie, constructor, destructor, reclaimer); 582 583 return create_hashed_object_cache(name, object_size, alignment, 584 maximum, flags, cookie, constructor, destructor, reclaimer); 585 } 586 587 588 void 589 delete_object_cache(object_cache *cache) 590 { 591 { 592 BenaphoreLocker _(sObjectCacheListLock); 593 sObjectCaches.Remove(cache); 594 } 595 596 benaphore_lock(&cache->lock); 597 598 if (!(cache->flags & CACHE_NO_DEPOT)) 599 object_depot_destroy(&cache->depot); 600 601 unregister_low_memory_handler(object_cache_low_memory, cache); 602 603 if (!cache->full.IsEmpty()) 604 panic("cache destroy: still has full slabs"); 605 606 if (!cache->partial.IsEmpty()) 607 panic("cache destroy: still has partial slabs"); 608 609 while (!cache->empty.IsEmpty()) 610 cache->ReturnSlab(cache->empty.RemoveHead()); 611 612 benaphore_destroy(&cache->lock); 613 delete_cache(cache); 614 } 615 616 617 void * 618 object_cache_alloc(object_cache *cache, uint32 flags) 619 { 620 // flags are read only. 621 if (!(cache->flags & CACHE_NO_DEPOT)) { 622 void *object = object_depot_obtain(&cache->depot); 623 if (object) 624 return object; 625 } 626 627 BenaphoreLocker _(cache->lock); 628 slab *source; 629 630 if (cache->partial.IsEmpty()) { 631 if (cache->empty.IsEmpty()) { 632 if (object_cache_reserve_internal(cache, 1, flags) < B_OK) 633 return NULL; 634 635 cache->pressure++; 636 } 637 638 source = cache->empty.RemoveHead(); 639 cache->empty_count--; 640 cache->partial.Add(source); 641 } else { 642 source = cache->partial.Head(); 643 } 644 645 object_link *link = _pop(source->free); 646 source->count--; 647 cache->used_count++; 648 649 TRACE_CACHE(cache, "allocate %p (%p) from %p, %lu remaining.", 650 link_to_object(link, cache->object_size), link, source, source->count); 651 652 if (source->count == 0) { 653 cache->partial.Remove(source); 654 cache->full.Add(source); 655 } 656 657 return link_to_object(link, cache->object_size); 658 } 659 660 661 static void 662 object_cache_return_to_slab(object_cache *cache, slab *source, void *object) 663 { 664 if (source == NULL) 665 panic("object_cache: free'd object has no slab"); 666 667 object_link *link = object_to_link(object, cache->object_size); 668 669 TRACE_CACHE(cache, "returning %p (%p) to %p, %lu used (%lu empty slabs).", 670 object, link, source, source->size - source->count, 671 cache->empty_count); 672 673 _push(source->free, link); 674 source->count++; 675 cache->used_count--; 676 677 if (source->count == source->size) { 678 cache->partial.Remove(source); 679 680 if (cache->empty_count < cache->pressure) { 681 cache->empty_count++; 682 cache->empty.Add(source); 683 } else { 684 cache->ReturnSlab(source); 685 } 686 } else if (source->count == 1) { 687 cache->full.Remove(source); 688 cache->partial.Add(source); 689 } 690 } 691 692 693 void 694 object_cache_free(object_cache *cache, void *object) 695 { 696 // flags are read only. 697 if (!(cache->flags & CACHE_NO_DEPOT)) { 698 if (object_depot_store(&cache->depot, object)) 699 return; 700 } 701 702 BenaphoreLocker _(cache->lock); 703 object_cache_return_to_slab(cache, cache->ObjectSlab(object), object); 704 } 705 706 707 static status_t 708 object_cache_reserve_internal(object_cache *cache, size_t object_count, 709 uint32 flags) 710 { 711 size_t numBytes = object_count * cache->object_size; 712 size_t slabCount = ((numBytes - 1) / cache->slab_size) + 1; 713 714 while (slabCount > 0) { 715 slab *newSlab = cache->CreateSlab(flags); 716 if (newSlab == NULL) 717 return B_NO_MEMORY; 718 719 cache->empty.Add(newSlab); 720 cache->empty_count++; 721 slabCount--; 722 } 723 724 return B_OK; 725 } 726 727 728 status_t 729 object_cache_reserve(object_cache *cache, size_t object_count, uint32 flags) 730 { 731 if (object_count == 0) 732 return B_OK; 733 734 BenaphoreLocker _(cache->lock); 735 return object_cache_reserve_internal(cache, object_count, flags); 736 } 737 738 739 slab * 740 object_cache::InitSlab(slab *slab, void *pages, size_t byteCount) 741 { 742 TRACE_CACHE(this, "construct (%p, %p .. %p, %lu)", slab, pages, 743 ((uint8 *)pages) + byteCount, byteCount); 744 745 slab->pages = pages; 746 slab->count = slab->size = byteCount / object_size; 747 slab->free = NULL; 748 749 size_t spareBytes = byteCount - (slab->size * object_size); 750 slab->offset = cache_color_cycle; 751 752 if (slab->offset > spareBytes) 753 cache_color_cycle = slab->offset = 0; 754 else 755 cache_color_cycle += kCacheColorPeriod; 756 757 TRACE_CACHE(this, " %lu objects, %lu spare bytes, offset %lu", 758 slab->size, spareBytes, slab->offset); 759 760 uint8 *data = ((uint8 *)pages) + slab->offset; 761 762 for (size_t i = 0; i < slab->size; i++) { 763 bool failedOnFirst = false; 764 765 status_t status = PrepareObject(slab, data); 766 if (status < B_OK) 767 failedOnFirst = true; 768 else if (constructor) 769 status = constructor(cookie, data); 770 771 if (status < B_OK) { 772 if (!failedOnFirst) 773 UnprepareObject(slab, data); 774 775 data = ((uint8 *)pages) + slab->offset; 776 for (size_t j = 0; j < i; j++) { 777 if (destructor) 778 destructor(cookie, data); 779 UnprepareObject(slab, data); 780 data += object_size; 781 } 782 783 return NULL; 784 } 785 786 _push(slab->free, object_to_link(data, object_size)); 787 data += object_size; 788 } 789 790 return slab; 791 } 792 793 794 void 795 object_cache::UninitSlab(slab *slab) 796 { 797 TRACE_CACHE(this, "destruct %p", slab); 798 799 if (slab->count != slab->size) 800 panic("cache: destroying a slab which isn't empty."); 801 802 uint8 *data = ((uint8 *)slab->pages) + slab->offset; 803 804 for (size_t i = 0; i < slab->size; i++) { 805 if (destructor) 806 destructor(cookie, data); 807 UnprepareObject(slab, data); 808 data += object_size; 809 } 810 } 811 812 813 static inline slab * 814 slab_in_pages(const void *pages, size_t slab_size) 815 { 816 return (slab *)(((uint8 *)pages) + slab_size - sizeof(slab)); 817 } 818 819 820 static inline const void * 821 lower_boundary(void *object, size_t byteCount) 822 { 823 const uint8 *null = (uint8 *)NULL; 824 return null + ((((uint8 *)object) - null) & ~(byteCount - 1)); 825 } 826 827 828 static inline bool 829 check_cache_quota(object_cache *cache) 830 { 831 if (cache->maximum == 0) 832 return true; 833 834 return (cache->usage + cache->slab_size) <= cache->maximum; 835 } 836 837 838 slab * 839 SmallObjectCache::CreateSlab(uint32 flags) 840 { 841 if (!check_cache_quota(this)) 842 return NULL; 843 844 void *pages; 845 846 if (allocate_pages(this, &pages, flags) < B_OK) 847 return NULL; 848 849 return InitSlab(slab_in_pages(pages, slab_size), pages, 850 slab_size - sizeof(slab)); 851 } 852 853 854 void 855 SmallObjectCache::ReturnSlab(slab *slab) 856 { 857 UninitSlab(slab); 858 free_pages(this, slab->pages); 859 } 860 861 862 slab * 863 SmallObjectCache::ObjectSlab(void *object) const 864 { 865 return slab_in_pages(lower_boundary(object, slab_size), slab_size); 866 } 867 868 869 static slab * 870 allocate_slab(uint32 flags) 871 { 872 return (slab *)internal_alloc(sizeof(slab), flags); 873 } 874 875 876 static void 877 free_slab(slab *slab) 878 { 879 internal_free(slab); 880 } 881 882 883 static HashedObjectCache::Link * 884 allocate_link(uint32 flags) 885 { 886 return (HashedObjectCache::Link *) 887 internal_alloc(sizeof(HashedObjectCache::Link), flags); 888 } 889 890 891 static void 892 free_link(HashedObjectCache::Link *link) 893 { 894 internal_free(link); 895 } 896 897 898 slab * 899 HashedObjectCache::CreateSlab(uint32 flags) 900 { 901 if (!check_cache_quota(this)) 902 return NULL; 903 904 slab *slab = allocate_slab(flags); 905 if (slab == NULL) 906 return NULL; 907 908 void *pages; 909 910 if (allocate_pages(this, &pages, flags) == B_OK) { 911 if (InitSlab(slab, pages, slab_size)) 912 return slab; 913 914 free_pages(this, pages); 915 } 916 917 free_slab(slab); 918 return NULL; 919 } 920 921 922 void 923 HashedObjectCache::ReturnSlab(slab *slab) 924 { 925 UninitSlab(slab); 926 free_pages(this, slab->pages); 927 } 928 929 930 slab * 931 HashedObjectCache::ObjectSlab(void *object) const 932 { 933 Link *link = hash_table.Lookup(object); 934 if (link == NULL) 935 panic("object cache: requested object missing from hash table"); 936 return link->parent; 937 } 938 939 940 status_t 941 HashedObjectCache::PrepareObject(slab *source, void *object) 942 { 943 Link *link = allocate_link(CACHE_DONT_SLEEP); 944 if (link == NULL) 945 return B_NO_MEMORY; 946 947 link->buffer = object; 948 link->parent = source; 949 950 hash_table.Insert(link); 951 return B_OK; 952 } 953 954 955 void 956 HashedObjectCache::UnprepareObject(slab *source, void *object) 957 { 958 Link *link = hash_table.Lookup(object); 959 if (link == NULL) 960 panic("object cache: requested object missing from hash table"); 961 if (link->parent != source) 962 panic("object cache: slab mismatch"); 963 964 hash_table.Remove(link); 965 free_link(link); 966 } 967 968 969 static inline bool 970 is_magazine_empty(depot_magazine *magazine) 971 { 972 return magazine->current_round == 0; 973 } 974 975 976 static inline bool 977 is_magazine_full(depot_magazine *magazine) 978 { 979 return magazine->current_round == magazine->round_count; 980 } 981 982 983 static inline void * 984 pop_magazine(depot_magazine *magazine) 985 { 986 return magazine->rounds[--magazine->current_round]; 987 } 988 989 990 static inline bool 991 push_magazine(depot_magazine *magazine, void *object) 992 { 993 if (is_magazine_full(magazine)) 994 return false; 995 magazine->rounds[magazine->current_round++] = object; 996 return true; 997 } 998 999 1000 static bool 1001 exchange_with_full(object_depot *depot, depot_magazine* &magazine) 1002 { 1003 BenaphoreLocker _(depot->lock); 1004 1005 if (depot->full == NULL) 1006 return false; 1007 1008 depot->full_count--; 1009 depot->empty_count++; 1010 1011 _push(depot->empty, magazine); 1012 magazine = _pop(depot->full); 1013 return true; 1014 } 1015 1016 1017 static bool 1018 exchange_with_empty(object_depot *depot, depot_magazine* &magazine) 1019 { 1020 BenaphoreLocker _(depot->lock); 1021 1022 if (depot->empty == NULL) { 1023 depot->empty = alloc_magazine(); 1024 if (depot->empty == NULL) 1025 return false; 1026 } else { 1027 depot->empty_count--; 1028 } 1029 1030 if (magazine) { 1031 _push(depot->full, magazine); 1032 depot->full_count++; 1033 } 1034 1035 magazine = _pop(depot->empty); 1036 return true; 1037 } 1038 1039 1040 static depot_magazine * 1041 alloc_magazine() 1042 { 1043 depot_magazine *magazine = (depot_magazine *)internal_alloc( 1044 sizeof(depot_magazine) + kMagazineCapacity * sizeof(void *), 0); 1045 if (magazine) { 1046 magazine->next = NULL; 1047 magazine->current_round = 0; 1048 magazine->round_count = kMagazineCapacity; 1049 } 1050 1051 return magazine; 1052 } 1053 1054 1055 static void 1056 free_magazine(depot_magazine *magazine) 1057 { 1058 internal_free(magazine); 1059 } 1060 1061 1062 static void 1063 empty_magazine(object_depot *depot, depot_magazine *magazine) 1064 { 1065 for (uint16 i = 0; i < magazine->current_round; i++) 1066 depot->return_object(depot, magazine->rounds[i]); 1067 free_magazine(magazine); 1068 } 1069 1070 1071 status_t 1072 object_depot_init(object_depot *depot, uint32 flags, 1073 void (*return_object)(object_depot *depot, void *object)) 1074 { 1075 depot->full = NULL; 1076 depot->empty = NULL; 1077 depot->full_count = depot->empty_count = 0; 1078 1079 status_t status = benaphore_boot_init(&depot->lock, "depot", flags); 1080 if (status < B_OK) 1081 return status; 1082 1083 depot->stores = (depot_cpu_store *)internal_alloc(sizeof(depot_cpu_store) 1084 * smp_get_num_cpus(), flags); 1085 if (depot->stores == NULL) { 1086 benaphore_destroy(&depot->lock); 1087 return B_NO_MEMORY; 1088 } 1089 1090 for (int i = 0; i < smp_get_num_cpus(); i++) { 1091 benaphore_boot_init(&depot->stores[i].lock, "cpu store", flags); 1092 depot->stores[i].loaded = depot->stores[i].previous = NULL; 1093 } 1094 1095 depot->return_object = return_object; 1096 1097 return B_OK; 1098 } 1099 1100 1101 status_t 1102 object_depot_init_locks(object_depot *depot) 1103 { 1104 status_t status = benaphore_init(&depot->lock, "depot"); 1105 if (status < B_OK) 1106 return status; 1107 1108 for (int i = 0; i < smp_get_num_cpus(); i++) { 1109 status = benaphore_init(&depot->stores[i].lock, "cpu store"); 1110 if (status < B_OK) 1111 return status; 1112 } 1113 1114 return B_OK; 1115 } 1116 1117 1118 void 1119 object_depot_destroy(object_depot *depot) 1120 { 1121 object_depot_make_empty(depot); 1122 1123 for (int i = 0; i < smp_get_num_cpus(); i++) { 1124 benaphore_destroy(&depot->stores[i].lock); 1125 } 1126 1127 internal_free(depot->stores); 1128 1129 benaphore_destroy(&depot->lock); 1130 } 1131 1132 1133 static void * 1134 object_depot_obtain_from_store(object_depot *depot, depot_cpu_store *store) 1135 { 1136 BenaphoreLocker _(store->lock); 1137 1138 // To better understand both the Alloc() and Free() logic refer to 1139 // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] 1140 1141 // In a nutshell, we try to get an object from the loaded magazine 1142 // if it's not empty, or from the previous magazine if it's full 1143 // and finally from the Slab if the magazine depot has no full magazines. 1144 1145 if (store->loaded == NULL) 1146 return NULL; 1147 1148 while (true) { 1149 if (!is_magazine_empty(store->loaded)) 1150 return pop_magazine(store->loaded); 1151 1152 if (store->previous && (is_magazine_full(store->previous) 1153 || exchange_with_full(depot, store->previous))) 1154 std::swap(store->previous, store->loaded); 1155 else 1156 return NULL; 1157 } 1158 } 1159 1160 1161 static int 1162 object_depot_return_to_store(object_depot *depot, depot_cpu_store *store, 1163 void *object) 1164 { 1165 BenaphoreLocker _(store->lock); 1166 1167 // We try to add the object to the loaded magazine if we have one 1168 // and it's not full, or to the previous one if it is empty. If 1169 // the magazine depot doesn't provide us with a new empty magazine 1170 // we return the object directly to the slab. 1171 1172 while (true) { 1173 if (store->loaded && push_magazine(store->loaded, object)) 1174 return 1; 1175 1176 if ((store->previous && is_magazine_empty(store->previous)) 1177 || exchange_with_empty(depot, store->previous)) 1178 std::swap(store->loaded, store->previous); 1179 else 1180 return 0; 1181 } 1182 } 1183 1184 1185 void * 1186 object_depot_obtain(object_depot *depot) 1187 { 1188 return object_depot_obtain_from_store(depot, object_depot_cpu(depot)); 1189 } 1190 1191 1192 int 1193 object_depot_store(object_depot *depot, void *object) 1194 { 1195 return object_depot_return_to_store(depot, object_depot_cpu(depot), 1196 object); 1197 } 1198 1199 1200 void 1201 object_depot_make_empty(object_depot *depot) 1202 { 1203 for (int i = 0; i < smp_get_num_cpus(); i++) { 1204 depot_cpu_store *store = &depot->stores[i]; 1205 1206 BenaphoreLocker _(store->lock); 1207 1208 if (depot->stores[i].loaded) 1209 empty_magazine(depot, depot->stores[i].loaded); 1210 if (depot->stores[i].previous) 1211 empty_magazine(depot, depot->stores[i].previous); 1212 depot->stores[i].loaded = depot->stores[i].previous = NULL; 1213 } 1214 1215 BenaphoreLocker _(depot->lock); 1216 1217 while (depot->full) 1218 empty_magazine(depot, _pop(depot->full)); 1219 1220 while (depot->empty) 1221 empty_magazine(depot, _pop(depot->empty)); 1222 } 1223 1224 1225 static int 1226 dump_slabs(int argc, char *argv[]) 1227 { 1228 kprintf("%10s %22s %8s %8s %6s %8s %8s %8s\n", "address", "name", 1229 "objsize", "usage", "empty", "usedobj", "total", "flags"); 1230 1231 ObjectCacheList::Iterator it = sObjectCaches.GetIterator(); 1232 1233 while (it.HasNext()) { 1234 object_cache *cache = it.Next(); 1235 1236 kprintf("%p %22s %8lu %8lu %6lu %8lu %8lu %8lx\n", cache, cache->name, 1237 cache->object_size, cache->usage, cache->empty_count, 1238 cache->used_count, cache->usage / cache->object_size, 1239 cache->flags); 1240 } 1241 1242 return 0; 1243 } 1244 1245 1246 static int 1247 dump_cache_info(int argc, char *argv[]) 1248 { 1249 if (argc < 2) { 1250 kprintf("usage: cache_info [address]\n"); 1251 return 0; 1252 } 1253 1254 object_cache *cache = (object_cache *)strtoul(argv[1], NULL, 16); 1255 1256 kprintf("name: %s\n", cache->name); 1257 kprintf("lock: { count: %ld, sem: %ld }\n", cache->lock.count, 1258 cache->lock.sem); 1259 kprintf("object_size: %lu\n", cache->object_size); 1260 kprintf("cache_color_cycle: %lu\n", cache->cache_color_cycle); 1261 kprintf("used_count: %lu\n", cache->used_count); 1262 kprintf("empty_count: %lu\n", cache->empty_count); 1263 kprintf("pressure: %lu\n", cache->pressure); 1264 kprintf("slab_size: %lu\n", cache->slab_size); 1265 kprintf("usage: %lu\n", cache->usage); 1266 kprintf("maximum: %lu\n", cache->maximum); 1267 kprintf("flags: 0x%lx\n", cache->flags); 1268 kprintf("cookie: %p\n", cache->cookie); 1269 1270 return 0; 1271 } 1272 1273 1274 void 1275 slab_init(kernel_args *args, addr_t initialBase, size_t initialSize) 1276 { 1277 dprintf("slab: init base %p + 0x%lx\n", (void *)initialBase, initialSize); 1278 1279 sInitialBegin = (uint8 *)initialBase; 1280 sInitialLimit = sInitialBegin + initialSize; 1281 sInitialPointer = sInitialBegin; 1282 1283 sKernelArgs = args; 1284 1285 new (&sObjectCaches) ObjectCacheList(); 1286 1287 block_allocator_init_boot(); 1288 1289 add_debugger_command("slabs", dump_slabs, "list all object caches"); 1290 add_debugger_command("cache_info", dump_cache_info, 1291 "dump information about a specific cache"); 1292 } 1293 1294 1295 void 1296 slab_init_post_sem() 1297 { 1298 status_t status = benaphore_init(&sObjectCacheListLock, "object cache list"); 1299 if (status < B_OK) 1300 panic("slab_init: failed to create object cache list lock"); 1301 1302 ObjectCacheList::Iterator it = sObjectCaches.GetIterator(); 1303 1304 while (it.HasNext()) { 1305 object_cache *cache = it.Next(); 1306 if (object_cache_init_locks(cache) < B_OK) 1307 panic("slab_init: failed to create sems"); 1308 if (cache->allocate_pages == early_allocate_pages) 1309 object_cache_commit_pre_pages(cache); 1310 } 1311 1312 block_allocator_init_rest(); 1313 } 1314 1315