1 /* Copyright (c) 2007, Sandia National Laboratories */
3 #include <lwk/kernel.h>
9 * This specifies the minimum sized memory block to request from the underlying
10 * buddy system memory allocator, 2^MIN_ORDER bytes. It must be at least big
11 * enough to hold a 'struct kmem_block_hdr'.
13 #define MIN_ORDER 5 /* 32 bytes */
17 * Magic value used for sanity checking. Every block of memory allocated via
18 * kmem_alloc() has this value in its block header.
20 #define KMEM_MAGIC 0xF0F0F0F0F0F0F0F0UL
24 * The kernel memory pool. This manages all memory available for dynamic
25 * allocation by the kernel. The kernel reserves some amount of memory
26 * (e.g., the first 8 MB, amount specifiable on kernel boot command line) for
27 * its own use, included in which is the kernel memory pool. The rest of memory
28 * is reserved for user applications.
30 * NOTE: There is currently only one buddy_mempool for the entire system.
31 * This may change to one per NUMA node in the future.
33 static struct buddy_mempool *kmem = NULL;
37 * Total number of bytes in the kernel memory pool.
39 static unsigned long kmem_bytes_managed;
43 * Total number of bytes allocated from the kernel memory pool.
45 static unsigned long kmem_bytes_allocated;
49 * Each block of memory allocated from the kernel memory pool has one of these
50 * structures at its head. The structure contains information needed to free
51 * the block and return it to the underlying memory allocator.
53 * When a block is allocated, the address returned to the caller is
54 * sizeof(struct kmem_block_hdr) bytes greater than the block allocated from
55 * the underlying memory allocator.
57 * WARNING: This structure is defined to be exactly 16 bytes in size.
58 * Do not change this unless you really know what you are doing.
60 struct kmem_block_hdr {
61 uint64_t order; /* order of the block allocated from buddy system */
62 uint64_t magic; /* magic value used as sanity check */
63 } __attribute__((packed));
67 * This adds a zone to the kernel memory pool. Zones exist to allow there to be
68 * multiple non-adjacent regions of physically contiguous memory. The
69 * bookkeeping needed to cover the gaps would waste a lot of memory and have no
73 * [IN] base_addr: Base address of the memory pool.
74 * [IN] size: Size of the memory pool in bytes.
76 * NOTE: Currently only one zone is supported. Calling kmem_create_zone() more
77 * than once will result in a panic.
80 kmem_create_zone(unsigned long base_addr, size_t size)
82 unsigned long pool_order = ilog2(roundup_pow_of_two(size));
83 unsigned long min_order = MIN_ORDER;
85 /* For now, protect against calling kmem_create_zone() more than once */
88 /* Initialize the underlying buddy allocator */
89 if ((kmem = buddy_init(base_addr, pool_order, min_order)) == NULL)
90 panic("buddy_init() failed.");
95 * This adds memory to the kernel memory pool. The memory region being added
96 * must fall within a zone previously specified via kmem_create_zone().
99 * [IN] base_addr: Base address of the memory region to add.
100 * [IN] size: Size of the memory region in bytes.
103 kmem_add_memory(unsigned long base_addr, size_t size)
106 * kmem buddy allocator is initially empty.
107 * Memory is added to it via buddy_free().
108 * buddy_free() will panic if there are any problems with the args.
110 buddy_free(kmem, (void *)base_addr, ilog2(size));
112 /* Update statistics */
113 kmem_bytes_managed += size;
118 * Allocates memory from the kernel memory pool. This will return a memory
119 * region that is at least 16-byte aligned. The memory returned is zeroed.
122 * [IN] size: Amount of memory to allocate in bytes.
125 * Success: Pointer to the start of the allocated memory.
129 kmem_alloc(size_t size)
132 struct kmem_block_hdr *hdr;
134 /* Make room for block header */
135 size += sizeof(struct kmem_block_hdr);
137 /* Calculate the block order needed */
138 order = ilog2(roundup_pow_of_two(size));
139 if (order < MIN_ORDER)
142 /* Allocate memory from the underlying buddy system */
143 if ((hdr = buddy_alloc(kmem, order)) == NULL)
147 memset(hdr, 0, (1UL << order));
149 /* Initialize the block header */
150 hdr->order = order; /* kmem_free() needs this to free the block */
151 hdr->magic = KMEM_MAGIC; /* used for sanity check */
153 /* Update statistics */
154 kmem_bytes_allocated += (1UL << order);
156 /* Return address of first byte after block header to caller */
162 * Frees memory previously allocated with kmem_alloc().
165 * [IN] addr: Address of the memory region to free.
167 * NOTE: The size of the memory region being freed is assumed to be in a
168 * 'struct kmem_block_hdr' header located immediately before the address
169 * passed in by the caller. This header is created and initialized by
173 kmem_free(void *addr)
175 struct kmem_block_hdr *hdr;
177 BUG_ON((unsigned long)addr < sizeof(struct kmem_block_hdr));
179 /* Find the block header */
180 hdr = (struct kmem_block_hdr *)addr - 1;
181 BUG_ON(hdr->magic != KMEM_MAGIC);
183 /* Return block to the underlying buddy system */
184 buddy_free(kmem, hdr, hdr->order);
186 kmem_bytes_allocated -= (1 << hdr->order);
191 * Allocates pages of memory from the kernel memory pool. The number of pages
192 * requested must be a power of two and the returned pages will be contiguous
193 * in physical memory. The memory returned is zeroed.
196 * [IN] order: Number of pages to allocated, 2^order:
203 * Success: Pointer to the start of the allocated memory.
207 kmem_get_pages(unsigned long order)
209 unsigned long block_order;
212 /* Calculate the block size needed; convert page order to byte order */
213 block_order = order + ilog2(PAGE_SIZE);
215 /* Allocate memory from the underlying buddy system */
216 if ((addr = buddy_alloc(kmem, block_order)) == NULL)
219 /* Zero the block and return its address */
220 memset(addr, 0, (1UL << block_order));
226 * Frees pages of memory previously allocated with kmem_get_pages().
229 * [IN] addr: Address of the memory region to free.
230 * [IN] order: Number of pages to free, 2^order.
231 * The order must match the value passed to kmem_get_pages()
232 * when the pages were allocated.
235 kmem_free_pages(void *addr, unsigned long order)
237 buddy_free(kmem, addr, order + ilog2(PAGE_SIZE));