* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2008-2010, Axel Dörfler. All Rights Reserved.
* Copyright 2007, Hugo Santos. All Rights Reserved.
*
* Distributed under the terms of the MIT License.
*/
#include "ObjectDepot.h"
#include <algorithm>
#include <interrupts.h>
#include <slab/Slab.h>
#include <smp.h>
#include <util/AutoLock.h>
#include "slab_debug.h"
#include "slab_private.h"
#include "slab_queue.h"
struct DepotMagazine : public slab_queue_link {
uint16 current_round;
uint16 round_count;
void* rounds[0];
public:
inline bool IsEmpty() const;
inline bool IsFull() const;
inline void* Pop();
inline bool Push(void* object);
#if PARANOID_KERNEL_FREE
bool ContainsObject(void* object) const;
#endif
};
#if PARANOID_KERNEL_FREE
struct depot_cpu_store {
DepotMagazine* obtain;
DepotMagazine* store;
};
#else
struct depot_cpu_store {
DepotMagazine* loaded;
DepotMagazine* previous;
};
#endif
RANGE_MARKER_FUNCTION_BEGIN(SlabObjectDepot)
bool
DepotMagazine::IsEmpty() const
{
return current_round == 0;
}
bool
DepotMagazine::IsFull() const
{
return current_round == round_count;
}
void*
DepotMagazine::Pop()
{
return rounds[--current_round];
}
bool
DepotMagazine::Push(void* object)
{
if (IsFull())
return false;
rounds[current_round++] = object;
return true;
}
#if PARANOID_KERNEL_FREE
bool
DepotMagazine::ContainsObject(void* object) const
{
for (uint16 i = 0; i < current_round; i++) {
if (rounds[i] == object)
return true;
}
return false;
}
#endif
static DepotMagazine*
alloc_magazine(object_depot* depot, uint32 flags)
{
DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc(
sizeof(DepotMagazine) + depot->magazine_capacity * sizeof(void*),
flags);
if (magazine) {
magazine->next = NULL;
magazine->current_round = 0;
magazine->round_count = depot->magazine_capacity;
}
return magazine;
}
static void
free_magazine(DepotMagazine* magazine, uint32 flags)
{
slab_internal_free(magazine, flags);
}
static void
empty_magazine(object_depot* depot, DepotMagazine* magazine, uint32 flags)
{
for (uint16 i = 0; i < magazine->current_round; i++)
depot->return_object(depot, depot->cookie, magazine->rounds[i], flags);
free_magazine(magazine, flags);
}
static bool
exchange_with_full(object_depot* depot, DepotMagazine*& magazine)
{
ASSERT(magazine == NULL || magazine->IsEmpty());
SpinLocker _(depot->inner_lock);
if (depot->full.head == NULL)
return false;
if (magazine != NULL) {
depot->empty.Push(magazine);
depot->empty_count++;
}
magazine = (DepotMagazine*)depot->full.Pop();
depot->full_count--;
return true;
}
static bool
exchange_with_empty(object_depot* depot, DepotMagazine*& magazine,
DepotMagazine*& freeMagazine)
{
ASSERT(magazine == NULL || magazine->IsFull());
#if PARANOID_KERNEL_FREE
if (magazine != NULL) {
for (int i = 0; i < magazine->current_round / 2; i++)
std::swap(magazine->rounds[i], magazine->rounds[magazine->current_round - i - 1]);
}
#endif
SpinLocker _(depot->inner_lock);
if (depot->empty.head == NULL)
return false;
if (magazine != NULL) {
if (depot->full_count < depot->max_count) {
depot->full.Push(magazine);
depot->full_count++;
freeMagazine = NULL;
} else
freeMagazine = magazine;
}
magazine = (DepotMagazine*)depot->empty.Pop();
depot->empty_count--;
return true;
}
static void
push_empty_magazine(object_depot* depot, DepotMagazine* magazine)
{
SpinLocker _(depot->inner_lock);
depot->empty.Push(magazine);
depot->empty_count++;
}
static inline depot_cpu_store*
object_depot_cpu(object_depot* depot)
{
return &depot->stores[smp_get_current_cpu()];
}
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))
{
depot->full.Init();
depot->empty.Init();
depot->full_count = depot->empty_count = 0;
depot->max_count = maxCount;
depot->magazine_capacity = capacity;
rw_lock_init(&depot->outer_lock, "object depot");
B_INITIALIZE_SPINLOCK(&depot->inner_lock);
int cpuCount = smp_get_num_cpus();
depot->stores = (depot_cpu_store*)slab_internal_alloc(
sizeof(depot_cpu_store) * cpuCount, flags);
if (depot->stores == NULL) {
rw_lock_destroy(&depot->outer_lock);
return B_NO_MEMORY;
}
for (int i = 0; i < cpuCount; i++) {
#if PARANOID_KERNEL_FREE
depot->stores[i].obtain = NULL;
depot->stores[i].store = NULL;
#else
depot->stores[i].loaded = NULL;
depot->stores[i].previous = NULL;
#endif
}
depot->cookie = cookie;
depot->return_object = return_object;
return B_OK;
}
void
object_depot_destroy(object_depot* depot, uint32 flags)
{
object_depot_make_empty(depot, flags);
slab_internal_free(depot->stores, flags);
rw_lock_destroy(&depot->outer_lock);
}
void*
object_depot_obtain(object_depot* depot)
{
ReadLocker readLocker(depot->outer_lock);
InterruptsLocker interruptsLocker;
depot_cpu_store* store = object_depot_cpu(depot);
#if PARANOID_KERNEL_FREE
if (store->obtain == NULL || store->obtain->IsEmpty()) {
if (!exchange_with_full(depot, store->obtain))
return NULL;
}
return store->obtain->Pop();
#else
if (store->loaded == NULL)
return NULL;
while (true) {
if (!store->loaded->IsEmpty())
return store->loaded->Pop();
if (store->previous != NULL
&& (store->previous->IsFull()
|| exchange_with_full(depot, store->previous))) {
std::swap(store->previous, store->loaded);
} else
return NULL;
}
#endif
}
void
object_depot_store(object_depot* depot, void* object, uint32 flags)
{
ReadLocker readLocker(depot->outer_lock);
InterruptsLocker interruptsLocker;
depot_cpu_store* store = object_depot_cpu(depot);
while (true) {
#if PARANOID_KERNEL_FREE
if (store->store != NULL && store->store->Push(object))
return;
DepotMagazine* freeMagazine = NULL;
if (exchange_with_empty(depot, store->store, freeMagazine)) {
#else
// We try to add the object to the loaded magazine if we have one
// and it's not full, or to the previous one if it is empty. If
// the magazine depot doesn't provide us with a new empty magazine
// we return the object directly to the slab.
if (store->loaded != NULL && store->loaded->Push(object))
return;
DepotMagazine* freeMagazine = NULL;
if ((store->previous != NULL && store->previous->IsEmpty())
|| exchange_with_empty(depot, store->previous, freeMagazine)) {
std::swap(store->loaded, store->previous);
#endif
if (freeMagazine != NULL) {
interruptsLocker.Unlock();
readLocker.Unlock();
empty_magazine(depot, freeMagazine, flags);
readLocker.Lock();
interruptsLocker.Lock();
store = object_depot_cpu(depot);
}
} else {
interruptsLocker.Unlock();
readLocker.Unlock();
DepotMagazine* magazine = alloc_magazine(depot, flags);
if (magazine == NULL) {
depot->return_object(depot, depot->cookie, object, flags);
return;
}
readLocker.Lock();
interruptsLocker.Lock();
push_empty_magazine(depot, magazine);
store = object_depot_cpu(depot);
}
}
}
void
object_depot_make_empty(object_depot* depot, uint32 flags)
{
WriteLocker writeLocker(depot->outer_lock);
slab_queue storeMagazines;
storeMagazines.Init();
int cpuCount = smp_get_num_cpus();
for (int i = 0; i < cpuCount; i++) {
depot_cpu_store& store = depot->stores[i];
#if PARANOID_KERNEL_FREE
if (store.obtain != NULL) {
storeMagazines.Push(store.obtain);
store.obtain = NULL;
}
if (store.store != NULL) {
storeMagazines.Push(store.store);
store.store = NULL;
}
#else
if (store.loaded != NULL) {
storeMagazines.Push(store.loaded);
store.loaded = NULL;
}
if (store.previous != NULL) {
storeMagazines.Push(store.previous);
store.previous = NULL;
}
#endif
}
slab_queue fullMagazines = depot->full;
depot->full.Init();
slab_queue emptyMagazines = depot->empty;
depot->empty.Init();
writeLocker.Unlock();
while (storeMagazines.head != NULL)
empty_magazine(depot, (DepotMagazine*)storeMagazines.Pop(), flags);
while (fullMagazines.head != NULL)
empty_magazine(depot, (DepotMagazine*)fullMagazines.Pop(), flags);
while (emptyMagazines.head != NULL)
free_magazine((DepotMagazine*)emptyMagazines.Pop(), flags);
}
#if PARANOID_KERNEL_FREE
bool
object_depot_contains_object(object_depot* depot, void* object)
{
WriteLocker writeLocker(depot->outer_lock);
int cpuCount = smp_get_num_cpus();
for (int i = 0; i < cpuCount; i++) {
depot_cpu_store& store = depot->stores[i];
if (store.obtain != NULL && !store.obtain->IsEmpty()) {
if (store.obtain->ContainsObject(object))
return true;
}
if (store.store != NULL && !store.store->IsEmpty()) {
if (store.store->ContainsObject(object))
return true;
}
}
for (DepotMagazine* magazine = (DepotMagazine*)depot->full.head; magazine != NULL;
magazine = (DepotMagazine*)magazine->next) {
if (magazine->ContainsObject(object))
return true;
}
return false;
}
#endif
void
dump_object_depot(object_depot* depot)
{
kprintf(" full: %p, count %lu\n", depot->full.head, depot->full_count);
kprintf(" empty: %p, count %lu\n", depot->empty.head, depot->empty_count);
kprintf(" max full: %lu\n", depot->max_count);
kprintf(" capacity: %lu\n", depot->magazine_capacity);
kprintf(" stores:\n");
int cpuCount = smp_get_num_cpus();
#if PARANOID_KERNEL_FREE
for (int i = 0; i < cpuCount; i++) {
kprintf(" [%d] obtain: %p\n", i, depot->stores[i].obtain);
kprintf(" store: %p\n", depot->stores[i].store);
}
#else
for (int i = 0; i < cpuCount; i++) {
kprintf(" [%d] loaded: %p\n", i, depot->stores[i].loaded);
kprintf(" previous: %p\n", depot->stores[i].previous);
}
#endif
}
int
dump_object_depot(int argCount, char** args)
{
if (argCount != 2)
kprintf("usage: %s [address]\n", args[0]);
else
dump_object_depot((object_depot*)parse_expression(args[1]));
return 0;
}
int
dump_depot_magazine(int argCount, char** args)
{
if (argCount != 2) {
kprintf("usage: %s [address]\n", args[0]);
return 0;
}
DepotMagazine* magazine = (DepotMagazine*)parse_expression(args[1]);
kprintf("next: %p\n", magazine->next);
kprintf("current_round: %u\n", magazine->current_round);
kprintf("round_count: %u\n", magazine->round_count);
for (uint16 i = 0; i < magazine->current_round; i++)
kprintf(" [%i] %p\n", i, magazine->rounds[i]);
return 0;
}
RANGE_MARKER_FUNCTION_END(SlabObjectDepot)