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.
6 * Distributed under the terms of the MIT License.
10 #include <slab/ObjectDepot.h>
15 #include <slab/Slab.h>
17 #include <util/AutoLock.h>
19 #include "slab_debug.h"
20 #include "slab_private.h"
23 struct DepotMagazine
{
30 inline bool IsEmpty() const;
31 inline bool IsFull() const;
34 inline bool Push(void* object
);
36 #if PARANOID_KERNEL_FREE
37 bool ContainsObject(void* object
) const;
42 struct depot_cpu_store
{
43 DepotMagazine
* loaded
;
44 DepotMagazine
* previous
;
48 RANGE_MARKER_FUNCTION_BEGIN(SlabObjectDepot
)
52 DepotMagazine::IsEmpty() const
54 return current_round
== 0;
59 DepotMagazine::IsFull() const
61 return current_round
== round_count
;
68 return rounds
[--current_round
];
73 DepotMagazine::Push(void* object
)
78 rounds
[current_round
++] = object
;
83 #if PARANOID_KERNEL_FREE
86 DepotMagazine::ContainsObject(void* object
) const
88 for (uint16 i
= 0; i
< current_round
; i
++) {
89 if (rounds
[i
] == object
)
96 #endif // PARANOID_KERNEL_FREE
102 static DepotMagazine
*
103 alloc_magazine(object_depot
* depot
, uint32 flags
)
105 DepotMagazine
* magazine
= (DepotMagazine
*)slab_internal_alloc(
106 sizeof(DepotMagazine
) + depot
->magazine_capacity
* sizeof(void*),
109 magazine
->next
= NULL
;
110 magazine
->current_round
= 0;
111 magazine
->round_count
= depot
->magazine_capacity
;
119 free_magazine(DepotMagazine
* magazine
, uint32 flags
)
121 slab_internal_free(magazine
, flags
);
126 empty_magazine(object_depot
* depot
, DepotMagazine
* magazine
, uint32 flags
)
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
);
135 exchange_with_full(object_depot
* depot
, DepotMagazine
*& magazine
)
137 ASSERT(magazine
->IsEmpty());
139 SpinLocker
_(depot
->inner_lock
);
141 if (depot
->full
== NULL
)
145 depot
->empty_count
++;
147 _push(depot
->empty
, magazine
);
148 magazine
= _pop(depot
->full
);
154 exchange_with_empty(object_depot
* depot
, DepotMagazine
*& magazine
,
155 DepotMagazine
*& freeMagazine
)
157 ASSERT(magazine
== NULL
|| magazine
->IsFull());
159 SpinLocker
_(depot
->inner_lock
);
161 if (depot
->empty
== NULL
)
164 depot
->empty_count
--;
166 if (magazine
!= NULL
) {
167 if (depot
->full_count
< depot
->max_count
) {
168 _push(depot
->full
, magazine
);
172 freeMagazine
= magazine
;
175 magazine
= _pop(depot
->empty
);
181 push_empty_magazine(object_depot
* depot
, DepotMagazine
* magazine
)
183 SpinLocker
_(depot
->inner_lock
);
185 _push(depot
->empty
, magazine
);
186 depot
->empty_count
++;
190 static inline depot_cpu_store
*
191 object_depot_cpu(object_depot
* depot
)
193 return &depot
->stores
[smp_get_current_cpu()];
197 // #pragma mark - public API
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
))
207 depot
->full_count
= depot
->empty_count
= 0;
208 depot
->max_count
= maxCount
;
209 depot
->magazine_capacity
= capacity
;
211 rw_lock_init(&depot
->outer_lock
, "object depot");
212 B_INITIALIZE_SPINLOCK(&depot
->inner_lock
);
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
);
222 for (int i
= 0; i
< cpuCount
; i
++) {
223 depot
->stores
[i
].loaded
= NULL
;
224 depot
->stores
[i
].previous
= NULL
;
227 depot
->cookie
= cookie
;
228 depot
->return_object
= return_object
;
235 object_depot_destroy(object_depot
* depot
, uint32 flags
)
237 object_depot_make_empty(depot
, flags
);
239 slab_internal_free(depot
->stores
, flags
);
241 rw_lock_destroy(&depot
->outer_lock
);
246 object_depot_obtain(object_depot
* depot
)
248 ReadLocker
readLocker(depot
->outer_lock
);
249 InterruptsLocker interruptsLocker
;
251 depot_cpu_store
* store
= object_depot_cpu(depot
);
253 // To better understand both the Alloc() and Free() logic refer to
254 // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings]
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.
260 if (store
->loaded
== NULL
)
264 if (!store
->loaded
->IsEmpty())
265 return store
->loaded
->Pop();
268 && (store
->previous
->IsFull()
269 || exchange_with_full(depot
, store
->previous
))) {
270 std::swap(store
->previous
, store
->loaded
);
278 object_depot_store(object_depot
* depot
, void* object
, uint32 flags
)
280 ReadLocker
readLocker(depot
->outer_lock
);
281 InterruptsLocker interruptsLocker
;
283 depot_cpu_store
* store
= object_depot_cpu(depot
);
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.
291 if (store
->loaded
!= NULL
&& store
->loaded
->Push(object
))
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
);
299 if (freeMagazine
!= NULL
) {
300 // Free the magazine that didn't have space in the list
301 interruptsLocker
.Unlock();
304 empty_magazine(depot
, freeMagazine
, flags
);
307 interruptsLocker
.Lock();
309 store
= object_depot_cpu(depot
);
312 // allocate a new empty magazine
313 interruptsLocker
.Unlock();
316 DepotMagazine
* magazine
= alloc_magazine(depot
, flags
);
317 if (magazine
== NULL
) {
318 depot
->return_object(depot
, depot
->cookie
, object
, flags
);
323 interruptsLocker
.Lock();
325 push_empty_magazine(depot
, magazine
);
326 store
= object_depot_cpu(depot
);
333 object_depot_make_empty(object_depot
* depot
, uint32 flags
)
335 WriteLocker
writeLocker(depot
->outer_lock
);
337 // collect the store magazines
339 DepotMagazine
* storeMagazines
= NULL
;
341 int cpuCount
= smp_get_num_cpus();
342 for (int i
= 0; i
< cpuCount
; i
++) {
343 depot_cpu_store
& store
= depot
->stores
[i
];
346 _push(storeMagazines
, store
.loaded
);
350 if (store
.previous
) {
351 _push(storeMagazines
, store
.previous
);
352 store
.previous
= NULL
;
356 // detach the depot's full and empty magazines
358 DepotMagazine
* fullMagazines
= depot
->full
;
361 DepotMagazine
* emptyMagazines
= depot
->empty
;
364 writeLocker
.Unlock();
366 // free all magazines
368 while (storeMagazines
!= NULL
)
369 empty_magazine(depot
, _pop(storeMagazines
), flags
);
371 while (fullMagazines
!= NULL
)
372 empty_magazine(depot
, _pop(fullMagazines
), flags
);
374 while (emptyMagazines
)
375 free_magazine(_pop(emptyMagazines
), flags
);
379 #if PARANOID_KERNEL_FREE
382 object_depot_contains_object(object_depot
* depot
, void* object
)
384 WriteLocker
writeLocker(depot
->outer_lock
);
386 int cpuCount
= smp_get_num_cpus();
387 for (int i
= 0; i
< cpuCount
; i
++) {
388 depot_cpu_store
& store
= depot
->stores
[i
];
390 if (store
.loaded
!= NULL
&& !store
.loaded
->IsEmpty()) {
391 if (store
.loaded
->ContainsObject(object
))
395 if (store
.previous
!= NULL
&& !store
.previous
->IsEmpty()) {
396 if (store
.previous
->ContainsObject(object
))
401 for (DepotMagazine
* magazine
= depot
->full
; magazine
!= NULL
;
402 magazine
= magazine
->next
) {
403 if (magazine
->ContainsObject(object
))
410 #endif // PARANOID_KERNEL_FREE
413 // #pragma mark - private kernel API
417 dump_object_depot(object_depot
* depot
)
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");
425 int cpuCount
= smp_get_num_cpus();
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
);
435 dump_object_depot(int argCount
, char** args
)
438 kprintf("usage: %s [address]\n", args
[0]);
440 dump_object_depot((object_depot
*)parse_expression(args
[1]));
447 dump_depot_magazine(int argCount
, char** args
)
450 kprintf("usage: %s [address]\n", args
[0]);
454 DepotMagazine
* magazine
= (DepotMagazine
*)parse_expression(args
[1]);
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
);
460 for (uint16 i
= 0; i
< magazine
->current_round
; i
++)
461 kprintf(" [%i] %p\n", i
, magazine
->rounds
[i
]);
467 RANGE_MARKER_FUNCTION_END(SlabObjectDepot
)