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* 79ff59ce68SAxel Dörfler alloc_magazine(object_depot* depot, uint32 flags) 80a8806e5eSIngo Weinhold { 81825566f8SIngo Weinhold DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc( 82ff59ce68SAxel Dörfler sizeof(DepotMagazine) + depot->magazine_capacity * sizeof(void*), 83ff59ce68SAxel Dörfler flags); 84a8806e5eSIngo Weinhold if (magazine) { 85a8806e5eSIngo Weinhold magazine->next = NULL; 86a8806e5eSIngo Weinhold magazine->current_round = 0; 87ff59ce68SAxel 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 { 113ff59ce68SAxel Dörfler ASSERT(magazine->IsEmpty()); 114ff59ce68SAxel 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 130ff59ce68SAxel Dörfler exchange_with_empty(object_depot* depot, DepotMagazine*& magazine, 131ff59ce68SAxel Dörfler DepotMagazine*& freeMagazine) 132a8806e5eSIngo Weinhold { 133ff59ce68SAxel Dörfler ASSERT(magazine == NULL || magazine->IsFull()); 134ff59ce68SAxel 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 142ff59ce68SAxel Dörfler if (magazine != NULL) { 143ff59ce68SAxel Dörfler if (depot->full_count < depot->max_count) { 144a8806e5eSIngo Weinhold _push(depot->full, magazine); 145a8806e5eSIngo Weinhold depot->full_count++; 146ff59ce68SAxel Dörfler } else 147ff59ce68SAxel 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 176ff59ce68SAxel Dörfler object_depot_init(object_depot* depot, size_t capacity, size_t maxCount, 177ff59ce68SAxel Dörfler uint32 flags, void* cookie, void (*return_object)(object_depot* depot, 178ff59ce68SAxel 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; 183ff59ce68SAxel Dörfler depot->max_count = maxCount; 184ff59ce68SAxel 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 { 255ff59ce68SAxel Dörfler DepotMagazine* freeMagazine = NULL; 256ff59ce68SAxel 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) { 268ff59ce68SAxel Dörfler if (store->loaded != NULL && store->loaded->Push(object)) 269a8806e5eSIngo Weinhold return 1; 270a8806e5eSIngo Weinhold 271ff59ce68SAxel Dörfler if ((store->previous != NULL && store->previous->IsEmpty()) 272ff59ce68SAxel Dörfler || exchange_with_empty(depot, store->previous, freeMagazine)) { 273a8806e5eSIngo Weinhold std::swap(store->loaded, store->previous); 274ff59ce68SAxel Dörfler 275ff59ce68SAxel Dörfler if (freeMagazine != NULL) { 276ff59ce68SAxel Dörfler // Free the magazine that didn't have space in the list 277ff59ce68SAxel Dörfler interruptsLocker.Unlock(); 278ff59ce68SAxel Dörfler readLocker.Unlock(); 279ff59ce68SAxel Dörfler 280ff59ce68SAxel Dörfler empty_magazine(depot, freeMagazine, flags); 281ff59ce68SAxel Dörfler 282ff59ce68SAxel Dörfler readLocker.Lock(); 283ff59ce68SAxel Dörfler interruptsLocker.Lock(); 284ff59ce68SAxel Dörfler 285ff59ce68SAxel Dörfler store = object_depot_cpu(depot); 286ff59ce68SAxel Dörfler } 287453a2bddSIngo Weinhold } else { 288453a2bddSIngo Weinhold // allocate a new empty magazine 289453a2bddSIngo Weinhold interruptsLocker.Unlock(); 290453a2bddSIngo Weinhold readLocker.Unlock(); 291453a2bddSIngo Weinhold 292ff59ce68SAxel Dörfler DepotMagazine* magazine = alloc_magazine(depot, flags); 293*462dd94aSIngo Weinhold if (magazine == NULL) { 294*462dd94aSIngo Weinhold depot->return_object(depot, depot->cookie, object, flags); 295a8806e5eSIngo Weinhold return 0; 296*462dd94aSIngo Weinhold } 297453a2bddSIngo Weinhold 298453a2bddSIngo Weinhold readLocker.Lock(); 299453a2bddSIngo Weinhold interruptsLocker.Lock(); 300453a2bddSIngo Weinhold 301453a2bddSIngo Weinhold push_empty_magazine(depot, magazine); 302453a2bddSIngo Weinhold store = object_depot_cpu(depot); 303a8806e5eSIngo Weinhold } 304a8806e5eSIngo Weinhold } 305a8806e5eSIngo Weinhold } 306a8806e5eSIngo Weinhold 307a8806e5eSIngo Weinhold 308a8806e5eSIngo Weinhold void 30986c794e5SIngo Weinhold object_depot_make_empty(object_depot* depot, uint32 flags) 310a8806e5eSIngo Weinhold { 311453a2bddSIngo Weinhold WriteLocker writeLocker(depot->outer_lock); 312453a2bddSIngo Weinhold 313453a2bddSIngo Weinhold // collect the store magazines 314453a2bddSIngo Weinhold 315453a2bddSIngo Weinhold DepotMagazine* storeMagazines = NULL; 316453a2bddSIngo Weinhold 317bcf73b3aSIngo Weinhold int cpuCount = smp_get_num_cpus(); 318bcf73b3aSIngo Weinhold for (int i = 0; i < cpuCount; i++) { 319453a2bddSIngo Weinhold depot_cpu_store& store = depot->stores[i]; 320a8806e5eSIngo Weinhold 321453a2bddSIngo Weinhold if (store.loaded) { 322453a2bddSIngo Weinhold _push(storeMagazines, store.loaded); 323453a2bddSIngo Weinhold store.loaded = NULL; 324a8806e5eSIngo Weinhold } 325a8806e5eSIngo Weinhold 326453a2bddSIngo Weinhold if (store.previous) { 327453a2bddSIngo Weinhold _push(storeMagazines, store.previous); 328453a2bddSIngo Weinhold store.previous = NULL; 329453a2bddSIngo Weinhold } 330453a2bddSIngo Weinhold } 331a8806e5eSIngo Weinhold 332453a2bddSIngo Weinhold // detach the depot's full and empty magazines 333a8806e5eSIngo Weinhold 334453a2bddSIngo Weinhold DepotMagazine* fullMagazines = depot->full; 335453a2bddSIngo Weinhold depot->full = NULL; 336453a2bddSIngo Weinhold 337453a2bddSIngo Weinhold DepotMagazine* emptyMagazines = depot->empty; 338453a2bddSIngo Weinhold depot->empty = NULL; 339453a2bddSIngo Weinhold 340453a2bddSIngo Weinhold writeLocker.Unlock(); 341453a2bddSIngo Weinhold 342453a2bddSIngo Weinhold // free all magazines 343453a2bddSIngo Weinhold 344453a2bddSIngo Weinhold while (storeMagazines != NULL) 34586c794e5SIngo Weinhold empty_magazine(depot, _pop(storeMagazines), flags); 346453a2bddSIngo Weinhold 347453a2bddSIngo Weinhold while (fullMagazines != NULL) 34886c794e5SIngo Weinhold empty_magazine(depot, _pop(fullMagazines), flags); 349453a2bddSIngo Weinhold 350453a2bddSIngo Weinhold while (emptyMagazines) 35186c794e5SIngo Weinhold free_magazine(_pop(emptyMagazines), flags); 352a8806e5eSIngo Weinhold } 35354f3267eSAxel Dörfler 35454f3267eSAxel Dörfler 35554f3267eSAxel Dörfler // #pragma mark - private kernel API 35654f3267eSAxel Dörfler 35754f3267eSAxel Dörfler 35854f3267eSAxel Dörfler void 35954f3267eSAxel Dörfler dump_object_depot(object_depot* depot) 36054f3267eSAxel Dörfler { 36154f3267eSAxel Dörfler kprintf(" full: %p, count %lu\n", depot->full, depot->full_count); 36254f3267eSAxel Dörfler kprintf(" empty: %p, count %lu\n", depot->empty, depot->empty_count); 3636426413dSAxel Dörfler kprintf(" max full: %lu\n", depot->max_count); 3646426413dSAxel Dörfler kprintf(" capacity: %lu\n", depot->magazine_capacity); 36554f3267eSAxel Dörfler kprintf(" stores:\n"); 36654f3267eSAxel Dörfler 36754f3267eSAxel Dörfler int cpuCount = smp_get_num_cpus(); 36854f3267eSAxel Dörfler 36954f3267eSAxel Dörfler for (int i = 0; i < cpuCount; i++) { 37054f3267eSAxel Dörfler kprintf(" [%d] loaded: %p\n", i, depot->stores[i].loaded); 37154f3267eSAxel Dörfler kprintf(" previous: %p\n", depot->stores[i].previous); 37254f3267eSAxel Dörfler } 37354f3267eSAxel Dörfler } 37454f3267eSAxel Dörfler 37554f3267eSAxel Dörfler 37654f3267eSAxel Dörfler int 37754f3267eSAxel Dörfler dump_object_depot(int argCount, char** args) 37854f3267eSAxel Dörfler { 37954f3267eSAxel Dörfler if (argCount != 2) 38054f3267eSAxel Dörfler kprintf("usage: %s [address]\n", args[0]); 38154f3267eSAxel Dörfler else 38254f3267eSAxel Dörfler dump_object_depot((object_depot*)parse_expression(args[1])); 38354f3267eSAxel Dörfler 38454f3267eSAxel Dörfler return 0; 38554f3267eSAxel Dörfler } 38654f3267eSAxel Dörfler 38754f3267eSAxel Dörfler 38854f3267eSAxel Dörfler int 38954f3267eSAxel Dörfler dump_depot_magazine(int argCount, char** args) 39054f3267eSAxel Dörfler { 39154f3267eSAxel Dörfler if (argCount != 2) { 39254f3267eSAxel Dörfler kprintf("usage: %s [address]\n", args[0]); 39354f3267eSAxel Dörfler return 0; 39454f3267eSAxel Dörfler } 39554f3267eSAxel Dörfler 39654f3267eSAxel Dörfler DepotMagazine* magazine = (DepotMagazine*)parse_expression(args[1]); 39754f3267eSAxel Dörfler 39854f3267eSAxel Dörfler kprintf("next: %p\n", magazine->next); 39954f3267eSAxel Dörfler kprintf("current_round: %u\n", magazine->current_round); 40054f3267eSAxel Dörfler kprintf("round_count: %u\n", magazine->round_count); 40154f3267eSAxel Dörfler 40254f3267eSAxel Dörfler for (uint16 i = 0; i < magazine->current_round; i++) 40354f3267eSAxel Dörfler kprintf(" [%i] %p\n", i, magazine->rounds[i]); 40454f3267eSAxel Dörfler 40554f3267eSAxel Dörfler return 0; 40654f3267eSAxel Dörfler } 407