xref: /haiku/src/tests/system/kernel/slab/Slab.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
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