1a8806e5eSIngo Weinhold /* 2453a2bddSIngo Weinhold * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 354f3267eSAxel Dörfler * Copyright 2008-2010, Axel Dörfler. All Rights Reserved. 4a8806e5eSIngo Weinhold * Copyright 2007, Hugo Santos. All Rights Reserved. 5a8806e5eSIngo Weinhold * 6a8806e5eSIngo Weinhold * Distributed under the terms of the MIT License. 7a8806e5eSIngo Weinhold */ 8a8806e5eSIngo Weinhold 9a8806e5eSIngo Weinhold 10a8806e5eSIngo Weinhold #include <slab/ObjectDepot.h> 11a8806e5eSIngo Weinhold 12a8806e5eSIngo Weinhold #include <algorithm> 13a8806e5eSIngo Weinhold 14453a2bddSIngo Weinhold #include <int.h> 15bb439b87SIngo Weinhold #include <slab/Slab.h> 16453a2bddSIngo Weinhold #include <smp.h> 17a8806e5eSIngo Weinhold #include <util/AutoLock.h> 18a8806e5eSIngo Weinhold 19a8806e5eSIngo Weinhold #include "slab_private.h" 20a8806e5eSIngo Weinhold 21a8806e5eSIngo Weinhold 22825566f8SIngo Weinhold struct DepotMagazine { 23825566f8SIngo Weinhold DepotMagazine* next; 24825566f8SIngo Weinhold uint16 current_round; 25825566f8SIngo Weinhold uint16 round_count; 26a8806e5eSIngo Weinhold void* rounds[0]; 27825566f8SIngo Weinhold 28825566f8SIngo Weinhold public: 29825566f8SIngo Weinhold inline bool IsEmpty() const; 30825566f8SIngo Weinhold inline bool IsFull() const; 31825566f8SIngo Weinhold 32825566f8SIngo Weinhold inline void* Pop(); 33825566f8SIngo Weinhold inline bool Push(void* object); 34a8806e5eSIngo Weinhold }; 35a8806e5eSIngo Weinhold 36a8806e5eSIngo Weinhold 37a8806e5eSIngo Weinhold struct depot_cpu_store { 38825566f8SIngo Weinhold DepotMagazine* loaded; 39825566f8SIngo Weinhold DepotMagazine* previous; 40a8806e5eSIngo Weinhold }; 41a8806e5eSIngo Weinhold 42a8806e5eSIngo Weinhold 43825566f8SIngo Weinhold bool 44825566f8SIngo Weinhold DepotMagazine::IsEmpty() const 45a8806e5eSIngo Weinhold { 46825566f8SIngo Weinhold return current_round == 0; 47a8806e5eSIngo Weinhold } 48a8806e5eSIngo Weinhold 49a8806e5eSIngo Weinhold 50825566f8SIngo Weinhold bool 51825566f8SIngo Weinhold DepotMagazine::IsFull() const 52a8806e5eSIngo Weinhold { 53825566f8SIngo Weinhold return current_round == round_count; 54a8806e5eSIngo Weinhold } 55a8806e5eSIngo Weinhold 56a8806e5eSIngo Weinhold 57825566f8SIngo Weinhold void* 58825566f8SIngo Weinhold DepotMagazine::Pop() 59a8806e5eSIngo Weinhold { 60825566f8SIngo Weinhold return rounds[--current_round]; 61a8806e5eSIngo Weinhold } 62a8806e5eSIngo Weinhold 63a8806e5eSIngo Weinhold 64825566f8SIngo Weinhold bool 65825566f8SIngo Weinhold DepotMagazine::Push(void* object) 66a8806e5eSIngo Weinhold { 67825566f8SIngo Weinhold if (IsFull()) 68a8806e5eSIngo Weinhold return false; 69825566f8SIngo Weinhold 70825566f8SIngo Weinhold rounds[current_round++] = object; 71a8806e5eSIngo Weinhold return true; 72a8806e5eSIngo Weinhold } 73a8806e5eSIngo Weinhold 74a8806e5eSIngo Weinhold 7554f3267eSAxel Dörfler // #pragma mark - 7654f3267eSAxel Dörfler 7754f3267eSAxel Dörfler 78825566f8SIngo Weinhold static DepotMagazine* 79*ff59ce68SAxel Dörfler alloc_magazine(object_depot* depot, uint32 flags) 80a8806e5eSIngo Weinhold { 81825566f8SIngo Weinhold DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc( 82*ff59ce68SAxel Dörfler sizeof(DepotMagazine) + depot->magazine_capacity * sizeof(void*), 83*ff59ce68SAxel Dörfler flags); 84a8806e5eSIngo Weinhold if (magazine) { 85a8806e5eSIngo Weinhold magazine->next = NULL; 86a8806e5eSIngo Weinhold magazine->current_round = 0; 87*ff59ce68SAxel Dörfler magazine->round_count = depot->magazine_capacity; 88a8806e5eSIngo Weinhold } 89a8806e5eSIngo Weinhold 90a8806e5eSIngo Weinhold return magazine; 91a8806e5eSIngo Weinhold } 92a8806e5eSIngo Weinhold 93a8806e5eSIngo Weinhold 94a8806e5eSIngo Weinhold static void 9586c794e5SIngo Weinhold free_magazine(DepotMagazine* magazine, uint32 flags) 96a8806e5eSIngo Weinhold { 9786c794e5SIngo Weinhold slab_internal_free(magazine, flags); 98a8806e5eSIngo Weinhold } 99a8806e5eSIngo Weinhold 100a8806e5eSIngo Weinhold 101a8806e5eSIngo Weinhold static void 10286c794e5SIngo Weinhold empty_magazine(object_depot* depot, DepotMagazine* magazine, uint32 flags) 103a8806e5eSIngo Weinhold { 104a8806e5eSIngo Weinhold for (uint16 i = 0; i < magazine->current_round; i++) 10586c794e5SIngo Weinhold depot->return_object(depot, depot->cookie, magazine->rounds[i], flags); 10686c794e5SIngo Weinhold free_magazine(magazine, flags); 107a8806e5eSIngo Weinhold } 108a8806e5eSIngo Weinhold 109a8806e5eSIngo Weinhold 110a8806e5eSIngo Weinhold static bool 111825566f8SIngo Weinhold exchange_with_full(object_depot* depot, DepotMagazine*& magazine) 112a8806e5eSIngo Weinhold { 113*ff59ce68SAxel Dörfler ASSERT(magazine->IsEmpty()); 114*ff59ce68SAxel Dörfler 115453a2bddSIngo Weinhold SpinLocker _(depot->inner_lock); 116a8806e5eSIngo Weinhold 117a8806e5eSIngo Weinhold if (depot->full == NULL) 118a8806e5eSIngo Weinhold return false; 119a8806e5eSIngo Weinhold 120a8806e5eSIngo Weinhold depot->full_count--; 121a8806e5eSIngo Weinhold depot->empty_count++; 122a8806e5eSIngo Weinhold 123a8806e5eSIngo Weinhold _push(depot->empty, magazine); 124a8806e5eSIngo Weinhold magazine = _pop(depot->full); 125a8806e5eSIngo Weinhold return true; 126a8806e5eSIngo Weinhold } 127a8806e5eSIngo Weinhold 128a8806e5eSIngo Weinhold 129a8806e5eSIngo Weinhold static bool 130*ff59ce68SAxel Dörfler exchange_with_empty(object_depot* depot, DepotMagazine*& magazine, 131*ff59ce68SAxel Dörfler DepotMagazine*& freeMagazine) 132a8806e5eSIngo Weinhold { 133*ff59ce68SAxel Dörfler ASSERT(magazine == NULL || magazine->IsFull()); 134*ff59ce68SAxel Dörfler 135453a2bddSIngo Weinhold SpinLocker _(depot->inner_lock); 136a8806e5eSIngo Weinhold 137a8806e5eSIngo Weinhold if (depot->empty == NULL) 138a8806e5eSIngo Weinhold return false; 139453a2bddSIngo Weinhold 140a8806e5eSIngo Weinhold depot->empty_count--; 141a8806e5eSIngo Weinhold 142*ff59ce68SAxel Dörfler if (magazine != NULL) { 143*ff59ce68SAxel Dörfler if (depot->full_count < depot->max_count) { 144a8806e5eSIngo Weinhold _push(depot->full, magazine); 145a8806e5eSIngo Weinhold depot->full_count++; 146*ff59ce68SAxel Dörfler } else 147*ff59ce68SAxel Dörfler freeMagazine = magazine; 148a8806e5eSIngo Weinhold } 149a8806e5eSIngo Weinhold 150a8806e5eSIngo Weinhold magazine = _pop(depot->empty); 151a8806e5eSIngo Weinhold return true; 152a8806e5eSIngo Weinhold } 153a8806e5eSIngo Weinhold 154a8806e5eSIngo Weinhold 155453a2bddSIngo Weinhold static void 156453a2bddSIngo Weinhold push_empty_magazine(object_depot* depot, DepotMagazine* magazine) 157453a2bddSIngo Weinhold { 158453a2bddSIngo Weinhold SpinLocker _(depot->inner_lock); 159453a2bddSIngo Weinhold 160453a2bddSIngo Weinhold _push(depot->empty, magazine); 16154f3267eSAxel Dörfler depot->empty_count++; 162453a2bddSIngo Weinhold } 163453a2bddSIngo Weinhold 164453a2bddSIngo Weinhold 165a8806e5eSIngo Weinhold static inline depot_cpu_store* 166a8806e5eSIngo Weinhold object_depot_cpu(object_depot* depot) 167a8806e5eSIngo Weinhold { 168a8806e5eSIngo Weinhold return &depot->stores[smp_get_current_cpu()]; 169a8806e5eSIngo Weinhold } 170a8806e5eSIngo Weinhold 171a8806e5eSIngo Weinhold 172453a2bddSIngo Weinhold // #pragma mark - public API 173453a2bddSIngo Weinhold 174453a2bddSIngo Weinhold 175453a2bddSIngo Weinhold status_t 176*ff59ce68SAxel Dörfler object_depot_init(object_depot* depot, size_t capacity, size_t maxCount, 177*ff59ce68SAxel Dörfler uint32 flags, void* cookie, void (*return_object)(object_depot* depot, 178*ff59ce68SAxel Dörfler void* cookie, void* object, uint32 flags)) 179a8806e5eSIngo Weinhold { 180453a2bddSIngo Weinhold depot->full = NULL; 181453a2bddSIngo Weinhold depot->empty = NULL; 182453a2bddSIngo Weinhold depot->full_count = depot->empty_count = 0; 183*ff59ce68SAxel Dörfler depot->max_count = maxCount; 184*ff59ce68SAxel Dörfler depot->magazine_capacity = capacity; 185453a2bddSIngo Weinhold 186453a2bddSIngo Weinhold rw_lock_init(&depot->outer_lock, "object depot"); 187453a2bddSIngo Weinhold B_INITIALIZE_SPINLOCK(&depot->inner_lock); 188453a2bddSIngo Weinhold 189453a2bddSIngo Weinhold int cpuCount = smp_get_num_cpus(); 190453a2bddSIngo Weinhold depot->stores = (depot_cpu_store*)slab_internal_alloc( 191453a2bddSIngo Weinhold sizeof(depot_cpu_store) * cpuCount, flags); 192453a2bddSIngo Weinhold if (depot->stores == NULL) { 193453a2bddSIngo Weinhold rw_lock_destroy(&depot->outer_lock); 194453a2bddSIngo Weinhold return B_NO_MEMORY; 195453a2bddSIngo Weinhold } 196453a2bddSIngo Weinhold 197453a2bddSIngo Weinhold for (int i = 0; i < cpuCount; i++) { 198453a2bddSIngo Weinhold depot->stores[i].loaded = NULL; 199453a2bddSIngo Weinhold depot->stores[i].previous = NULL; 200453a2bddSIngo Weinhold } 201453a2bddSIngo Weinhold 202453a2bddSIngo Weinhold depot->cookie = cookie; 203453a2bddSIngo Weinhold depot->return_object = return_object; 204453a2bddSIngo Weinhold 205453a2bddSIngo Weinhold return B_OK; 206453a2bddSIngo Weinhold } 207453a2bddSIngo Weinhold 208453a2bddSIngo Weinhold 209453a2bddSIngo Weinhold void 21086c794e5SIngo Weinhold object_depot_destroy(object_depot* depot, uint32 flags) 211453a2bddSIngo Weinhold { 21286c794e5SIngo Weinhold object_depot_make_empty(depot, flags); 213453a2bddSIngo Weinhold 21486c794e5SIngo Weinhold slab_internal_free(depot->stores, flags); 215453a2bddSIngo Weinhold 216453a2bddSIngo Weinhold rw_lock_destroy(&depot->outer_lock); 217453a2bddSIngo Weinhold } 218453a2bddSIngo Weinhold 219453a2bddSIngo Weinhold 220453a2bddSIngo Weinhold void* 221453a2bddSIngo Weinhold object_depot_obtain(object_depot* depot) 222453a2bddSIngo Weinhold { 223453a2bddSIngo Weinhold ReadLocker readLocker(depot->outer_lock); 224453a2bddSIngo Weinhold InterruptsLocker interruptsLocker; 225453a2bddSIngo Weinhold 226453a2bddSIngo Weinhold depot_cpu_store* store = object_depot_cpu(depot); 227a8806e5eSIngo Weinhold 228a8806e5eSIngo Weinhold // To better understand both the Alloc() and Free() logic refer to 229a8806e5eSIngo Weinhold // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] 230a8806e5eSIngo Weinhold 231a8806e5eSIngo Weinhold // In a nutshell, we try to get an object from the loaded magazine 232a8806e5eSIngo Weinhold // if it's not empty, or from the previous magazine if it's full 233a8806e5eSIngo Weinhold // and finally from the Slab if the magazine depot has no full magazines. 234a8806e5eSIngo Weinhold 235a8806e5eSIngo Weinhold if (store->loaded == NULL) 236a8806e5eSIngo Weinhold return NULL; 237a8806e5eSIngo Weinhold 238a8806e5eSIngo Weinhold while (true) { 239825566f8SIngo Weinhold if (!store->loaded->IsEmpty()) 240825566f8SIngo Weinhold return store->loaded->Pop(); 241a8806e5eSIngo Weinhold 242825566f8SIngo Weinhold if (store->previous 243825566f8SIngo Weinhold && (store->previous->IsFull() 244825566f8SIngo Weinhold || exchange_with_full(depot, store->previous))) { 245a8806e5eSIngo Weinhold std::swap(store->previous, store->loaded); 246825566f8SIngo Weinhold } else 247a8806e5eSIngo Weinhold return NULL; 248a8806e5eSIngo Weinhold } 249a8806e5eSIngo Weinhold } 250a8806e5eSIngo Weinhold 251a8806e5eSIngo Weinhold 252453a2bddSIngo Weinhold int 25386c794e5SIngo Weinhold object_depot_store(object_depot* depot, void* object, uint32 flags) 254a8806e5eSIngo Weinhold { 255*ff59ce68SAxel Dörfler DepotMagazine* freeMagazine = NULL; 256*ff59ce68SAxel Dörfler 257453a2bddSIngo Weinhold ReadLocker readLocker(depot->outer_lock); 258453a2bddSIngo Weinhold InterruptsLocker interruptsLocker; 259453a2bddSIngo Weinhold 260453a2bddSIngo Weinhold depot_cpu_store* store = object_depot_cpu(depot); 261a8806e5eSIngo Weinhold 262a8806e5eSIngo Weinhold // We try to add the object to the loaded magazine if we have one 263a8806e5eSIngo Weinhold // and it's not full, or to the previous one if it is empty. If 264a8806e5eSIngo Weinhold // the magazine depot doesn't provide us with a new empty magazine 265a8806e5eSIngo Weinhold // we return the object directly to the slab. 266a8806e5eSIngo Weinhold 267a8806e5eSIngo Weinhold while (true) { 268*ff59ce68SAxel Dörfler if (store->loaded != NULL && store->loaded->Push(object)) 269a8806e5eSIngo Weinhold return 1; 270a8806e5eSIngo Weinhold 271*ff59ce68SAxel Dörfler if ((store->previous != NULL && store->previous->IsEmpty()) 272*ff59ce68SAxel Dörfler || exchange_with_empty(depot, store->previous, freeMagazine)) { 273a8806e5eSIngo Weinhold std::swap(store->loaded, store->previous); 274*ff59ce68SAxel Dörfler 275*ff59ce68SAxel Dörfler if (freeMagazine != NULL) { 276*ff59ce68SAxel Dörfler // Free the magazine that didn't have space in the list 277*ff59ce68SAxel Dörfler interruptsLocker.Unlock(); 278*ff59ce68SAxel Dörfler readLocker.Unlock(); 279*ff59ce68SAxel Dörfler 280*ff59ce68SAxel Dörfler empty_magazine(depot, freeMagazine, flags); 281*ff59ce68SAxel Dörfler 282*ff59ce68SAxel Dörfler readLocker.Lock(); 283*ff59ce68SAxel Dörfler interruptsLocker.Lock(); 284*ff59ce68SAxel Dörfler 285*ff59ce68SAxel Dörfler store = object_depot_cpu(depot); 286*ff59ce68SAxel Dörfler } 287453a2bddSIngo Weinhold } else { 288453a2bddSIngo Weinhold // allocate a new empty magazine 289453a2bddSIngo Weinhold interruptsLocker.Unlock(); 290453a2bddSIngo Weinhold readLocker.Unlock(); 291453a2bddSIngo Weinhold 292*ff59ce68SAxel Dörfler DepotMagazine* magazine = alloc_magazine(depot, flags); 293453a2bddSIngo Weinhold if (magazine == NULL) 294a8806e5eSIngo Weinhold return 0; 295453a2bddSIngo Weinhold 296453a2bddSIngo Weinhold readLocker.Lock(); 297453a2bddSIngo Weinhold interruptsLocker.Lock(); 298453a2bddSIngo Weinhold 299453a2bddSIngo Weinhold push_empty_magazine(depot, magazine); 300453a2bddSIngo Weinhold store = object_depot_cpu(depot); 301a8806e5eSIngo Weinhold } 302a8806e5eSIngo Weinhold } 303a8806e5eSIngo Weinhold } 304a8806e5eSIngo Weinhold 305a8806e5eSIngo Weinhold 306a8806e5eSIngo Weinhold void 30786c794e5SIngo Weinhold object_depot_make_empty(object_depot* depot, uint32 flags) 308a8806e5eSIngo Weinhold { 309453a2bddSIngo Weinhold WriteLocker writeLocker(depot->outer_lock); 310453a2bddSIngo Weinhold 311453a2bddSIngo Weinhold // collect the store magazines 312453a2bddSIngo Weinhold 313453a2bddSIngo Weinhold DepotMagazine* storeMagazines = NULL; 314453a2bddSIngo Weinhold 315bcf73b3aSIngo Weinhold int cpuCount = smp_get_num_cpus(); 316bcf73b3aSIngo Weinhold for (int i = 0; i < cpuCount; i++) { 317453a2bddSIngo Weinhold depot_cpu_store& store = depot->stores[i]; 318a8806e5eSIngo Weinhold 319453a2bddSIngo Weinhold if (store.loaded) { 320453a2bddSIngo Weinhold _push(storeMagazines, store.loaded); 321453a2bddSIngo Weinhold store.loaded = NULL; 322a8806e5eSIngo Weinhold } 323a8806e5eSIngo Weinhold 324453a2bddSIngo Weinhold if (store.previous) { 325453a2bddSIngo Weinhold _push(storeMagazines, store.previous); 326453a2bddSIngo Weinhold store.previous = NULL; 327453a2bddSIngo Weinhold } 328453a2bddSIngo Weinhold } 329a8806e5eSIngo Weinhold 330453a2bddSIngo Weinhold // detach the depot's full and empty magazines 331a8806e5eSIngo Weinhold 332453a2bddSIngo Weinhold DepotMagazine* fullMagazines = depot->full; 333453a2bddSIngo Weinhold depot->full = NULL; 334453a2bddSIngo Weinhold 335453a2bddSIngo Weinhold DepotMagazine* emptyMagazines = depot->empty; 336453a2bddSIngo Weinhold depot->empty = NULL; 337453a2bddSIngo Weinhold 338453a2bddSIngo Weinhold writeLocker.Unlock(); 339453a2bddSIngo Weinhold 340453a2bddSIngo Weinhold // free all magazines 341453a2bddSIngo Weinhold 342453a2bddSIngo Weinhold while (storeMagazines != NULL) 34386c794e5SIngo Weinhold empty_magazine(depot, _pop(storeMagazines), flags); 344453a2bddSIngo Weinhold 345453a2bddSIngo Weinhold while (fullMagazines != NULL) 34686c794e5SIngo Weinhold empty_magazine(depot, _pop(fullMagazines), flags); 347453a2bddSIngo Weinhold 348453a2bddSIngo Weinhold while (emptyMagazines) 34986c794e5SIngo Weinhold free_magazine(_pop(emptyMagazines), flags); 350a8806e5eSIngo Weinhold } 35154f3267eSAxel Dörfler 35254f3267eSAxel Dörfler 35354f3267eSAxel Dörfler // #pragma mark - private kernel API 35454f3267eSAxel Dörfler 35554f3267eSAxel Dörfler 35654f3267eSAxel Dörfler void 35754f3267eSAxel Dörfler dump_object_depot(object_depot* depot) 35854f3267eSAxel Dörfler { 35954f3267eSAxel Dörfler kprintf(" full: %p, count %lu\n", depot->full, depot->full_count); 36054f3267eSAxel Dörfler kprintf(" empty: %p, count %lu\n", depot->empty, depot->empty_count); 36154f3267eSAxel Dörfler kprintf(" stores:\n"); 36254f3267eSAxel Dörfler 36354f3267eSAxel Dörfler int cpuCount = smp_get_num_cpus(); 36454f3267eSAxel Dörfler 36554f3267eSAxel Dörfler for (int i = 0; i < cpuCount; i++) { 36654f3267eSAxel Dörfler kprintf(" [%d] loaded: %p\n", i, depot->stores[i].loaded); 36754f3267eSAxel Dörfler kprintf(" previous: %p\n", depot->stores[i].previous); 36854f3267eSAxel Dörfler } 36954f3267eSAxel Dörfler } 37054f3267eSAxel Dörfler 37154f3267eSAxel Dörfler 37254f3267eSAxel Dörfler int 37354f3267eSAxel Dörfler dump_object_depot(int argCount, char** args) 37454f3267eSAxel Dörfler { 37554f3267eSAxel Dörfler if (argCount != 2) 37654f3267eSAxel Dörfler kprintf("usage: %s [address]\n", args[0]); 37754f3267eSAxel Dörfler else 37854f3267eSAxel Dörfler dump_object_depot((object_depot*)parse_expression(args[1])); 37954f3267eSAxel Dörfler 38054f3267eSAxel Dörfler return 0; 38154f3267eSAxel Dörfler } 38254f3267eSAxel Dörfler 38354f3267eSAxel Dörfler 38454f3267eSAxel Dörfler int 38554f3267eSAxel Dörfler dump_depot_magazine(int argCount, char** args) 38654f3267eSAxel Dörfler { 38754f3267eSAxel Dörfler if (argCount != 2) { 38854f3267eSAxel Dörfler kprintf("usage: %s [address]\n", args[0]); 38954f3267eSAxel Dörfler return 0; 39054f3267eSAxel Dörfler } 39154f3267eSAxel Dörfler 39254f3267eSAxel Dörfler DepotMagazine* magazine = (DepotMagazine*)parse_expression(args[1]); 39354f3267eSAxel Dörfler 39454f3267eSAxel Dörfler kprintf("next: %p\n", magazine->next); 39554f3267eSAxel Dörfler kprintf("current_round: %u\n", magazine->current_round); 39654f3267eSAxel Dörfler kprintf("round_count: %u\n", magazine->round_count); 39754f3267eSAxel Dörfler 39854f3267eSAxel Dörfler for (uint16 i = 0; i < magazine->current_round; i++) 39954f3267eSAxel Dörfler kprintf(" [%i] %p\n", i, magazine->rounds[i]); 40054f3267eSAxel Dörfler 40154f3267eSAxel Dörfler return 0; 40254f3267eSAxel Dörfler } 403