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