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 <stdio.h> 12 #include <malloc.h> 13 14 15 // TODO this value should be dynamically tuned per cache. 16 static const int kMagazineCapacity = 32; 17 18 static const size_t kCacheColorPeriod = 8; 19 20 21 template<typename Type> static Type * 22 SListPop(Type* &head) 23 { 24 Type *oldHead = head; 25 head = head->next; 26 return oldHead; 27 } 28 29 30 template<typename Type> static inline void 31 SListPush(Type* &head, Type *object) 32 { 33 object->next = head; 34 head = object; 35 } 36 37 38 status_t 39 MallocBackend::AllocatePages(BaseCache *cache, AllocationID *id, void **pages, 40 size_t byteCount, uint32_t flags) 41 { 42 size_t alignment = 16; 43 44 if (flags & CACHE_ALIGN_TO_PAGE_TOTAL) 45 alignment = byteCount; 46 47 *pages = memalign(alignment, byteCount); 48 if (*pages == NULL) 49 return B_NO_MEMORY; 50 51 *id = *pages; 52 return B_OK; 53 } 54 55 void 56 MallocBackend::FreePages(BaseCache *cache, void *pages) 57 { 58 free(pages); 59 } 60 61 62 status_t 63 AreaBackend::AllocatePages(BaseCache *cache, area_id *id, void **pages, 64 size_t byteCount, uint32_t flags) 65 { 66 if (flags & CACHE_ALIGN_TO_PAGE_TOTAL) 67 ; // panic() 68 69 area_id areaId = create_area(cache->Name(), pages, B_ANY_ADDRESS, //B_ANY_KERNEL_ADDRESS, 70 byteCount, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 71 if (areaId < 0) 72 return areaId; 73 74 printf("AreaBackend::AllocatePages() = { %ld, %p }\n", areaId, *pages); 75 76 *id = areaId; 77 return B_OK; 78 } 79 80 void 81 AreaBackend::FreePages(BaseCache *cache, area_id area) 82 { 83 printf("AreaBackend::DeletePages(%ld)\n", area); 84 delete_area(area); 85 } 86 87 88 BaseCache::BaseCache(const char *_name, size_t objectSize, size_t alignment, 89 Constructor _constructor, Destructor _destructor, void *_cookie) 90 : fConstructor(_constructor), fDestructor(_destructor), fCookie(_cookie) 91 { 92 strncpy(fName, _name, sizeof(fName)); 93 fName[sizeof(fName) - 1] = 0; 94 95 if (alignment > 0 && (objectSize & (alignment - 1))) 96 fObjectSize = objectSize + alignment - (objectSize & (alignment - 1)); 97 else 98 fObjectSize = objectSize; 99 100 fCacheColorCycle = 0; 101 } 102 103 104 BaseCache::ObjectLink *BaseCache::AllocateObject() 105 { 106 Slab *slab = fPartialSlabs.Head(); 107 108 printf("BaseCache::AllocateObject() from %p, %lu remaining\n", 109 slab, slab->count); 110 111 ObjectLink *link = SListPop(slab->free); 112 slab->count--; 113 if (slab->count == 0) { 114 // move the partial slab to the full list 115 fPartialSlabs.Remove(slab); 116 fFullSlabs.Add(slab); 117 } 118 119 return link; 120 } 121 122 123 bool 124 BaseCache::ReturnObject(const ObjectInfo &object) 125 { 126 Slab *slab = object.first; 127 ObjectLink *link = object.second; 128 129 // We return true if the slab is completely unused. 130 131 SListPush(slab->free, link); 132 slab->count++; 133 if (slab->count == slab->size) { 134 fPartialSlabs.Remove(slab); 135 return true; 136 } else if (slab->count == 1) { 137 fFullSlabs.Remove(slab); 138 fPartialSlabs.Add(slab); 139 } 140 141 return false; 142 } 143 144 145 BaseCache::Slab * 146 BaseCache::ConstructSlab(Slab *slab, void *pages, size_t byteCount, 147 ObjectLink *(*getLink)(void *parent, void *object), void *parent) 148 { 149 printf("BaseCache::ConstructSlab(%p, %p, %lu, %p, %p)\n", slab, pages, 150 byteCount, getLink, parent); 151 152 slab->pages = pages; 153 slab->count = slab->size = byteCount / fObjectSize; 154 slab->free = NULL; 155 156 size_t spareBytes = byteCount - (slab->size * fObjectSize); 157 size_t cycle = fCacheColorCycle; 158 159 if (cycle > spareBytes) 160 cycle = 0; 161 else 162 fCacheColorCycle += kCacheColorPeriod; 163 164 printf(" %lu objects, %lu spare bytes, cycle %lu\n", 165 slab->size, spareBytes, cycle); 166 167 uint8_t *data = ((uint8_t *)pages) + cycle; 168 169 for (size_t i = 0; i < slab->size; i++) { 170 if (fConstructor) 171 fConstructor(fCookie, data); 172 SListPush(slab->free, getLink(parent, data)); 173 data += fObjectSize; 174 } 175 176 return slab; 177 } 178 179 180 void 181 BaseCache::DestructSlab(Slab *slab) 182 { 183 if (fDestructor == NULL) 184 return; 185 186 uint8_t *data = (uint8_t *)slab->pages; 187 188 for (size_t i = 0; i < slab->size; i++) { 189 fDestructor(fCookie, data); 190 data += fObjectSize; 191 } 192 } 193 194 195 static inline bool 196 _IsMagazineEmpty(BaseDepot::Magazine *magazine) 197 { 198 return magazine->current_round == 0; 199 } 200 201 202 static inline bool 203 _IsMagazineFull(BaseDepot::Magazine *magazine) 204 { 205 return magazine->current_round == magazine->round_count; 206 } 207 208 209 static inline void * 210 _PopMagazine(BaseDepot::Magazine *magazine) 211 { 212 return magazine->rounds[--magazine->current_round]; 213 } 214 215 216 static inline bool 217 _PushMagazine(BaseDepot::Magazine *magazine, void *object) 218 { 219 if (_IsMagazineFull(magazine)) 220 return false; 221 magazine->rounds[magazine->current_round++] = object; 222 return true; 223 } 224 225 226 BaseDepot::BaseDepot() 227 : fFull(NULL), fEmpty(NULL), fFullCount(0), fEmptyCount(0) 228 { 229 // benaphore_init(...) 230 fStores = new (std::nothrow) CPUStore[smp_get_num_cpus()]; 231 232 if (fStores) { 233 for (int i = 0; i < smp_get_num_cpus(); i++) { 234 // benaphore_init(...) 235 fStores[i].loaded = fStores[i].previous = NULL; 236 } 237 } 238 } 239 240 241 BaseDepot::~BaseDepot() 242 { 243 // MakeEmpty may not be used here as ReturnObject is 244 // no longer available by then. 245 246 delete [] fStores; 247 248 // benaphore_destroy() 249 } 250 251 252 status_t 253 BaseDepot::InitCheck() const 254 { 255 return fStores ? B_OK : B_NO_MEMORY; 256 } 257 258 259 void * 260 BaseDepot::ObtainFromStore(CPUStore *store) 261 { 262 BenaphoreLocker _(store->lock); 263 264 // To better understand both the Alloc() and Free() logic refer to 265 // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] 266 267 // In a nutshell, we try to get an object from the loaded magazine 268 // if it's not empty, or from the previous magazine if it's full 269 // and finally from the Slab if the magazine depot has no full magazines. 270 271 if (store->loaded == NULL) 272 return NULL; 273 274 while (true) { 275 if (!_IsMagazineEmpty(store->loaded)) 276 return _PopMagazine(store->loaded); 277 278 if (store->previous && (_IsMagazineFull(store->previous) 279 || _ExchangeWithFull(store->previous))) 280 std::swap(store->previous, store->loaded); 281 else 282 return NULL; 283 } 284 } 285 286 287 bool 288 BaseDepot::ReturnToStore(CPUStore *store, void *object) 289 { 290 BenaphoreLocker _(store->lock); 291 292 // We try to add the object to the loaded magazine if we have one 293 // and it's not full, or to the previous one if it is empty. If 294 // the magazine depot doesn't provide us with a new empty magazine 295 // we return the object directly to the slab. 296 297 while (true) { 298 if (store->loaded && _PushMagazine(store->loaded, object)) 299 return true; 300 301 if ((store->previous && _IsMagazineEmpty(store->previous)) 302 || _ExchangeWithEmpty(store->previous)) 303 std::swap(store->loaded, store->previous); 304 else 305 return false; 306 } 307 } 308 309 310 void 311 BaseDepot::MakeEmpty() 312 { 313 for (int i = 0; i < smp_get_num_cpus(); i++) { 314 if (fStores[i].loaded) 315 _EmptyMagazine(fStores[i].loaded); 316 if (fStores[i].previous) 317 _EmptyMagazine(fStores[i].previous); 318 fStores[i].loaded = fStores[i].previous = NULL; 319 } 320 321 while (fFull) 322 _EmptyMagazine(SListPop(fFull)); 323 324 while (fEmpty) 325 _EmptyMagazine(SListPop(fEmpty)); 326 } 327 328 329 bool 330 BaseDepot::_ExchangeWithFull(Magazine* &magazine) 331 { 332 BenaphoreLocker _(fLock); 333 334 if (fFull == NULL) 335 return false; 336 337 fFullCount--; 338 fEmptyCount++; 339 340 SListPush(fEmpty, magazine); 341 magazine = SListPop(fFull); 342 return true; 343 } 344 345 346 bool 347 BaseDepot::_ExchangeWithEmpty(Magazine* &magazine) 348 { 349 BenaphoreLocker _(fLock); 350 351 if (fEmpty == NULL) { 352 fEmpty = _AllocMagazine(); 353 if (fEmpty == NULL) 354 return false; 355 } else { 356 fEmptyCount--; 357 } 358 359 if (magazine) { 360 SListPush(fFull, magazine); 361 fFullCount++; 362 } 363 364 magazine = SListPop(fEmpty); 365 return true; 366 } 367 368 369 void 370 BaseDepot::_EmptyMagazine(Magazine *magazine) 371 { 372 for (uint16_t i = 0; i < magazine->current_round; i++) 373 ReturnObject(magazine->rounds[i]); 374 _FreeMagazine(magazine); 375 } 376 377 378 BaseDepot::Magazine * 379 BaseDepot::_AllocMagazine() 380 { 381 Magazine *magazine = (Magazine *)malloc(sizeof(Magazine) 382 + kMagazineCapacity * sizeof(void *)); 383 if (magazine) { 384 magazine->next = NULL; 385 magazine->current_round = 0; 386 magazine->round_count = kMagazineCapacity; 387 } 388 389 return magazine; 390 } 391 392 393 void 394 BaseDepot::_FreeMagazine(Magazine *magazine) 395 { 396 free(magazine); 397 } 398 399 400 401 typedef MergedLinkCacheStrategy<MallocBackend> MallocMergedCacheStrategy; 402 typedef Cache<MallocMergedCacheStrategy> MallocMergedCache; 403 typedef LocalCache<MallocMergedCache> MallocLocalCache; 404 405 typedef HashCacheStrategy<MallocBackend> MallocHashCacheStrategy; 406 typedef Cache<MallocHashCacheStrategy> MallocHashCache; 407 408 object_cache_t 409 object_cache_create(const char *name, size_t object_size, size_t alignment, 410 void (*_constructor)(void *, void *), void (*_destructor)(void *, void *), 411 void *cookie) 412 { 413 return new (std::nothrow) MallocLocalCache(name, object_size, alignment, 414 _constructor, _destructor, cookie); 415 } 416 417 418 void * 419 object_cache_alloc(object_cache_t cache) 420 { 421 return ((MallocLocalCache *)cache)->Alloc(0); 422 } 423 424 425 void * 426 object_cache_alloc_etc(object_cache_t cache, uint32_t flags) 427 { 428 return ((MallocLocalCache *)cache)->Alloc(flags); 429 } 430 431 432 void 433 object_cache_free(object_cache_t cache, void *object) 434 { 435 ((MallocLocalCache *)cache)->Free(object); 436 } 437 438 439 void 440 object_cache_destroy(object_cache_t cache) 441 { 442 delete (MallocLocalCache *)cache; 443 } 444 445 446 void test1() 447 { 448 MallocLocalCache cache("foobar", sizeof(int), 0, NULL, NULL, NULL); 449 450 static const int N = 4096; 451 void *buf[N]; 452 453 for (int i = 0; i < N; i++) 454 buf[i] = cache.Alloc(0); 455 456 for (int i = 0; i < N; i++) 457 cache.Free(buf[i]); 458 459 cache.Destroy(); 460 } 461 462 void test2() 463 { 464 TypedCache<int, MallocBackend> cache("int cache", 0); 465 466 static const int N = 4096; 467 int *buf[N]; 468 469 for (int i = 0; i < N; i++) 470 buf[i] = cache.Alloc(0); 471 472 for (int i = 0; i < N; i++) 473 cache.Free(buf[i]); 474 } 475 476 void test3() 477 { 478 Cache<HashCacheStrategy<AreaBackend> > cache("512byte hash cache", 512, 0, NULL, 479 NULL, NULL); 480 481 static const int N = 128; 482 void *buf[N]; 483 484 for (int i = 0; i < N; i++) 485 buf[i] = cache.AllocateObject(0); 486 487 for (int i = 0; i < N; i++) 488 cache.ReturnObject(buf[i]); 489 } 490 491 void test4() 492 { 493 LocalCache<MallocHashCache> cache("foobar", 512, 0, NULL, NULL, NULL); 494 495 static const int N = 128; 496 void *buf[N]; 497 498 for (int i = 0; i < N; i++) 499 buf[i] = cache.Alloc(0); 500 501 for (int i = 0; i < N; i++) 502 cache.Free(buf[i]); 503 504 cache.Destroy(); 505 } 506 507 void test5() 508 { 509 object_cache_t cache = object_cache_create("foobar", 16, 0, 510 NULL, NULL, NULL); 511 512 static const int N = 1024; 513 void *buf[N]; 514 515 for (int i = 0; i < N; i++) 516 buf[i] = object_cache_alloc(cache); 517 518 for (int i = 0; i < N; i++) 519 object_cache_free(cache, buf[i]); 520 521 object_cache_destroy(cache); 522 } 523 524 525 int main() 526 { 527 //test1(); 528 //test2(); 529 test3(); 530 //test4(); 531 //test5(); 532 return 0; 533 } 534