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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 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
Line 
1/*
2 * Copyright (c) 2006 Ondrej Palkovsky
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
29/** @addtogroup genericmm
30 * @{
31 */
32
33/**
34 * @file
35 * @brief Slab allocator.
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/
39 *
40 * with the following exceptions:
41 * @li empty slabs are deallocated immediately
42 * (in Linux they are kept in linked list, in Solaris ???)
43 * @li empty magazines are deallocated when not needed
44 * (in Solaris they are held in linked list in slab cache)
45 *
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
49 * supported, but we would need to adjust allocation strategy)
50 *
51 * The slab allocator supports per-CPU caches ('magazines') to facilitate
52 * good SMP scaling.
53 *
54 * When a new object is being allocated, it is first checked, if it is
55 * available in a CPU-bound magazine. If it is not found there, it is
56 * allocated from a CPU-shared slab - if a partially full one is found,
57 * it is used, otherwise a new one is allocated.
58 *
59 * When an object is being deallocated, it is put to a CPU-bound magazine.
60 * If there is no such magazine, a new one is allocated (if this fails,
61 * the object is deallocated into slab). If the magazine is full, it is
62 * put into cpu-shared list of magazines and a new one is allocated.
63 *
64 * The CPU-bound magazine is actually a pair of magazines in order to avoid
65 * thrashing when somebody is allocating/deallocating 1 item at the magazine
66 * size boundary. LIFO order is enforced, which should avoid fragmentation
67 * as much as possible.
68 *
69 * Every cache contains list of full slabs and list of partially full slabs.
70 * Empty slabs are immediately freed (thrashing will be avoided because
71 * of magazines).
72 *
73 * The slab information structure is kept inside the data area, if possible.
74 * The cache can be marked that it should not use magazines. This is used
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).
77 *
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().
80 * It tries 'light reclaim' first, then brutal reclaim. The light reclaim
81 * releases slabs from cpu-shared magazine-list, until at least 1 slab
82 * is deallocated in each cache (this algorithm should probably change).
83 * The brutal reclaim removes all cached objects, even from CPU-bound
84 * magazines.
85 *
86 * @todo
87 * For better CPU-scaling the magazine allocation strategy should
88 * be extended. Currently, if the cache does not have magazine, it asks
89 * for non-cpu cached magazine cache to provide one. It might be feasible
90 * to add cpu-cached magazine cache (which would allocate it's magazines
91 * from non-cpu-cached mag. cache). This would provide a nice per-cpu
92 * buffer. The other possibility is to use the per-cache
93 * 'empty-magazine-list', which decreases competing for 1 per-system
94 * magazine cache.
95 *
96 * @todo
97 * It might be good to add granularity of locks even to slab level,
98 * we could then try_spinlock over all partial slabs and thus improve
99 * scalability even on slab level.
100 *
101 */
102
103#include <assert.h>
104#include <errno.h>
105#include <synch/spinlock.h>
106#include <mm/slab.h>
107#include <adt/list.h>
108#include <mem.h>
109#include <align.h>
110#include <mm/frame.h>
111#include <config.h>
112#include <print.h>
113#include <arch.h>
114#include <panic.h>
115#include <bitops.h>
116#include <macros.h>
117#include <cpu.h>
118
119IRQ_SPINLOCK_STATIC_INITIALIZE(slab_cache_lock);
120static LIST_INITIALIZE(slab_cache_list);
121
122/** Magazine cache */
123static slab_cache_t mag_cache;
124
125/** Cache for cache descriptors */
126static slab_cache_t slab_cache_cache;
127
128/** Cache for per-CPU magazines of caches */
129static slab_cache_t slab_mag_cache;
130
131/** Cache for external slab descriptors
132 * This time we want per-cpu cache, so do not make it static
133 * - using slab for internal slab structures will not deadlock,
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;
138
139/** Caches for malloc */
140static slab_cache_t *malloc_caches[SLAB_MAX_MALLOC_W - SLAB_MIN_MALLOC_W + 1];
141
142static const char *malloc_names[] = {
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",
157 "malloc-256K",
158 "malloc-512K",
159 "malloc-1M",
160 "malloc-2M",
161 "malloc-4M"
162};
163
164/** Slab descriptor */
165typedef struct {
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. */
171} slab_t;
172
173#ifdef CONFIG_DEBUG
174static unsigned int _slab_initialized = 0;
175#endif
176
177/**************************************/
178/* Slab allocation functions */
179/**************************************/
180
181/** Allocate frames for slab space and initialize
182 *
183 */
184NO_TRACE static slab_t *slab_space_alloc(slab_cache_t *cache,
185 unsigned int flags)
186{
187 size_t zone = 0;
188
189 uintptr_t data_phys =
190 frame_alloc_generic(cache->frames, flags, 0, &zone);
191 if (!data_phys)
192 return NULL;
193
194 void *data = (void *) PA2KA(data_phys);
195
196 slab_t *slab;
197 size_t fsize;
198
199 if (!(cache->flags & SLAB_CACHE_SLINSIDE)) {
200 slab = slab_alloc(slab_extern_cache, flags);
201 if (!slab) {
202 frame_free(KA2PA(data), cache->frames);
203 return NULL;
204 }
205 } else {
206 fsize = FRAMES2SIZE(cache->frames);
207 slab = data + fsize - sizeof(*slab);
208 }
209
210 /* Fill in slab structures */
211 size_t i;
212 for (i = 0; i < cache->frames; i++)
213 frame_set_parent(ADDR2PFN(KA2PA(data)) + i, slab, zone);
214
215 slab->start = data;
216 slab->available = cache->objects;
217 slab->nextavail = 0;
218 slab->cache = cache;
219
220 for (i = 0; i < cache->objects; i++)
221 *((size_t *) (slab->start + i * cache->size)) = i + 1;
222
223 atomic_inc(&cache->allocated_slabs);
224 return slab;
225}
226
227/** Deallocate space associated with slab
228 *
229 * @return number of freed frames
230 *
231 */
232NO_TRACE static size_t slab_space_free(slab_cache_t *cache, slab_t *slab)
233{
234 frame_free(KA2PA(slab->start), slab->cache->frames);
235 if (!(cache->flags & SLAB_CACHE_SLINSIDE))
236 slab_free(slab_extern_cache, slab);
237
238 atomic_dec(&cache->allocated_slabs);
239
240 return cache->frames;
241}
242
243/** Map object to slab structure */
244NO_TRACE static slab_t *obj2slab(void *obj)
245{
246 return (slab_t *) frame_get_parent(ADDR2PFN(KA2PA(obj)), 0);
247}
248
249/******************/
250/* Slab functions */
251/******************/
252
253/** Return object to slab and call a destructor
254 *
255 * @param slab If the caller knows directly slab of the object, otherwise NULL
256 *
257 * @return Number of freed pages
258 *
259 */
260NO_TRACE static size_t slab_obj_destroy(slab_cache_t *cache, void *obj,
261 slab_t *slab)
262{
263 if (!slab)
264 slab = obj2slab(obj);
265
266 assert(slab->cache == cache);
267
268 size_t freed = 0;
269
270 if (cache->destructor)
271 freed = cache->destructor(obj);
272
273 irq_spinlock_lock(&cache->slablock, true);
274 assert(slab->available < cache->objects);
275
276 *((size_t *) obj) = slab->nextavail;
277 slab->nextavail = (obj - slab->start) / cache->size;
278 slab->available++;
279
280 /* Move it to correct list */
281 if (slab->available == cache->objects) {
282 /* Free associated memory */
283 list_remove(&slab->link);
284 irq_spinlock_unlock(&cache->slablock, true);
285
286 return freed + slab_space_free(cache, slab);
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);
291 }
292
293 irq_spinlock_unlock(&cache->slablock, true);
294 return freed;
295}
296
297/** Take new object from slab or create new if needed
298 *
299 * @return Object address or null
300 *
301 */
302NO_TRACE static void *slab_obj_create(slab_cache_t *cache, unsigned int flags)
303{
304 irq_spinlock_lock(&cache->slablock, true);
305
306 slab_t *slab;
307
308 if (list_empty(&cache->partial_slabs)) {
309 /*
310 * Allow recursion and reclaiming
311 * - this should work, as the slab control structures
312 * are small and do not need to allocate with anything
313 * other than frame_alloc when they are allocating,
314 * that's why we should get recursion at most 1-level deep
315 *
316 */
317 irq_spinlock_unlock(&cache->slablock, true);
318 slab = slab_space_alloc(cache, flags);
319 if (!slab)
320 return NULL;
321
322 irq_spinlock_lock(&cache->slablock, true);
323 } else {
324 slab = list_get_instance(list_first(&cache->partial_slabs),
325 slab_t, link);
326 list_remove(&slab->link);
327 }
328
329 void *obj = slab->start + slab->nextavail * cache->size;
330 slab->nextavail = *((size_t *) obj);
331 slab->available--;
332
333 if (!slab->available)
334 list_prepend(&slab->link, &cache->full_slabs);
335 else
336 list_prepend(&slab->link, &cache->partial_slabs);
337
338 irq_spinlock_unlock(&cache->slablock, true);
339
340 if ((cache->constructor) && (cache->constructor(obj, flags) != EOK)) {
341 /* Bad, bad, construction failed */
342 slab_obj_destroy(cache, obj, slab);
343 return NULL;
344 }
345
346 return obj;
347}
348
349/****************************/
350/* CPU-Cache slab functions */
351/****************************/
352
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.
356 *
357 */
358NO_TRACE static slab_magazine_t *get_mag_from_cache(slab_cache_t *cache,
359 bool first)
360{
361 slab_magazine_t *mag = NULL;
362 link_t *cur;
363
364 irq_spinlock_lock(&cache->maglock, true);
365 if (!list_empty(&cache->magazines)) {
366 if (first)
367 cur = list_first(&cache->magazines);
368 else
369 cur = list_last(&cache->magazines);
370
371 mag = list_get_instance(cur, slab_magazine_t, link);
372 list_remove(&mag->link);
373 atomic_dec(&cache->magazine_counter);
374 }
375 irq_spinlock_unlock(&cache->maglock, true);
376
377 return mag;
378}
379
380/** Prepend magazine to magazine list in cache
381 *
382 */
383NO_TRACE static void put_mag_to_cache(slab_cache_t *cache,
384 slab_magazine_t *mag)
385{
386 irq_spinlock_lock(&cache->maglock, true);
387
388 list_prepend(&mag->link, &cache->magazines);
389 atomic_inc(&cache->magazine_counter);
390
391 irq_spinlock_unlock(&cache->maglock, true);
392}
393
394/** Free all objects in magazine and free memory associated with magazine
395 *
396 * @return Number of freed pages
397 *
398 */
399NO_TRACE static size_t magazine_destroy(slab_cache_t *cache,
400 slab_magazine_t *mag)
401{
402 size_t i;
403 size_t frames = 0;
404
405 for (i = 0; i < mag->busy; i++) {
406 frames += slab_obj_destroy(cache, mag->objs[i], NULL);
407 atomic_dec(&cache->cached_objs);
408 }
409
410 slab_free(&mag_cache, mag);
411
412 return frames;
413}
414
415/** Find full magazine, set it as current and return it
416 *
417 */
418NO_TRACE static slab_magazine_t *get_full_current_mag(slab_cache_t *cache)
419{
420 slab_magazine_t *cmag = cache->mag_cache[CPU->id].current;
421 slab_magazine_t *lastmag = cache->mag_cache[CPU->id].last;
422
423 assert(irq_spinlock_locked(&cache->mag_cache[CPU->id].lock));
424
425 if (cmag) { /* First try local CPU magazines */
426 if (cmag->busy)
427 return cmag;
428
429 if ((lastmag) && (lastmag->busy)) {
430 cache->mag_cache[CPU->id].current = lastmag;
431 cache->mag_cache[CPU->id].last = cmag;
432 return lastmag;
433 }
434 }
435
436 /* Local magazines are empty, import one from magazine list */
437 slab_magazine_t *newmag = get_mag_from_cache(cache, 1);
438 if (!newmag)
439 return NULL;
440
441 if (lastmag)
442 magazine_destroy(cache, lastmag);
443
444 cache->mag_cache[CPU->id].last = cmag;
445 cache->mag_cache[CPU->id].current = newmag;
446
447 return newmag;
448}
449
450/** Try to find object in CPU-cache magazines
451 *
452 * @return Pointer to object or NULL if not available
453 *
454 */
455NO_TRACE static void *magazine_obj_get(slab_cache_t *cache)
456{
457 if (!CPU)
458 return NULL;
459
460 irq_spinlock_lock(&cache->mag_cache[CPU->id].lock, true);
461
462 slab_magazine_t *mag = get_full_current_mag(cache);
463 if (!mag) {
464 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
465 return NULL;
466 }
467
468 void *obj = mag->objs[--mag->busy];
469 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
470
471 atomic_dec(&cache->cached_objs);
472
473 return obj;
474}
475
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
478 *
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.
483 *
484 */
485NO_TRACE static slab_magazine_t *make_empty_current_mag(slab_cache_t *cache)
486{
487 slab_magazine_t *cmag = cache->mag_cache[CPU->id].current;
488 slab_magazine_t *lastmag = cache->mag_cache[CPU->id].last;
489
490 assert(irq_spinlock_locked(&cache->mag_cache[CPU->id].lock));
491
492 if (cmag) {
493 if (cmag->busy < cmag->size)
494 return cmag;
495
496 if ((lastmag) && (lastmag->busy < lastmag->size)) {
497 cache->mag_cache[CPU->id].last = cmag;
498 cache->mag_cache[CPU->id].current = lastmag;
499 return lastmag;
500 }
501 }
502
503 /* current | last are full | nonexistent, allocate new */
504
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);
513 if (!newmag)
514 return NULL;
515
516 newmag->size = SLAB_MAG_SIZE;
517 newmag->busy = 0;
518
519 /* Flush last to magazine list */
520 if (lastmag)
521 put_mag_to_cache(cache, lastmag);
522
523 /* Move current as last, save new as current */
524 cache->mag_cache[CPU->id].last = cmag;
525 cache->mag_cache[CPU->id].current = newmag;
526
527 return newmag;
528}
529
530/** Put object into CPU-cache magazine
531 *
532 * @return 0 on success, -1 on no memory
533 *
534 */
535NO_TRACE static int magazine_obj_put(slab_cache_t *cache, void *obj)
536{
537 if (!CPU)
538 return -1;
539
540 irq_spinlock_lock(&cache->mag_cache[CPU->id].lock, true);
541
542 slab_magazine_t *mag = make_empty_current_mag(cache);
543 if (!mag) {
544 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
545 return -1;
546 }
547
548 mag->objs[mag->busy++] = obj;
549
550 irq_spinlock_unlock(&cache->mag_cache[CPU->id].lock, true);
551
552 atomic_inc(&cache->cached_objs);
553
554 return 0;
555}
556
557/************************/
558/* Slab cache functions */
559/************************/
560
561/** Return number of objects that fit in certain cache size
562 *
563 */
564NO_TRACE static size_t comp_objects(slab_cache_t *cache)
565{
566 if (cache->flags & SLAB_CACHE_SLINSIDE)
567 return (FRAMES2SIZE(cache->frames) - sizeof(slab_t)) /
568 cache->size;
569 else
570 return FRAMES2SIZE(cache->frames) / cache->size;
571}
572
573/** Return wasted space in slab
574 *
575 */
576NO_TRACE static size_t badness(slab_cache_t *cache)
577{
578 size_t objects = comp_objects(cache);
579 size_t ssize = FRAMES2SIZE(cache->frames);
580
581 if (cache->flags & SLAB_CACHE_SLINSIDE)
582 ssize -= sizeof(slab_t);
583
584 return ssize - objects * cache->size;
585}
586
587/** Initialize mag_cache structure in slab cache
588 *
589 */
590NO_TRACE static bool make_magcache(slab_cache_t *cache)
591{
592 assert(_slab_initialized >= 2);
593
594 cache->mag_cache = slab_alloc(&slab_mag_cache, FRAME_ATOMIC);
595 if (!cache->mag_cache)
596 return false;
597
598 size_t i;
599 for (i = 0; i < config.cpu_count; i++) {
600 memsetb(&cache->mag_cache[i], sizeof(cache->mag_cache[i]), 0);
601 irq_spinlock_initialize(&cache->mag_cache[i].lock,
602 "slab.cache.mag_cache[].lock");
603 }
604
605 return true;
606}
607
608/** Initialize allocated memory as a slab cache
609 *
610 */
611NO_TRACE static void _slab_cache_create(slab_cache_t *cache, const char *name,
612 size_t size, size_t align, errno_t (*constructor)(void *obj,
613 unsigned int kmflag), size_t (*destructor)(void *obj), unsigned int flags)
614{
615 assert(size > 0);
616
617 memsetb(cache, sizeof(*cache), 0);
618 cache->name = name;
619
620 if (align < sizeof(sysarg_t))
621 align = sizeof(sysarg_t);
622
623 size = ALIGN_UP(size, align);
624
625 cache->size = size;
626 cache->constructor = constructor;
627 cache->destructor = destructor;
628 cache->flags = flags;
629
630 list_initialize(&cache->full_slabs);
631 list_initialize(&cache->partial_slabs);
632 list_initialize(&cache->magazines);
633
634 irq_spinlock_initialize(&cache->slablock, "slab.cache.slablock");
635 irq_spinlock_initialize(&cache->maglock, "slab.cache.maglock");
636
637 if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
638 (void) make_magcache(cache);
639
640 /* Compute slab sizes, object counts in slabs etc. */
641 if (cache->size < SLAB_INSIDE_SIZE)
642 cache->flags |= SLAB_CACHE_SLINSIDE;
643
644 /* Minimum slab frames */
645 cache->frames = SIZE2FRAMES(cache->size);
646
647 while (badness(cache) > SLAB_MAX_BADNESS(cache))
648 cache->frames <<= 1;
649
650 cache->objects = comp_objects(cache);
651
652 /* If info fits in, put it inside */
653 if (badness(cache) > sizeof(slab_t))
654 cache->flags |= SLAB_CACHE_SLINSIDE;
655
656 /* Add cache to cache list */
657 irq_spinlock_lock(&slab_cache_lock, true);
658 list_append(&cache->link, &slab_cache_list);
659 irq_spinlock_unlock(&slab_cache_lock, true);
660}
661
662/** Create slab cache
663 *
664 */
665slab_cache_t *slab_cache_create(const char *name, size_t size, size_t align,
666 errno_t (*constructor)(void *obj, unsigned int kmflag),
667 size_t (*destructor)(void *obj), unsigned int flags)
668{
669 slab_cache_t *cache = slab_alloc(&slab_cache_cache, 0);
670 _slab_cache_create(cache, name, size, align, constructor, destructor,
671 flags);
672
673 return cache;
674}
675
676/** Reclaim space occupied by objects that are already free
677 *
678 * @param flags If contains SLAB_RECLAIM_ALL, do aggressive freeing
679 *
680 * @return Number of freed pages
681 *
682 */
683NO_TRACE static size_t _slab_reclaim(slab_cache_t *cache, unsigned int flags)
684{
685 if (cache->flags & SLAB_CACHE_NOMAGAZINE)
686 return 0; /* Nothing to do */
687
688 /*
689 * We count up to original magazine count to avoid
690 * endless loop
691 */
692 atomic_count_t magcount = atomic_get(&cache->magazine_counter);
693
694 slab_magazine_t *mag;
695 size_t frames = 0;
696
697 while ((magcount--) && (mag = get_mag_from_cache(cache, 0))) {
698 frames += magazine_destroy(cache, mag);
699 if ((!(flags & SLAB_RECLAIM_ALL)) && (frames))
700 break;
701 }
702
703 if (flags & SLAB_RECLAIM_ALL) {
704 /* Free cpu-bound magazines */
705 /* Destroy CPU magazines */
706 size_t i;
707 for (i = 0; i < config.cpu_count; i++) {
708 irq_spinlock_lock(&cache->mag_cache[i].lock, true);
709
710 mag = cache->mag_cache[i].current;
711 if (mag)
712 frames += magazine_destroy(cache, mag);
713 cache->mag_cache[i].current = NULL;
714
715 mag = cache->mag_cache[i].last;
716 if (mag)
717 frames += magazine_destroy(cache, mag);
718 cache->mag_cache[i].last = NULL;
719
720 irq_spinlock_unlock(&cache->mag_cache[i].lock, true);
721 }
722 }
723
724 return frames;
725}
726
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();
733
734 if ((cache->flags & SLAB_CACHE_NOMAGAZINE) ||
735 (magazine_obj_put(cache, obj)))
736 slab_obj_destroy(cache, obj, slab);
737
738 interrupts_restore(ipl);
739 atomic_dec(&cache->allocated_objs);
740}
741
742/** Check that there are no slabs and remove cache from system
743 *
744 */
745void slab_cache_destroy(slab_cache_t *cache)
746{
747 /*
748 * First remove cache from link, so that we don't need
749 * to disable interrupts later
750 *
751 */
752 irq_spinlock_lock(&slab_cache_lock, true);
753 list_remove(&cache->link);
754 irq_spinlock_unlock(&slab_cache_lock, true);
755
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 */
761
762 /* Destroy all magazines */
763 _slab_reclaim(cache, SLAB_RECLAIM_ALL);
764
765 /* All slabs must be empty */
766 if ((!list_empty(&cache->full_slabs)) ||
767 (!list_empty(&cache->partial_slabs)))
768 panic("Destroying cache that is not empty.");
769
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 }
774
775 slab_free(&slab_cache_cache, cache);
776}
777
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)
782{
783 /* Disable interrupts to avoid deadlocks with interrupt handlers */
784 ipl_t ipl = interrupts_disable();
785
786 void *result = NULL;
787
788 if (!(cache->flags & SLAB_CACHE_NOMAGAZINE))
789 result = magazine_obj_get(cache);
790
791 if (!result)
792 result = slab_obj_create(cache, flags);
793
794 interrupts_restore(ipl);
795
796 if (result)
797 atomic_inc(&cache->allocated_objs);
798
799 return result;
800}
801
802/** Return slab object to cache
803 *
804 */
805void slab_free(slab_cache_t *cache, void *obj)
806{
807 _slab_free(cache, obj, NULL);
808}
809
810/** Go through all caches and reclaim what is possible */
811size_t slab_reclaim(unsigned int flags)
812{
813 irq_spinlock_lock(&slab_cache_lock, true);
814
815 size_t frames = 0;
816 list_foreach(slab_cache_list, link, slab_cache_t, cache) {
817 frames += _slab_reclaim(cache, flags);
818 }
819
820 irq_spinlock_unlock(&slab_cache_lock, true);
821
822 return frames;
823}
824
825/* Print list of caches */
826void slab_print_list(void)
827{
828 printf("[cache name ] [size ] [pages ] [obj/pg] [slabs ]"
829 " [cached] [alloc ] [ctl]\n");
830
831 size_t skip = 0;
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 */
855
856 irq_spinlock_lock(&slab_cache_lock, true);
857
858 link_t *cur;
859 size_t i;
860 for (i = 0, cur = slab_cache_list.head.next;
861 (i < skip) && (cur != &slab_cache_list.head);
862 i++, cur = cur->next);
863
864 if (cur == &slab_cache_list.head) {
865 irq_spinlock_unlock(&slab_cache_lock, true);
866 break;
867 }
868
869 skip++;
870
871 slab_cache_t *cache = list_get_instance(cur, slab_cache_t, link);
872
873 const char *name = cache->name;
874 size_t frames = cache->frames;
875 size_t size = cache->size;
876 size_t objects = cache->objects;
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);
880 unsigned int flags = cache->flags;
881
882 irq_spinlock_unlock(&slab_cache_lock, true);
883
884 printf("%-18s %8zu %8zu %8zu %8ld %8ld %8ld %-5s\n",
885 name, size, frames, objects, allocated_slabs,
886 cached_objs, allocated_objs,
887 flags & SLAB_CACHE_SLINSIDE ? "in" : "out");
888 }
889}
890
891void slab_cache_init(void)
892{
893 /* Initialize magazine cache */
894 _slab_cache_create(&mag_cache, "slab_magazine_t",
895 sizeof(slab_magazine_t) + SLAB_MAG_SIZE * sizeof(void *),
896 sizeof(uintptr_t), NULL, NULL, SLAB_CACHE_NOMAGAZINE |
897 SLAB_CACHE_SLINSIDE);
898
899 /* Initialize slab_cache cache */
900 _slab_cache_create(&slab_cache_cache, "slab_cache_cache",
901 sizeof(slab_cache_cache), sizeof(uintptr_t), NULL, NULL,
902 SLAB_CACHE_NOMAGAZINE | SLAB_CACHE_SLINSIDE);
903
904 /* Initialize external slab cache */
905 slab_extern_cache = slab_cache_create("slab_t", sizeof(slab_t), 0,
906 NULL, NULL, SLAB_CACHE_SLINSIDE | SLAB_CACHE_MAGDEFERRED);
907
908 /* Initialize structures for malloc */
909 size_t i;
910 size_t size;
911
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);
917 }
918
919#ifdef CONFIG_DEBUG
920 _slab_initialized = 1;
921#endif
922}
923
924/** Enable cpu_cache
925 *
926 * Kernel calls this function, when it knows the real number of
927 * processors. Allocate slab for cpucache and enable it on all
928 * existing slabs that are SLAB_CACHE_MAGDEFERRED
929 *
930 */
931void slab_enable_cpucache(void)
932{
933#ifdef CONFIG_DEBUG
934 _slab_initialized = 2;
935#endif
936
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);
940
941 irq_spinlock_lock(&slab_cache_lock, false);
942
943 list_foreach(slab_cache_list, link, slab_cache_t, slab) {
944 if ((slab->flags & SLAB_CACHE_MAGDEFERRED) !=
945 SLAB_CACHE_MAGDEFERRED)
946 continue;
947
948 (void) make_magcache(slab);
949 slab->flags &= ~SLAB_CACHE_MAGDEFERRED;
950 }
951
952 irq_spinlock_unlock(&slab_cache_lock, false);
953}
954
955void *malloc(size_t size, unsigned int flags)
956{
957 assert(_slab_initialized);
958 assert(size <= (1 << SLAB_MAX_MALLOC_W));
959
960 if (size < (1 << SLAB_MIN_MALLOC_W))
961 size = (1 << SLAB_MIN_MALLOC_W);
962
963 uint8_t idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
964
965 return slab_alloc(malloc_caches[idx], flags);
966}
967
968void *realloc(void *ptr, size_t size, unsigned int flags)
969{
970 assert(_slab_initialized);
971 assert(size <= (1 << SLAB_MAX_MALLOC_W));
972
973 void *new_ptr;
974
975 if (size > 0) {
976 if (size < (1 << SLAB_MIN_MALLOC_W))
977 size = (1 << SLAB_MIN_MALLOC_W);
978 uint8_t idx = fnzb(size - 1) - SLAB_MIN_MALLOC_W + 1;
979
980 new_ptr = slab_alloc(malloc_caches[idx], flags);
981 } else
982 new_ptr = NULL;
983
984 if ((new_ptr != NULL) && (ptr != NULL)) {
985 slab_t *slab = obj2slab(ptr);
986 memcpy(new_ptr, ptr, min(size, slab->cache->size));
987 }
988
989 if (ptr != NULL)
990 free(ptr);
991
992 return new_ptr;
993}
994
995void free(void *ptr)
996{
997 if (!ptr)
998 return;
999
1000 slab_t *slab = obj2slab(ptr);
1001 _slab_free(slab->cache, ptr, slab);
1002}
1003
1004/** @}
1005 */
Note: See TracBrowser for help on using the repository browser.