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.


Generalization of constraints on page allocation and implementation/use
[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 (*filter_func)(void *paddr, void *filter_state), void *filter_state) {
25     uintptr_t addr = 0; 
26     int any = node_id==-1; // can allocate on any
27
28     if (node_id == -1) {
29         int cpu_id = get_cpu();
30         put_cpu();
31         
32         node_id = numa_cpu_to_node(cpu_id); // try first preferentially for the calling pcore
33
34     } else if (numa_num_nodes() == 1) {
35         node_id = 0;
36     } else if (node_id >= numa_num_nodes()) {
37         ERROR("Requesting memory from an invalid NUMA node. (Node: %d) (%d nodes on system)\n", 
38               node_id, numa_num_nodes());
39         return 0;
40     }
41
42     addr = buddy_alloc(memzones[node_id], get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT, filter_func, filter_state);
43
44     if (!addr && any) { 
45         int i;
46         // do a scan to see if we can satisfy request on any node
47         for (i=0; i< numa_num_nodes(); i++) { 
48             if (i!=node_id) { 
49                 addr = buddy_alloc(memzones[i], get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT, filter_func, filter_state);
50                 if (addr) {
51                     break;
52                 }
53             }
54         }
55     }
56                 
57
58     //DEBUG("Returning from alloc addr=%p, vaddr=%p\n", (void *)addr, __va(addr));
59     return addr;
60 }
61
62
63
64 void free_palacios_pgs(uintptr_t pg_addr, u64 num_pages) {
65     int node_id = numa_addr_to_node(pg_addr);
66
67     //DEBUG("Freeing Memory page %p\n", (void *)pg_addr);
68     if (buddy_free(memzones[node_id], pg_addr, get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT)) {
69       // it is possible that the allocation was actually on a different zone,
70       // so, just to be sure, we'll try to dellocate on each
71       for (node_id=0;node_id<numa_num_nodes();node_id++) { 
72         if (!buddy_free(memzones[node_id], pg_addr, get_order(num_pages * PAGE_SIZE) + PAGE_SHIFT)) {
73           // successfully freed on different zone, which is also OK
74           break;
75         }
76       }
77       if (node_id==numa_num_nodes()) { 
78         ERROR("Unable to free pages -addr=%p, numpages=%llu on any node\n",(void*)pg_addr,num_pages);
79       }
80     }
81     
82     return;
83 }
84
85
86 unsigned long long pow2(int i)
87 {
88     unsigned long long x=1;
89     for (;i!=0;i--) { x*=2; } 
90     return x;
91 }
92
93 static unsigned long long get_palacios_mem_block_size(void)
94 {
95     char *s = v3_lookup_option("mem_block_size");
96
97     if (!s) { 
98         return V3_CONFIG_MEM_BLOCK_SIZE;
99     } else {
100         unsigned long long temp;
101
102         if (strict_strtoull(s,0,&temp)) { 
103             return V3_CONFIG_MEM_BLOCK_SIZE; // odd...
104         } else {
105             return temp;
106         }
107     }
108 }
109
110 int add_palacios_memory(struct v3_mem_region *r) {
111     int pool_order = 0;
112     int node_id = 0;
113
114
115     struct v3_mem_region *keep;
116
117     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);
118
119     // fixup request regardless of its type
120     if (r->num_pages*4096 < get_palacios_mem_block_size()) { 
121         WARNING("Allocating a memory pool smaller than the Palacios block size - may not be useful\n");
122     }
123
124     if (pow2(get_order(r->num_pages*PAGE_SIZE)) != r->num_pages) { 
125         WARNING("Allocating a memory pool that is not a power of two (is %llu) - it will be rounded down!\n", r->num_pages);
126         r->num_pages=pow2(get_order(r->num_pages*PAGE_SIZE));
127         WARNING("Rounded request is for %llu pages\n", r->num_pages);
128     }
129
130
131     if (!(keep=palacios_alloc(sizeof(struct v3_mem_region)))) { 
132         ERROR("Error allocating space for tracking region\n");
133         return -1;
134     }
135
136
137     if (r->type==REQUESTED || r->type==REQUESTED32) { 
138         struct page *pgs;
139
140         INFO("Attempting to allocate %llu pages of %s memory\n", r->num_pages,
141              r->type==REQUESTED ? "64 bit (unrestricted)" : 
142              r->type==REQUESTED32 ? "32 bit (restricted)" : "unknown (assuming 64 bit unrestricted)");
143              
144         pgs = alloc_pages_node(r->node, 
145                                r->type==REQUESTED ? GFP_KERNEL :
146                                r->type==REQUESTED32 ? GFP_DMA32 : GFP_KERNEL, 
147                                get_order(r->num_pages*PAGE_SIZE));
148         if (!pgs) { 
149             ERROR("Unable to satisfy allocation request\n");
150             palacios_free(keep);
151             return -1;
152         } 
153         r->base_addr = page_to_pfn(pgs) << PAGE_SHIFT;
154     }
155         
156
157     *keep = *r;
158
159     node_id = numa_addr_to_node(r->base_addr);
160
161     if (node_id == -1) {
162         ERROR("Error locating node for addr %p\n", (void *)(r->base_addr));
163         return -1;
164     }
165
166     if ((node_id != r->node) && (r->node!=-1)) { 
167         INFO("Memory add request is for node %d, but memory is in node %d\n",r->node,node_id);
168     }
169
170     pool_order = get_order(r->num_pages * PAGE_SIZE) + PAGE_SHIFT;
171
172     if (buddy_add_pool(memzones[node_id], r->base_addr, pool_order, keep)) {
173         ERROR("ALERT ALERT ALERT Unable to add pool to buddy allocator...\n");
174         if (r->type==REQUESTED || r->type==REQUESTED32) { 
175             free_pages((uintptr_t)__va(r->base_addr), get_order(r->num_pages*PAGE_SIZE));
176         }
177         palacios_free(keep);
178         return -1;
179     }
180
181     return 0;
182 }
183
184
185
186 int remove_palacios_memory(struct v3_mem_region *req) {
187     int node_id = numa_addr_to_node(req->base_addr);
188     struct v3_mem_region *r;
189
190     if (buddy_remove_pool(memzones[node_id], req->base_addr, 0, (void**)(&r))) { //unforced remove
191         ERROR("Cannot remove memory at base address 0x%p\n", (void*)(req->base_addr));
192         return -1;
193     }
194
195     if (r) {
196         if (r->type==REQUESTED || r->type==REQUESTED32) { 
197             free_pages((uintptr_t)__va(r->base_addr), get_order(r->num_pages*PAGE_SIZE));
198         } else {
199             // user space responsible for onlining
200         }
201         palacios_free(r);
202     }
203
204     return 0;
205 }
206
207
208 static int handle_free(void *meta)
209 {
210     struct v3_mem_region *r = (struct v3_mem_region *)meta;
211
212     if (r) { 
213         if (r->type==REQUESTED || r->type==REQUESTED32) { 
214             //INFO("Freeing %llu pages at %p\n",r->num_pages,(void*)(r->base_addr));
215             free_pages((uintptr_t)__va(r->base_addr), get_order(r->num_pages*PAGE_SIZE));
216         } else {
217             // user space responsible for onlining
218         }
219         palacios_free(r);
220     }
221     
222     return 0;
223 }
224
225         
226
227
228 int palacios_deinit_mm( void ) {
229
230     int i = 0;
231
232     if (memzones) {
233         for (i = 0; i < numa_num_nodes(); i++) {
234             
235             if (memzones[i]) {
236                 INFO("Deiniting memory zone %d\n",i);
237                 buddy_deinit(memzones[i],handle_free);
238             }
239             
240             // note that the memory is not onlined here - offlining and onlining
241             // is the resposibility of the caller
242             
243             if (seed_addrs[i]) {
244                 // free the seed regions
245                 INFO("Freeing seed addrs %d\n",i);
246                 free_pages((uintptr_t)__va(seed_addrs[i]), MAX_ORDER - 1);
247             }
248         }
249         
250         palacios_free(memzones);
251         palacios_free(seed_addrs);
252     }
253
254     return 0;
255 }
256
257 int palacios_init_mm( void ) {
258     int num_nodes = numa_num_nodes();
259     int node_id = 0;
260
261     INFO("memory manager init: MAX_ORDER=%d (%llu bytes)\n",MAX_ORDER, PAGE_SIZE*pow2(MAX_ORDER));
262
263     memzones = palacios_alloc_extended(sizeof(struct buddy_memzone *) * num_nodes, GFP_KERNEL,-1);
264
265     if (!memzones) { 
266         ERROR("Cannot allocate space for memory zones\n");
267         palacios_deinit_mm();
268         return -1;
269     }
270
271     memset(memzones, 0, sizeof(struct buddy_memzone *) * num_nodes);
272
273     seed_addrs = palacios_alloc_extended(sizeof(uintptr_t) * num_nodes, GFP_KERNEL,-1);
274
275     if (!seed_addrs) { 
276         ERROR("Cannot allocate space for seed addrs\n");
277         palacios_deinit_mm();
278         return -1;
279     }
280
281     memset(seed_addrs, 0, sizeof(uintptr_t) * num_nodes);
282
283     for (node_id = 0; node_id < num_nodes; node_id++) {
284         struct buddy_memzone * zone = NULL;
285
286         // Seed the allocator with a small set of pages to allow initialization to complete. 
287         // For now we will just grab some random pages, but in the future we will need to grab NUMA specific regions
288         // See: alloc_pages_node()
289
290         {
291             struct page * pgs;
292             int actual_node;
293               
294             // attempt to first allocate below 4 GB for compatibility with
295             // 32 bit shadow paging
296             pgs = alloc_pages_node(node_id, GFP_DMA32, MAX_ORDER - 1);
297
298
299             if (!pgs) {
300                 INFO("Could not allocate initial memory block for node %d below 4GB\n", node_id);
301                 
302                 pgs = alloc_pages_node(node_id, GFP_KERNEL, MAX_ORDER - 1);
303
304                 if (!pgs) {
305                     INFO("Could not allocate initial memory block for node %d below 4GB\n", node_id);
306                     if (!pgs) {
307                         ERROR("Could not allocate initial memory block for node %d without restrictions\n", node_id);
308                         BUG_ON(!pgs);
309                         palacios_deinit_mm();
310                         return -1;
311                     }
312                 } else {
313                   actual_node=numa_addr_to_node((uintptr_t)(page_to_pfn(pgs) << PAGE_SHIFT));
314                   if (actual_node != node_id) { 
315                     WARNING("Initial 64 bit allocation attempt for node %d resulted in allocation on node %d\n",node_id,actual_node);
316                   }
317                 }
318                   
319             } else {
320               actual_node=numa_addr_to_node((uintptr_t)(page_to_pfn(pgs) << PAGE_SHIFT));
321               if (actual_node != node_id) { 
322                 WARNING("Initial 32bit-limited allocation attempt for node %d resulted in allocation on node %d\n",node_id,actual_node);
323               }
324             }
325
326             seed_addrs[node_id] = page_to_pfn(pgs) << PAGE_SHIFT;
327         }
328
329     // Initialization is done using the compile-time memory block size since
330     // at this point, we do not yet know what the run-time size is
331         zone = buddy_init(get_order(V3_CONFIG_MEM_BLOCK_SIZE) + PAGE_SHIFT, PAGE_SHIFT, node_id);
332
333         if (zone == NULL) {
334             ERROR("Could not initialization memory management for node %d\n", node_id);
335             palacios_deinit_mm();
336             return -1;
337         }
338
339         printk("Zone initialized, Adding seed region (order=%d)\n", 
340                (MAX_ORDER - 1) + PAGE_SHIFT);
341
342         if (buddy_add_pool(zone, seed_addrs[node_id], (MAX_ORDER - 1) + PAGE_SHIFT,0)) { 
343             ERROR("Could not add pool to buddy allocator\n");
344             palacios_deinit_mm();
345             return -1;
346         }
347
348         memzones[node_id] = zone;
349     }
350
351     return 0;
352
353 }
354