[malloc] Add cache discard mechanism

Add a facility allowing cached data to be discarded in order to
satisfy memory allocations that would otherwise fail.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2010-07-21 11:58:50 +01:00
parent 68613047f0
commit 9dc51afa2c
2 changed files with 85 additions and 46 deletions

View File

@@ -85,6 +85,21 @@ size_t freemem;
/** The heap itself */ /** The heap itself */
static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) )));
/**
* Discard some cached data
*
* @ret discarded Number of cached items discarded
*/
static unsigned int discard_cache ( void ) {
struct cache_discarder *discarder;
unsigned int discarded = 0;
for_each_table_entry ( discarder, CACHE_DISCARDERS ) {
discarded += discarder->discard();
}
return discarded;
}
/** /**
* Allocate a memory block * Allocate a memory block
* *
@@ -112,55 +127,62 @@ void * alloc_memblock ( size_t size, size_t align ) {
align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ); align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 );
DBG ( "Allocating %#zx (aligned %#zx)\n", size, align ); DBG ( "Allocating %#zx (aligned %#zx)\n", size, align );
while ( 1 ) {
/* Search through blocks for the first one with enough space */ /* Search through blocks for the first one with enough space */
list_for_each_entry ( block, &free_blocks, list ) { list_for_each_entry ( block, &free_blocks, list ) {
pre_size = ( - virt_to_phys ( block ) ) & align_mask; pre_size = ( - virt_to_phys ( block ) ) & align_mask;
post_size = block->size - pre_size - size; post_size = block->size - pre_size - size;
if ( post_size >= 0 ) { if ( post_size >= 0 ) {
/* Split block into pre-block, block, and /* Split block into pre-block, block, and
* post-block. After this split, the "pre" * post-block. After this split, the "pre"
* block is the one currently linked into the * block is the one currently linked into the
* free list. * free list.
*/ */
pre = block; pre = block;
block = ( ( ( void * ) pre ) + pre_size ); block = ( ( ( void * ) pre ) + pre_size );
post = ( ( ( void * ) block ) + size ); post = ( ( ( void * ) block ) + size );
DBG ( "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, DBG ( "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre,
( ( ( void * ) pre ) + pre->size ), pre, block, ( ( ( void * ) pre ) + pre->size ),
post, ( ( ( void * ) pre ) + pre->size ) ); pre, block, post,
/* If there is a "post" block, add it in to ( ( ( void * ) pre ) + pre->size ) );
* the free list. Leak it if it is too small /* If there is a "post" block, add it in to
* (which can happen only at the very end of * the free list. Leak it if it is too small
* the heap). * (which can happen only at the very end of
*/ * the heap).
if ( ( size_t ) post_size >= MIN_MEMBLOCK_SIZE ) { */
post->size = post_size; if ( (size_t) post_size >= MIN_MEMBLOCK_SIZE ) {
list_add ( &post->list, &pre->list ); post->size = post_size;
list_add ( &post->list, &pre->list );
}
/* Shrink "pre" block, leaving the main block
* isolated and no longer part of the free
* list.
*/
pre->size = pre_size;
/* If there is no "pre" block, remove it from
* the list. Also remove it (i.e. leak it) if
* it is too small, which can happen only at
* the very start of the heap.
*/
if ( pre_size < MIN_MEMBLOCK_SIZE )
list_del ( &pre->list );
/* Update total free memory */
freemem -= size;
/* Return allocated block */
DBG ( "Allocated [%p,%p)\n", block,
( ( ( void * ) block ) + size ) );
return block;
} }
/* Shrink "pre" block, leaving the main block }
* isolated and no longer part of the free
* list. /* Try discarding some cached data to free up memory */
*/ if ( ! discard_cache() ) {
pre->size = pre_size; /* Nothing available to discard */
/* If there is no "pre" block, remove it from DBG ( "Failed to allocate %#zx (aligned %#zx)\n",
* the list. Also remove it (i.e. leak it) if size, align );
* it is too small, which can happen only at return NULL;
* the very start of the heap.
*/
if ( pre_size < MIN_MEMBLOCK_SIZE )
list_del ( &pre->list );
/* Update total free memory */
freemem -= size;
/* Return allocated block */
DBG ( "Allocated [%p,%p)\n", block,
( ( ( void * ) block ) + size ) );
return block;
} }
} }
DBG ( "Failed to allocate %#zx (aligned %#zx)\n", size, align );
return NULL;
} }
/** /**

View File

@@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
* *
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <ipxe/tables.h>
extern size_t freemem; extern size_t freemem;
@@ -56,4 +57,20 @@ static inline void free_dma ( void *ptr, size_t size ) {
free_memblock ( ptr, size ); free_memblock ( ptr, size );
} }
/** A cache discarder */
struct cache_discarder {
/**
* Discard some cached data
*
* @ret discarded Number of cached items discarded
*/
unsigned int ( * discard ) ( void );
};
/** Cache discarder table */
#define CACHE_DISCARDERS __table ( struct cache_discarder, "cache_discarders" )
/** Declare a cache discarder */
#define __cache_discarder __table_entry ( CACHE_DISCARDERS, 01 )
#endif /* _IPXE_MALLOC_H */ #endif /* _IPXE_MALLOC_H */