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