source: mainline/kernel/generic/src/mm/slab.c@ 43dd8028

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 43dd8028 was 5df1963, checked in by Martin Decky <martin@…>, 12 years ago

bitmap frame allocator does not keep track of the size of the allocated frame blocks
to avoid memory leaks the number of allocated frames needs to be passed explicitly during deallocation

  • Property mode set to 100644
File size: 26.1 KB
RevLine 
[4e147a6]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[4e147a6]3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
[cc73a8a1]29/** @addtogroup genericmm
[b45c443]30 * @{
31 */
32
[9179d0a]33/**
[b45c443]34 * @file
[da1bafb]35 * @brief Slab allocator.
[9179d0a]36 *
37 * The slab allocator is closely modelled after OpenSolaris slab allocator.
38 * @see http://www.usenix.org/events/usenix01/full_papers/bonwick/bonwick_html/
[fb10289b]39 *
40 * with the following exceptions:
[9179d0a]41 * @li empty slabs are deallocated immediately
[fb10289b]42 * (in Linux they are kept in linked list, in Solaris ???)
[9179d0a]43 * @li empty magazines are deallocated when not needed
[fb10289b]44 * (in Solaris they are held in linked list in slab cache)
45 *
[9179d0a]46 * Following features are not currently supported but would be easy to do:
47 * @li cache coloring
48 * @li dynamic magazine growing (different magazine sizes are already
[5b04fc7]49 * supported, but we would need to adjust allocation strategy)
[fb10289b]50 *
[9179d0a]51 * The slab allocator supports per-CPU caches ('magazines') to facilitate
[da1bafb]52 * good SMP scaling.
[fb10289b]53 *
54 * When a new object is being allocated, it is first checked, if it is
[7669bcf]55 * available in a CPU-bound magazine. If it is not found there, it is
56 * allocated from a CPU-shared slab - if a partially full one is found,
57 * it is used, otherwise a new one is allocated.
[fb10289b]58 *
[7669bcf]59 * When an object is being deallocated, it is put to a CPU-bound magazine.
60 * If there is no such magazine, a new one is allocated (if this fails,
[9179d0a]61 * the object is deallocated into slab). If the magazine is full, it is
[7669bcf]62 * put into cpu-shared list of magazines and a new one is allocated.
[fb10289b]63 *
[7669bcf]64 * The CPU-bound magazine is actually a pair of magazines in order to avoid
[fb10289b]65 * thrashing when somebody is allocating/deallocating 1 item at the magazine
66 * size boundary. LIFO order is enforced, which should avoid fragmentation
[da1bafb]67 * as much as possible.
68 *
[7669bcf]69 * Every cache contains list of full slabs and list of partially full slabs.
[9179d0a]70 * Empty slabs are immediately freed (thrashing will be avoided because
[da1bafb]71 * of magazines).
[fb10289b]72 *
[9179d0a]73 * The slab information structure is kept inside the data area, if possible.
[fb10289b]74 * The cache can be marked that it should not use magazines. This is used
[9179d0a]75 * only for slab related caches to avoid deadlocks and infinite recursion
76 * (the slab allocator uses itself for allocating all it's control structures).
[fb10289b]77 *
[7669bcf]78 * The slab allocator allocates a lot of space and does not free it. When
79 * the frame allocator fails to allocate a frame, it calls slab_reclaim().
[fb10289b]80 * It tries 'light reclaim' first, then brutal reclaim. The light reclaim
81 * releases slabs from cpu-shared magazine-list, until at least 1 slab
82 * is deallocated in each cache (this algorithm should probably change).
83 * The brutal reclaim removes all cached objects, even from CPU-bound
84 * magazines.
85 *
[cc73a8a1]86 * @todo
[9179d0a]87 * For better CPU-scaling the magazine allocation strategy should
[10e16a7]88 * be extended. Currently, if the cache does not have magazine, it asks
89 * for non-cpu cached magazine cache to provide one. It might be feasible
90 * to add cpu-cached magazine cache (which would allocate it's magazines
91 * from non-cpu-cached mag. cache). This would provide a nice per-cpu
92 * buffer. The other possibility is to use the per-cache
93 * 'empty-magazine-list', which decreases competing for 1 per-system
94 * magazine cache.
95 *
[cc73a8a1]96 * @todo
[da1bafb]97 * It might be good to add granularity of locks even to slab level,
[cc73a8a1]98 * we could then try_spinlock over all partial slabs and thus improve
[da1bafb]99 * scalability even on slab level.
100 *
[fb10289b]101 */
102
[4e147a6]103#include <synch/spinlock.h>
104#include <mm/slab.h>
[5c9a08b]105#include <adt/list.h>
[4e147a6]106#include <memstr.h>
107#include <align.h>
[a294ad0]108#include <mm/frame.h>
[4e147a6]109#include <config.h>
110#include <print.h>
111#include <arch.h>
112#include <panic.h>
[a294ad0]113#include <debug.h>
[c352c2e]114#include <bitops.h>
[ce8aed1]115#include <macros.h>
[4e147a6]116
[da1bafb]117IRQ_SPINLOCK_STATIC_INITIALIZE(slab_cache_lock);
[fb10289b]118static LIST_INITIALIZE(slab_cache_list);
119
120/** Magazine cache */
121static slab_cache_t mag_cache;
[da1bafb]122
[fb10289b]123/** Cache for cache descriptors */
124static slab_cache_t slab_cache_cache;
[da1bafb]125
[fb10289b]126/** Cache for external slab descriptors
127 * This time we want per-cpu cache, so do not make it static
[9179d0a]128 * - using slab for internal slab structures will not deadlock,
[fb10289b]129 * as all slab structures are 'small' - control structures of
130 * their caches do not require further allocation
131 */
132static slab_cache_t *slab_extern_cache;
[da1bafb]133
[c352c2e]134/** Caches for malloc */
[ce8aed1]135static slab_cache_t *malloc_caches[SLAB_MAX_MALLOC_W - SLAB_MIN_MALLOC_W + 1];
[da1bafb]136
[a000878c]137static const char *malloc_names[] = {
[ce8aed1]138 "malloc-16",
139 "malloc-32",
140 "malloc-64",
141 "malloc-128",
142 "malloc-256",
143 "malloc-512",
144 "malloc-1K",
145 "malloc-2K",
146 "malloc-4K",
147 "malloc-8K",
148 "malloc-16K",
149 "malloc-32K",
150 "malloc-64K",
151 "malloc-128K",
[c3ebc47]152 "malloc-256K",
153 "malloc-512K",
154 "malloc-1M",
155 "malloc-2M",
156 "malloc-4M"
[c352c2e]157};
[a294ad0]158
[fb10289b]159/** Slab descriptor */
[a294ad0]160typedef struct {
[da1bafb]161 slab_cache_t *cache; /**< Pointer to parent cache. */
162 link_t link; /**< List of full/partial slabs. */
163 void *start; /**< Start address of first available item. */
164 size_t available; /**< Count of available items in this slab. */
165 size_t nextavail; /**< The index of next available item. */
[ce8aed1]166} slab_t;
[a294ad0]167
[214f5bb]168#ifdef CONFIG_DEBUG
[da1bafb]169static unsigned int _slab_initialized = 0;
[214f5bb]170#endif
171
[a294ad0]172/**************************************/
[9179d0a]173/* Slab allocation functions */
[da1bafb]174/**************************************/
[a294ad0]175
[da1bafb]176/** Allocate frames for slab space and initialize
[a294ad0]177 *
178 */
[7a0359b]179NO_TRACE static slab_t *slab_space_alloc(slab_cache_t *cache,
180 unsigned int flags)
[a294ad0]181{
[98000fb]182 size_t zone = 0;
[085d973]183
[8cbf1c3]184 void *data = (void *)
[b0c2075]185 PA2KA(frame_alloc_generic(cache->frames, flags, 0, &zone));
[e2a0d76]186 if (!data)
[a294ad0]187 return NULL;
[da1bafb]188
189 slab_t *slab;
190 size_t fsize;
191
[46c1234]192 if (!(cache->flags & SLAB_CACHE_SLINSIDE)) {
[fb10289b]193 slab = slab_alloc(slab_extern_cache, flags);
[a294ad0]194 if (!slab) {
[5df1963]195 frame_free(KA2PA(data), cache->frames);
[a294ad0]196 return NULL;
197 }
198 } else {
[b0c2075]199 fsize = FRAMES2SIZE(cache->frames);
[a294ad0]200 slab = data + fsize - sizeof(*slab);
201 }
[e3c762cd]202
[a294ad0]203 /* Fill in slab structures */
[da1bafb]204 size_t i;
[b0c2075]205 for (i = 0; i < cache->frames; i++)
[6c441cf8]206 frame_set_parent(ADDR2PFN(KA2PA(data)) + i, slab, zone);
[da1bafb]207
[a294ad0]208 slab->start = data;
209 slab->available = cache->objects;
210 slab->nextavail = 0;
[4a5b2b0e]211 slab->cache = cache;
[da1bafb]212
[6c441cf8]213 for (i = 0; i < cache->objects; i++)
[da1bafb]214 *((size_t *) (slab->start + i * cache->size)) = i + 1;
215
[bc504ef2]216 atomic_inc(&cache->allocated_slabs);
[a294ad0]217 return slab;
218}
219
[da1bafb]220/** Deallocate space associated with slab
[a294ad0]221 *
222 * @return number of freed frames
[da1bafb]223 *
[a294ad0]224 */
[7a0359b]225NO_TRACE static size_t slab_space_free(slab_cache_t *cache, slab_t *slab)
[a294ad0]226{
[5df1963]227 frame_free(KA2PA(slab->start), slab->cache->frames);
[da1bafb]228 if (!(cache->flags & SLAB_CACHE_SLINSIDE))
[fb10289b]229 slab_free(slab_extern_cache, slab);
[da1bafb]230
[bc504ef2]231 atomic_dec(&cache->allocated_slabs);
232
[b0c2075]233 return cache->frames;
[a294ad0]234}
235
236/** Map object to slab structure */
[7a0359b]237NO_TRACE static slab_t *obj2slab(void *obj)
[a294ad0]238{
[ce8aed1]239 return (slab_t *) frame_get_parent(ADDR2PFN(KA2PA(obj)), 0);
[a294ad0]240}
241
[da1bafb]242/******************/
[9179d0a]243/* Slab functions */
[da1bafb]244/******************/
[4e147a6]245
[da1bafb]246/** Return object to slab and call a destructor
[4e147a6]247 *
[a294ad0]248 * @param slab If the caller knows directly slab of the object, otherwise NULL
249 *
[4e147a6]250 * @return Number of freed pages
[da1bafb]251 *
[4e147a6]252 */
[7a0359b]253NO_TRACE static size_t slab_obj_destroy(slab_cache_t *cache, void *obj,
254 slab_t *slab)
[4e147a6]255{
[a294ad0]256 if (!slab)
257 slab = obj2slab(obj);
[da1bafb]258
[4a5b2b0e]259 ASSERT(slab->cache == cache);
[da1bafb]260
261 size_t freed = 0;
262
[266294a9]263 if (cache->destructor)
264 freed = cache->destructor(obj);
265
[ddb56be]266 irq_spinlock_lock(&cache->slablock, true);
[8e1ea655]267 ASSERT(slab->available < cache->objects);
[da1bafb]268
269 *((size_t *) obj) = slab->nextavail;
[46c1234]270 slab->nextavail = (obj - slab->start) / cache->size;
[a294ad0]271 slab->available++;
[da1bafb]272
[a294ad0]273 /* Move it to correct list */
274 if (slab->available == cache->objects) {
275 /* Free associated memory */
276 list_remove(&slab->link);
[ddb56be]277 irq_spinlock_unlock(&cache->slablock, true);
[da1bafb]278
[266294a9]279 return freed + slab_space_free(cache, slab);
[e72b0a3]280 } else if (slab->available == 1) {
281 /* It was in full, move to partial */
282 list_remove(&slab->link);
283 list_prepend(&slab->link, &cache->partial_slabs);
[a294ad0]284 }
[da1bafb]285
[ddb56be]286 irq_spinlock_unlock(&cache->slablock, true);
[266294a9]287 return freed;
[a294ad0]288}
[4e147a6]289
[da1bafb]290/** Take new object from slab or create new if needed
[4e147a6]291 *
292 * @return Object address or null
[da1bafb]293 *
[4e147a6]294 */
[7a0359b]295NO_TRACE static void *slab_obj_create(slab_cache_t *cache, unsigned int flags)
[4e147a6]296{
[ddb56be]297 irq_spinlock_lock(&cache->slablock, true);
[da1bafb]298
299 slab_t *slab;
300
[a294ad0]301 if (list_empty(&cache->partial_slabs)) {
[da1bafb]302 /*
303 * Allow recursion and reclaiming
[9179d0a]304 * - this should work, as the slab control structures
[e3c762cd]305 * are small and do not need to allocate with anything
306 * other than frame_alloc when they are allocating,
[a294ad0]307 * that's why we should get recursion at most 1-level deep
[da1bafb]308 *
[a294ad0]309 */
[ddb56be]310 irq_spinlock_unlock(&cache->slablock, true);
[a294ad0]311 slab = slab_space_alloc(cache, flags);
[428aabf]312 if (!slab)
[e72b0a3]313 return NULL;
[da1bafb]314
[ddb56be]315 irq_spinlock_lock(&cache->slablock, true);
[a294ad0]316 } else {
[55b77d9]317 slab = list_get_instance(list_first(&cache->partial_slabs),
318 slab_t, link);
[a294ad0]319 list_remove(&slab->link);
320 }
[da1bafb]321
322 void *obj = slab->start + slab->nextavail * cache->size;
323 slab->nextavail = *((size_t *) obj);
[a294ad0]324 slab->available--;
[da1bafb]325
[f3272e98]326 if (!slab->available)
[bc504ef2]327 list_prepend(&slab->link, &cache->full_slabs);
[a294ad0]328 else
[bc504ef2]329 list_prepend(&slab->link, &cache->partial_slabs);
[da1bafb]330
[ddb56be]331 irq_spinlock_unlock(&cache->slablock, true);
[da1bafb]332
333 if ((cache->constructor) && (cache->constructor(obj, flags))) {
[266294a9]334 /* Bad, bad, construction failed */
335 slab_obj_destroy(cache, obj, slab);
336 return NULL;
337 }
[da1bafb]338
[a294ad0]339 return obj;
[4e147a6]340}
341
[da1bafb]342/****************************/
[4e147a6]343/* CPU-Cache slab functions */
[da1bafb]344/****************************/
[4e147a6]345
[da1bafb]346/** Find a full magazine in cache, take it from list and return it
347 *
348 * @param first If true, return first, else last mag.
[5158549]349 *
350 */
[7a0359b]351NO_TRACE static slab_magazine_t *get_mag_from_cache(slab_cache_t *cache,
352 bool first)
[5158549]353{
354 slab_magazine_t *mag = NULL;
355 link_t *cur;
[da1bafb]356
[4d194be]357 irq_spinlock_lock(&cache->maglock, true);
[5158549]358 if (!list_empty(&cache->magazines)) {
359 if (first)
[55b77d9]360 cur = list_first(&cache->magazines);
[5158549]361 else
[55b77d9]362 cur = list_last(&cache->magazines);
[da1bafb]363
[5158549]364 mag = list_get_instance(cur, slab_magazine_t, link);
365 list_remove(&mag->link);
366 atomic_dec(&cache->magazine_counter);
367 }
[4d194be]368 irq_spinlock_unlock(&cache->maglock, true);
[25ebfbd]369
[5158549]370 return mag;
371}
372
[da1bafb]373/** Prepend magazine to magazine list in cache
374 *
375 */
[7a0359b]376NO_TRACE static void put_mag_to_cache(slab_cache_t *cache,
377 slab_magazine_t *mag)
[5158549]378{
[4d194be]379 irq_spinlock_lock(&cache->maglock, true);
[da1bafb]380
[5158549]381 list_prepend(&mag->link, &cache->magazines);
382 atomic_inc(&cache->magazine_counter);
383
[4d194be]384 irq_spinlock_unlock(&cache->maglock, true);
[5158549]385}
386
[da1bafb]387/** Free all objects in magazine and free memory associated with magazine
[4e147a6]388 *
389 * @return Number of freed pages
[da1bafb]390 *
[4e147a6]391 */
[7a0359b]392NO_TRACE static size_t magazine_destroy(slab_cache_t *cache,
393 slab_magazine_t *mag)
[4e147a6]394{
[da1bafb]395 size_t i;
[98000fb]396 size_t frames = 0;
[da1bafb]397
[6c441cf8]398 for (i = 0; i < mag->busy; i++) {
[a294ad0]399 frames += slab_obj_destroy(cache, mag->objs[i], NULL);
[4a5b2b0e]400 atomic_dec(&cache->cached_objs);
401 }
[4e147a6]402
403 slab_free(&mag_cache, mag);
[da1bafb]404
[4e147a6]405 return frames;
406}
407
[da1bafb]408/** Find full magazine, set it as current and return it
409 *
[fb10289b]410 */
[7a0359b]411NO_TRACE static slab_magazine_t *get_full_current_mag(slab_cache_t *cache)
[fb10289b]412{
[da1bafb]413 slab_magazine_t *cmag = cache->mag_cache[CPU->id].current;
414 slab_magazine_t *lastmag = cache->mag_cache[CPU->id].last;
[7a0359b]415
[25ebfbd]416 ASSERT(irq_spinlock_locked(&cache->mag_cache[CPU->id].lock));
[da1bafb]417
[fb10289b]418 if (cmag) { /* First try local CPU magazines */
419 if (cmag->busy)
420 return cmag;
[da1bafb]421
422 if ((lastmag) && (lastmag->busy)) {
[fb10289b]423 cache->mag_cache[CPU->id].current = lastmag;
424 cache->mag_cache[CPU->id].last = cmag;
425 return lastmag;
426 }
427 }
[da1bafb]428
[fb10289b]429 /* Local magazines are empty, import one from magazine list */
[da1bafb]430 slab_magazine_t *newmag = get_mag_from_cache(cache, 1);
[5158549]431 if (!newmag)
[fb10289b]432 return NULL;
[da1bafb]433
[fb10289b]434 if (lastmag)
[5158549]435 magazine_destroy(cache, lastmag);
[da1bafb]436
[fb10289b]437 cache->mag_cache[CPU->id].last = cmag;
438 cache->mag_cache[CPU->id].current = newmag;
[da1bafb]439
[fb10289b]440 return newmag;
441}
442
[da1bafb]443/** Try to find object in CPU-cache magazines
[4e147a6]444 *
445 * @return Pointer to object or NULL if not available
[da1bafb]446 *
[4e147a6]447 */
[7a0359b]448NO_TRACE static void *magazine_obj_get(slab_cache_t *cache)
[4e147a6]449{
[81e52f2a]450 if (!CPU)
451 return NULL;
[da1bafb]452
[25ebfbd]453 irq_spinlock_lock(&cache->mag_cache[CPU->id].lock, true);
[da1bafb]454
455 slab_magazine_t *mag = get_full_current_mag(cache);
[fb10289b]456 if (!mag) {
[25ebfbd]457 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
[fb10289b]458 return NULL;
[4e147a6]459 }
[da1bafb]460
461 void *obj = mag->objs[--mag->busy];
[25ebfbd]462 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
[da1bafb]463
[4a5b2b0e]464 atomic_dec(&cache->cached_objs);
465
466 return obj;
[4e147a6]467}
468
[da1bafb]469/** Assure that the current magazine is empty, return pointer to it,
470 * or NULL if no empty magazine is available and cannot be allocated
[4e147a6]471 *
[da1bafb]472 * We have 2 magazines bound to processor.
473 * First try the current.
474 * If full, try the last.
475 * If full, put to magazines list.
[4e147a6]476 *
[086a600]477 */
[7a0359b]478NO_TRACE static slab_magazine_t *make_empty_current_mag(slab_cache_t *cache)
[086a600]479{
[da1bafb]480 slab_magazine_t *cmag = cache->mag_cache[CPU->id].current;
481 slab_magazine_t *lastmag = cache->mag_cache[CPU->id].last;
482
[25ebfbd]483 ASSERT(irq_spinlock_locked(&cache->mag_cache[CPU->id].lock));
[7a0359b]484
[086a600]485 if (cmag) {
486 if (cmag->busy < cmag->size)
487 return cmag;
[da1bafb]488
489 if ((lastmag) && (lastmag->busy < lastmag->size)) {
[086a600]490 cache->mag_cache[CPU->id].last = cmag;
491 cache->mag_cache[CPU->id].current = lastmag;
492 return lastmag;
493 }
494 }
[da1bafb]495
[086a600]496 /* current | last are full | nonexistent, allocate new */
[da1bafb]497
498 /*
499 * We do not want to sleep just because of caching,
500 * especially we do not want reclaiming to start, as
501 * this would deadlock.
502 *
503 */
504 slab_magazine_t *newmag = slab_alloc(&mag_cache,
505 FRAME_ATOMIC | FRAME_NO_RECLAIM);
[086a600]506 if (!newmag)
507 return NULL;
[da1bafb]508
[086a600]509 newmag->size = SLAB_MAG_SIZE;
510 newmag->busy = 0;
[da1bafb]511
[086a600]512 /* Flush last to magazine list */
[5158549]513 if (lastmag)
514 put_mag_to_cache(cache, lastmag);
[da1bafb]515
[086a600]516 /* Move current as last, save new as current */
[da1bafb]517 cache->mag_cache[CPU->id].last = cmag;
518 cache->mag_cache[CPU->id].current = newmag;
519
[086a600]520 return newmag;
521}
522
[da1bafb]523/** Put object into CPU-cache magazine
524 *
525 * @return 0 on success, -1 on no memory
[086a600]526 *
[4e147a6]527 */
[7a0359b]528NO_TRACE static int magazine_obj_put(slab_cache_t *cache, void *obj)
[4e147a6]529{
[81e52f2a]530 if (!CPU)
531 return -1;
[da1bafb]532
[25ebfbd]533 irq_spinlock_lock(&cache->mag_cache[CPU->id].lock, true);
[da1bafb]534
535 slab_magazine_t *mag = make_empty_current_mag(cache);
[fb10289b]536 if (!mag) {
[25ebfbd]537 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
[fb10289b]538 return -1;
539 }
[4e147a6]540
541 mag->objs[mag->busy++] = obj;
[da1bafb]542
[25ebfbd]543 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
[da1bafb]544
[4a5b2b0e]545 atomic_inc(&cache->cached_objs);
[da1bafb]546
[4e147a6]547 return 0;
548}
549
[da1bafb]550/************************/
[9179d0a]551/* Slab cache functions */
[da1bafb]552/************************/
[a294ad0]553
[da1bafb]554/** Return number of objects that fit in certain cache size
555 *
556 */
[7a0359b]557NO_TRACE static size_t comp_objects(slab_cache_t *cache)
[a294ad0]558{
559 if (cache->flags & SLAB_CACHE_SLINSIDE)
[b0c2075]560 return (FRAMES2SIZE(cache->frames) - sizeof(slab_t)) /
561 cache->size;
[da1bafb]562 else
[b0c2075]563 return FRAMES2SIZE(cache->frames) / cache->size;
[a294ad0]564}
565
[da1bafb]566/** Return wasted space in slab
567 *
568 */
[7a0359b]569NO_TRACE static size_t badness(slab_cache_t *cache)
[a294ad0]570{
[da1bafb]571 size_t objects = comp_objects(cache);
[b0c2075]572 size_t ssize = FRAMES2SIZE(cache->frames);
[da1bafb]573
[a294ad0]574 if (cache->flags & SLAB_CACHE_SLINSIDE)
575 ssize -= sizeof(slab_t);
[da1bafb]576
[6c441cf8]577 return ssize - objects * cache->size;
[a294ad0]578}
[4e147a6]579
[da1bafb]580/** Initialize mag_cache structure in slab cache
581 *
[8e1ea655]582 */
[7a0359b]583NO_TRACE static bool make_magcache(slab_cache_t *cache)
[8e1ea655]584{
[214f5bb]585 ASSERT(_slab_initialized >= 2);
[da1bafb]586
[46c1234]587 cache->mag_cache = malloc(sizeof(slab_mag_cache_t) * config.cpu_count,
[55821eea]588 FRAME_ATOMIC);
589 if (!cache->mag_cache)
590 return false;
[da1bafb]591
592 size_t i;
[6c441cf8]593 for (i = 0; i < config.cpu_count; i++) {
[e32e092]594 memsetb(&cache->mag_cache[i], sizeof(cache->mag_cache[i]), 0);
[25ebfbd]595 irq_spinlock_initialize(&cache->mag_cache[i].lock,
[da1bafb]596 "slab.cache.mag_cache[].lock");
[8e1ea655]597 }
[da1bafb]598
[55821eea]599 return true;
[8e1ea655]600}
601
[da1bafb]602/** Initialize allocated memory as a slab cache
603 *
604 */
[7a0359b]605NO_TRACE static void _slab_cache_create(slab_cache_t *cache, const char *name,
[da1bafb]606 size_t size, size_t align, int (*constructor)(void *obj,
607 unsigned int kmflag), size_t (*destructor)(void *obj), unsigned int flags)
[4e147a6]608{
[e32e092]609 memsetb(cache, sizeof(*cache), 0);
[4e147a6]610 cache->name = name;
[da1bafb]611
[96b02eb9]612 if (align < sizeof(sysarg_t))
613 align = sizeof(sysarg_t);
[da1bafb]614
[14e5d88]615 size = ALIGN_UP(size, align);
[da1bafb]616
[a294ad0]617 cache->size = size;
[4e147a6]618 cache->constructor = constructor;
619 cache->destructor = destructor;
620 cache->flags = flags;
[da1bafb]621
[4e147a6]622 list_initialize(&cache->full_slabs);
623 list_initialize(&cache->partial_slabs);
624 list_initialize(&cache->magazines);
[da1bafb]625
[ddb56be]626 irq_spinlock_initialize(&cache->slablock, "slab.cache.slablock");
[4d194be]627 irq_spinlock_initialize(&cache->maglock, "slab.cache.maglock");
[da1bafb]628
[46c1234]629 if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
[55821eea]630 (void) make_magcache(cache);
[da1bafb]631
[4e147a6]632 /* Compute slab sizes, object counts in slabs etc. */
633 if (cache->size < SLAB_INSIDE_SIZE)
634 cache->flags |= SLAB_CACHE_SLINSIDE;
[da1bafb]635
[b0c2075]636 /* Minimum slab frames */
637 cache->frames = SIZE2FRAMES(cache->size);
[da1bafb]638
639 while (badness(cache) > SLAB_MAX_BADNESS(cache))
[b0c2075]640 cache->frames <<= 1;
[da1bafb]641
[a294ad0]642 cache->objects = comp_objects(cache);
[da1bafb]643
[14e5d88]644 /* If info fits in, put it inside */
645 if (badness(cache) > sizeof(slab_t))
646 cache->flags |= SLAB_CACHE_SLINSIDE;
[da1bafb]647
[248fc1a]648 /* Add cache to cache list */
[da1bafb]649 irq_spinlock_lock(&slab_cache_lock, true);
[4e147a6]650 list_append(&cache->link, &slab_cache_list);
[da1bafb]651 irq_spinlock_unlock(&slab_cache_lock, true);
[4e147a6]652}
653
[da1bafb]654/** Create slab cache
655 *
656 */
[a000878c]657slab_cache_t *slab_cache_create(const char *name, size_t size, size_t align,
[da1bafb]658 int (*constructor)(void *obj, unsigned int kmflag),
659 size_t (*destructor)(void *obj), unsigned int flags)
[4e147a6]660{
[da1bafb]661 slab_cache_t *cache = slab_alloc(&slab_cache_cache, 0);
[4e147a6]662 _slab_cache_create(cache, name, size, align, constructor, destructor,
[46c1234]663 flags);
[da1bafb]664
[4e147a6]665 return cache;
666}
667
[da1bafb]668/** Reclaim space occupied by objects that are already free
[4e147a6]669 *
670 * @param flags If contains SLAB_RECLAIM_ALL, do aggressive freeing
[da1bafb]671 *
[4e147a6]672 * @return Number of freed pages
[da1bafb]673 *
[4e147a6]674 */
[7a0359b]675NO_TRACE static size_t _slab_reclaim(slab_cache_t *cache, unsigned int flags)
[4e147a6]676{
677 if (cache->flags & SLAB_CACHE_NOMAGAZINE)
678 return 0; /* Nothing to do */
[da1bafb]679
680 /*
681 * We count up to original magazine count to avoid
682 * endless loop
[5158549]683 */
[da1bafb]684 atomic_count_t magcount = atomic_get(&cache->magazine_counter);
685
686 slab_magazine_t *mag;
687 size_t frames = 0;
688
689 while ((magcount--) && (mag = get_mag_from_cache(cache, 0))) {
690 frames += magazine_destroy(cache, mag);
691 if ((!(flags & SLAB_RECLAIM_ALL)) && (frames))
[5158549]692 break;
[fb10289b]693 }
[4e147a6]694
695 if (flags & SLAB_RECLAIM_ALL) {
[5158549]696 /* Free cpu-bound magazines */
[4e147a6]697 /* Destroy CPU magazines */
[da1bafb]698 size_t i;
[6c441cf8]699 for (i = 0; i < config.cpu_count; i++) {
[25ebfbd]700 irq_spinlock_lock(&cache->mag_cache[i].lock, true);
[da1bafb]701
[4e147a6]702 mag = cache->mag_cache[i].current;
703 if (mag)
704 frames += magazine_destroy(cache, mag);
705 cache->mag_cache[i].current = NULL;
706
707 mag = cache->mag_cache[i].last;
708 if (mag)
709 frames += magazine_destroy(cache, mag);
710 cache->mag_cache[i].last = NULL;
[da1bafb]711
[25ebfbd]712 irq_spinlock_unlock(&cache->mag_cache[i].lock, true);
[5158549]713 }
[428aabf]714 }
[da1bafb]715
[4e147a6]716 return frames;
717}
718
[da1bafb]719/** Check that there are no slabs and remove cache from system
720 *
721 */
[4e147a6]722void slab_cache_destroy(slab_cache_t *cache)
723{
[da1bafb]724 /*
725 * First remove cache from link, so that we don't need
[5158549]726 * to disable interrupts later
[da1bafb]727 *
[5158549]728 */
[da1bafb]729 irq_spinlock_lock(&slab_cache_lock, true);
[5158549]730 list_remove(&cache->link);
[da1bafb]731 irq_spinlock_unlock(&slab_cache_lock, true);
732
733 /*
734 * Do not lock anything, we assume the software is correct and
735 * does not touch the cache when it decides to destroy it
736 *
737 */
[4e147a6]738
739 /* Destroy all magazines */
740 _slab_reclaim(cache, SLAB_RECLAIM_ALL);
[da1bafb]741
[4e147a6]742 /* All slabs must be empty */
[da1bafb]743 if ((!list_empty(&cache->full_slabs)) ||
744 (!list_empty(&cache->partial_slabs)))
[4e147a6]745 panic("Destroying cache that is not empty.");
[da1bafb]746
[8e1ea655]747 if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
[bb68433]748 free(cache->mag_cache);
[da1bafb]749
[fb10289b]750 slab_free(&slab_cache_cache, cache);
[4e147a6]751}
752
[da1bafb]753/** Allocate new object from cache - if no flags given, always returns memory
754 *
755 */
756void *slab_alloc(slab_cache_t *cache, unsigned int flags)
[4e147a6]757{
[da1bafb]758 /* Disable interrupts to avoid deadlocks with interrupt handlers */
759 ipl_t ipl = interrupts_disable();
760
[4e147a6]761 void *result = NULL;
[c5613b72]762
[da1bafb]763 if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
[4e147a6]764 result = magazine_obj_get(cache);
[da1bafb]765
[428aabf]766 if (!result)
[4e147a6]767 result = slab_obj_create(cache, flags);
[da1bafb]768
[4e147a6]769 interrupts_restore(ipl);
[da1bafb]770
[fb10289b]771 if (result)
772 atomic_inc(&cache->allocated_objs);
[da1bafb]773
[4e147a6]774 return result;
775}
776
[da1bafb]777/** Return object to cache, use slab if known
778 *
779 */
[7a0359b]780NO_TRACE static void _slab_free(slab_cache_t *cache, void *obj, slab_t *slab)
[4e147a6]781{
[da1bafb]782 ipl_t ipl = interrupts_disable();
783
[46c1234]784 if ((cache->flags & SLAB_CACHE_NOMAGAZINE) ||
[da1bafb]785 (magazine_obj_put(cache, obj)))
[c352c2e]786 slab_obj_destroy(cache, obj, slab);
[da1bafb]787
[4e147a6]788 interrupts_restore(ipl);
[fb10289b]789 atomic_dec(&cache->allocated_objs);
[4e147a6]790}
791
[da1bafb]792/** Return slab object to cache
793 *
794 */
[c352c2e]795void slab_free(slab_cache_t *cache, void *obj)
796{
[ce8aed1]797 _slab_free(cache, obj, NULL);
[c352c2e]798}
799
[ab6f2507]800/** Go through all caches and reclaim what is possible */
[da1bafb]801size_t slab_reclaim(unsigned int flags)
[4e147a6]802{
[ab6f2507]803 irq_spinlock_lock(&slab_cache_lock, true);
[da1bafb]804
[98000fb]805 size_t frames = 0;
[feeac0d]806 list_foreach(slab_cache_list, link, slab_cache_t, cache) {
[4e147a6]807 frames += _slab_reclaim(cache, flags);
808 }
[da1bafb]809
[ab6f2507]810 irq_spinlock_unlock(&slab_cache_lock, true);
[da1bafb]811
[4e147a6]812 return frames;
813}
814
[da1bafb]815/* Print list of slabs
816 *
817 */
[4e147a6]818void slab_print_list(void)
819{
[ccb426c]820 printf("[slab name ] [size ] [pages ] [obj/pg] [slabs ]"
821 " [cached] [alloc ] [ctl]\n");
[da1bafb]822
823 size_t skip = 0;
[599d6f5]824 while (true) {
825 /*
826 * We must not hold the slab_cache_lock spinlock when printing
827 * the statistics. Otherwise we can easily deadlock if the print
828 * needs to allocate memory.
829 *
830 * Therefore, we walk through the slab cache list, skipping some
831 * amount of already processed caches during each iteration and
832 * gathering statistics about the first unprocessed cache. For
833 * the sake of printing the statistics, we realese the
834 * slab_cache_lock and reacquire it afterwards. Then the walk
835 * starts again.
836 *
837 * This limits both the efficiency and also accuracy of the
838 * obtained statistics. The efficiency is decreased because the
839 * time complexity of the algorithm is quadratic instead of
840 * linear. The accuracy is impacted because we drop the lock
841 * after processing one cache. If there is someone else
842 * manipulating the cache list, we might omit an arbitrary
843 * number of caches or process one cache multiple times.
844 * However, we don't bleed for this algorithm for it is only
845 * statistics.
846 */
[da1bafb]847
848 irq_spinlock_lock(&slab_cache_lock, true);
849
850 link_t *cur;
851 size_t i;
[55b77d9]852 for (i = 0, cur = slab_cache_list.head.next;
853 (i < skip) && (cur != &slab_cache_list.head);
[da1bafb]854 i++, cur = cur->next);
855
[55b77d9]856 if (cur == &slab_cache_list.head) {
[da1bafb]857 irq_spinlock_unlock(&slab_cache_lock, true);
[599d6f5]858 break;
859 }
[da1bafb]860
[599d6f5]861 skip++;
[da1bafb]862
863 slab_cache_t *cache = list_get_instance(cur, slab_cache_t, link);
864
[a000878c]865 const char *name = cache->name;
[b0c2075]866 size_t frames = cache->frames;
[599d6f5]867 size_t size = cache->size;
[da1bafb]868 size_t objects = cache->objects;
[599d6f5]869 long allocated_slabs = atomic_get(&cache->allocated_slabs);
870 long cached_objs = atomic_get(&cache->cached_objs);
871 long allocated_objs = atomic_get(&cache->allocated_objs);
[da1bafb]872 unsigned int flags = cache->flags;
[599d6f5]873
[da1bafb]874 irq_spinlock_unlock(&slab_cache_lock, true);
[6536a4a9]875
[b0c2075]876 printf("%-18s %8zu %8zu %8zu %8ld %8ld %8ld %-5s\n",
877 name, size, frames, objects, allocated_slabs,
[599d6f5]878 cached_objs, allocated_objs,
879 flags & SLAB_CACHE_SLINSIDE ? "in" : "out");
[4e147a6]880 }
881}
882
883void slab_cache_init(void)
884{
885 /* Initialize magazine cache */
[f97f1e51]886 _slab_cache_create(&mag_cache, "slab_magazine_t",
[46c1234]887 sizeof(slab_magazine_t) + SLAB_MAG_SIZE * sizeof(void*),
888 sizeof(uintptr_t), NULL, NULL, SLAB_CACHE_NOMAGAZINE |
889 SLAB_CACHE_SLINSIDE);
[da1bafb]890
[fb10289b]891 /* Initialize slab_cache cache */
[f97f1e51]892 _slab_cache_create(&slab_cache_cache, "slab_cache_cache",
[46c1234]893 sizeof(slab_cache_cache), sizeof(uintptr_t), NULL, NULL,
894 SLAB_CACHE_NOMAGAZINE | SLAB_CACHE_SLINSIDE);
[da1bafb]895
[fb10289b]896 /* Initialize external slab cache */
[f97f1e51]897 slab_extern_cache = slab_cache_create("slab_t", sizeof(slab_t), 0,
[46c1234]898 NULL, NULL, SLAB_CACHE_SLINSIDE | SLAB_CACHE_MAGDEFERRED);
[da1bafb]899
[4e147a6]900 /* Initialize structures for malloc */
[da1bafb]901 size_t i;
902 size_t size;
903
[46c1234]904 for (i = 0, size = (1 << SLAB_MIN_MALLOC_W);
905 i < (SLAB_MAX_MALLOC_W - SLAB_MIN_MALLOC_W + 1);
906 i++, size <<= 1) {
907 malloc_caches[i] = slab_cache_create(malloc_names[i], size, 0,
908 NULL, NULL, SLAB_CACHE_MAGDEFERRED);
[c352c2e]909 }
[da1bafb]910
[a000878c]911#ifdef CONFIG_DEBUG
[04225a7]912 _slab_initialized = 1;
913#endif
[c352c2e]914}
915
[8e1ea655]916/** Enable cpu_cache
917 *
918 * Kernel calls this function, when it knows the real number of
[da1bafb]919 * processors. Allocate slab for cpucache and enable it on all
920 * existing slabs that are SLAB_CACHE_MAGDEFERRED
921 *
[8e1ea655]922 */
923void slab_enable_cpucache(void)
924{
[214f5bb]925#ifdef CONFIG_DEBUG
926 _slab_initialized = 2;
927#endif
[8e1ea655]928
[da1bafb]929 irq_spinlock_lock(&slab_cache_lock, false);
930
[feeac0d]931 list_foreach(slab_cache_list, link, slab_cache_t, slab) {
[da1bafb]932 if ((slab->flags & SLAB_CACHE_MAGDEFERRED) !=
[46c1234]933 SLAB_CACHE_MAGDEFERRED)
[8e1ea655]934 continue;
[da1bafb]935
936 (void) make_magcache(slab);
937 slab->flags &= ~SLAB_CACHE_MAGDEFERRED;
[8e1ea655]938 }
[da1bafb]939
940 irq_spinlock_unlock(&slab_cache_lock, false);
[8e1ea655]941}
942
[da1bafb]943void *malloc(size_t size, unsigned int flags)
[c352c2e]944{
[04225a7]945 ASSERT(_slab_initialized);
[c259b9b]946 ASSERT(size <= (1 << SLAB_MAX_MALLOC_W));
[c352c2e]947
948 if (size < (1 << SLAB_MIN_MALLOC_W))
949 size = (1 << SLAB_MIN_MALLOC_W);
[da1bafb]950
951 uint8_t idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
952
[c352c2e]953 return slab_alloc(malloc_caches[idx], flags);
954}
955
[da1bafb]956void *realloc(void *ptr, size_t size, unsigned int flags)
[c352c2e]957{
[ce8aed1]958 ASSERT(_slab_initialized);
959 ASSERT(size <= (1 << SLAB_MAX_MALLOC_W));
960
961 void *new_ptr;
962
963 if (size > 0) {
964 if (size < (1 << SLAB_MIN_MALLOC_W))
965 size = (1 << SLAB_MIN_MALLOC_W);
[da1bafb]966 uint8_t idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
[ce8aed1]967
968 new_ptr = slab_alloc(malloc_caches[idx], flags);
969 } else
970 new_ptr = NULL;
971
972 if ((new_ptr != NULL) && (ptr != NULL)) {
973 slab_t *slab = obj2slab(ptr);
974 memcpy(new_ptr, ptr, min(size, slab->cache->size));
975 }
976
977 if (ptr != NULL)
978 free(ptr);
979
980 return new_ptr;
981}
[5158549]982
[ce8aed1]983void free(void *ptr)
984{
985 if (!ptr)
[f3272e98]986 return;
[da1bafb]987
[ce8aed1]988 slab_t *slab = obj2slab(ptr);
989 _slab_free(slab->cache, ptr, slab);
[4e147a6]990}
[b45c443]991
[cc73a8a1]992/** @}
[b45c443]993 */
Note: See TracBrowser for help on using the repository browser.