xref: /haiku/src/system/kernel/slab/ObjectDepot.cpp (revision a84e14ca84d32e9469c91372d71556488bd3d48b)
1 /*
2  * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2008-2010, Axel Dörfler. All Rights Reserved.
4  * Copyright 2007, Hugo Santos. All Rights Reserved.
5  *
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include <slab/ObjectDepot.h>
11 
12 #include <algorithm>
13 
14 #include <int.h>
15 #include <slab/Slab.h>
16 #include <smp.h>
17 #include <util/AutoLock.h>
18 
19 #include "slab_debug.h"
20 #include "slab_private.h"
21 
22 
23 struct DepotMagazine {
24 			DepotMagazine*		next;
25 			uint16				current_round;
26 			uint16				round_count;
27 			void*				rounds[0];
28 
29 public:
30 	inline	bool				IsEmpty() const;
31 	inline	bool				IsFull() const;
32 
33 	inline	void*				Pop();
34 	inline	bool				Push(void* object);
35 
36 #if PARANOID_KERNEL_FREE
37 			bool				ContainsObject(void* object) const;
38 #endif
39 };
40 
41 
42 struct depot_cpu_store {
43 	DepotMagazine*	loaded;
44 	DepotMagazine*	previous;
45 };
46 
47 
RANGE_MARKER_FUNCTION_BEGIN(SlabObjectDepot)48 RANGE_MARKER_FUNCTION_BEGIN(SlabObjectDepot)
49 
50 
51 bool
52 DepotMagazine::IsEmpty() const
53 {
54 	return current_round == 0;
55 }
56 
57 
58 bool
IsFull() const59 DepotMagazine::IsFull() const
60 {
61 	return current_round == round_count;
62 }
63 
64 
65 void*
Pop()66 DepotMagazine::Pop()
67 {
68 	return rounds[--current_round];
69 }
70 
71 
72 bool
Push(void * object)73 DepotMagazine::Push(void* object)
74 {
75 	if (IsFull())
76 		return false;
77 
78 	rounds[current_round++] = object;
79 	return true;
80 }
81 
82 
83 #if PARANOID_KERNEL_FREE
84 
85 bool
ContainsObject(void * object) const86 DepotMagazine::ContainsObject(void* object) const
87 {
88 	for (uint16 i = 0; i < current_round; i++) {
89 		if (rounds[i] == object)
90 			return true;
91 	}
92 
93 	return false;
94 }
95 
96 #endif // PARANOID_KERNEL_FREE
97 
98 
99 // #pragma mark -
100 
101 
102 static DepotMagazine*
alloc_magazine(object_depot * depot,uint32 flags)103 alloc_magazine(object_depot* depot, uint32 flags)
104 {
105 	DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc(
106 		sizeof(DepotMagazine) + depot->magazine_capacity * sizeof(void*),
107 		flags);
108 	if (magazine) {
109 		magazine->next = NULL;
110 		magazine->current_round = 0;
111 		magazine->round_count = depot->magazine_capacity;
112 	}
113 
114 	return magazine;
115 }
116 
117 
118 static void
free_magazine(DepotMagazine * magazine,uint32 flags)119 free_magazine(DepotMagazine* magazine, uint32 flags)
120 {
121 	slab_internal_free(magazine, flags);
122 }
123 
124 
125 static void
empty_magazine(object_depot * depot,DepotMagazine * magazine,uint32 flags)126 empty_magazine(object_depot* depot, DepotMagazine* magazine, uint32 flags)
127 {
128 	for (uint16 i = 0; i < magazine->current_round; i++)
129 		depot->return_object(depot, depot->cookie, magazine->rounds[i], flags);
130 	free_magazine(magazine, flags);
131 }
132 
133 
134 static bool
exchange_with_full(object_depot * depot,DepotMagazine * & magazine)135 exchange_with_full(object_depot* depot, DepotMagazine*& magazine)
136 {
137 	ASSERT(magazine->IsEmpty());
138 
139 	SpinLocker _(depot->inner_lock);
140 
141 	if (depot->full == NULL)
142 		return false;
143 
144 	depot->full_count--;
145 	depot->empty_count++;
146 
147 	_push(depot->empty, magazine);
148 	magazine = _pop(depot->full);
149 	return true;
150 }
151 
152 
153 static bool
exchange_with_empty(object_depot * depot,DepotMagazine * & magazine,DepotMagazine * & freeMagazine)154 exchange_with_empty(object_depot* depot, DepotMagazine*& magazine,
155 	DepotMagazine*& freeMagazine)
156 {
157 	ASSERT(magazine == NULL || magazine->IsFull());
158 
159 	SpinLocker _(depot->inner_lock);
160 
161 	if (depot->empty == NULL)
162 		return false;
163 
164 	depot->empty_count--;
165 
166 	if (magazine != NULL) {
167 		if (depot->full_count < depot->max_count) {
168 			_push(depot->full, magazine);
169 			depot->full_count++;
170 			freeMagazine = NULL;
171 		} else
172 			freeMagazine = magazine;
173 	}
174 
175 	magazine = _pop(depot->empty);
176 	return true;
177 }
178 
179 
180 static void
push_empty_magazine(object_depot * depot,DepotMagazine * magazine)181 push_empty_magazine(object_depot* depot, DepotMagazine* magazine)
182 {
183 	SpinLocker _(depot->inner_lock);
184 
185 	_push(depot->empty, magazine);
186 	depot->empty_count++;
187 }
188 
189 
190 static inline depot_cpu_store*
object_depot_cpu(object_depot * depot)191 object_depot_cpu(object_depot* depot)
192 {
193 	return &depot->stores[smp_get_current_cpu()];
194 }
195 
196 
197 // #pragma mark - public API
198 
199 
200 status_t
object_depot_init(object_depot * depot,size_t capacity,size_t maxCount,uint32 flags,void * cookie,void (* return_object)(object_depot * depot,void * cookie,void * object,uint32 flags))201 object_depot_init(object_depot* depot, size_t capacity, size_t maxCount,
202 	uint32 flags, void* cookie, void (*return_object)(object_depot* depot,
203 		void* cookie, void* object, uint32 flags))
204 {
205 	depot->full = NULL;
206 	depot->empty = NULL;
207 	depot->full_count = depot->empty_count = 0;
208 	depot->max_count = maxCount;
209 	depot->magazine_capacity = capacity;
210 
211 	rw_lock_init(&depot->outer_lock, "object depot");
212 	B_INITIALIZE_SPINLOCK(&depot->inner_lock);
213 
214 	int cpuCount = smp_get_num_cpus();
215 	depot->stores = (depot_cpu_store*)slab_internal_alloc(
216 		sizeof(depot_cpu_store) * cpuCount, flags);
217 	if (depot->stores == NULL) {
218 		rw_lock_destroy(&depot->outer_lock);
219 		return B_NO_MEMORY;
220 	}
221 
222 	for (int i = 0; i < cpuCount; i++) {
223 		depot->stores[i].loaded = NULL;
224 		depot->stores[i].previous = NULL;
225 	}
226 
227 	depot->cookie = cookie;
228 	depot->return_object = return_object;
229 
230 	return B_OK;
231 }
232 
233 
234 void
object_depot_destroy(object_depot * depot,uint32 flags)235 object_depot_destroy(object_depot* depot, uint32 flags)
236 {
237 	object_depot_make_empty(depot, flags);
238 
239 	slab_internal_free(depot->stores, flags);
240 
241 	rw_lock_destroy(&depot->outer_lock);
242 }
243 
244 
245 void*
object_depot_obtain(object_depot * depot)246 object_depot_obtain(object_depot* depot)
247 {
248 	ReadLocker readLocker(depot->outer_lock);
249 	InterruptsLocker interruptsLocker;
250 
251 	depot_cpu_store* store = object_depot_cpu(depot);
252 
253 	// To better understand both the Alloc() and Free() logic refer to
254 	// Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings]
255 
256 	// In a nutshell, we try to get an object from the loaded magazine
257 	// if it's not empty, or from the previous magazine if it's full
258 	// and finally from the Slab if the magazine depot has no full magazines.
259 
260 	if (store->loaded == NULL)
261 		return NULL;
262 
263 	while (true) {
264 		if (!store->loaded->IsEmpty())
265 			return store->loaded->Pop();
266 
267 		if (store->previous
268 			&& (store->previous->IsFull()
269 				|| exchange_with_full(depot, store->previous))) {
270 			std::swap(store->previous, store->loaded);
271 		} else
272 			return NULL;
273 	}
274 }
275 
276 
277 void
object_depot_store(object_depot * depot,void * object,uint32 flags)278 object_depot_store(object_depot* depot, void* object, uint32 flags)
279 {
280 	ReadLocker readLocker(depot->outer_lock);
281 	InterruptsLocker interruptsLocker;
282 
283 	depot_cpu_store* store = object_depot_cpu(depot);
284 
285 	// We try to add the object to the loaded magazine if we have one
286 	// and it's not full, or to the previous one if it is empty. If
287 	// the magazine depot doesn't provide us with a new empty magazine
288 	// we return the object directly to the slab.
289 
290 	while (true) {
291 		if (store->loaded != NULL && store->loaded->Push(object))
292 			return;
293 
294 		DepotMagazine* freeMagazine = NULL;
295 		if ((store->previous != NULL && store->previous->IsEmpty())
296 			|| exchange_with_empty(depot, store->previous, freeMagazine)) {
297 			std::swap(store->loaded, store->previous);
298 
299 			if (freeMagazine != NULL) {
300 				// Free the magazine that didn't have space in the list
301 				interruptsLocker.Unlock();
302 				readLocker.Unlock();
303 
304 				empty_magazine(depot, freeMagazine, flags);
305 
306 				readLocker.Lock();
307 				interruptsLocker.Lock();
308 
309 				store = object_depot_cpu(depot);
310 			}
311 		} else {
312 			// allocate a new empty magazine
313 			interruptsLocker.Unlock();
314 			readLocker.Unlock();
315 
316 			DepotMagazine* magazine = alloc_magazine(depot, flags);
317 			if (magazine == NULL) {
318 				depot->return_object(depot, depot->cookie, object, flags);
319 				return;
320 			}
321 
322 			readLocker.Lock();
323 			interruptsLocker.Lock();
324 
325 			push_empty_magazine(depot, magazine);
326 			store = object_depot_cpu(depot);
327 		}
328 	}
329 }
330 
331 
332 void
object_depot_make_empty(object_depot * depot,uint32 flags)333 object_depot_make_empty(object_depot* depot, uint32 flags)
334 {
335 	WriteLocker writeLocker(depot->outer_lock);
336 
337 	// collect the store magazines
338 
339 	DepotMagazine* storeMagazines = NULL;
340 
341 	int cpuCount = smp_get_num_cpus();
342 	for (int i = 0; i < cpuCount; i++) {
343 		depot_cpu_store& store = depot->stores[i];
344 
345 		if (store.loaded) {
346 			_push(storeMagazines, store.loaded);
347 			store.loaded = NULL;
348 		}
349 
350 		if (store.previous) {
351 			_push(storeMagazines, store.previous);
352 			store.previous = NULL;
353 		}
354 	}
355 
356 	// detach the depot's full and empty magazines
357 
358 	DepotMagazine* fullMagazines = depot->full;
359 	depot->full = NULL;
360 
361 	DepotMagazine* emptyMagazines = depot->empty;
362 	depot->empty = NULL;
363 
364 	writeLocker.Unlock();
365 
366 	// free all magazines
367 
368 	while (storeMagazines != NULL)
369 		empty_magazine(depot, _pop(storeMagazines), flags);
370 
371 	while (fullMagazines != NULL)
372 		empty_magazine(depot, _pop(fullMagazines), flags);
373 
374 	while (emptyMagazines)
375 		free_magazine(_pop(emptyMagazines), flags);
376 }
377 
378 
379 #if PARANOID_KERNEL_FREE
380 
381 bool
object_depot_contains_object(object_depot * depot,void * object)382 object_depot_contains_object(object_depot* depot, void* object)
383 {
384 	WriteLocker writeLocker(depot->outer_lock);
385 
386 	int cpuCount = smp_get_num_cpus();
387 	for (int i = 0; i < cpuCount; i++) {
388 		depot_cpu_store& store = depot->stores[i];
389 
390 		if (store.loaded != NULL && !store.loaded->IsEmpty()) {
391 			if (store.loaded->ContainsObject(object))
392 				return true;
393 		}
394 
395 		if (store.previous != NULL && !store.previous->IsEmpty()) {
396 			if (store.previous->ContainsObject(object))
397 				return true;
398 		}
399 	}
400 
401 	for (DepotMagazine* magazine = depot->full; magazine != NULL;
402 			magazine = magazine->next) {
403 		if (magazine->ContainsObject(object))
404 			return true;
405 	}
406 
407 	return false;
408 }
409 
410 #endif // PARANOID_KERNEL_FREE
411 
412 
413 // #pragma mark - private kernel API
414 
415 
416 void
dump_object_depot(object_depot * depot)417 dump_object_depot(object_depot* depot)
418 {
419 	kprintf("  full:     %p, count %lu\n", depot->full, depot->full_count);
420 	kprintf("  empty:    %p, count %lu\n", depot->empty, depot->empty_count);
421 	kprintf("  max full: %lu\n", depot->max_count);
422 	kprintf("  capacity: %lu\n", depot->magazine_capacity);
423 	kprintf("  stores:\n");
424 
425 	int cpuCount = smp_get_num_cpus();
426 
427 	for (int i = 0; i < cpuCount; i++) {
428 		kprintf("  [%d] loaded:   %p\n", i, depot->stores[i].loaded);
429 		kprintf("      previous: %p\n", depot->stores[i].previous);
430 	}
431 }
432 
433 
434 int
dump_object_depot(int argCount,char ** args)435 dump_object_depot(int argCount, char** args)
436 {
437 	if (argCount != 2)
438 		kprintf("usage: %s [address]\n", args[0]);
439 	else
440 		dump_object_depot((object_depot*)parse_expression(args[1]));
441 
442 	return 0;
443 }
444 
445 
446 int
dump_depot_magazine(int argCount,char ** args)447 dump_depot_magazine(int argCount, char** args)
448 {
449 	if (argCount != 2) {
450 		kprintf("usage: %s [address]\n", args[0]);
451 		return 0;
452 	}
453 
454 	DepotMagazine* magazine = (DepotMagazine*)parse_expression(args[1]);
455 
456 	kprintf("next:          %p\n", magazine->next);
457 	kprintf("current_round: %u\n", magazine->current_round);
458 	kprintf("round_count:   %u\n", magazine->round_count);
459 
460 	for (uint16 i = 0; i < magazine->current_round; i++)
461 		kprintf("  [%i] %p\n", i, magazine->rounds[i]);
462 
463 	return 0;
464 }
465 
466 
467 RANGE_MARKER_FUNCTION_END(SlabObjectDepot)
468