source: mainline/kernel/generic/src/mm/slab.c@ 314f4b59

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 314f4b59 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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