Author: bonefish Date: 2010-01-20 14:00:57 +0100 (Wed, 20 Jan 2010) New Revision: 35200 Changeset: http://dev.haiku-os.org/changeset/35200/haiku Modified: haiku/trunk/headers/private/kernel/slab/ObjectDepot.h haiku/trunk/src/system/kernel/slab/ObjectDepot.cpp Log: Replaced the locking strategy (formerly a recursive lock for the depot and one for each per CPU store): * The depot is now protected by a R/W lock combined with a spinlock. It is required to either hold read lock + spinlock or just the write lock. * When accessing the per CPU stores we only need to acquire the read lock and disable interrupts. When switching magazines with the depot we additionally get the spinlock. * When allocating a new magazine we do completely unlock. Modified: haiku/trunk/headers/private/kernel/slab/ObjectDepot.h =================================================================== --- haiku/trunk/headers/private/kernel/slab/ObjectDepot.h 2010-01-20 12:12:54 UTC (rev 35199) +++ haiku/trunk/headers/private/kernel/slab/ObjectDepot.h 2010-01-20 13:00:57 UTC (rev 35200) @@ -13,7 +13,8 @@ struct DepotMagazine; typedef struct object_depot { - recursive_lock lock; + rw_lock outer_lock; + spinlock inner_lock; DepotMagazine* full; DepotMagazine* empty; size_t full_count; Modified: haiku/trunk/src/system/kernel/slab/ObjectDepot.cpp =================================================================== --- haiku/trunk/src/system/kernel/slab/ObjectDepot.cpp 2010-01-20 12:12:54 UTC (rev 35199) +++ haiku/trunk/src/system/kernel/slab/ObjectDepot.cpp 2010-01-20 13:00:57 UTC (rev 35200) @@ -1,4 +1,5 @@ /* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx * Copyright 2008, Axel Dörfler. All Rights Reserved. * Copyright 2007, Hugo Santos. All Rights Reserved. * @@ -10,6 +11,8 @@ #include <algorithm> +#include <int.h> +#include <smp.h> #include <util/AutoLock.h> #include "slab_private.h" @@ -35,7 +38,6 @@ struct depot_cpu_store { - recursive_lock lock; DepotMagazine* loaded; DepotMagazine* previous; }; @@ -107,7 +109,7 @@ static bool exchange_with_full(object_depot* depot, DepotMagazine*& magazine) { - RecursiveLocker _(depot->lock); + SpinLocker _(depot->inner_lock); if (depot->full == NULL) return false; @@ -124,16 +126,13 @@ static bool exchange_with_empty(object_depot* depot, DepotMagazine*& magazine) { - RecursiveLocker _(depot->lock); + SpinLocker _(depot->inner_lock); - if (depot->empty == NULL) { - depot->empty = alloc_magazine(); - if (depot->empty == NULL) - return false; - } else { - depot->empty_count--; - } + if (depot->empty == NULL) + return false; + depot->empty_count--; + if (magazine) { _push(depot->full, magazine); depot->full_count++; @@ -144,63 +143,19 @@ } -static inline depot_cpu_store* -object_depot_cpu(object_depot* depot) +static void +push_empty_magazine(object_depot* depot, DepotMagazine* magazine) { - return &depot->stores[smp_get_current_cpu()]; -} + SpinLocker _(depot->inner_lock); - -static void* -object_depot_obtain_from_store(object_depot* depot, depot_cpu_store* store) -{ - RecursiveLocker _(store->lock); - - // To better understand both the Alloc() and Free() logic refer to - // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] - - // In a nutshell, we try to get an object from the loaded magazine - // if it's not empty, or from the previous magazine if it's full - // and finally from the Slab if the magazine depot has no full magazines. - - if (store->loaded == NULL) - return NULL; - - while (true) { - if (!store->loaded->IsEmpty()) - return store->loaded->Pop(); - - if (store->previous - && (store->previous->IsFull() - || exchange_with_full(depot, store->previous))) { - std::swap(store->previous, store->loaded); - } else - return NULL; - } + _push(depot->empty, magazine); } -static int -object_depot_return_to_store(object_depot* depot, depot_cpu_store* store, - void* object) +static inline depot_cpu_store* +object_depot_cpu(object_depot* depot) { - RecursiveLocker _(store->lock); - - // 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. - - while (true) { - if (store->loaded && store->loaded->Push(object)) - return 1; - - if ((store->previous && store->previous->IsEmpty()) - || exchange_with_empty(depot, store->previous)) - std::swap(store->loaded, store->previous); - else - return 0; - } + return &depot->stores[smp_get_current_cpu()]; } @@ -215,19 +170,20 @@ depot->empty = NULL; depot->full_count = depot->empty_count = 0; - recursive_lock_init(&depot->lock, "depot"); + 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) { - recursive_lock_destroy(&depot->lock); + rw_lock_destroy(&depot->outer_lock); return B_NO_MEMORY; } for (int i = 0; i < cpuCount; i++) { - recursive_lock_init(&depot->stores[i].lock, "cpu store"); - depot->stores[i].loaded = depot->stores[i].previous = NULL; + depot->stores[i].loaded = NULL; + depot->stores[i].previous = NULL; } depot->cookie = cookie; @@ -242,52 +198,125 @@ { object_depot_make_empty(depot); - int cpuCount = smp_get_num_cpus(); - for (int i = 0; i < cpuCount; i++) - recursive_lock_destroy(&depot->stores[i].lock); - slab_internal_free(depot->stores); - recursive_lock_destroy(&depot->lock); + rw_lock_destroy(&depot->outer_lock); } void* object_depot_obtain(object_depot* depot) { - return object_depot_obtain_from_store(depot, object_depot_cpu(depot)); + ReadLocker readLocker(depot->outer_lock); + InterruptsLocker interruptsLocker; + + depot_cpu_store* store = object_depot_cpu(depot); + + // To better understand both the Alloc() and Free() logic refer to + // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] + + // In a nutshell, we try to get an object from the loaded magazine + // if it's not empty, or from the previous magazine if it's full + // and finally from the Slab if the magazine depot has no full magazines. + + if (store->loaded == NULL) + return NULL; + + while (true) { + if (!store->loaded->IsEmpty()) + return store->loaded->Pop(); + + if (store->previous + && (store->previous->IsFull() + || exchange_with_full(depot, store->previous))) { + std::swap(store->previous, store->loaded); + } else + return NULL; + } } int object_depot_store(object_depot* depot, void* object) { - return object_depot_return_to_store(depot, object_depot_cpu(depot), - object); + ReadLocker readLocker(depot->outer_lock); + InterruptsLocker interruptsLocker; + + depot_cpu_store* store = object_depot_cpu(depot); + + // 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. + + while (true) { + if (store->loaded && store->loaded->Push(object)) + return 1; + + if ((store->previous && store->previous->IsEmpty()) + || exchange_with_empty(depot, store->previous)) { + std::swap(store->loaded, store->previous); + } else { + // allocate a new empty magazine + interruptsLocker.Unlock(); + readLocker.Unlock(); + + DepotMagazine* magazine = alloc_magazine(); + if (magazine == NULL) + return 0; + + readLocker.Lock(); + interruptsLocker.Lock(); + + push_empty_magazine(depot, magazine); + store = object_depot_cpu(depot); + } + } } void object_depot_make_empty(object_depot* depot) { + WriteLocker writeLocker(depot->outer_lock); + + // collect the store magazines + + DepotMagazine* storeMagazines = NULL; + int cpuCount = smp_get_num_cpus(); for (int i = 0; i < cpuCount; i++) { - depot_cpu_store* store = &depot->stores[i]; + depot_cpu_store& store = depot->stores[i]; - RecursiveLocker _(store->lock); + if (store.loaded) { + _push(storeMagazines, store.loaded); + store.loaded = NULL; + } - if (depot->stores[i].loaded) - empty_magazine(depot, depot->stores[i].loaded); - if (depot->stores[i].previous) - empty_magazine(depot, depot->stores[i].previous); - depot->stores[i].loaded = depot->stores[i].previous = NULL; + if (store.previous) { + _push(storeMagazines, store.previous); + store.previous = NULL; + } } - RecursiveLocker _(depot->lock); + // detach the depot's full and empty magazines - while (depot->full) - empty_magazine(depot, _pop(depot->full)); + DepotMagazine* fullMagazines = depot->full; + depot->full = NULL; - while (depot->empty) - empty_magazine(depot, _pop(depot->empty)); + DepotMagazine* emptyMagazines = depot->empty; + depot->empty = NULL; + + writeLocker.Unlock(); + + // free all magazines + + while (storeMagazines != NULL) + empty_magazine(depot, _pop(storeMagazines)); + + while (fullMagazines != NULL) + empty_magazine(depot, _pop(fullMagazines)); + + while (emptyMagazines) + free_magazine(_pop(emptyMagazines)); }