1*a8806e5eSIngo Weinhold /* 2*a8806e5eSIngo Weinhold * Copyright 2008, Axel Dörfler. All Rights Reserved. 3*a8806e5eSIngo Weinhold * Copyright 2007, Hugo Santos. All Rights Reserved. 4*a8806e5eSIngo Weinhold * 5*a8806e5eSIngo Weinhold * Distributed under the terms of the MIT License. 6*a8806e5eSIngo Weinhold */ 7*a8806e5eSIngo Weinhold 8*a8806e5eSIngo Weinhold 9*a8806e5eSIngo Weinhold #include <slab/ObjectDepot.h> 10*a8806e5eSIngo Weinhold 11*a8806e5eSIngo Weinhold #include <algorithm> 12*a8806e5eSIngo Weinhold 13*a8806e5eSIngo Weinhold #include <util/AutoLock.h> 14*a8806e5eSIngo Weinhold 15*a8806e5eSIngo Weinhold #include "slab_private.h" 16*a8806e5eSIngo Weinhold 17*a8806e5eSIngo Weinhold 18*a8806e5eSIngo Weinhold static const int kMagazineCapacity = 32; 19*a8806e5eSIngo Weinhold // TODO: Should be dynamically tuned per cache. 20*a8806e5eSIngo Weinhold 21*a8806e5eSIngo Weinhold 22*a8806e5eSIngo Weinhold struct depot_magazine { 23*a8806e5eSIngo Weinhold struct depot_magazine *next; 24*a8806e5eSIngo Weinhold uint16 current_round, round_count; 25*a8806e5eSIngo Weinhold void *rounds[0]; 26*a8806e5eSIngo Weinhold }; 27*a8806e5eSIngo Weinhold 28*a8806e5eSIngo Weinhold 29*a8806e5eSIngo Weinhold struct depot_cpu_store { 30*a8806e5eSIngo Weinhold recursive_lock lock; 31*a8806e5eSIngo Weinhold struct depot_magazine *loaded, *previous; 32*a8806e5eSIngo Weinhold }; 33*a8806e5eSIngo Weinhold 34*a8806e5eSIngo Weinhold 35*a8806e5eSIngo Weinhold static inline bool 36*a8806e5eSIngo Weinhold is_magazine_empty(depot_magazine *magazine) 37*a8806e5eSIngo Weinhold { 38*a8806e5eSIngo Weinhold return magazine->current_round == 0; 39*a8806e5eSIngo Weinhold } 40*a8806e5eSIngo Weinhold 41*a8806e5eSIngo Weinhold 42*a8806e5eSIngo Weinhold static inline bool 43*a8806e5eSIngo Weinhold is_magazine_full(depot_magazine *magazine) 44*a8806e5eSIngo Weinhold { 45*a8806e5eSIngo Weinhold return magazine->current_round == magazine->round_count; 46*a8806e5eSIngo Weinhold } 47*a8806e5eSIngo Weinhold 48*a8806e5eSIngo Weinhold 49*a8806e5eSIngo Weinhold static inline void * 50*a8806e5eSIngo Weinhold pop_magazine(depot_magazine *magazine) 51*a8806e5eSIngo Weinhold { 52*a8806e5eSIngo Weinhold return magazine->rounds[--magazine->current_round]; 53*a8806e5eSIngo Weinhold } 54*a8806e5eSIngo Weinhold 55*a8806e5eSIngo Weinhold 56*a8806e5eSIngo Weinhold static inline bool 57*a8806e5eSIngo Weinhold push_magazine(depot_magazine *magazine, void *object) 58*a8806e5eSIngo Weinhold { 59*a8806e5eSIngo Weinhold if (is_magazine_full(magazine)) 60*a8806e5eSIngo Weinhold return false; 61*a8806e5eSIngo Weinhold magazine->rounds[magazine->current_round++] = object; 62*a8806e5eSIngo Weinhold return true; 63*a8806e5eSIngo Weinhold } 64*a8806e5eSIngo Weinhold 65*a8806e5eSIngo Weinhold 66*a8806e5eSIngo Weinhold static depot_magazine * 67*a8806e5eSIngo Weinhold alloc_magazine() 68*a8806e5eSIngo Weinhold { 69*a8806e5eSIngo Weinhold depot_magazine *magazine = (depot_magazine *)internal_alloc( 70*a8806e5eSIngo Weinhold sizeof(depot_magazine) + kMagazineCapacity * sizeof(void *), 0); 71*a8806e5eSIngo Weinhold if (magazine) { 72*a8806e5eSIngo Weinhold magazine->next = NULL; 73*a8806e5eSIngo Weinhold magazine->current_round = 0; 74*a8806e5eSIngo Weinhold magazine->round_count = kMagazineCapacity; 75*a8806e5eSIngo Weinhold } 76*a8806e5eSIngo Weinhold 77*a8806e5eSIngo Weinhold return magazine; 78*a8806e5eSIngo Weinhold } 79*a8806e5eSIngo Weinhold 80*a8806e5eSIngo Weinhold 81*a8806e5eSIngo Weinhold static void 82*a8806e5eSIngo Weinhold free_magazine(depot_magazine *magazine) 83*a8806e5eSIngo Weinhold { 84*a8806e5eSIngo Weinhold internal_free(magazine); 85*a8806e5eSIngo Weinhold } 86*a8806e5eSIngo Weinhold 87*a8806e5eSIngo Weinhold 88*a8806e5eSIngo Weinhold static void 89*a8806e5eSIngo Weinhold empty_magazine(object_depot *depot, depot_magazine *magazine) 90*a8806e5eSIngo Weinhold { 91*a8806e5eSIngo Weinhold for (uint16 i = 0; i < magazine->current_round; i++) 92*a8806e5eSIngo Weinhold depot->return_object(depot, magazine->rounds[i]); 93*a8806e5eSIngo Weinhold free_magazine(magazine); 94*a8806e5eSIngo Weinhold } 95*a8806e5eSIngo Weinhold 96*a8806e5eSIngo Weinhold 97*a8806e5eSIngo Weinhold static bool 98*a8806e5eSIngo Weinhold exchange_with_full(object_depot *depot, depot_magazine* &magazine) 99*a8806e5eSIngo Weinhold { 100*a8806e5eSIngo Weinhold RecursiveLocker _(depot->lock); 101*a8806e5eSIngo Weinhold 102*a8806e5eSIngo Weinhold if (depot->full == NULL) 103*a8806e5eSIngo Weinhold return false; 104*a8806e5eSIngo Weinhold 105*a8806e5eSIngo Weinhold depot->full_count--; 106*a8806e5eSIngo Weinhold depot->empty_count++; 107*a8806e5eSIngo Weinhold 108*a8806e5eSIngo Weinhold _push(depot->empty, magazine); 109*a8806e5eSIngo Weinhold magazine = _pop(depot->full); 110*a8806e5eSIngo Weinhold return true; 111*a8806e5eSIngo Weinhold } 112*a8806e5eSIngo Weinhold 113*a8806e5eSIngo Weinhold 114*a8806e5eSIngo Weinhold static bool 115*a8806e5eSIngo Weinhold exchange_with_empty(object_depot *depot, depot_magazine* &magazine) 116*a8806e5eSIngo Weinhold { 117*a8806e5eSIngo Weinhold RecursiveLocker _(depot->lock); 118*a8806e5eSIngo Weinhold 119*a8806e5eSIngo Weinhold if (depot->empty == NULL) { 120*a8806e5eSIngo Weinhold depot->empty = alloc_magazine(); 121*a8806e5eSIngo Weinhold if (depot->empty == NULL) 122*a8806e5eSIngo Weinhold return false; 123*a8806e5eSIngo Weinhold } else { 124*a8806e5eSIngo Weinhold depot->empty_count--; 125*a8806e5eSIngo Weinhold } 126*a8806e5eSIngo Weinhold 127*a8806e5eSIngo Weinhold if (magazine) { 128*a8806e5eSIngo Weinhold _push(depot->full, magazine); 129*a8806e5eSIngo Weinhold depot->full_count++; 130*a8806e5eSIngo Weinhold } 131*a8806e5eSIngo Weinhold 132*a8806e5eSIngo Weinhold magazine = _pop(depot->empty); 133*a8806e5eSIngo Weinhold return true; 134*a8806e5eSIngo Weinhold } 135*a8806e5eSIngo Weinhold 136*a8806e5eSIngo Weinhold 137*a8806e5eSIngo Weinhold static inline depot_cpu_store * 138*a8806e5eSIngo Weinhold object_depot_cpu(object_depot *depot) 139*a8806e5eSIngo Weinhold { 140*a8806e5eSIngo Weinhold return &depot->stores[smp_get_current_cpu()]; 141*a8806e5eSIngo Weinhold } 142*a8806e5eSIngo Weinhold 143*a8806e5eSIngo Weinhold 144*a8806e5eSIngo Weinhold // #pragma mark - 145*a8806e5eSIngo Weinhold 146*a8806e5eSIngo Weinhold 147*a8806e5eSIngo Weinhold status_t 148*a8806e5eSIngo Weinhold object_depot_init(object_depot *depot, uint32 flags, 149*a8806e5eSIngo Weinhold void (*return_object)(object_depot *depot, void *object)) 150*a8806e5eSIngo Weinhold { 151*a8806e5eSIngo Weinhold depot->full = NULL; 152*a8806e5eSIngo Weinhold depot->empty = NULL; 153*a8806e5eSIngo Weinhold depot->full_count = depot->empty_count = 0; 154*a8806e5eSIngo Weinhold 155*a8806e5eSIngo Weinhold recursive_lock_init(&depot->lock, "depot"); 156*a8806e5eSIngo Weinhold 157*a8806e5eSIngo Weinhold depot->stores = (depot_cpu_store *)internal_alloc(sizeof(depot_cpu_store) 158*a8806e5eSIngo Weinhold * smp_get_num_cpus(), flags); 159*a8806e5eSIngo Weinhold if (depot->stores == NULL) { 160*a8806e5eSIngo Weinhold recursive_lock_destroy(&depot->lock); 161*a8806e5eSIngo Weinhold return B_NO_MEMORY; 162*a8806e5eSIngo Weinhold } 163*a8806e5eSIngo Weinhold 164*a8806e5eSIngo Weinhold for (int i = 0; i < smp_get_num_cpus(); i++) { 165*a8806e5eSIngo Weinhold recursive_lock_init(&depot->stores[i].lock, "cpu store"); 166*a8806e5eSIngo Weinhold depot->stores[i].loaded = depot->stores[i].previous = NULL; 167*a8806e5eSIngo Weinhold } 168*a8806e5eSIngo Weinhold 169*a8806e5eSIngo Weinhold depot->return_object = return_object; 170*a8806e5eSIngo Weinhold 171*a8806e5eSIngo Weinhold return B_OK; 172*a8806e5eSIngo Weinhold } 173*a8806e5eSIngo Weinhold 174*a8806e5eSIngo Weinhold 175*a8806e5eSIngo Weinhold void 176*a8806e5eSIngo Weinhold object_depot_destroy(object_depot *depot) 177*a8806e5eSIngo Weinhold { 178*a8806e5eSIngo Weinhold object_depot_make_empty(depot); 179*a8806e5eSIngo Weinhold 180*a8806e5eSIngo Weinhold for (int i = 0; i < smp_get_num_cpus(); i++) { 181*a8806e5eSIngo Weinhold recursive_lock_destroy(&depot->stores[i].lock); 182*a8806e5eSIngo Weinhold } 183*a8806e5eSIngo Weinhold 184*a8806e5eSIngo Weinhold internal_free(depot->stores); 185*a8806e5eSIngo Weinhold 186*a8806e5eSIngo Weinhold recursive_lock_destroy(&depot->lock); 187*a8806e5eSIngo Weinhold } 188*a8806e5eSIngo Weinhold 189*a8806e5eSIngo Weinhold 190*a8806e5eSIngo Weinhold static void * 191*a8806e5eSIngo Weinhold object_depot_obtain_from_store(object_depot *depot, depot_cpu_store *store) 192*a8806e5eSIngo Weinhold { 193*a8806e5eSIngo Weinhold RecursiveLocker _(store->lock); 194*a8806e5eSIngo Weinhold 195*a8806e5eSIngo Weinhold // To better understand both the Alloc() and Free() logic refer to 196*a8806e5eSIngo Weinhold // Bonwick's ``Magazines and Vmem'' [in 2001 USENIX proceedings] 197*a8806e5eSIngo Weinhold 198*a8806e5eSIngo Weinhold // In a nutshell, we try to get an object from the loaded magazine 199*a8806e5eSIngo Weinhold // if it's not empty, or from the previous magazine if it's full 200*a8806e5eSIngo Weinhold // and finally from the Slab if the magazine depot has no full magazines. 201*a8806e5eSIngo Weinhold 202*a8806e5eSIngo Weinhold if (store->loaded == NULL) 203*a8806e5eSIngo Weinhold return NULL; 204*a8806e5eSIngo Weinhold 205*a8806e5eSIngo Weinhold while (true) { 206*a8806e5eSIngo Weinhold if (!is_magazine_empty(store->loaded)) 207*a8806e5eSIngo Weinhold return pop_magazine(store->loaded); 208*a8806e5eSIngo Weinhold 209*a8806e5eSIngo Weinhold if (store->previous && (is_magazine_full(store->previous) 210*a8806e5eSIngo Weinhold || exchange_with_full(depot, store->previous))) 211*a8806e5eSIngo Weinhold std::swap(store->previous, store->loaded); 212*a8806e5eSIngo Weinhold else 213*a8806e5eSIngo Weinhold return NULL; 214*a8806e5eSIngo Weinhold } 215*a8806e5eSIngo Weinhold } 216*a8806e5eSIngo Weinhold 217*a8806e5eSIngo Weinhold 218*a8806e5eSIngo Weinhold static int 219*a8806e5eSIngo Weinhold object_depot_return_to_store(object_depot *depot, depot_cpu_store *store, 220*a8806e5eSIngo Weinhold void *object) 221*a8806e5eSIngo Weinhold { 222*a8806e5eSIngo Weinhold RecursiveLocker _(store->lock); 223*a8806e5eSIngo Weinhold 224*a8806e5eSIngo Weinhold // We try to add the object to the loaded magazine if we have one 225*a8806e5eSIngo Weinhold // and it's not full, or to the previous one if it is empty. If 226*a8806e5eSIngo Weinhold // the magazine depot doesn't provide us with a new empty magazine 227*a8806e5eSIngo Weinhold // we return the object directly to the slab. 228*a8806e5eSIngo Weinhold 229*a8806e5eSIngo Weinhold while (true) { 230*a8806e5eSIngo Weinhold if (store->loaded && push_magazine(store->loaded, object)) 231*a8806e5eSIngo Weinhold return 1; 232*a8806e5eSIngo Weinhold 233*a8806e5eSIngo Weinhold if ((store->previous && is_magazine_empty(store->previous)) 234*a8806e5eSIngo Weinhold || exchange_with_empty(depot, store->previous)) 235*a8806e5eSIngo Weinhold std::swap(store->loaded, store->previous); 236*a8806e5eSIngo Weinhold else 237*a8806e5eSIngo Weinhold return 0; 238*a8806e5eSIngo Weinhold } 239*a8806e5eSIngo Weinhold } 240*a8806e5eSIngo Weinhold 241*a8806e5eSIngo Weinhold 242*a8806e5eSIngo Weinhold void * 243*a8806e5eSIngo Weinhold object_depot_obtain(object_depot *depot) 244*a8806e5eSIngo Weinhold { 245*a8806e5eSIngo Weinhold return object_depot_obtain_from_store(depot, object_depot_cpu(depot)); 246*a8806e5eSIngo Weinhold } 247*a8806e5eSIngo Weinhold 248*a8806e5eSIngo Weinhold 249*a8806e5eSIngo Weinhold int 250*a8806e5eSIngo Weinhold object_depot_store(object_depot *depot, void *object) 251*a8806e5eSIngo Weinhold { 252*a8806e5eSIngo Weinhold return object_depot_return_to_store(depot, object_depot_cpu(depot), 253*a8806e5eSIngo Weinhold object); 254*a8806e5eSIngo Weinhold } 255*a8806e5eSIngo Weinhold 256*a8806e5eSIngo Weinhold 257*a8806e5eSIngo Weinhold void 258*a8806e5eSIngo Weinhold object_depot_make_empty(object_depot *depot) 259*a8806e5eSIngo Weinhold { 260*a8806e5eSIngo Weinhold for (int i = 0; i < smp_get_num_cpus(); i++) { 261*a8806e5eSIngo Weinhold depot_cpu_store *store = &depot->stores[i]; 262*a8806e5eSIngo Weinhold 263*a8806e5eSIngo Weinhold RecursiveLocker _(store->lock); 264*a8806e5eSIngo Weinhold 265*a8806e5eSIngo Weinhold if (depot->stores[i].loaded) 266*a8806e5eSIngo Weinhold empty_magazine(depot, depot->stores[i].loaded); 267*a8806e5eSIngo Weinhold if (depot->stores[i].previous) 268*a8806e5eSIngo Weinhold empty_magazine(depot, depot->stores[i].previous); 269*a8806e5eSIngo Weinhold depot->stores[i].loaded = depot->stores[i].previous = NULL; 270*a8806e5eSIngo Weinhold } 271*a8806e5eSIngo Weinhold 272*a8806e5eSIngo Weinhold RecursiveLocker _(depot->lock); 273*a8806e5eSIngo Weinhold 274*a8806e5eSIngo Weinhold while (depot->full) 275*a8806e5eSIngo Weinhold empty_magazine(depot, _pop(depot->full)); 276*a8806e5eSIngo Weinhold 277*a8806e5eSIngo Weinhold while (depot->empty) 278*a8806e5eSIngo Weinhold empty_magazine(depot, _pop(depot->empty)); 279*a8806e5eSIngo Weinhold } 280