481 lines
13 KiB
C
481 lines
13 KiB
C
/* $NetBSD: aml_memman.c,v 1.3 2009/01/18 09:46:59 lukem Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1999, 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
*
|
|
* Id: aml_memman.c,v 1.10 2000/08/09 14:47:43 iwasaki Exp
|
|
* $FreeBSD: src/usr.sbin/acpi/amldb/aml/aml_memman.c,v 1.2 2000/11/09 06:24:45 iwasaki Exp $
|
|
*/
|
|
#include <sys/cdefs.h>
|
|
__RCSID("$NetBSD: aml_memman.c,v 1.3 2009/01/18 09:46:59 lukem Exp $");
|
|
|
|
/*
|
|
* Generic Memory Management
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <aml/aml_memman.h>
|
|
|
|
#ifndef _KERNEL
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#else /* _KERNEL */
|
|
#include <sys/kernel.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
MALLOC_DEFINE(M_MEMMAN, "memman", "Generic and Simple Memory Management");
|
|
#endif /* !_KERNEL */
|
|
|
|
unsigned int memid_unkown = 255;
|
|
|
|
static int manage_block(struct memman *memman, unsigned int id,
|
|
void *block, unsigned static_mem,
|
|
unsigned entries);
|
|
static int blockman_init(struct memman *memman, unsigned int id);
|
|
static void memman_flexsize_add_histogram(struct memman *memman,
|
|
size_t size,
|
|
int tolerance);
|
|
static int memman_comp_histogram_size(const void *a,
|
|
const void *b);
|
|
static void memman_sort_histogram_by_size(struct memman *memman);
|
|
static unsigned int memman_guess_memid(struct memman *memman, void *chunk);
|
|
static void memman_statistics_fixedsize(struct memman *memman);
|
|
static void memman_statistics_flexsize(struct memman *memman);
|
|
|
|
static int
|
|
manage_block(struct memman *memman, unsigned int id, void *block,
|
|
unsigned static_mem, unsigned entries)
|
|
{
|
|
unsigned int i;
|
|
size_t alloc_size;
|
|
void *tmp, *realblock;
|
|
struct memman_blockman *bmp;
|
|
struct memman_block *memblock;
|
|
struct memman_node *memnodes;
|
|
|
|
bmp = &memman->blockman[id];
|
|
alloc_size = MEMMAN_BLOCKNODE_SIZE(entries);
|
|
|
|
if (static_mem) {
|
|
tmp = (void *)block;
|
|
realblock = (char *)block + alloc_size;
|
|
} else {
|
|
tmp = MEMMAN_SYSMALLOC(alloc_size);
|
|
if (!tmp) {
|
|
return (-1);
|
|
}
|
|
realblock = block;
|
|
|
|
memman->allocated_mem += alloc_size;
|
|
memman->salloc_called++;
|
|
}
|
|
|
|
memblock = (struct memman_block *)tmp;
|
|
memnodes = (struct memman_node *)((char *)tmp + sizeof(struct memman_block));
|
|
|
|
memblock->block = realblock;
|
|
memblock->static_mem = static_mem;
|
|
memblock->allocated = entries;
|
|
memblock->available = entries;
|
|
if (!static_mem) {
|
|
alloc_size += roundup(bmp->size * entries, ROUNDUP_UNIT);
|
|
}
|
|
memblock->allocated_mem = alloc_size;
|
|
LIST_INSERT_HEAD(&bmp->block_list, memblock, links);
|
|
|
|
for (i = 0; i < entries; ++i) {
|
|
memnodes[i].node = ((char *)realblock + (i * (bmp->size)));
|
|
memnodes[i].memblock = memblock;
|
|
LIST_INSERT_HEAD(&bmp->free_node_list, &memnodes[i], links);
|
|
}
|
|
bmp->available = entries;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
blockman_init(struct memman *memman, unsigned int id)
|
|
{
|
|
int status;
|
|
struct memman_blockman *bmp;
|
|
|
|
bmp = &memman->blockman[id];
|
|
bmp->initialized = 1;
|
|
LIST_INIT(&bmp->block_list);
|
|
LIST_INIT(&bmp->free_node_list);
|
|
LIST_INIT(&bmp->occupied_node_list);
|
|
status = manage_block(memman, id, bmp->initial_block,
|
|
1, MEMMAN_INITIAL_SIZE);
|
|
return (status);
|
|
}
|
|
|
|
void *
|
|
memman_alloc(struct memman *memman, unsigned int id)
|
|
{
|
|
size_t alloc_size;
|
|
void *chunk, *block;
|
|
struct memman_blockman *bmp;
|
|
struct memman_node *memnode;
|
|
|
|
if (memman->max_memid <= id) {
|
|
printf("memman_alloc: invalid memory type id\n");
|
|
return (NULL);
|
|
}
|
|
bmp = &memman->blockman[id];
|
|
if (!bmp->initialized) {
|
|
if (blockman_init(memman, id)) {
|
|
goto malloc_fail;
|
|
}
|
|
}
|
|
memman->alloc_called++;
|
|
|
|
if (bmp->available == 0) {
|
|
alloc_size = roundup(bmp->size * MEMMAN_INCR_SIZE,
|
|
ROUNDUP_UNIT);
|
|
block = MEMMAN_SYSMALLOC(alloc_size);
|
|
if (!block) {
|
|
goto malloc_fail;
|
|
}
|
|
memman->required_mem += bmp->size * MEMMAN_INCR_SIZE;
|
|
memman->allocated_mem += alloc_size;
|
|
memman->salloc_called++;
|
|
|
|
if (manage_block(memman, id, block, 0, MEMMAN_INCR_SIZE)) {
|
|
goto malloc_fail;
|
|
}
|
|
}
|
|
memnode = LIST_FIRST(&bmp->free_node_list);
|
|
LIST_REMOVE(memnode, links);
|
|
chunk = memnode->node;
|
|
LIST_INSERT_HEAD(&bmp->occupied_node_list, memnode, links);
|
|
memnode->memblock->available--;
|
|
bmp->available--;
|
|
|
|
return (chunk);
|
|
|
|
malloc_fail:
|
|
printf("memman_alloc: could not allocate memory\n");
|
|
return (NULL);
|
|
}
|
|
|
|
static void
|
|
memman_flexsize_add_histogram(struct memman *memman, size_t size,
|
|
int tolerance)
|
|
{
|
|
unsigned int i;
|
|
int gap;
|
|
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
for (i = 0; i < memman->flex_mem_histogram_ptr; i++) {
|
|
gap = memman->flex_mem_histogram[i].mem_size - size;
|
|
if (gap >= (tolerance * -1) && gap <= tolerance) {
|
|
memman->flex_mem_histogram[i].count++;
|
|
if (memman->flex_mem_histogram[i].mem_size < size) {
|
|
memman->flex_mem_histogram[i].mem_size = size;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (memman->flex_mem_histogram_ptr == MEMMAN_HISTOGRAM_SIZE) {
|
|
memman_flexsize_add_histogram(memman, size, tolerance + 1);
|
|
return;
|
|
}
|
|
i = memman->flex_mem_histogram_ptr;
|
|
memman->flex_mem_histogram[i].mem_size = size;
|
|
memman->flex_mem_histogram[i].count = 1;
|
|
memman->flex_mem_histogram_ptr++;
|
|
}
|
|
|
|
static int
|
|
memman_comp_histogram_size(const void *a, const void *b)
|
|
{
|
|
int delta;
|
|
|
|
delta = ((const struct memman_histogram *)a)->mem_size -
|
|
((const struct memman_histogram *)b)->mem_size;
|
|
return (delta);
|
|
}
|
|
|
|
static void
|
|
memman_sort_histogram_by_size(struct memman *memman)
|
|
{
|
|
qsort(memman->flex_mem_histogram, memman->flex_mem_histogram_ptr,
|
|
sizeof(struct memman_histogram), memman_comp_histogram_size);
|
|
}
|
|
|
|
void *
|
|
memman_alloc_flexsize(struct memman *memman, size_t size)
|
|
{
|
|
void *mem;
|
|
struct memman_flexmem_info *info;
|
|
|
|
if (size == 0) {
|
|
return (NULL);
|
|
}
|
|
if ((mem = MEMMAN_SYSMALLOC(size)) != NULL) { /* XXX */
|
|
|
|
info = MEMMAN_SYSMALLOC(sizeof(struct memman_flexmem_info));
|
|
if (info) {
|
|
if (!memman->flex_mem_initialized) {
|
|
LIST_INIT(&memman->flexmem_info_list);
|
|
bzero(memman->flex_mem_histogram,
|
|
sizeof(struct memman_histogram));
|
|
memman->flex_mem_initialized = 1;
|
|
}
|
|
info->addr = mem;
|
|
info->mem_size = size;
|
|
LIST_INSERT_HEAD(&memman->flexmem_info_list, info, links);
|
|
}
|
|
memman->flex_alloc_called++;
|
|
memman->flex_salloc_called++;
|
|
memman->flex_required_mem += size;
|
|
memman->flex_allocated_mem += size;
|
|
if (memman->flex_mem_size_min == 0 ||
|
|
memman->flex_mem_size_min > size) {
|
|
memman->flex_mem_size_min = size;
|
|
}
|
|
if (memman->flex_mem_size_max < size) {
|
|
memman->flex_mem_size_max = size;
|
|
}
|
|
if (memman->flex_peak_mem_usage <
|
|
(memman->flex_allocated_mem - memman->flex_reclaimed_mem)) {
|
|
memman->flex_peak_mem_usage =
|
|
(memman->flex_allocated_mem - memman->flex_reclaimed_mem);
|
|
}
|
|
memman_flexsize_add_histogram(memman, size,
|
|
memman->flex_mem_histogram_initial_tolerance);
|
|
}
|
|
return (mem);
|
|
}
|
|
|
|
static unsigned int
|
|
memman_guess_memid(struct memman *memman, void *chunk)
|
|
{
|
|
unsigned int id;
|
|
struct memman_blockman *bmp;
|
|
struct memman_node *memnode;
|
|
|
|
for (id = 0; id < memman->max_memid; id++) {
|
|
bmp = &memman->blockman[id];
|
|
if (!bmp->initialized) {
|
|
if (blockman_init(memman, id)) {
|
|
printf("memman_free: could not initialized\n");
|
|
}
|
|
}
|
|
LIST_FOREACH(memnode, &bmp->occupied_node_list, links) {
|
|
if (memnode->node == chunk) {
|
|
return (id); /* got it! */
|
|
}
|
|
}
|
|
}
|
|
return (memid_unkown); /* gave up */
|
|
}
|
|
|
|
void
|
|
memman_free(struct memman *memman, unsigned int memid, void *chunk)
|
|
{
|
|
unsigned int id;
|
|
unsigned found;
|
|
void *block;
|
|
struct memman_blockman *bmp;
|
|
struct memman_block *memblock;
|
|
struct memman_node *memnode;
|
|
|
|
id = memid;
|
|
if (memid == memid_unkown) {
|
|
id = memman_guess_memid(memman, chunk);
|
|
}
|
|
if (memman->max_memid <= id) {
|
|
printf("memman_free: invalid memory type id\n");
|
|
MEMMAN_SYSABORT();
|
|
return;
|
|
}
|
|
bmp = &memman->blockman[id];
|
|
if (!bmp->initialized) {
|
|
if (blockman_init(memman, id)) {
|
|
printf("memman_free: could not initialized\n");
|
|
}
|
|
}
|
|
found = 0;
|
|
LIST_FOREACH(memnode, &bmp->occupied_node_list, links) {
|
|
if (memnode->node == chunk) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
printf("memman_free: invalid address\n");
|
|
return;
|
|
}
|
|
memman->free_called++;
|
|
|
|
LIST_REMOVE(memnode, links);
|
|
memblock = memnode->memblock;
|
|
memblock->available++;
|
|
LIST_INSERT_HEAD(&bmp->free_node_list, memnode, links);
|
|
bmp->available++;
|
|
|
|
if (!memblock->static_mem &&
|
|
memblock->available == memblock->allocated) {
|
|
LIST_FOREACH(memnode, &bmp->free_node_list, links) {
|
|
if (memnode->memblock != memblock) {
|
|
continue;
|
|
}
|
|
LIST_REMOVE(memnode, links);
|
|
bmp->available--;
|
|
}
|
|
block = memblock->block;
|
|
MEMMAN_SYSFREE(block);
|
|
memman->sfree_called++;
|
|
|
|
LIST_REMOVE(memblock, links);
|
|
memman->sfree_called++;
|
|
memman->reclaimed_mem += memblock->allocated_mem;
|
|
MEMMAN_SYSFREE(memblock);
|
|
}
|
|
}
|
|
|
|
void
|
|
memman_free_flexsize(struct memman *memman, void *chunk)
|
|
{
|
|
struct memman_flexmem_info *info;
|
|
|
|
LIST_FOREACH(info, &memman->flexmem_info_list, links) {
|
|
if (info->addr == chunk) {
|
|
memman->flex_reclaimed_mem += info->mem_size;
|
|
LIST_REMOVE(info, links);
|
|
MEMMAN_SYSFREE(info);
|
|
break;
|
|
}
|
|
}
|
|
/* XXX */
|
|
memman->flex_free_called++;
|
|
memman->flex_sfree_called++;
|
|
MEMMAN_SYSFREE(chunk);
|
|
}
|
|
|
|
void
|
|
memman_freeall(struct memman *memman)
|
|
{
|
|
unsigned int id;
|
|
void *chunk;
|
|
struct memman_blockman *bmp;
|
|
struct memman_node *memnode;
|
|
struct memman_block *memblock;
|
|
struct memman_flexmem_info *info;
|
|
|
|
for (id = 0; id < memman->max_memid; id++) {
|
|
bmp = &memman->blockman[id];
|
|
|
|
while ((memnode = LIST_FIRST(&bmp->occupied_node_list))) {
|
|
chunk = memnode->node;
|
|
printf("memman_freeall: fixed size (id = %u)\n", id);
|
|
memman_free(memman, id, chunk);
|
|
}
|
|
while ((memblock = LIST_FIRST(&bmp->block_list))) {
|
|
LIST_REMOVE(memblock, links);
|
|
if (!memblock->static_mem) {
|
|
memman->sfree_called++;
|
|
memman->reclaimed_mem += memblock->allocated_mem;
|
|
MEMMAN_SYSFREE(memblock);
|
|
}
|
|
}
|
|
bmp->initialized = 0;
|
|
}
|
|
|
|
LIST_FOREACH(info, &memman->flexmem_info_list, links) {
|
|
printf("memman_freeall: flex size (size = %zd, addr = %p)\n",
|
|
info->mem_size, info->addr);
|
|
memman_free_flexsize(memman, info->addr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
memman_statistics_fixedsize(struct memman *memman)
|
|
{
|
|
printf(" fixed size memory blocks\n");
|
|
printf(" alloc(): %d times\n", memman->alloc_called);
|
|
printf(" system malloc(): %d times\n", memman->salloc_called);
|
|
printf(" free(): %d times\n", memman->free_called);
|
|
printf(" system free(): %d times\n", memman->sfree_called);
|
|
printf(" required memory: %zd bytes\n", memman->required_mem);
|
|
printf(" allocated memory: %zd bytes\n", memman->allocated_mem);
|
|
printf(" reclaimed memory: %zd bytes\n", memman->reclaimed_mem);
|
|
}
|
|
|
|
static void
|
|
memman_statistics_flexsize(struct memman *memman)
|
|
{
|
|
unsigned int i;
|
|
|
|
printf(" flexible size memory blocks\n");
|
|
printf(" alloc(): %d times\n", memman->flex_alloc_called);
|
|
printf(" system malloc(): %d times\n", memman->flex_salloc_called);
|
|
printf(" free(): %d times\n", memman->flex_free_called);
|
|
printf(" system free(): %d times\n", memman->flex_sfree_called);
|
|
printf(" required memory: %zd bytes\n", memman->flex_required_mem);
|
|
printf(" allocated memory: %zd bytes\n", memman->flex_allocated_mem);
|
|
printf(" reclaimed memory: %zd bytes\n", memman->flex_reclaimed_mem);
|
|
printf(" peak memory usage: %zd bytes\n", memman->flex_peak_mem_usage);
|
|
printf(" min memory size: %zd bytes\n", memman->flex_mem_size_min);
|
|
printf(" max memory size: %zd bytes\n", memman->flex_mem_size_max);
|
|
printf(" avg memory size: %zd bytes\n",
|
|
(memman->flex_alloc_called) ?
|
|
memman->flex_allocated_mem / memman->flex_alloc_called : 0);
|
|
|
|
printf(" memory size histogram (%d entries):\n",
|
|
memman->flex_mem_histogram_ptr);
|
|
printf(" size count\n");
|
|
memman_sort_histogram_by_size(memman);
|
|
for (i = 0; i < memman->flex_mem_histogram_ptr; i++) {
|
|
printf(" %zu %d\n",
|
|
memman->flex_mem_histogram[i].mem_size,
|
|
memman->flex_mem_histogram[i].count);
|
|
}
|
|
}
|
|
|
|
void
|
|
memman_statistics(struct memman *memman)
|
|
{
|
|
printf("memman: reporting statistics\n");
|
|
memman_statistics_fixedsize(memman);
|
|
memman_statistics_flexsize(memman);
|
|
}
|
|
|
|
size_t
|
|
memman_memid2size(struct memman *memman, unsigned int id)
|
|
{
|
|
if (memman->max_memid <= id) {
|
|
printf("memman_alloc: invalid memory type id\n");
|
|
return (0);
|
|
}
|
|
return (memman->blockman[id].size);
|
|
}
|