summaryrefslogtreecommitdiff
path: root/src/slab.c
diff options
context:
space:
mode:
authorbernard.xiong <[email protected]>2009-07-02 22:48:23 +0000
committerbernard.xiong <[email protected]>2009-07-02 22:48:23 +0000
commitbda4730a9482c23083006abea939ee8d95d6ecff (patch)
tree83005121824bd50ac74e63688c0d5eac148665f8 /src/slab.c
parent80c72e66de0b00b1a6de3e3508366a4c3eee458d (diff)
import RT-Thread RTOS 0.3.x to Google SVN
git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2 bbd45198-f89e-11dd-88c7-29a3b14d5316
Diffstat (limited to 'src/slab.c')
-rw-r--r--src/slab.c815
1 files changed, 815 insertions, 0 deletions
diff --git a/src/slab.c b/src/slab.c
new file mode 100644
index 0000000000..59a1db7aa1
--- /dev/null
+++ b/src/slab.c
@@ -0,0 +1,815 @@
+/*
+ * File : slab.c
+ * This file is part of RT-Thread RTOS
+ * COPYRIGHT (C) 2008, RT-Thread Development Team
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rt-thread.org/license/LICENSE
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2008-07-12 Bernard the first version
+ */
+
+/*
+ * KERN_SLABALLOC.C - Kernel SLAB memory allocator
+ *
+ * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <[email protected]>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+
+/* #define RT_SLAB_DEBUG */
+
+#if defined (RT_USING_HEAP) && defined (RT_USING_SLAB)
+#ifdef RT_USING_HOOK
+static void (*rt_malloc_hook)(void *ptr, rt_size_t size);
+static void (*rt_free_hook)(void *ptr);
+
+/**
+ * @addtogroup Hook
+ */
+/*@{*/
+
+/**
+ * This function will set a hook function, which will be invoked when a memory
+ * block is allocated from heap memory.
+ *
+ * @param hook the hook function
+ */
+void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size))
+{
+ rt_malloc_hook = hook;
+}
+
+/**
+ * This function will set a hook function, which will be invoked when a memory
+ * block is released to heap memory.
+ *
+ * @param hook the hook function
+ */
+void rt_free_sethook(void (*hook)(void *ptr))
+{
+ rt_free_hook = hook;
+}
+
+/*@}*/
+
+#endif
+
+/*
+ * slab allocator implementation
+ *
+ * A slab allocator reserves a ZONE for each chunk size, then lays the
+ * chunks out in an array within the zone. Allocation and deallocation
+ * is nearly instantanious, and fragmentation/overhead losses are limited
+ * to a fixed worst-case amount.
+ *
+ * The downside of this slab implementation is in the chunk size
+ * multiplied by the number of zones. ~80 zones * 128K = 10MB of VM per cpu.
+ * In a kernel implementation all this memory will be physical so
+ * the zone size is adjusted downward on machines with less physical
+ * memory. The upside is that overhead is bounded... this is the *worst*
+ * case overhead.
+ *
+ * Slab management is done on a per-cpu basis and no locking or mutexes
+ * are required, only a critical section. When one cpu frees memory
+ * belonging to another cpu's slab manager an asynchronous IPI message
+ * will be queued to execute the operation. In addition, both the
+ * high level slab allocator and the low level zone allocator optimize
+ * M_ZERO requests, and the slab allocator does not have to pre initialize
+ * the linked list of chunks.
+ *
+ * XXX Balancing is needed between cpus. Balance will be handled through
+ * asynchronous IPIs primarily by reassigning the z_Cpu ownership of chunks.
+ *
+ * XXX If we have to allocate a new zone and M_USE_RESERVE is set, use of
+ * the new zone should be restricted to M_USE_RESERVE requests only.
+ *
+ * Alloc Size Chunking Number of zones
+ * 0-127 8 16
+ * 128-255 16 8
+ * 256-511 32 8
+ * 512-1023 64 8
+ * 1024-2047 128 8
+ * 2048-4095 256 8
+ * 4096-8191 512 8
+ * 8192-16383 1024 8
+ * 16384-32767 2048 8
+ * (if RT_MM_PAGE_SIZE is 4K the maximum zone allocation is 16383)
+ *
+ * Allocations >= zone_limit go directly to kmem.
+ *
+ * API REQUIREMENTS AND SIDE EFFECTS
+ *
+ * To operate as a drop-in replacement to the FreeBSD-4.x malloc() we
+ * have remained compatible with the following API requirements:
+ *
+ * + small power-of-2 sized allocations are power-of-2 aligned (kern_tty)
+ * + all power-of-2 sized allocations are power-of-2 aligned (twe)
+ * + malloc(0) is allowed and returns non-RT_NULL (ahc driver)
+ * + ability to allocate arbitrarily large chunks of memory
+ */
+
+/*
+ * Chunk structure for free elements
+ */
+typedef struct slab_chunk
+{
+ struct slab_chunk *c_next;
+} slab_chunk;
+
+/*
+ * The IN-BAND zone header is placed at the beginning of each zone.
+ */
+typedef struct slab_zone {
+ rt_int32_t z_magic; /* magic number for sanity check */
+ rt_int32_t z_nfree; /* total free chunks / ualloc space in zone */
+ rt_int32_t z_nmax; /* maximum free chunks */
+
+ struct slab_zone *z_next; /* zoneary[] link if z_nfree non-zero */
+ rt_uint8_t *z_baseptr; /* pointer to start of chunk array */
+
+ rt_int32_t z_uindex; /* current initial allocation index */
+ rt_int32_t z_chunksize; /* chunk size for validation */
+
+ rt_int32_t z_zoneindex; /* zone index */
+ slab_chunk *z_freechunk; /* free chunk list */
+} slab_zone;
+
+#define ZALLOC_SLAB_MAGIC 0x51ab51ab
+#define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */
+#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */
+#define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */
+#define NZONES 72 /* number of zones */
+#define ZONE_RELEASE_THRESH 2 /* threshold number of zones */
+
+static slab_zone *zone_array[NZONES]; /* linked list of zones NFree > 0 */
+static slab_zone *zone_free; /* whole zones that have become free */
+
+static int zone_free_cnt;
+static int zone_size;
+static int zone_limit;
+static int zone_page_cnt;
+
+#ifdef RT_MEM_STATS
+/* some statistical variable */
+static rt_uint32_t rt_mem_allocated = 0;
+static rt_uint32_t rt_mem_zone = 0;
+static rt_uint32_t rt_mem_page_allocated = 0;
+#endif
+
+/*
+ * Misc constants. Note that allocations that are exact multiples of
+ * RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module.
+ */
+#define MIN_CHUNK_SIZE 8 /* in bytes */
+#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1)
+
+/*
+ * Array of descriptors that describe the contents of each page
+ */
+#define PAGE_TYPE_FREE 0x00
+#define PAGE_TYPE_SMALL 0x01
+#define PAGE_TYPE_LARGE 0x02
+struct memusage {
+ rt_uint32_t type:2 ; /* page type */
+ rt_uint32_t size:30; /* pages allocated or offset from zone */
+};
+static struct memusage *memusage = RT_NULL;
+#define btokup(addr) (&memusage[((rt_uint32_t)(addr) - heap_start) >> RT_MM_PAGE_BITS])
+
+static rt_uint32_t heap_start, heap_end;
+
+/* page allocator */
+struct rt_page_head
+{
+ struct rt_page_head *next; /* next valid page */
+ rt_size_t page; /* number of page */
+
+ /* dummy */
+ char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_page_head*) + sizeof (rt_size_t))];
+};
+static struct rt_page_head *rt_page_list;
+
+static void *rt_page_alloc(rt_size_t npages)
+{
+ struct rt_page_head *b, *n;
+ struct rt_page_head **prev;
+
+ RT_ASSERT(npages != 0);
+
+ for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next))
+ {
+ if (b->page > npages)
+ {
+ /* splite pages */
+ n = b + npages;
+ n->next = b->next;
+ n->page = b->page - npages;
+ *prev = n;
+ break;
+ }
+
+ if (b->page == npages)
+ {
+ /* this node fit, remove this node */
+ *prev = b->next;
+ break;
+ }
+ }
+
+ return b;
+}
+
+static void rt_page_free(void *addr, rt_size_t npages)
+{
+ struct rt_page_head *b, *n;
+ struct rt_page_head **prev;
+
+ RT_ASSERT(addr != RT_NULL);
+ RT_ASSERT((rt_uint32_t)addr % RT_MM_PAGE_SIZE == 0);
+ RT_ASSERT(npages != 0);
+
+ n = (struct rt_page_head *)addr;
+
+ for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next))
+ {
+ RT_ASSERT(b->page > 0);
+ RT_ASSERT(b > n || b + b->page <= n);
+
+ if (b + b->page == n)
+ {
+ if (b + (b->page += npages) == b->next)
+ {
+ b->page += b->next->page;
+ b->next = b->next->next;
+ }
+
+ return;
+ }
+
+ if (b == n + npages)
+ {
+ n->page = b->page + npages;
+ n->next = b->next;
+ *prev = n;
+
+ return;
+ }
+
+ if (b > n + npages) break;
+ }
+
+ n->page = npages;
+ n->next = b;
+ *prev = n;
+}
+
+/*
+ * Initialize the page allocator
+ */
+static void rt_page_init(void* addr, rt_size_t npages)
+{
+ RT_ASSERT(addr != RT_NULL);
+ RT_ASSERT(npages != 0);
+
+ rt_page_list = RT_NULL;
+ rt_page_free(addr, npages);
+}
+
+/**
+ * @ingroup SystemInit
+ *
+ * This function will init system heap
+ *
+ * @param begin_addr the beginning address of system page
+ * @param end_addr the end address of system page
+ *
+ */
+void rt_system_heap_init(void *begin_addr, void* end_addr)
+{
+ rt_uint32_t limsize, npages;
+
+ /* align begin and end addr to page */
+ heap_start = RT_ALIGN((rt_uint32_t)begin_addr, RT_MM_PAGE_SIZE);
+ heap_end = RT_ALIGN((rt_uint32_t)end_addr, RT_MM_PAGE_SIZE);
+
+ limsize = heap_end - heap_start;
+ npages = limsize / RT_MM_PAGE_SIZE;
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("heap[0x%x - 0x%x], size 0x%x, 0x%x pages\n", heap_start, heap_end, limsize, npages);
+#endif
+
+ /* init pages */
+ rt_page_init((void*)heap_start, npages);
+
+ /* calculate zone size */
+ zone_size = ZALLOC_MIN_ZONE_SIZE;
+ while (zone_size < ZALLOC_MAX_ZONE_SIZE && (zone_size << 1) < (limsize/1024))
+ zone_size <<= 1;
+
+ zone_limit = zone_size / 4;
+ if (zone_limit > ZALLOC_ZONE_LIMIT) zone_limit = ZALLOC_ZONE_LIMIT;
+
+ zone_page_cnt = zone_size / RT_MM_PAGE_SIZE;
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("zone size 0x%x, zone page count 0x%x\n", zone_size, zone_page_cnt);
+#endif
+
+ /* allocate memusage array */
+ limsize = npages * sizeof(struct memusage);
+ limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE);
+ memusage = rt_page_alloc(limsize/RT_MM_PAGE_SIZE);
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("memusage 0x%x, size 0x%x\n", (rt_uint32_t)memusage, limsize);
+#endif
+}
+
+/*
+ * Calculate the zone index for the allocation request size and set the
+ * allocation request size to that particular zone's chunk size.
+ */
+rt_inline int zoneindex(rt_uint32_t *bytes)
+{
+ rt_uint32_t n = (rt_uint32_t)*bytes; /* unsigned for shift opt */
+
+ if (n < 128)
+ {
+ *bytes = n = (n + 7) & ~7;
+ return(n / 8 - 1); /* 8 byte chunks, 16 zones */
+ }
+ if (n < 256)
+ {
+ *bytes = n = (n + 15) & ~15;
+ return(n / 16 + 7);
+ }
+ if (n < 8192)
+ {
+ if (n < 512)
+ {
+ *bytes = n = (n + 31) & ~31;
+ return(n / 32 + 15);
+ }
+ if (n < 1024)
+ {
+ *bytes = n = (n + 63) & ~63;
+ return(n / 64 + 23);
+ }
+ if (n < 2048)
+ {
+ *bytes = n = (n + 127) & ~127;
+ return(n / 128 + 31);
+ }
+ if (n < 4096)
+ {
+ *bytes = n = (n + 255) & ~255;
+ return(n / 256 + 39);
+ }
+ *bytes = n = (n + 511) & ~511;
+ return(n / 512 + 47);
+ }
+ if (n < 16384)
+ {
+ *bytes = n = (n + 1023) & ~1023;
+ return(n / 1024 + 55);
+ }
+
+ rt_kprintf("Unexpected byte count %d", n);
+ return 0;
+}
+
+/**
+ * @addtogroup MM
+ */
+
+/*@{*/
+
+/**
+ * This function will allocate a block from system heap memory.
+ * - If the nbytes is less than zero,
+ * or
+ * - If there is no nbytes sized memory valid in system,
+ * the RT_NULL is returned.
+ *
+ * @param size the size of memory to be allocated
+ *
+ * @return the allocated memory
+ *
+ */
+void *rt_malloc(rt_size_t size)
+{
+ slab_zone *z;
+ rt_int32_t zi;
+ slab_chunk *chunk;
+ rt_base_t interrupt_level;
+ struct memusage *kup;
+
+ /* zero size, return RT_NULL */
+ if (size == 0) return RT_NULL;
+
+ /*
+ * Handle large allocations directly. There should not be very many of
+ * these so performance is not a big issue.
+ */
+ if (size >= zone_limit)
+ {
+ size = RT_ALIGN(size, RT_MM_PAGE_SIZE);
+
+ chunk = rt_page_alloc(size >> RT_MM_PAGE_BITS);
+ if (chunk == RT_NULL) return RT_NULL;
+
+ /* set kup */
+ kup = btokup(chunk);
+ kup->type = PAGE_TYPE_LARGE;
+ kup->size = size >> RT_MM_PAGE_BITS;
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("malloc a large memory 0x%x, page cnt %d, kup %d\n",
+ size,
+ size >> RT_MM_PAGE_BITS,
+ ((rt_uint32_t)chunk - heap_start) >> RT_MM_PAGE_BITS);
+#endif
+
+ /* lock interrupt */
+ interrupt_level = rt_hw_interrupt_disable();
+ goto done;
+ }
+
+ /*
+ * Attempt to allocate out of an existing zone. First try the free list,
+ * then allocate out of unallocated space. If we find a good zone move
+ * it to the head of the list so later allocations find it quickly
+ * (we might have thousands of zones in the list).
+ *
+ * Note: zoneindex() will panic of size is too large.
+ */
+ zi = zoneindex(&size);
+ RT_ASSERT(zi < NZONES);
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("try to malloc 0x%x on zone: %d\n", size, zi);
+#endif
+
+ interrupt_level = rt_hw_interrupt_disable();
+ if ((z = zone_array[zi]) != RT_NULL)
+ {
+ RT_ASSERT(z->z_nfree > 0);
+
+ /* Remove us from the zone_array[] when we become empty */
+ if (--z->z_nfree == 0)
+ {
+ zone_array[zi] = z->z_next;
+ z->z_next = RT_NULL;
+ }
+
+ /*
+ * No chunks are available but nfree said we had some memory, so
+ * it must be available in the never-before-used-memory area
+ * governed by uindex. The consequences are very serious if our zone
+ * got corrupted so we use an explicit rt_kprintf rather then a KASSERT.
+ */
+ if (z->z_uindex + 1 != z->z_nmax)
+ {
+ z->z_uindex = z->z_uindex + 1;
+ chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size);
+ }
+ else
+ {
+ /* find on free chunk list */
+ chunk = z->z_freechunk;
+
+ /* remove this chunk from list */
+ z->z_freechunk = z->z_freechunk->c_next;
+ }
+
+ goto done;
+ }
+
+ /*
+ * If all zones are exhausted we need to allocate a new zone for this
+ * index.
+ *
+ * At least one subsystem, the tty code (see CROUND) expects power-of-2
+ * allocations to be power-of-2 aligned. We maintain compatibility by
+ * adjusting the base offset below.
+ */
+ {
+ rt_int32_t off;
+
+ if ((z = zone_free) != RT_NULL)
+ {
+ /* remove zone from free zone list */
+ zone_free = z->z_next;
+ --zone_free_cnt;
+ }
+ else
+ {
+ /* allocate a zone from page */
+ z = rt_page_alloc(zone_size / RT_MM_PAGE_SIZE);
+ if (z == RT_NULL) goto fail;
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("alloc a new zone: 0x%x\n", (rt_uint32_t)z);
+#endif
+
+ /* set message usage */
+ for (off = 0, kup = btokup(z); off < zone_page_cnt; off ++)
+ {
+ kup->type = PAGE_TYPE_SMALL;
+ kup->size = off;
+
+ kup ++;
+ }
+ }
+
+ /* clear to zero */
+ rt_memset(z, 0, sizeof(slab_zone));
+
+ /* offset of slab zone struct in zone */
+ off = sizeof(slab_zone);
+
+ /*
+ * Guarentee power-of-2 alignment for power-of-2-sized chunks.
+ * Otherwise just 8-byte align the data.
+ */
+ if ((size | (size - 1)) + 1 == (size << 1))
+ off = (off + size - 1) & ~(size - 1);
+ else
+ off = (off + MIN_CHUNK_MASK) & ~MIN_CHUNK_MASK;
+
+ z->z_magic = ZALLOC_SLAB_MAGIC;
+ z->z_zoneindex = zi;
+ z->z_nmax = (zone_size - off) / size;
+ z->z_nfree = z->z_nmax - 1;
+ z->z_baseptr = (rt_uint8_t*)z + off;
+ z->z_uindex = 0;
+ z->z_chunksize = size;
+
+ chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size);
+
+ /* link to zone array */
+ z->z_next = zone_array[zi];
+ zone_array[zi] = z;
+ }
+
+done:
+ rt_hw_interrupt_enable(interrupt_level);
+
+#ifdef RT_USING_HOOK
+ if (rt_malloc_hook != RT_NULL) rt_malloc_hook((char*)chunk, size);
+#endif
+
+ return chunk;
+
+fail:
+ rt_hw_interrupt_enable(interrupt_level);
+ return RT_NULL;
+}
+
+/**
+ * This function will change the size of previously allocated memory block.
+ *
+ * @param ptr the previously allocated memory block
+ * @param size the new size of memory block
+ *
+ * @return the allocated memory
+ */
+void *rt_realloc(void *ptr, rt_size_t size)
+{
+ void *nptr;
+ slab_zone *z;
+ struct memusage *kup;
+
+ if (ptr == RT_NULL) return rt_malloc(size);
+ if (size == 0)
+ {
+ rt_free(ptr);
+ return RT_NULL;
+ }
+
+ /*
+ * Get the original allocation's zone. If the new request winds up
+ * using the same chunk size we do not have to do anything.
+ */
+ kup = btokup((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK);
+ if (kup->type == PAGE_TYPE_LARGE)
+ {
+ rt_size_t osize;
+
+ osize = kup->size << RT_MM_PAGE_BITS;
+ if ((nptr = rt_malloc(size)) == RT_NULL) return RT_NULL;
+ rt_memcpy(nptr, ptr, size > osize? osize : size);
+ rt_free(ptr);
+
+ return nptr;
+ }
+ else if (kup->type == PAGE_TYPE_SMALL)
+ {
+ z = (slab_zone*)(((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK) - kup->size * RT_MM_PAGE_SIZE);
+ RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
+
+ zoneindex(&size);
+ if (z->z_chunksize == size) return(ptr); /* same chunk */
+
+ /*
+ * Allocate memory for the new request size. Note that zoneindex has
+ * already adjusted the request size to the appropriate chunk size, which
+ * should optimize our bcopy(). Then copy and return the new pointer.
+ */
+ if ((nptr = rt_malloc(size)) == RT_NULL) return RT_NULL;
+
+ rt_memcpy(nptr, ptr, size > z->z_chunksize? z->z_chunksize : size);
+ rt_free(ptr);
+
+ return nptr;
+ }
+
+ return RT_NULL;
+}
+
+/**
+ * This function will contiguously allocate enough space for count objects
+ * that are size bytes of memory each and returns a pointer to the allocated
+ * memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ *
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *rt_calloc(rt_size_t count, rt_size_t size)
+{
+ void *p;
+
+ /* allocate 'count' objects of size 'size' */
+ p = rt_malloc(count * size);
+
+ /* zero the memory */
+ if (p) rt_memset(p, 0, count * size);
+
+ return p;
+}
+
+/**
+ * This function will release the previously allocated memory block by rt_malloc.
+ * The released memory block is taken back to system heap.
+ *
+ * @param ptr the address of memory which will be released
+ */
+void rt_free(void *ptr)
+{
+ slab_zone *z;
+ slab_chunk *chunk;
+ struct memusage *kup;
+ rt_base_t interrupt_level;
+
+ /* free a RT_NULL pointer */
+ if (ptr == RT_NULL) return ;
+
+#ifdef RT_USING_HOOK
+ if (rt_free_hook != RT_NULL) rt_free_hook(ptr);
+#endif
+
+ /* get memory usage */
+#ifdef RT_SLAB_DEBUG
+ rt_uint32 addr = ((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK);
+ rt_kprintf("free a memory 0x%x and align to 0x%x, kup index %d\n",
+ (rt_uint32_t)ptr,
+ (rt_uint32_t)addr,
+ ((rt_uint32_t)(addr) - heap_start) >> RT_MM_PAGE_BITS);
+#endif
+
+ kup = btokup((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK);
+ /* release large allocation */
+ if (kup->type == PAGE_TYPE_LARGE)
+ {
+ rt_uint32_t size;
+
+ /* clear page counter */
+ interrupt_level = rt_hw_interrupt_disable();
+ size = kup->size;
+ kup->size = 0;
+ rt_hw_interrupt_enable(interrupt_level);
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("free large memory block 0x%x, page count %d\n", (rt_uint32_t)ptr, size);
+#endif
+
+ /* free this page */
+ rt_page_free(ptr, size);
+ return;
+ }
+
+ /* zone case. get out zone. */
+ z = (slab_zone*)(((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK) - kup->size * RT_MM_PAGE_SIZE);
+ RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
+
+ interrupt_level = rt_hw_interrupt_disable();
+ chunk = (slab_chunk*)ptr;
+ chunk->c_next = z->z_freechunk;
+ z->z_freechunk = chunk;
+
+ /*
+ * Bump the number of free chunks. If it becomes non-zero the zone
+ * must be added back onto the appropriate list.
+ */
+ if (z->z_nfree++ == 0)
+ {
+ z->z_next = zone_array[z->z_zoneindex];
+ zone_array[z->z_zoneindex] = z;
+ }
+
+ /*
+ * If the zone becomes totally free, and there are other zones we
+ * can allocate from, move this zone to the FreeZones list. Since
+ * this code can be called from an IPI callback, do *NOT* try to mess
+ * with kernel_map here. Hysteresis will be performed at malloc() time.
+ */
+ if (z->z_nfree == z->z_nmax &&
+ (z->z_next || zone_array[z->z_zoneindex] != z))
+ {
+ slab_zone **pz;
+
+#ifdef RT_SLAB_DEBUG
+ rt_kprintf("free zone 0x%x\n", (rt_uint32_t)z, z->z_zoneindex);
+#endif
+
+ /* remove zone from zone array list */
+ for (pz = &zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next) ;
+ *pz = z->z_next;
+
+ /* reset zone */
+ z->z_magic = -1;
+
+ /* insert to free zone list */
+ z->z_next = zone_free;
+ zone_free = z;
+
+ ++zone_free_cnt;
+
+ /* release zone to page allocator */
+ if (zone_free_cnt > ZONE_RELEASE_THRESH)
+ {
+ register rt_base_t i;
+
+ z = zone_free;
+ zone_free = z->z_next;
+ --zone_free_cnt;
+
+ /* set message usage */
+ for (i = 0, kup = btokup(z); i < zone_page_cnt; i ++)
+ {
+ kup->type = PAGE_TYPE_FREE;
+ kup->size = 0;
+ kup ++;
+ }
+
+ /* release pages */
+ rt_page_free(z, zone_size);
+ }
+ }
+ rt_hw_interrupt_enable(interrupt_level);
+}
+
+/*@}*/
+
+#endif