/* * Copyright (c) 2001, Swedish Institute of Computer Science. * All rights reserved. * * 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 Institute 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 INSTITUTE 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 INSTITUTE 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. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels <adam@sics.se> * * $Id$ */ /*-----------------------------------------------------------------------------------*/ /* mem.c * * Memory manager. * */ /*-----------------------------------------------------------------------------------*/ #include <ubixos/types.h> #include "net/debug.h" #include "net/arch.h" #include "net/opt.h" #include "net/def.h" #include "net/mem.h" #include "net/sys.h" #include "net/stats.h" #if MEM_RECLAIM struct mem_reclaim_ { struct mem_reclaim_ *next; mem_reclaim_func f; void *arg; }; #endif /* MEM_RECLAIM */ struct mem { mem_size_t next, prev; uInt8 used; #if MEM_ALIGNMENT == 2 uInt8 dummy; #endif /* MEM_ALIGNEMNT == 2 */ }; static struct mem *ram_end; static uInt8 ram[MEM_ALIGN_SIZE(MEM_SIZE + sizeof(struct mem))]; #define MIN_SIZE 12 #define SIZEOF_STRUCT_MEM MEM_ALIGN_SIZE(sizeof(struct mem)) /*#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \ (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \ (4 - (sizeof(struct mem) % MEM_ALIGNMENT))))*/ static struct mem *lfree; /* pointer to the lowest free block */ #if MEM_RECLAIM static struct mem_reclaim_ *mrlist; #endif /* MEM_RECLAIM */ static sys_sem_t mem_sem; /*-----------------------------------------------------------------------------------*/ static void plug_holes(struct mem *mem) { struct mem *nmem; struct mem *pmem; ASSERT("plug_holes: mem >= ram", (uInt8 *)mem >= ram); ASSERT("plug_holes: mem < ram_end", (uInt8 *)mem < (uInt8 *)ram_end); ASSERT("plug_holes: mem->used == 0", mem->used == 0); /* plug hole forward */ ASSERT("plug_holes: mem->next <= MEM_SIZE", mem->next <= MEM_SIZE); nmem = (struct mem *)&ram[mem->next]; if(mem != nmem && nmem->used == 0 && (uInt8 *)nmem != (uInt8 *)ram_end) { if(lfree == nmem) { lfree = mem; } mem->next = nmem->next; ((struct mem *)&ram[nmem->next])->prev = (uInt8 *)mem - ram; } /* plug hole backward */ pmem = (struct mem *)&ram[mem->prev]; if(pmem != mem && pmem->used == 0) { if(lfree == mem) { lfree = pmem; } pmem->next = mem->next; ((struct mem *)&ram[mem->next])->prev = (uInt8 *)pmem - ram; } } /*-----------------------------------------------------------------------------------*/ void mem_init(void) { struct mem *mem; bzero(ram, MEM_SIZE); mem = (struct mem *)ram; mem->next = MEM_SIZE; mem->prev = 0; mem->used = 0; ram_end = (struct mem *)&ram[MEM_SIZE]; ram_end->used = 1; ram_end->next = MEM_SIZE; ram_end->prev = MEM_SIZE; mem_sem = sys_sem_new(1); lfree = (struct mem *)ram; #if MEM_RECLAIM mrlist = NULL; #endif /* MEM_RECLAIM */ #ifdef MEM_STATS stats.mem.avail = MEM_SIZE; #endif /* MEM_STATS */ } /*-----------------------------------------------------------------------------------*/ #if MEM_RECLAIM void mem_reclaim(unsigned int size) { struct mem_reclaim_ *mr; int rec; rec = 0; for(mr = mrlist; mr != NULL; mr = mr->next) { DEBUGF(MEM_DEBUG, ("mem_malloc: calling reclaimer\n")); rec += mr->f(mr->arg, size); } #ifdef MEM_STATS stats.mem.reclaimed += rec; #endif /* MEM_STATS */ } #endif /* MEM_RECLAIM */ /*-----------------------------------------------------------------------------------*/ void * mem_malloc2(mem_size_t size) { void *mem; mem = mem_malloc(size); #if MEM_RECLAIM if(mem == NULL) { mem_reclaim(size); mem = mem_malloc(size); } #endif /* MEM_RECLAIM */ return mem; } /*-----------------------------------------------------------------------------------*/ void * mem_malloc(mem_size_t size) { mem_size_t ptr, ptr2; struct mem *mem, *mem2; if(size == 0) { return NULL; } /* Expand the size of the allocated memory region so that we can adjust for alignment. */ if((size % MEM_ALIGNMENT) != 0) { size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT); } if(size > MEM_SIZE) { return NULL; } sys_sem_wait(mem_sem); for(ptr = (uInt8 *)lfree - ram; ptr < MEM_SIZE; ptr = ((struct mem *)&ram[ptr])->next) { mem = (struct mem *)&ram[ptr]; if(!mem->used && mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) { ptr2 = ptr + SIZEOF_STRUCT_MEM + size; mem2 = (struct mem *)&ram[ptr2]; mem2->prev = ptr; mem2->next = mem->next; mem->next = ptr2; if(mem2->next != MEM_SIZE) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } mem2->used = 0; mem->used = 1; #ifdef MEM_STATS stats.mem.used += size; /* if(stats.mem.max < stats.mem.used) { stats.mem.max = stats.mem.used; } */ if(stats.mem.max < ptr2) { stats.mem.max = ptr2; } #ifdef MEM_PERF mem_perf_output(); #endif /* MEM_PERF */ #endif /* MEM_STATS */ if(mem == lfree) { /* Find next free block after mem */ while(lfree->used && lfree != ram_end) { lfree = (struct mem *)&ram[lfree->next]; } ASSERT("mem_malloc: !lfree->used", !lfree->used); } sys_sem_signal(mem_sem); ASSERT("mem_malloc: allocated memory not above ram_end.", (uInt32)mem + SIZEOF_STRUCT_MEM + size <= (uInt32)ram_end); ASSERT("mem_malloc: allocated memory properly aligned.", (unsigned long)((uInt8 *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); return (uInt8 *)mem + SIZEOF_STRUCT_MEM; } } DEBUGF(MEM_DEBUG, ("mem_malloc: could not allocate %d bytes\n", (int)size)); #ifdef MEM_STATS ++stats.mem.err; #endif /* MEM_STATS */ sys_sem_signal(mem_sem); return NULL; } /*-----------------------------------------------------------------------------------*/ void mem_free(void *rmem) { struct mem *mem; if(rmem == NULL) { return; } sys_sem_wait(mem_sem); ASSERT("mem_free: legal memory", (uInt8 *)rmem >= (uInt8 *)ram && (uInt8 *)rmem < (uInt8 *)ram_end); if((uInt8 *)rmem < (uInt8 *)ram || (uInt8 *)rmem >= (uInt8 *)ram_end) { DEBUGF(MEM_DEBUG, ("mem_free: illegal memory\n")); #ifdef MEM_STATS ++stats.mem.err; #endif /* MEM_STATS */ return; } mem = (struct mem *)((uInt8 *)rmem - SIZEOF_STRUCT_MEM); ASSERT("mem_free: mem->used", mem->used); mem->used = 0; if(mem < lfree) { lfree = mem; } #ifdef MEM_STATS stats.mem.used -= mem->next - ((uInt8 *)mem - ram) - SIZEOF_STRUCT_MEM; #ifdef MEM_PERF mem_perf_output(); #endif /* MEM_PERF */ #endif /* MEM_STATS */ plug_holes(mem); sys_sem_signal(mem_sem); } /*-----------------------------------------------------------------------------------*/ void * mem_reallocm(void *rmem, mem_size_t newsize) { void *nmem; nmem = mem_malloc(newsize); if(nmem == NULL) { return mem_realloc(rmem, newsize); } bcopy(rmem, nmem, newsize); mem_free(rmem); return nmem; } /*-----------------------------------------------------------------------------------*/ void * mem_realloc(void *rmem, mem_size_t newsize) { mem_size_t size; mem_size_t ptr, ptr2; struct mem *mem, *mem2; sys_sem_wait(mem_sem); ASSERT("mem_realloc: legal memory", (uInt8 *)rmem >= (uInt8 *)ram && (uInt8 *)rmem < (uInt8 *)ram_end); if((uInt8 *)rmem < (uInt8 *)ram || (uInt8 *)rmem >= (uInt8 *)ram_end) { DEBUGF(MEM_DEBUG, ("mem_free: illegal memory\n")); return rmem; } mem = (struct mem *)((uInt8 *)rmem - SIZEOF_STRUCT_MEM); ptr = (uInt8 *)mem - ram; size = mem->next - ptr - SIZEOF_STRUCT_MEM; #ifdef MEM_STATS stats.mem.used -= (size - newsize); #ifdef MEM_PERF mem_perf_output(); #endif /* MEM_PERF */ #endif /* MEM_STATS */ if(newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) { ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; mem2 = (struct mem *)&ram[ptr2]; mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; mem->next = ptr2; if(mem2->next != MEM_SIZE) { ((struct mem *)&ram[mem2->next])->prev = ptr2; } plug_holes(mem2); } sys_sem_signal(mem_sem); return rmem; } /*-----------------------------------------------------------------------------------*/ #if MEM_RECLAIM void mem_register_reclaim(mem_reclaim_func f, void *arg) { struct mem_reclaim_ *mr; mr = mem_malloc(sizeof(struct mem_reclaim_)); if(mr == NULL) { return; } mr->next = mrlist; mrlist = mr; mr->f = f; mr->arg = arg; } #endif /* MEM_RECLAIM */ /*-----------------------------------------------------------------------------------*/