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

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

Modify kernel malloc()

This new implementation places the allocation size in front of the allocated
object, instead of relying on the slab allocator being able to determine source
slab cache for an object. This should improve scalability and help reduce
complexity of the memory management subsystem (further changes coming).

The drawback is more memory consumed by small malloc() allocations, however that
can be mitigated by switching to an API where the user provides known object
size to deallocation (most users know it either statically or from length they
necessarily remember).

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