X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=kitten%2Fmm%2Fkmem.c;fp=kitten%2Fmm%2Fkmem.c;h=693054a43ed4cd422f3c70d340679cbae8a1e0d5;hb=66a1a4c7a9edcd7d8bc207aca093d694a6e6b5b2;hp=0000000000000000000000000000000000000000;hpb=f7cf9c19ecb0a589dd45ae0d2c91814bd3c2acc2;p=palacios-OLD.git diff --git a/kitten/mm/kmem.c b/kitten/mm/kmem.c new file mode 100644 index 0000000..693054a --- /dev/null +++ b/kitten/mm/kmem.c @@ -0,0 +1,240 @@ +/* Copyright (c) 2007, Sandia National Laboratories */ + +#include +#include +#include + + +/** + * This specifies the minimum sized memory block to request from the underlying + * buddy system memory allocator, 2^MIN_ORDER bytes. It must be at least big + * enough to hold a 'struct kmem_block_hdr'. + */ +#define MIN_ORDER 5 /* 32 bytes */ + + +/** + * Magic value used for sanity checking. Every block of memory allocated via + * kmem_alloc() has this value in its block header. + */ +#define KMEM_MAGIC 0xF0F0F0F0F0F0F0F0UL + + +/** + * The kernel memory pool. This manages all memory available for dynamic + * allocation by the kernel. The kernel reserves some amount of memory + * (e.g., the first 8 MB, amount specifiable on kernel boot command line) for + * its own use, included in which is the kernel memory pool. The rest of memory + * is reserved for user applications. + * + * NOTE: There is currently only one buddy_mempool for the entire system. + * This may change to one per NUMA node in the future. + */ +static struct buddy_mempool *kmem = NULL; + + +/** + * Total number of bytes in the kernel memory pool. + */ +static unsigned long kmem_bytes_managed; + + +/** + * Total number of bytes allocated from the kernel memory pool. + */ +static unsigned long kmem_bytes_allocated; + + +/** + * Each block of memory allocated from the kernel memory pool has one of these + * structures at its head. The structure contains information needed to free + * the block and return it to the underlying memory allocator. + * + * When a block is allocated, the address returned to the caller is + * sizeof(struct kmem_block_hdr) bytes greater than the block allocated from + * the underlying memory allocator. + * + * WARNING: This structure is defined to be exactly 16 bytes in size. + * Do not change this unless you really know what you are doing. + */ +struct kmem_block_hdr { + uint64_t order; /* order of the block allocated from buddy system */ + uint64_t magic; /* magic value used as sanity check */ +} __attribute__((packed)); + + +/** + * This adds a zone to the kernel memory pool. Zones exist to allow there to be + * multiple non-adjacent regions of physically contiguous memory. The + * bookkeeping needed to cover the gaps would waste a lot of memory and have no + * benefit. + * + * Arguments: + * [IN] base_addr: Base address of the memory pool. + * [IN] size: Size of the memory pool in bytes. + * + * NOTE: Currently only one zone is supported. Calling kmem_create_zone() more + * than once will result in a panic. + */ +void +kmem_create_zone(unsigned long base_addr, size_t size) +{ + unsigned long pool_order = ilog2(roundup_pow_of_two(size)); + unsigned long min_order = MIN_ORDER; + + /* For now, protect against calling kmem_create_zone() more than once */ + BUG_ON(kmem != NULL); + + /* Initialize the underlying buddy allocator */ + if ((kmem = buddy_init(base_addr, pool_order, min_order)) == NULL) + panic("buddy_init() failed."); +} + + +/** + * This adds memory to the kernel memory pool. The memory region being added + * must fall within a zone previously specified via kmem_create_zone(). + * + * Arguments: + * [IN] base_addr: Base address of the memory region to add. + * [IN] size: Size of the memory region in bytes. + */ +void +kmem_add_memory(unsigned long base_addr, size_t size) +{ + /* + * kmem buddy allocator is initially empty. + * Memory is added to it via buddy_free(). + * buddy_free() will panic if there are any problems with the args. + */ + buddy_free(kmem, (void *)base_addr, ilog2(size)); + + /* Update statistics */ + kmem_bytes_managed += size; +} + + +/** + * Allocates memory from the kernel memory pool. This will return a memory + * region that is at least 16-byte aligned. The memory returned is zeroed. + * + * Arguments: + * [IN] size: Amount of memory to allocate in bytes. + * + * Returns: + * Success: Pointer to the start of the allocated memory. + * Failure: NULL + */ +void * +kmem_alloc(size_t size) +{ + unsigned long order; + struct kmem_block_hdr *hdr; + + /* Make room for block header */ + size += sizeof(struct kmem_block_hdr); + + /* Calculate the block order needed */ + order = ilog2(roundup_pow_of_two(size)); + if (order < MIN_ORDER) + order = MIN_ORDER; + + /* Allocate memory from the underlying buddy system */ + if ((hdr = buddy_alloc(kmem, order)) == NULL) + return NULL; + + /* Zero the block */ + memset(hdr, 0, (1UL << order)); + + /* Initialize the block header */ + hdr->order = order; /* kmem_free() needs this to free the block */ + hdr->magic = KMEM_MAGIC; /* used for sanity check */ + + /* Update statistics */ + kmem_bytes_allocated += (1UL << order); + + /* Return address of first byte after block header to caller */ + return hdr + 1; +} + + +/** + * Frees memory previously allocated with kmem_alloc(). + * + * Arguments: + * [IN] addr: Address of the memory region to free. + * + * NOTE: The size of the memory region being freed is assumed to be in a + * 'struct kmem_block_hdr' header located immediately before the address + * passed in by the caller. This header is created and initialized by + * kmem_alloc(). + */ +void +kmem_free(void *addr) +{ + struct kmem_block_hdr *hdr; + + BUG_ON((unsigned long)addr < sizeof(struct kmem_block_hdr)); + + /* Find the block header */ + hdr = (struct kmem_block_hdr *)addr - 1; + BUG_ON(hdr->magic != KMEM_MAGIC); + + /* Return block to the underlying buddy system */ + buddy_free(kmem, hdr, hdr->order); + + kmem_bytes_allocated -= (1 << hdr->order); +} + + +/** + * Allocates pages of memory from the kernel memory pool. The number of pages + * requested must be a power of two and the returned pages will be contiguous + * in physical memory. The memory returned is zeroed. + * + * Arguments: + * [IN] order: Number of pages to allocated, 2^order: + * 0 = 1 page + * 1 = 2 pages + * 2 = 4 pages + * 3 = 8 pages + * ... + * Returns: + * Success: Pointer to the start of the allocated memory. + * Failure: NULL + */ +void * +kmem_get_pages(unsigned long order) +{ + unsigned long block_order; + void *addr; + + /* Calculate the block size needed; convert page order to byte order */ + block_order = order + ilog2(PAGE_SIZE); + + /* Allocate memory from the underlying buddy system */ + if ((addr = buddy_alloc(kmem, block_order)) == NULL) + return NULL; + + /* Zero the block and return its address */ + memset(addr, 0, (1UL << block_order)); + return addr; +} + + +/** + * Frees pages of memory previously allocated with kmem_get_pages(). + * + * Arguments: + * [IN] addr: Address of the memory region to free. + * [IN] order: Number of pages to free, 2^order. + * The order must match the value passed to kmem_get_pages() + * when the pages were allocated. + */ +void +kmem_free_pages(void *addr, unsigned long order) +{ + buddy_free(kmem, addr, order + ilog2(PAGE_SIZE)); +} + +