Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / mm / kmem.c
1 /* Copyright (c) 2007, Sandia National Laboratories */
2
3 #include <lwk/kernel.h>
4 #include <lwk/buddy.h>
5 #include <lwk/log2.h>
6
7
8 /**
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'.
12  */
13 #define MIN_ORDER       5  /* 32 bytes */
14
15
16 /**
17  * Magic value used for sanity checking. Every block of memory allocated via
18  * kmem_alloc() has this value in its block header.
19  */
20 #define KMEM_MAGIC      0xF0F0F0F0F0F0F0F0UL
21
22
23 /**
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.
29  *
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.
32  */
33 static struct buddy_mempool *kmem = NULL;
34
35
36 /**
37  * Total number of bytes in the kernel memory pool.
38  */
39 static unsigned long kmem_bytes_managed;
40
41
42 /**
43  * Total number of bytes allocated from the kernel memory pool.
44  */
45 static unsigned long kmem_bytes_allocated;
46
47
48 /**
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.
52  *
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.
56  *
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.
59  */
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));
64
65
66 /**
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
70  * benefit.
71  *
72  * Arguments:
73  *       [IN] base_addr: Base address of the memory pool.
74  *       [IN] size:      Size of the memory pool in bytes.
75  *
76  * NOTE: Currently only one zone is supported. Calling kmem_create_zone() more
77  *       than once will result in a panic.
78  */
79 void
80 kmem_create_zone(unsigned long base_addr, size_t size)
81 {
82         unsigned long pool_order = ilog2(roundup_pow_of_two(size));
83         unsigned long min_order  = MIN_ORDER;
84
85         /* For now, protect against calling kmem_create_zone() more than once */
86         BUG_ON(kmem != NULL);
87
88         /* Initialize the underlying buddy allocator */
89         if ((kmem = buddy_init(base_addr, pool_order, min_order)) == NULL)
90                 panic("buddy_init() failed.");
91 }
92
93
94 /**
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().
97  *
98  * Arguments:
99  *       [IN] base_addr: Base address of the memory region to add.
100  *       [IN] size:      Size of the memory region in bytes.
101  */
102 void
103 kmem_add_memory(unsigned long base_addr, size_t size)
104 {
105         /*
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.
109          */
110         buddy_free(kmem, (void *)base_addr, ilog2(size));
111
112         /* Update statistics */
113         kmem_bytes_managed += size;
114 }
115
116
117 /**
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.
120  *
121  * Arguments:
122  *       [IN] size: Amount of memory to allocate in bytes.
123  *
124  * Returns:
125  *       Success: Pointer to the start of the allocated memory.
126  *       Failure: NULL
127  */
128 void *
129 kmem_alloc(size_t size)
130 {
131         unsigned long order;
132         struct kmem_block_hdr *hdr;
133
134         /* Make room for block header */
135         size += sizeof(struct kmem_block_hdr);
136
137         /* Calculate the block order needed */
138         order = ilog2(roundup_pow_of_two(size));
139         if (order < MIN_ORDER)
140                 order = MIN_ORDER;
141
142         /* Allocate memory from the underlying buddy system */
143         if ((hdr = buddy_alloc(kmem, order)) == NULL)
144                 return NULL;
145
146         /* Zero the block */
147         memset(hdr, 0, (1UL << order));
148
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 */
152
153         /* Update statistics */
154         kmem_bytes_allocated += (1UL << order);
155
156         /* Return address of first byte after block header to caller */
157         return hdr + 1;
158 }
159
160
161 /**
162  * Frees memory previously allocated with kmem_alloc().
163  *
164  * Arguments:
165  *       [IN] addr: Address of the memory region to free.
166  *
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
170  *       kmem_alloc().
171  */
172 void
173 kmem_free(void *addr)
174 {
175         struct kmem_block_hdr *hdr;
176
177         BUG_ON((unsigned long)addr < sizeof(struct kmem_block_hdr));
178
179         /* Find the block header */
180         hdr = (struct kmem_block_hdr *)addr - 1;
181         BUG_ON(hdr->magic != KMEM_MAGIC);
182
183         /* Return block to the underlying buddy system */
184         buddy_free(kmem, hdr, hdr->order);
185
186         kmem_bytes_allocated -= (1 << hdr->order);
187 }
188
189
190 /**
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.
194  *
195  * Arguments:
196  *       [IN] order: Number of pages to allocated, 2^order:
197  *                       0 = 1 page
198  *                       1 = 2 pages
199  *                       2 = 4 pages
200  *                       3 = 8 pages
201  *                       ...
202  * Returns:
203  *       Success: Pointer to the start of the allocated memory.
204  *       Failure: NULL
205  */
206 void *
207 kmem_get_pages(unsigned long order)
208 {
209         unsigned long block_order;
210         void *addr;
211
212         /* Calculate the block size needed; convert page order to byte order */
213         block_order = order + ilog2(PAGE_SIZE);
214
215         /* Allocate memory from the underlying buddy system */
216         if ((addr = buddy_alloc(kmem, block_order)) == NULL)
217                 return NULL;
218
219         /* Zero the block and return its address */
220         memset(addr, 0, (1UL << block_order));
221         return addr;
222 }
223
224
225 /**
226  * Frees pages of memory previously allocated with kmem_get_pages().
227  *
228  * Arguments:
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.
233  */
234 void
235 kmem_free_pages(void *addr, unsigned long order)
236 {
237         buddy_free(kmem, addr, order + ilog2(PAGE_SIZE));
238 }
239
240