1a8806e5eSIngo Weinhold /* 2453a2bddSIngo Weinhold * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3a8806e5eSIngo Weinhold * Copyright 2008, 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 22a8806e5eSIngo Weinhold static const int kMagazineCapacity = 32; 23a8806e5eSIngo Weinhold // TODO: Should be dynamically tuned per cache. 24a8806e5eSIngo Weinhold 25a8806e5eSIngo Weinhold 26825566f8SIngo Weinhold struct DepotMagazine { 27825566f8SIngo Weinhold DepotMagazine* next; 28825566f8SIngo Weinhold uint16 current_round; 29825566f8SIngo Weinhold uint16 round_count; 30a8806e5eSIngo Weinhold void* rounds[0]; 31825566f8SIngo Weinhold 32825566f8SIngo Weinhold public: 33825566f8SIngo Weinhold inline bool IsEmpty() const; 34825566f8SIngo Weinhold inline bool IsFull() const; 35825566f8SIngo Weinhold 36825566f8SIngo Weinhold inline void* Pop(); 37825566f8SIngo Weinhold inline bool Push(void* object); 38a8806e5eSIngo Weinhold }; 39a8806e5eSIngo Weinhold 40a8806e5eSIngo Weinhold 41a8806e5eSIngo Weinhold struct depot_cpu_store { 42825566f8SIngo Weinhold DepotMagazine* loaded; 43825566f8SIngo Weinhold DepotMagazine* previous; 44a8806e5eSIngo Weinhold }; 45a8806e5eSIngo Weinhold 46a8806e5eSIngo Weinhold 47825566f8SIngo Weinhold bool 48825566f8SIngo Weinhold DepotMagazine::IsEmpty() const 49a8806e5eSIngo Weinhold { 50825566f8SIngo Weinhold return current_round == 0; 51a8806e5eSIngo Weinhold } 52a8806e5eSIngo Weinhold 53a8806e5eSIngo Weinhold 54825566f8SIngo Weinhold bool 55825566f8SIngo Weinhold DepotMagazine::IsFull() const 56a8806e5eSIngo Weinhold { 57825566f8SIngo Weinhold return current_round == round_count; 58a8806e5eSIngo Weinhold } 59a8806e5eSIngo Weinhold 60a8806e5eSIngo Weinhold 61825566f8SIngo Weinhold void* 62825566f8SIngo Weinhold DepotMagazine::Pop() 63a8806e5eSIngo Weinhold { 64825566f8SIngo Weinhold return rounds[--current_round]; 65a8806e5eSIngo Weinhold } 66a8806e5eSIngo Weinhold 67a8806e5eSIngo Weinhold 68825566f8SIngo Weinhold bool 69825566f8SIngo Weinhold DepotMagazine::Push(void* object) 70a8806e5eSIngo Weinhold { 71825566f8SIngo Weinhold if (IsFull()) 72a8806e5eSIngo Weinhold return false; 73825566f8SIngo Weinhold 74825566f8SIngo Weinhold rounds[current_round++] = object; 75a8806e5eSIngo Weinhold return true; 76a8806e5eSIngo Weinhold } 77a8806e5eSIngo Weinhold 78a8806e5eSIngo Weinhold 79825566f8SIngo Weinhold static DepotMagazine* 80a8806e5eSIngo Weinhold alloc_magazine() 81a8806e5eSIngo Weinhold { 82825566f8SIngo Weinhold DepotMagazine* magazine = (DepotMagazine*)slab_internal_alloc( 83bb439b87SIngo Weinhold sizeof(DepotMagazine) + kMagazineCapacity * sizeof(void*), 84bb439b87SIngo Weinhold CACHE_DONT_SLEEP); 85a8806e5eSIngo Weinhold if (magazine) { 86a8806e5eSIngo Weinhold magazine->next = NULL; 87a8806e5eSIngo Weinhold magazine->current_round = 0; 88a8806e5eSIngo Weinhold magazine->round_count = kMagazineCapacity; 89a8806e5eSIngo Weinhold } 90a8806e5eSIngo Weinhold 91a8806e5eSIngo Weinhold return magazine; 92a8806e5eSIngo Weinhold } 93a8806e5eSIngo Weinhold 94a8806e5eSIngo Weinhold 95a8806e5eSIngo Weinhold static void 96*86c794e5SIngo Weinhold free_magazine(DepotMagazine* magazine, uint32 flags) 97a8806e5eSIngo Weinhold { 98*86c794e5SIngo Weinhold slab_internal_free(magazine, flags); 99a8806e5eSIngo Weinhold } 100a8806e5eSIngo Weinhold 101a8806e5eSIngo Weinhold 102a8806e5eSIngo Weinhold static void 103*86c794e5SIngo Weinhold empty_magazine(object_depot* depot, DepotMagazine* magazine, uint32 flags) 104a8806e5eSIngo Weinhold { 105a8806e5eSIngo Weinhold for (uint16 i = 0; i < magazine->current_round; i++) 106*86c794e5SIngo Weinhold depot->return_object(depot, depot->cookie, magazine->rounds[i], flags); 107*86c794e5SIngo Weinhold free_magazine(magazine, flags); 108a8806e5eSIngo Weinhold } 109a8806e5eSIngo Weinhold 110a8806e5eSIngo Weinhold 111a8806e5eSIngo Weinhold static bool 112825566f8SIngo Weinhold exchange_with_full(object_depot* depot, DepotMagazine*& magazine) 113a8806e5eSIngo Weinhold { 114453a2bddSIngo Weinhold SpinLocker _(depot->inner_lock); 115a8806e5eSIngo Weinhold 116a8806e5eSIngo Weinhold if (depot->full == NULL) 117a8806e5eSIngo Weinhold return false; 118a8806e5eSIngo Weinhold 119a8806e5eSIngo Weinhold depot->full_count--; 120a8806e5eSIngo Weinhold depot->empty_count++; 121a8806e5eSIngo Weinhold 122a8806e5eSIngo Weinhold _push(depot->empty, magazine); 123a8806e5eSIngo Weinhold magazine = _pop(depot->full); 124a8806e5eSIngo Weinhold return true; 125a8806e5eSIngo Weinhold } 126a8806e5eSIngo Weinhold 127a8806e5eSIngo Weinhold 128a8806e5eSIngo Weinhold static bool 129825566f8SIngo Weinhold exchange_with_empty(object_depot* depot, DepotMagazine*& magazine) 130a8806e5eSIngo Weinhold { 131453a2bddSIngo Weinhold SpinLocker _(depot->inner_lock); 132a8806e5eSIngo Weinhold 133a8806e5eSIngo Weinhold if (depot->empty == NULL) 134a8806e5eSIngo Weinhold return false; 135453a2bddSIngo Weinhold 136a8806e5eSIngo Weinhold depot->empty_count--; 137a8806e5eSIngo Weinhold 138a8806e5eSIngo Weinhold if (magazine) { 139a8806e5eSIngo Weinhold _push(depot->full, magazine); 140a8806e5eSIngo Weinhold depot->full_count++; 141a8806e5eSIngo Weinhold } 142a8806e5eSIngo Weinhold 143a8806e5eSIngo Weinhold magazine = _pop(depot->empty); 144a8806e5eSIngo Weinhold return true; 145a8806e5eSIngo Weinhold } 146a8806e5eSIngo Weinhold 147a8806e5eSIngo Weinhold 148453a2bddSIngo Weinhold static void 149453a2bddSIngo Weinhold push_empty_magazine(object_depot* depot, DepotMagazine* magazine) 150453a2bddSIngo Weinhold { 151453a2bddSIngo Weinhold SpinLocker _(depot->inner_lock); 152453a2bddSIngo Weinhold 153453a2bddSIngo Weinhold _push(depot->empty, magazine); 154453a2bddSIngo Weinhold } 155453a2bddSIngo Weinhold 156453a2bddSIngo Weinhold 157a8806e5eSIngo Weinhold static inline depot_cpu_store* 158a8806e5eSIngo Weinhold object_depot_cpu(object_depot* depot) 159a8806e5eSIngo Weinhold { 160a8806e5eSIngo Weinhold return &depot->stores[smp_get_current_cpu()]; 161a8806e5eSIngo Weinhold } 162a8806e5eSIngo Weinhold 163a8806e5eSIngo Weinhold 164453a2bddSIngo Weinhold // #pragma mark - public API 165453a2bddSIngo Weinhold 166453a2bddSIngo Weinhold 167453a2bddSIngo Weinhold status_t 168453a2bddSIngo Weinhold object_depot_init(object_depot* depot, uint32 flags, void* cookie, 169*86c794e5SIngo Weinhold void (*return_object)(object_depot* depot, void* cookie, void* object, 170*86c794e5SIngo Weinhold uint32 flags)) 171a8806e5eSIngo Weinhold { 172453a2bddSIngo Weinhold depot->full = NULL; 173453a2bddSIngo Weinhold depot->empty = NULL; 174453a2bddSIngo Weinhold depot->full_count = depot->empty_count = 0; 175453a2bddSIngo Weinhold 176453a2bddSIngo Weinhold rw_lock_init(&depot->outer_lock, "object depot"); 177453a2bddSIngo Weinhold B_INITIALIZE_SPINLOCK(&depot->inner_lock); 178453a2bddSIngo Weinhold 179453a2bddSIngo Weinhold int cpuCount = smp_get_num_cpus(); 180453a2bddSIngo Weinhold depot->stores = (depot_cpu_store*)slab_internal_alloc( 181453a2bddSIngo Weinhold sizeof(depot_cpu_store) * cpuCount, flags); 182453a2bddSIngo Weinhold if (depot->stores == NULL) { 183453a2bddSIngo Weinhold rw_lock_destroy(&depot->outer_lock); 184453a2bddSIngo Weinhold return B_NO_MEMORY; 185453a2bddSIngo Weinhold } 186453a2bddSIngo Weinhold 187453a2bddSIngo Weinhold for (int i = 0; i < cpuCount; i++) { 188453a2bddSIngo Weinhold depot->stores[i].loaded = NULL; 189453a2bddSIngo Weinhold depot->stores[i].previous = NULL; 190453a2bddSIngo Weinhold } 191453a2bddSIngo Weinhold 192453a2bddSIngo Weinhold depot->cookie = cookie; 193453a2bddSIngo Weinhold depot->return_object = return_object; 194453a2bddSIngo Weinhold 195453a2bddSIngo Weinhold return B_OK; 196453a2bddSIngo Weinhold } 197453a2bddSIngo Weinhold 198453a2bddSIngo Weinhold 199453a2bddSIngo Weinhold void 200*86c794e5SIngo Weinhold object_depot_destroy(object_depot* depot, uint32 flags) 201453a2bddSIngo Weinhold { 202*86c794e5SIngo Weinhold object_depot_make_empty(depot, flags); 203453a2bddSIngo Weinhold 204*86c794e5SIngo Weinhold slab_internal_free(depot->stores, flags); 205453a2bddSIngo Weinhold 206453a2bddSIngo Weinhold rw_lock_destroy(&depot->outer_lock); 207453a2bddSIngo Weinhold } 208453a2bddSIngo Weinhold 209453a2bddSIngo Weinhold 210453a2bddSIngo Weinhold void* 211453a2bddSIngo Weinhold object_depot_obtain(object_depot* depot) 212453a2bddSIngo Weinhold { 213453a2bddSIngo Weinhold ReadLocker readLocker(depot->outer_lock); 214453a2bddSIngo Weinhold InterruptsLocker interruptsLocker; 215453a2bddSIngo Weinhold 216453a2bddSIngo Weinhold depot_cpu_store* store = object_depot_cpu(depot); 217a8806e5eSIngo Weinhold 218a8806e5eSIngo Weinhold // To better understand both the Alloc() and Free() logic refer to 219a8806e5eSIngo Weinhold // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] 220a8806e5eSIngo Weinhold 221a8806e5eSIngo Weinhold // In a nutshell, we try to get an object from the loaded magazine 222a8806e5eSIngo Weinhold // if it's not empty, or from the previous magazine if it's full 223a8806e5eSIngo Weinhold // and finally from the Slab if the magazine depot has no full magazines. 224a8806e5eSIngo Weinhold 225a8806e5eSIngo Weinhold if (store->loaded == NULL) 226a8806e5eSIngo Weinhold return NULL; 227a8806e5eSIngo Weinhold 228a8806e5eSIngo Weinhold while (true) { 229825566f8SIngo Weinhold if (!store->loaded->IsEmpty()) 230825566f8SIngo Weinhold return store->loaded->Pop(); 231a8806e5eSIngo Weinhold 232825566f8SIngo Weinhold if (store->previous 233825566f8SIngo Weinhold && (store->previous->IsFull() 234825566f8SIngo Weinhold || exchange_with_full(depot, store->previous))) { 235a8806e5eSIngo Weinhold std::swap(store->previous, store->loaded); 236825566f8SIngo Weinhold } else 237a8806e5eSIngo Weinhold return NULL; 238a8806e5eSIngo Weinhold } 239a8806e5eSIngo Weinhold } 240a8806e5eSIngo Weinhold 241a8806e5eSIngo Weinhold 242453a2bddSIngo Weinhold int 243*86c794e5SIngo Weinhold object_depot_store(object_depot* depot, void* object, uint32 flags) 244a8806e5eSIngo Weinhold { 245453a2bddSIngo Weinhold ReadLocker readLocker(depot->outer_lock); 246453a2bddSIngo Weinhold InterruptsLocker interruptsLocker; 247453a2bddSIngo Weinhold 248453a2bddSIngo Weinhold depot_cpu_store* store = object_depot_cpu(depot); 249a8806e5eSIngo Weinhold 250a8806e5eSIngo Weinhold // We try to add the object to the loaded magazine if we have one 251a8806e5eSIngo Weinhold // and it's not full, or to the previous one if it is empty. If 252a8806e5eSIngo Weinhold // the magazine depot doesn't provide us with a new empty magazine 253a8806e5eSIngo Weinhold // we return the object directly to the slab. 254a8806e5eSIngo Weinhold 255a8806e5eSIngo Weinhold while (true) { 256825566f8SIngo Weinhold if (store->loaded && store->loaded->Push(object)) 257a8806e5eSIngo Weinhold return 1; 258a8806e5eSIngo Weinhold 259825566f8SIngo Weinhold if ((store->previous && store->previous->IsEmpty()) 260453a2bddSIngo Weinhold || exchange_with_empty(depot, store->previous)) { 261a8806e5eSIngo Weinhold std::swap(store->loaded, store->previous); 262453a2bddSIngo Weinhold } else { 263453a2bddSIngo Weinhold // allocate a new empty magazine 264453a2bddSIngo Weinhold interruptsLocker.Unlock(); 265453a2bddSIngo Weinhold readLocker.Unlock(); 266453a2bddSIngo Weinhold 267453a2bddSIngo Weinhold DepotMagazine* magazine = alloc_magazine(); 268453a2bddSIngo Weinhold if (magazine == NULL) 269a8806e5eSIngo Weinhold return 0; 270453a2bddSIngo Weinhold 271453a2bddSIngo Weinhold readLocker.Lock(); 272453a2bddSIngo Weinhold interruptsLocker.Lock(); 273453a2bddSIngo Weinhold 274453a2bddSIngo Weinhold push_empty_magazine(depot, magazine); 275453a2bddSIngo Weinhold store = object_depot_cpu(depot); 276a8806e5eSIngo Weinhold } 277a8806e5eSIngo Weinhold } 278a8806e5eSIngo Weinhold } 279a8806e5eSIngo Weinhold 280a8806e5eSIngo Weinhold 281a8806e5eSIngo Weinhold void 282*86c794e5SIngo Weinhold object_depot_make_empty(object_depot* depot, uint32 flags) 283a8806e5eSIngo Weinhold { 284453a2bddSIngo Weinhold WriteLocker writeLocker(depot->outer_lock); 285453a2bddSIngo Weinhold 286453a2bddSIngo Weinhold // collect the store magazines 287453a2bddSIngo Weinhold 288453a2bddSIngo Weinhold DepotMagazine* storeMagazines = NULL; 289453a2bddSIngo Weinhold 290bcf73b3aSIngo Weinhold int cpuCount = smp_get_num_cpus(); 291bcf73b3aSIngo Weinhold for (int i = 0; i < cpuCount; i++) { 292453a2bddSIngo Weinhold depot_cpu_store& store = depot->stores[i]; 293a8806e5eSIngo Weinhold 294453a2bddSIngo Weinhold if (store.loaded) { 295453a2bddSIngo Weinhold _push(storeMagazines, store.loaded); 296453a2bddSIngo Weinhold store.loaded = NULL; 297a8806e5eSIngo Weinhold } 298a8806e5eSIngo Weinhold 299453a2bddSIngo Weinhold if (store.previous) { 300453a2bddSIngo Weinhold _push(storeMagazines, store.previous); 301453a2bddSIngo Weinhold store.previous = NULL; 302453a2bddSIngo Weinhold } 303453a2bddSIngo Weinhold } 304a8806e5eSIngo Weinhold 305453a2bddSIngo Weinhold // detach the depot's full and empty magazines 306a8806e5eSIngo Weinhold 307453a2bddSIngo Weinhold DepotMagazine* fullMagazines = depot->full; 308453a2bddSIngo Weinhold depot->full = NULL; 309453a2bddSIngo Weinhold 310453a2bddSIngo Weinhold DepotMagazine* emptyMagazines = depot->empty; 311453a2bddSIngo Weinhold depot->empty = NULL; 312453a2bddSIngo Weinhold 313453a2bddSIngo Weinhold writeLocker.Unlock(); 314453a2bddSIngo Weinhold 315453a2bddSIngo Weinhold // free all magazines 316453a2bddSIngo Weinhold 317453a2bddSIngo Weinhold while (storeMagazines != NULL) 318*86c794e5SIngo Weinhold empty_magazine(depot, _pop(storeMagazines), flags); 319453a2bddSIngo Weinhold 320453a2bddSIngo Weinhold while (fullMagazines != NULL) 321*86c794e5SIngo Weinhold empty_magazine(depot, _pop(fullMagazines), flags); 322453a2bddSIngo Weinhold 323453a2bddSIngo Weinhold while (emptyMagazines) 324*86c794e5SIngo Weinhold free_magazine(_pop(emptyMagazines), flags); 325a8806e5eSIngo Weinhold } 326