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 *
SListPop(Type * & head)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
SListPush(Type * & head,Type * object)31 SListPush(Type* &head, Type *object)
32 {
33 object->next = head;
34 head = object;
35 }
36
37
38 status_t
AllocatePages(BaseCache * cache,AllocationID * id,void ** pages,size_t byteCount,uint32_t flags)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
FreePages(BaseCache * cache,void * pages)56 MallocBackend::FreePages(BaseCache *cache, void *pages)
57 {
58 free(pages);
59 }
60
61
62 status_t
AllocatePages(BaseCache * cache,area_id * id,void ** pages,size_t byteCount,uint32_t flags)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
FreePages(BaseCache * cache,area_id area)81 AreaBackend::FreePages(BaseCache *cache, area_id area)
82 {
83 printf("AreaBackend::DeletePages(%ld)\n", area);
84 delete_area(area);
85 }
86
87
BaseCache(const char * _name,size_t objectSize,size_t alignment,Constructor _constructor,Destructor _destructor,void * _cookie)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
AllocateObject()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
ReturnObject(const ObjectInfo & object)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 *
ConstructSlab(Slab * slab,void * pages,size_t byteCount,ObjectLink * (* getLink)(void * parent,void * object),void * parent)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
DestructSlab(Slab * slab)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
_IsMagazineEmpty(BaseDepot::Magazine * magazine)196 _IsMagazineEmpty(BaseDepot::Magazine *magazine)
197 {
198 return magazine->current_round == 0;
199 }
200
201
202 static inline bool
_IsMagazineFull(BaseDepot::Magazine * magazine)203 _IsMagazineFull(BaseDepot::Magazine *magazine)
204 {
205 return magazine->current_round == magazine->round_count;
206 }
207
208
209 static inline void *
_PopMagazine(BaseDepot::Magazine * magazine)210 _PopMagazine(BaseDepot::Magazine *magazine)
211 {
212 return magazine->rounds[--magazine->current_round];
213 }
214
215
216 static inline bool
_PushMagazine(BaseDepot::Magazine * magazine,void * object)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
BaseDepot()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
~BaseDepot()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
InitCheck() const253 BaseDepot::InitCheck() const
254 {
255 return fStores ? B_OK : B_NO_MEMORY;
256 }
257
258
259 void *
ObtainFromStore(CPUStore * store)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
ReturnToStore(CPUStore * store,void * object)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
MakeEmpty()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
_ExchangeWithFull(Magazine * & magazine)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
_ExchangeWithEmpty(Magazine * & magazine)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
_EmptyMagazine(Magazine * magazine)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 *
_AllocMagazine()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
_FreeMagazine(Magazine * magazine)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
object_cache_create(const char * name,size_t object_size,size_t alignment,void (* _constructor)(void *,void *),void (* _destructor)(void *,void *),void * cookie)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 *
object_cache_alloc(object_cache_t cache)419 object_cache_alloc(object_cache_t cache)
420 {
421 return ((MallocLocalCache *)cache)->Alloc(0);
422 }
423
424
425 void *
object_cache_alloc_etc(object_cache_t cache,uint32_t flags)426 object_cache_alloc_etc(object_cache_t cache, uint32_t flags)
427 {
428 return ((MallocLocalCache *)cache)->Alloc(flags);
429 }
430
431
432 void
object_cache_free(object_cache_t cache,void * object)433 object_cache_free(object_cache_t cache, void *object)
434 {
435 ((MallocLocalCache *)cache)->Free(object);
436 }
437
438
439 void
object_cache_destroy(object_cache_t cache)440 object_cache_destroy(object_cache_t cache)
441 {
442 delete (MallocLocalCache *)cache;
443 }
444
445
test1()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
test2()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
test3()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
test4()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
test5()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
main()525 int main()
526 {
527 //test1();
528 //test2();
529 test3();
530 //test4();
531 //test5();
532 return 0;
533 }
534