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.


Memory management enhancements: dynamic removal, cleanup at module remove time
[palacios.git] / linux_module / mm.c
1 /* Palacios memory manager 
2  * (c) Jack Lange, 2010
3  */
4
5 #include <asm/page_64_types.h>
6 #include <linux/kernel.h>
7 #include <linux/list.h>
8 #include <linux/slab.h>
9 #include <linux/mm.h>
10 //static struct list_head pools;
11
12 #include "palacios.h"
13 #include "mm.h"
14 #include "buddy.h"
15 #include "numa.h"
16 #include "palacios/vmm.h"
17
18
19 static struct buddy_memzone ** memzones = NULL;
20 static uintptr_t * seed_addrs = NULL;
21
22
23 // alignment is in bytes
24 uintptr_t alloc_palacios_pgs(u64 num_pages, u32 alignment, int node_id, int constraints) {
25     uintptr_t addr = 0; 
26     int any = node_id==-1; // can allocate on any
27     int buddy_constraints=0;
28
29     if (constraints && constraints!=V3_ALLOC_PAGES_CONSTRAINT_4GB) { 
30         ERROR("Unknown constraint mask 0x%x\n",constraints);
31         return 0;
32     }
33     
34     if (constraints & V3_ALLOC_PAGES_CONSTRAINT_4GB) { 
35         buddy_constraints |= LWK_BUDDY_CONSTRAINT_4GB;
36     }
37
38
39     if (node_id == -1) {
40         int cpu_id = get_cpu();
41         put_cpu();
42         
43         node_id = numa_cpu_to_node(cpu_id); // try first preferentially for the calling pcore
44
45     } else if (numa_num_nodes() == 1) {
46         node_id = 0;
47     } else if (node_id >= numa_num_nodes()) {
48         ERROR("Requesting memory from an invalid NUMA node. (Node: %d) (%d nodes on system)\n", 
49               node_id, numa_num_nodes());
50         return 0;
51     }
52
53     addr = buddy_alloc(memzones[node_id], get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT, buddy_constraints);
54
55     if (!addr && any) { 
56         int i;
57         // do a scan to see if we can satisfy request on any node
58         for (i=0; i< numa_num_nodes(); i++) { 
59             if (i!=node_id) { 
60                 addr = buddy_alloc(memzones[i], get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT, buddy_constraints);
61                 if (addr) {
62                     break;
63                 }
64             }
65         }
66     }
67                 
68
69     //DEBUG("Returning from alloc addr=%p, vaddr=%p\n", (void *)addr, __va(addr));
70     return addr;
71 }
72
73
74
75 void free_palacios_pgs(uintptr_t pg_addr, u64 num_pages) {
76     int node_id = numa_addr_to_node(pg_addr);
77
78     //DEBUG("Freeing Memory page %p\n", (void *)pg_addr);
79     buddy_free(memzones[node_id], pg_addr, get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT);
80     
81     return;
82 }
83
84
85 unsigned long long pow2(int i)
86 {
87     unsigned long long x=1;
88     for (;i!=0;i--) { x*=2; } 
89     return x;
90 }
91
92 int add_palacios_memory(struct v3_mem_region *r) {
93     int pool_order = 0;
94     int node_id = 0;
95
96     struct v3_mem_region *keep;
97
98     INFO("Palacios Memory Add Request: type=%d, node=%d, base_addr=0x%llx, num_pages=%llu\n",r->type,r->node,r->base_addr,r->num_pages);
99
100     // fixup request regardless of its type
101     if (r->num_pages*4096 < V3_CONFIG_MEM_BLOCK_SIZE) { 
102         WARNING("Allocating a memory pool smaller than the Palacios block size - may not be useful\n");
103     }
104
105     if (pow2(get_order(r->num_pages*PAGE_SIZE)) != r->num_pages) { 
106         WARNING("Allocating a memory pool that is not a power of two (is %llu) - it will be rounded down!\n", r->num_pages);
107         r->num_pages=pow2(get_order(r->num_pages*PAGE_SIZE));
108         WARNING("Rounded request is for %llu pages\n", r->num_pages);
109     }
110
111
112     if (!(keep=palacios_alloc(sizeof(struct v3_mem_region)))) { 
113         ERROR("Error allocating space for tracking region\n");
114         return -1;
115     }
116
117
118     if (r->type==REQUESTED || r->type==REQUESTED32) { 
119         struct page *pgs;
120
121         INFO("Attempting to allocate %llu pages of %s memory\n", r->num_pages,
122              r->type==REQUESTED ? "64 bit (unrestricted)" : 
123              r->type==REQUESTED32 ? "32 bit (restricted)" : "unknown (assuming 64 bit unrestricted)");
124              
125         pgs = alloc_pages_node(r->node, 
126                                r->type==REQUESTED ? GFP_KERNEL :
127                                r->type==REQUESTED32 ? GFP_DMA32 : GFP_KERNEL, 
128                                get_order(r->num_pages*PAGE_SIZE));
129         if (!pgs) { 
130             ERROR("Unable to satisfy allocation request\n");
131             palacios_free(keep);
132             return -1;
133         }
134         r->base_addr = page_to_pfn(pgs) << PAGE_SHIFT;
135     }
136         
137
138     *keep = *r;
139
140     node_id = numa_addr_to_node(r->base_addr);
141
142     if (node_id == -1) {
143         ERROR("Error locating node for addr %p\n", (void *)(r->base_addr));
144         return -1;
145     }
146
147     if ((node_id != r->node) && (r->node!=-1)) { 
148         INFO("Memory add request is for node %d, but memory is in node %d\n",r->node,node_id);
149     }
150
151     pool_order = get_order(r->num_pages * PAGE_SIZE) + PAGE_SHIFT;
152
153     if (buddy_add_pool(memzones[node_id], r->base_addr, pool_order, keep)) {
154         ERROR("ALERT ALERT ALERT Unable to add pool to buddy allocator...\n");
155         if (r->type==REQUESTED || r->type==REQUESTED32) { 
156             free_pages((uintptr_t)__va(r->base_addr), get_order(r->num_pages*PAGE_SIZE));
157         }
158         palacios_free(keep);
159         return -1;
160     }
161
162     return 0;
163 }
164
165
166
167 int remove_palacios_memory(struct v3_mem_region *req) {
168     int node_id = numa_addr_to_node(req->base_addr);
169     struct v3_mem_region *r;
170
171     if (buddy_remove_pool(memzones[node_id], req->base_addr, 0, (void**)(&r))) { //unforced remove
172         ERROR("Cannot remove memory at base address 0x%p\n", (void*)(req->base_addr));
173         return -1;
174     }
175
176     if (r) {
177         if (r->type==REQUESTED || r->type==REQUESTED32) { 
178             free_pages((uintptr_t)__va(r->base_addr), get_order(r->num_pages*PAGE_SIZE));
179         } else {
180             // user space responsible for onlining
181         }
182         palacios_free(r);
183     }
184
185     return 0;
186 }
187
188
189 static int handle_free(void *meta)
190 {
191     struct v3_mem_region *r = (struct v3_mem_region *)meta;
192
193     if (r) { 
194         if (r->type==REQUESTED || r->type==REQUESTED32) { 
195             //INFO("Freeing %llu pages at %p\n",r->num_pages,(void*)(r->base_addr));
196             free_pages((uintptr_t)__va(r->base_addr), get_order(r->num_pages*PAGE_SIZE));
197         } else {
198             // user space responsible for onlining
199         }
200         palacios_free(r);
201     }
202     
203     return 0;
204 }
205
206         
207
208
209 int palacios_deinit_mm( void ) {
210
211     int i = 0;
212
213     if (memzones) {
214         for (i = 0; i < numa_num_nodes(); i++) {
215             
216             if (memzones[i]) {
217                 INFO("Deiniting memory zone %d\n",i);
218                 buddy_deinit(memzones[i],handle_free);
219             }
220             
221             // note that the memory is not onlined here - offlining and onlining
222             // is the resposibility of the caller
223             
224             if (seed_addrs[i]) {
225                 // free the seed regions
226                 INFO("Freeing seed addrs %d\n",i);
227                 free_pages((uintptr_t)__va(seed_addrs[i]), MAX_ORDER - 1);
228             }
229         }
230         
231         palacios_free(memzones);
232         palacios_free(seed_addrs);
233     }
234
235     return 0;
236 }
237
238 int palacios_init_mm( void ) {
239     int num_nodes = numa_num_nodes();
240     int node_id = 0;
241
242     INFO("memory manager init: MAX_ORDER=%d (%llu bytes)\n",MAX_ORDER, PAGE_SIZE*pow2(MAX_ORDER));
243
244     memzones = palacios_alloc_extended(sizeof(struct buddy_memzone *) * num_nodes, GFP_KERNEL,-1);
245
246     if (!memzones) { 
247         ERROR("Cannot allocate space for memory zones\n");
248         palacios_deinit_mm();
249         return -1;
250     }
251
252     memset(memzones, 0, sizeof(struct buddy_memzone *) * num_nodes);
253
254     seed_addrs = palacios_alloc_extended(sizeof(uintptr_t) * num_nodes, GFP_KERNEL,-1);
255
256     if (!seed_addrs) { 
257         ERROR("Cannot allocate space for seed addrs\n");
258         palacios_deinit_mm();
259         return -1;
260     }
261
262     memset(seed_addrs, 0, sizeof(uintptr_t) * num_nodes);
263
264     for (node_id = 0; node_id < num_nodes; node_id++) {
265         struct buddy_memzone * zone = NULL;
266
267         // Seed the allocator with a small set of pages to allow initialization to complete. 
268         // For now we will just grab some random pages, but in the future we will need to grab NUMA specific regions
269         // See: alloc_pages_node()
270
271         {
272             struct page * pgs;
273
274             // attempt to first allocate below 4 GB for compatibility with
275             // 32 bit shadow paging
276             pgs = alloc_pages_node(node_id, GFP_DMA32, MAX_ORDER - 1);
277
278             if (!pgs) {
279                 INFO("Could not allocate initial memory block for node %d below 4GB\n", node_id);
280                 
281                 pgs = alloc_pages_node(node_id, GFP_KERNEL, MAX_ORDER - 1);
282
283                 if (!pgs) {
284                     INFO("Could not allocate initial memory block for node %d beloew 4GB\n", node_id);
285                     if (!pgs) {
286                         ERROR("Could not allocate initial memory block for node %d without restrictions\n", node_id);
287                         BUG_ON(!pgs);
288                         palacios_deinit_mm();
289                         return -1;
290                     }
291                 }
292             }
293
294             seed_addrs[node_id] = page_to_pfn(pgs) << PAGE_SHIFT;
295         }
296
297         zone = buddy_init(get_order(V3_CONFIG_MEM_BLOCK_SIZE) + PAGE_SHIFT, PAGE_SHIFT, node_id);
298
299         if (zone == NULL) {
300             ERROR("Could not initialization memory management for node %d\n", node_id);
301             palacios_deinit_mm();
302             return -1;
303         }
304
305         printk("Zone initialized, Adding seed region (order=%d)\n", 
306                (MAX_ORDER - 1) + PAGE_SHIFT);
307
308         if (buddy_add_pool(zone, seed_addrs[node_id], (MAX_ORDER - 1) + PAGE_SHIFT,0)) { 
309             ERROR("Could not add pool to buddy allocator\n");
310             palacios_deinit_mm();
311             return -1;
312         }
313
314         memzones[node_id] = zone;
315     }
316
317     return 0;
318
319 }
320