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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bab75df6 was bab75df6, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Let kernel code get printf via the standard stdio header. Clean up unused includes.

  • Property mode set to 100644
File size: 26.7 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
[174156fd]29/** @addtogroup kernel_generic_mm
[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>
[bab75df6]112#include <stdio.h>
[4e147a6]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 =
[482f968]190 frame_alloc_generic(cache->frames, FRAME_LOWMEM | flags, 0, &zone);
[cd3b380]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 */
[3cfe2b8]692 size_t magcount = atomic_load(&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
[eb748a0]858 link_t *cur = slab_cache_list.head.next;
859 size_t i = 0;
860 while (i < skip && cur != &slab_cache_list.head) {
861 i++;
862 cur = cur->next;
863 }
[a35b458]864
[55b77d9]865 if (cur == &slab_cache_list.head) {
[da1bafb]866 irq_spinlock_unlock(&slab_cache_lock, true);
[599d6f5]867 break;
868 }
[a35b458]869
[599d6f5]870 skip++;
[a35b458]871
[da1bafb]872 slab_cache_t *cache = list_get_instance(cur, slab_cache_t, link);
[a35b458]873
[a000878c]874 const char *name = cache->name;
[b0c2075]875 size_t frames = cache->frames;
[599d6f5]876 size_t size = cache->size;
[da1bafb]877 size_t objects = cache->objects;
[036e97c]878 long allocated_slabs = atomic_load(&cache->allocated_slabs);
879 long cached_objs = atomic_load(&cache->cached_objs);
880 long allocated_objs = atomic_load(&cache->allocated_objs);
[da1bafb]881 unsigned int flags = cache->flags;
[a35b458]882
[da1bafb]883 irq_spinlock_unlock(&slab_cache_lock, true);
[a35b458]884
[b0c2075]885 printf("%-18s %8zu %8zu %8zu %8ld %8ld %8ld %-5s\n",
886 name, size, frames, objects, allocated_slabs,
[599d6f5]887 cached_objs, allocated_objs,
888 flags & SLAB_CACHE_SLINSIDE ? "in" : "out");
[4e147a6]889 }
890}
891
892void slab_cache_init(void)
893{
894 /* Initialize magazine cache */
[f97f1e51]895 _slab_cache_create(&mag_cache, "slab_magazine_t",
[7ec3c56]896 sizeof(slab_magazine_t) + SLAB_MAG_SIZE * sizeof(void *),
[46c1234]897 sizeof(uintptr_t), NULL, NULL, SLAB_CACHE_NOMAGAZINE |
898 SLAB_CACHE_SLINSIDE);
[a35b458]899
[fb10289b]900 /* Initialize slab_cache cache */
[f97f1e51]901 _slab_cache_create(&slab_cache_cache, "slab_cache_cache",
[46c1234]902 sizeof(slab_cache_cache), sizeof(uintptr_t), NULL, NULL,
903 SLAB_CACHE_NOMAGAZINE | SLAB_CACHE_SLINSIDE);
[a35b458]904
[fb10289b]905 /* Initialize external slab cache */
[f97f1e51]906 slab_extern_cache = slab_cache_create("slab_t", sizeof(slab_t), 0,
[46c1234]907 NULL, NULL, SLAB_CACHE_SLINSIDE | SLAB_CACHE_MAGDEFERRED);
[a35b458]908
[4e147a6]909 /* Initialize structures for malloc */
[da1bafb]910 size_t i;
911 size_t size;
[a35b458]912
[46c1234]913 for (i = 0, size = (1 << SLAB_MIN_MALLOC_W);
914 i < (SLAB_MAX_MALLOC_W - SLAB_MIN_MALLOC_W + 1);
915 i++, size <<= 1) {
916 malloc_caches[i] = slab_cache_create(malloc_names[i], size, 0,
917 NULL, NULL, SLAB_CACHE_MAGDEFERRED);
[c352c2e]918 }
[a35b458]919
[a000878c]920#ifdef CONFIG_DEBUG
[04225a7]921 _slab_initialized = 1;
922#endif
[c352c2e]923}
924
[8e1ea655]925/** Enable cpu_cache
926 *
927 * Kernel calls this function, when it knows the real number of
[da1bafb]928 * processors. Allocate slab for cpucache and enable it on all
929 * existing slabs that are SLAB_CACHE_MAGDEFERRED
930 *
[8e1ea655]931 */
932void slab_enable_cpucache(void)
933{
[214f5bb]934#ifdef CONFIG_DEBUG
935 _slab_initialized = 2;
936#endif
[a35b458]937
[4ed41b3]938 _slab_cache_create(&slab_mag_cache, "slab_mag_cache",
939 sizeof(slab_mag_cache_t) * config.cpu_count, sizeof(uintptr_t),
940 NULL, NULL, SLAB_CACHE_NOMAGAZINE | SLAB_CACHE_SLINSIDE);
[a35b458]941
[da1bafb]942 irq_spinlock_lock(&slab_cache_lock, false);
[a35b458]943
[feeac0d]944 list_foreach(slab_cache_list, link, slab_cache_t, slab) {
[da1bafb]945 if ((slab->flags & SLAB_CACHE_MAGDEFERRED) !=
[46c1234]946 SLAB_CACHE_MAGDEFERRED)
[8e1ea655]947 continue;
[a35b458]948
[da1bafb]949 (void) make_magcache(slab);
950 slab->flags &= ~SLAB_CACHE_MAGDEFERRED;
[8e1ea655]951 }
[a35b458]952
[da1bafb]953 irq_spinlock_unlock(&slab_cache_lock, false);
[8e1ea655]954}
955
[11b285d]956static void *_malloc(size_t size, unsigned int flags)
[c352c2e]957{
[63e27ef]958 assert(_slab_initialized);
959 assert(size <= (1 << SLAB_MAX_MALLOC_W));
[a35b458]960
[c352c2e]961 if (size < (1 << SLAB_MIN_MALLOC_W))
962 size = (1 << SLAB_MIN_MALLOC_W);
[a35b458]963
[da1bafb]964 uint8_t idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
[a35b458]965
[c352c2e]966 return slab_alloc(malloc_caches[idx], flags);
967}
968
[11b285d]969void *malloc(size_t size)
970{
971 return _malloc(size, FRAME_ATOMIC);
972}
973
974/** Non-failing malloc.
975 * Never returns NULL, but may block forever if no memory is available.
976 */
977void *nfmalloc(size_t size)
978{
979 return _malloc(size, 0);
980}
981
982static void *_realloc(void *ptr, size_t size, unsigned int flags)
[c352c2e]983{
[63e27ef]984 assert(_slab_initialized);
985 assert(size <= (1 << SLAB_MAX_MALLOC_W));
[a35b458]986
[ce8aed1]987 void *new_ptr;
[a35b458]988
[ce8aed1]989 if (size > 0) {
990 if (size < (1 << SLAB_MIN_MALLOC_W))
991 size = (1 << SLAB_MIN_MALLOC_W);
[da1bafb]992 uint8_t idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
[a35b458]993
[ce8aed1]994 new_ptr = slab_alloc(malloc_caches[idx], flags);
995 } else
996 new_ptr = NULL;
[a35b458]997
[ce8aed1]998 if ((new_ptr != NULL) && (ptr != NULL)) {
999 slab_t *slab = obj2slab(ptr);
1000 memcpy(new_ptr, ptr, min(size, slab->cache->size));
1001 }
[a35b458]1002
[ce8aed1]1003 if (ptr != NULL)
1004 free(ptr);
[a35b458]1005
[ce8aed1]1006 return new_ptr;
1007}
[5158549]1008
[11b285d]1009void *realloc(void *ptr, size_t size)
1010{
1011 return _realloc(ptr, size, FRAME_ATOMIC);
1012}
1013
[ce8aed1]1014void free(void *ptr)
1015{
1016 if (!ptr)
[f3272e98]1017 return;
[a35b458]1018
[ce8aed1]1019 slab_t *slab = obj2slab(ptr);
1020 _slab_free(slab->cache, ptr, slab);
[4e147a6]1021}
[b45c443]1022
[cc73a8a1]1023/** @}
[b45c443]1024 */
Note: See TracBrowser for help on using the repository browser.