123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968 |
- /*
- * File : slab.c
- * This file is part of RT-Thread RTOS
- * COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date Author Notes
- * 2008-07-12 Bernard the first version
- * 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
- * 2010-10-23 yi.qiu add module memory allocator
- * 2010-12-18 yi.qiu fix zone release bug
- */
- /*
- * 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 <dillon@backplane.com>
- *
- * 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_MEM_STATS
- #if defined (RT_USING_HEAP) && defined (RT_USING_SLAB)
- /* some statistical variable */
- #ifdef RT_MEM_STATS
- static rt_size_t used_mem, max_mem;
- #endif
- #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;
- }
- RTM_EXPORT(rt_malloc_sethook);
- /**
- * 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;
- }
- RTM_EXPORT(rt_free_sethook);
- /**@}*/
- #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;
- /*
- * 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 struct rt_semaphore heap_sem;
- void *rt_page_alloc(rt_size_t npages)
- {
- struct rt_page_head *b, *n;
- struct rt_page_head **prev;
- if (npages == 0)
- return RT_NULL;
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- 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;
- }
- }
- /* unlock heap */
- rt_sem_release(&heap_sem);
- return b;
- }
- 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;
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- 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;
- }
- goto _return;
- }
- if (b == n + npages)
- {
- n->page = b->page + npages;
- n->next = b->next;
- *prev = n;
- goto _return;
- }
- if (b > n + npages)
- break;
- }
- n->page = npages;
- n->next = b;
- *prev = n;
- _return:
- /* unlock heap */
- rt_sem_release(&heap_sem);
- }
- /*
- * 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;
- RT_DEBUG_NOT_IN_INTERRUPT;
- /* align begin and end addr to page */
- heap_start = RT_ALIGN((rt_uint32_t)begin_addr, RT_MM_PAGE_SIZE);
- heap_end = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_MM_PAGE_SIZE);
- if (heap_start >= heap_end)
- {
- rt_kprintf("rt_system_heap_init, wrong address[0x%x - 0x%x]\n",
- (rt_uint32_t)begin_addr, (rt_uint32_t)end_addr);
- return;
- }
- limsize = heap_end - heap_start;
- npages = limsize / RT_MM_PAGE_SIZE;
- /* initialize heap semaphore */
- rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO);
- RT_DEBUG_LOG(RT_DEBUG_SLAB, ("heap[0x%x - 0x%x], size 0x%x, 0x%x pages\n",
- heap_start, heap_end, limsize, npages));
- /* 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;
- RT_DEBUG_LOG(RT_DEBUG_SLAB, ("zone size 0x%x, zone page count 0x%x\n",
- zone_size, zone_page_cnt));
- /* 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);
- RT_DEBUG_LOG(RT_DEBUG_SLAB, ("memusage 0x%x, size 0x%x\n",
- (rt_uint32_t)memusage, limsize));
- }
- /*
- * 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)
- {
- /* unsigned for shift opt */
- rt_uint32_t n = (rt_uint32_t) * bytes;
- if (n < 128)
- {
- *bytes = n = (n + 7) & ~7;
- /* 8 byte chunks, 16 zones */
- return (n / 8 - 1);
- }
- 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;
- struct memusage *kup;
- /* zero size, return RT_NULL */
- if (size == 0)
- return RT_NULL;
- #ifdef RT_USING_MODULE
- if (rt_module_self() != RT_NULL)
- return rt_module_malloc(size);
- #endif
- /*
- * 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;
- RT_DEBUG_LOG(RT_DEBUG_SLAB,
- ("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));
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- #ifdef RT_MEM_STATS
- used_mem += size;
- if (used_mem > max_mem)
- max_mem = used_mem;
- #endif
- goto done;
- }
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- /*
- * 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);
- RT_DEBUG_LOG(RT_DEBUG_SLAB, ("try to malloc 0x%x on zone: %d\n", size, zi));
- 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;
- }
- #ifdef RT_MEM_STATS
- used_mem += z->z_chunksize;
- if (used_mem > max_mem)
- max_mem = used_mem;
- #endif
- 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
- {
- /* unlock heap, since page allocator will think about lock */
- rt_sem_release(&heap_sem);
- /* allocate a zone from page */
- z = rt_page_alloc(zone_size / RT_MM_PAGE_SIZE);
- if (z == RT_NULL)
- goto fail;
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- RT_DEBUG_LOG(RT_DEBUG_SLAB, ("alloc a new zone: 0x%x\n",
- (rt_uint32_t)z));
- /* 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;
- #ifdef RT_MEM_STATS
- used_mem += z->z_chunksize;
- if (used_mem > max_mem)
- max_mem = used_mem;
- #endif
- }
- done:
- rt_sem_release(&heap_sem);
- RT_OBJECT_HOOK_CALL(rt_malloc_hook, ((char *)chunk, size));
- return chunk;
- fail:
- rt_sem_release(&heap_sem);
- return RT_NULL;
- }
- RTM_EXPORT(rt_malloc);
- /**
- * 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;
- }
- #ifdef RT_USING_MODULE
- if (rt_module_self() != RT_NULL)
- return rt_module_realloc(ptr, size);
- #endif
- /*
- * 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;
- }
- RTM_EXPORT(rt_realloc);
- /**
- * 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;
- }
- RTM_EXPORT(rt_calloc);
- /**
- * This function will release the previous 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;
- /* free a RT_NULL pointer */
- if (ptr == RT_NULL)
- return ;
- RT_OBJECT_HOOK_CALL(rt_free_hook, (ptr));
- #ifdef RT_USING_MODULE
- if (rt_module_self() != RT_NULL)
- {
- rt_module_free(rt_module_self(), ptr);
- return;
- }
- #endif
- /* get memory usage */
- #if RT_DEBUG_SLAB
- {
- rt_uint32_t addr = ((rt_uint32_t)ptr & ~RT_MM_PAGE_MASK);
- RT_DEBUG_LOG(RT_DEBUG_SLAB,
- ("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;
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- /* clear page counter */
- size = kup->size;
- kup->size = 0;
- #ifdef RT_MEM_STATS
- used_mem -= size * RT_MM_PAGE_SIZE;
- #endif
- rt_sem_release(&heap_sem);
- RT_DEBUG_LOG(RT_DEBUG_SLAB,
- ("free large memory block 0x%x, page count %d\n",
- (rt_uint32_t)ptr, size));
- /* free this page */
- rt_page_free(ptr, size);
- return;
- }
- /* lock heap */
- rt_sem_take(&heap_sem, RT_WAITING_FOREVER);
- /* 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);
- chunk = (slab_chunk *)ptr;
- chunk->c_next = z->z_freechunk;
- z->z_freechunk = chunk;
- #ifdef RT_MEM_STATS
- used_mem -= z->z_chunksize;
- #endif
- /*
- * 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;
- RT_DEBUG_LOG(RT_DEBUG_SLAB, ("free zone 0x%x\n",
- (rt_uint32_t)z, z->z_zoneindex));
- /* 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 ++;
- }
- /* unlock heap */
- rt_sem_release(&heap_sem);
- /* release pages */
- rt_page_free(z, zone_size / RT_MM_PAGE_SIZE);
- return;
- }
- }
- /* unlock heap */
- rt_sem_release(&heap_sem);
- }
- RTM_EXPORT(rt_free);
- #ifdef RT_MEM_STATS
- void rt_memory_info(rt_uint32_t *total,
- rt_uint32_t *used,
- rt_uint32_t *max_used)
- {
- if (total != RT_NULL)
- *total = heap_end - heap_start;
- if (used != RT_NULL)
- *used = used_mem;
- if (max_used != RT_NULL)
- *max_used = max_mem;
- }
- #ifdef RT_USING_FINSH
- #include <finsh.h>
- void list_mem(void)
- {
- rt_kprintf("total memory: %d\n", heap_end - heap_start);
- rt_kprintf("used memory : %d\n", used_mem);
- rt_kprintf("maximum allocated memory: %d\n", max_mem);
- }
- FINSH_FUNCTION_EXPORT(list_mem, list memory usage information)
- #endif
- #endif
- /**@}*/
- #endif
|