/*
* 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 */
/*-----------------------------------------------------------------------------------*/