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.


Constraints in page allocation, and code changes to use them; shadow paging allocati...
[palacios.git] / linux_module / buddy.c
1 /* Copyright (c) 2007, Sandia National Laboratories */
2 /* Modified by Jack Lange, 2012 */
3
4 #include <linux/module.h>
5 #include <linux/slab.h>
6 #include <linux/log2.h>
7 #include "buddy.h"
8 #include "palacios.h"
9
10 #include <linux/proc_fs.h>
11 #include <linux/seq_file.h>
12
13
14 /**
15  * Converts a block address to its block index in the specified buddy allocator.
16  * A block's index is used to find the block's tag bit, mp->tag_bits[block_id].
17  */
18 static unsigned long
19 block_to_id(struct buddy_mempool *mp, struct block *block)
20 {
21     unsigned long block_id =
22         ((unsigned long)__pa(block) - mp->base_addr) >> mp->zone->min_order;
23     BUG_ON(block_id >= mp->num_blocks);
24     return block_id;
25 }
26
27
28 /**
29  * Marks a block as free by setting its tag bit to one.
30  */
31 static void
32 mark_available(struct buddy_mempool *mp, struct block *block)
33 {
34     __set_bit(block_to_id(mp, block), mp->tag_bits);
35 }
36
37
38 /**
39  * Marks a block as allocated by setting its tag bit to zero.
40  */
41 static void
42 mark_allocated(struct buddy_mempool *mp, struct block *block)
43 {
44     __clear_bit(block_to_id(mp, block), mp->tag_bits);
45 }
46
47
48 /**
49  * Returns true if block is free, false if it is allocated.
50  */
51 static int
52 is_available(struct buddy_mempool *mp, struct block *block)
53 {
54     return test_bit(block_to_id(mp, block), mp->tag_bits);
55 }
56
57
58 /**
59  * Returns the address of the block's buddy block.
60  */
61 static void *
62 find_buddy(struct buddy_mempool *mp, struct block *block, unsigned long order)
63 {
64     unsigned long _block;
65     unsigned long _buddy;
66
67     BUG_ON((unsigned long)__pa(block) < mp->base_addr);
68
69     /* Fixup block address to be zero-relative */
70     _block = (unsigned long)__pa(block) - mp->base_addr;
71
72     /* Calculate buddy in zero-relative space */
73     _buddy = _block ^ (1UL << order);
74
75     /* Return the buddy's address */
76     return (void *)(_buddy + __va(mp->base_addr));
77 }
78
79
80 static inline uintptr_t pool_end_addr(struct buddy_mempool * pool) {
81     return pool->base_addr + (1UL << pool->pool_order);
82 }
83
84
85 static struct buddy_mempool * 
86 find_mempool(struct buddy_memzone * zone, uintptr_t addr) {
87     struct rb_node * n = zone->mempools.rb_node;
88     struct buddy_mempool * pool = NULL;
89
90     while (n) {
91         pool = rb_entry(n, struct buddy_mempool, tree_node);
92
93         if (addr < pool->base_addr) {
94             n = n->rb_left;
95         } else if (addr >= pool_end_addr(pool)) {
96             n = n->rb_right;
97         } else {
98             return pool;
99         }
100     }
101
102     return NULL;
103 }
104
105
106
107 static int 
108 insert_mempool(struct buddy_memzone * zone, 
109                struct buddy_mempool * pool) {
110     struct rb_node ** p = &(zone->mempools.rb_node);
111     struct rb_node * parent = NULL;
112     struct buddy_mempool * tmp_pool;
113
114     while (*p) {
115         parent = *p;
116         tmp_pool = rb_entry(parent, struct buddy_mempool, tree_node);
117
118         if (pool_end_addr(pool) <= tmp_pool->base_addr) {
119             p = &(*p)->rb_left;
120         } else if (pool->base_addr >= pool_end_addr(tmp_pool)) {
121             p = &(*p)->rb_right;
122         } else {
123             return -1;
124         }
125     }
126
127     rb_link_node(&(pool->tree_node), parent, p);  
128     rb_insert_color(&(pool->tree_node), &(zone->mempools));
129     zone->num_pools++;
130
131     return 0;
132 }
133
134
135
136
137 /* This adds a pool of a given size to a buddy allocated zone
138  */
139
140 int buddy_add_pool(struct buddy_memzone * zone, 
141                    unsigned long base_addr, 
142                    unsigned long pool_order,
143                    void *user_metadata) {
144     struct buddy_mempool * mp = NULL;
145     unsigned long flags = 0;
146     int ret = 0;
147
148     if (pool_order > zone->max_order) {
149         ERROR("Pool order size is larger than max allowable zone size (pool_order=%lu) (max_order=%lu)\n", pool_order, zone->max_order);
150         return -1;
151     } else if (pool_order < zone->min_order) {
152         ERROR("Pool order is smaller than min allowable zone size (pool_order=%lu) (min_order=%lu)\n", pool_order, zone->min_order);
153         return -1;
154     }
155
156     mp = palacios_alloc_extended(sizeof(struct buddy_mempool), GFP_KERNEL, zone->node_id);
157
158     if (IS_ERR(mp)) {
159         ERROR("Could not allocate mempool\n");
160         return -1;
161     }
162
163     mp->base_addr  = base_addr;
164     mp->pool_order = pool_order;
165     mp->zone = zone;
166     mp->num_free_blocks = 0;
167
168     mp->user_metadata = user_metadata;
169
170     /* Allocate a bitmap with 1 bit per minimum-sized block */
171     mp->num_blocks = (1UL << pool_order) / (1UL << zone->min_order);
172
173     mp->tag_bits   = palacios_alloc_extended(
174                                   BITS_TO_LONGS(mp->num_blocks) * sizeof(long), GFP_KERNEL, zone->node_id
175                                   );
176
177     /* Initially mark all minimum-sized blocks as allocated */
178     bitmap_zero(mp->tag_bits, mp->num_blocks);
179
180     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
181     ret = insert_mempool(zone, mp);
182     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
183
184     if (ret == -1) {
185         ERROR("Error: Could not insert mempool into zone\n");
186         palacios_free(mp->tag_bits);
187         palacios_free(mp);
188
189         return -1;
190     }
191
192     buddy_free(zone, base_addr, pool_order);
193
194     INFO("Added memory pool (addr=%p), order=%lu\n", (void *)base_addr, pool_order);
195
196     return 0;
197 }
198
199
200 /** 
201  * Removes a mempool from a zone, 
202  * assumes the zone lock is already held 
203  */
204 static int __buddy_remove_mempool(struct buddy_memzone * zone, 
205                                   unsigned long base_addr, 
206                                   unsigned char force,
207                                   void **user_metadata) 
208 {
209
210     struct buddy_mempool * pool = NULL;
211     struct block * block = NULL;
212
213     pool = find_mempool(zone, base_addr);
214
215     if (pool == NULL) {
216         ERROR("Could not find mempool with base address (%p)\n", (void *)base_addr);
217         return -1;
218     }
219
220     if (!bitmap_empty(pool->tag_bits, pool->num_blocks)) {
221         ERROR("Trying to remove an in use memory pool\n");
222         return -1;
223     }
224
225     *user_metadata = pool->user_metadata;
226     
227     block = (struct block *)__va(pool->base_addr);
228
229     list_del(&(block->link));
230     rb_erase(&(pool->tree_node), &(zone->mempools));
231
232     palacios_free(pool->tag_bits);
233     palacios_free(pool);
234
235     zone->num_pools--;
236
237     return 0;
238 }
239
240 int buddy_remove_pool(struct buddy_memzone * zone,
241                       unsigned long base_addr, 
242                       unsigned char force,
243                       void          **user_metadata)
244 {
245     unsigned long flags = 0;
246     int ret = 0;
247
248     palacios_spinlock_lock_irqsave(&(zone->lock), flags);    
249     ret = __buddy_remove_mempool(zone, base_addr, force, user_metadata);
250     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
251
252     return ret;
253 }
254
255
256
257
258 /**
259  * Allocates a block of memory of the requested size (2^order bytes).
260  *
261  * Arguments:
262  *       [IN] mp:    Buddy system memory allocator object.
263  *       [IN] order: Block size to allocate (2^order bytes).
264  *       [IN] constraints: bitmmask showing restrictions for scan. currently: 0=none, or LWK_BUDDY_CONSTRAINT_4GB
265  * Returns:
266  *       Success: Pointer to the start of the allocated memory block.
267  *       Failure: NULL
268  */
269 uintptr_t
270 buddy_alloc(struct buddy_memzone *zone, unsigned long order, int constraints)
271 {
272     unsigned long j;
273     struct buddy_mempool * mp = NULL;
274     struct list_head * list = NULL;
275     struct list_head * cur = NULL;
276     struct block * block = NULL;
277     struct block * buddy_block = NULL;
278     unsigned long flags = 0;
279
280     if (constraints && constraints!=LWK_BUDDY_CONSTRAINT_4GB) { 
281         ERROR("Do not know how to satisfy constraint mask 0x%x\n", constraints);
282         return (uintptr_t) NULL;
283     }
284
285     BUG_ON(zone == NULL);
286     BUG_ON(order > zone->max_order);
287
288     /* Fixup requested order to be at least the minimum supported */
289     if (order < zone->min_order) {
290         order = zone->min_order;
291     }
292
293     INFO("zone=%p, order=%lu\n", zone, order);
294
295     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
296
297     for (j = order; j <= zone->max_order; j++) {
298
299         INFO("Order iter=%lu\n", j);
300
301         block=NULL;
302
303         list = &(zone->avail[j]);
304         
305         if (list_empty(list)) {
306             continue;
307         }
308
309         list_for_each(cur, list) {
310             block = list_entry(cur, struct block, link);
311
312             if (!constraints) {
313                 // without a constraint, we just want the first one
314                 break;
315             }
316             
317             if (constraints & LWK_BUDDY_CONSTRAINT_4GB) {
318                 // under this constraint, we will only use if the entirity
319                 // of the allocation within the block will be below 4 GB
320                 void *block_pa = (void*)__pa(block);
321                 if ((block_pa + (1ULL<<order)) <= (void*)(0x100000000ULL)) {
322                     // this block will work
323                     break;
324                 } else {
325                     // look for the next block
326                     block=NULL;
327                     continue;
328                 }
329             }
330         }
331         
332         if (!block) { 
333             // uh oh, no block, look to next order
334             continue;
335         }
336
337         // have appropriate block, will allocate
338
339         list_del(&(block->link));
340
341         mp = block->mp;
342
343         mark_allocated(mp, block);
344
345         INFO("pool=%p, block=%p, order=%lu, j=%lu\n", mp, block, order, j);
346
347         /* Trim if a higher order block than necessary was allocated */
348         while (j > order) {
349             --j;
350             buddy_block = (struct block *)((unsigned long)block + (1UL << j));
351             buddy_block->mp = mp;
352             buddy_block->order = j;
353             mark_available(mp, buddy_block);
354             list_add(&(buddy_block->link), &(zone->avail[j]));
355         }
356
357         mp->num_free_blocks -= (1UL << (order - zone->min_order));
358
359         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
360
361         return __pa(block);
362     }
363
364     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
365
366     return (uintptr_t)NULL;
367 }
368
369
370 /**
371  * Returns a block of memory to the buddy system memory allocator.
372  */
373 void
374 buddy_free(
375         //!    Buddy system memory allocator object.
376         struct buddy_memzone *  zone,
377         //!  Address of memory block to free.
378         uintptr_t addr,
379         //! Size of the memory block (2^order bytes).
380         unsigned long           order
381
382 {
383     struct block * block  = NULL;
384     struct buddy_mempool * pool = NULL;
385     unsigned long flags = 0;
386
387     BUG_ON(zone == NULL);
388     BUG_ON(order > zone->max_order);
389
390
391     if ((addr & ((1UL << zone->min_order) - 1)) != 0) {
392         ERROR("Attempting to free an invalid memory address (%p)\n", (void *)addr);
393         BUG_ON(1);
394     }
395
396
397     /* Fixup requested order to be at least the minimum supported */
398     if (order < zone->min_order) {
399         order = zone->min_order;
400     }
401
402
403     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
404
405     pool = find_mempool(zone, addr);
406
407     if ((pool == NULL) || (order > pool->pool_order)) {
408         WARNING("Attempted to free an invalid page address (%p)\n", (void *)addr);
409         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
410         return;
411     }
412
413
414     /* Overlay block structure on the memory block being freed */
415     block = (struct block *) __va(addr);
416     
417     if (is_available(pool, block)) {
418         ERROR("Error: Freeing an available block\n");
419         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
420         return;
421     }
422
423     pool->num_free_blocks += (1UL << (order - zone->min_order));
424
425     /* Coalesce as much as possible with adjacent free buddy blocks */
426     while (order < pool->pool_order) {
427         /* Determine our buddy block's address */
428         struct block * buddy = find_buddy(pool, block, order);
429
430         /* Make sure buddy is available and has the same size as us */
431         if (!is_available(pool, buddy))
432             break;
433         if (is_available(pool, buddy) && (buddy->order != order))
434             break;
435
436         /* OK, we're good to go... buddy merge! */
437         list_del(&buddy->link);
438         if (buddy < block)
439             block = buddy;
440         ++order;
441         block->order = order;
442     }
443
444     /* Add the (possibly coalesced) block to the appropriate free list */
445     block->order = order;
446     block->mp = pool;
447     mark_available(pool, block);
448     list_add(&(block->link), &(zone->avail[order]));
449
450     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
451 }
452
453
454
455
456 /**
457  * Dumps the state of a buddy system memory allocator object to the console.
458  */
459 static int 
460 zone_mem_show(struct seq_file * s, void * v) {
461     struct buddy_memzone * zone = s->private;
462     unsigned long i;
463     unsigned long num_blocks;
464     struct list_head * entry = NULL;
465     unsigned long flags = 0;
466
467
468     if (!zone) {
469         seq_printf(s, "Null Zone Pointer!!\n");
470         return 0;
471     }
472
473     seq_printf(s, "DUMP OF BUDDY MEMORY ZONE:\n");
474     seq_printf(s, "  Zone Max Order=%lu, Min Order=%lu\n", 
475            zone->max_order, zone->min_order);
476
477     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
478
479     for (i = zone->min_order; i <= zone->max_order; i++) {
480
481         /* Count the number of memory blocks in the list */
482         num_blocks = 0;
483         list_for_each(entry, &zone->avail[i]) {
484             ++num_blocks;
485         }
486
487         seq_printf(s, "  order %2lu: %lu free blocks\n", i, num_blocks);
488     }
489
490
491     seq_printf(s, " %lu memory pools\n", zone->num_pools);
492     // list pools in zone
493     {
494         struct rb_node * node = rb_first(&(zone->mempools));
495         struct buddy_mempool * pool = NULL;
496     
497         while (node) {
498             pool = rb_entry(node, struct buddy_mempool, tree_node);
499             
500             seq_printf(s, "    Base Addr=%p, order=%lu, size=%lu, free=%lu\n", 
501                        (void *)pool->base_addr, pool->pool_order, (1UL << pool->pool_order),
502                        pool->num_free_blocks << zone->min_order);
503
504
505             node = rb_next(node);
506         }
507     }
508
509     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
510
511     return 0;
512 }
513
514
515 static int zone_proc_open(struct inode * inode, struct file * filp) {
516     struct proc_dir_entry * proc_entry = PDE(inode);
517     INFO("proc_entry at %p, data at %p\n", proc_entry, proc_entry->data);
518     return single_open(filp, zone_mem_show, proc_entry->data);
519 }
520
521
522 static struct file_operations zone_proc_ops = {
523     .owner = THIS_MODULE,
524     .open = zone_proc_open, 
525     .read = seq_read,
526     .llseek = seq_lseek, 
527     .release = single_release,
528 };
529
530
531 void buddy_deinit(struct buddy_memzone * zone) {
532     unsigned long flags;
533
534     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
535
536     // for each pool, free it
537 #warning We really need to free the memory pools here
538
539     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
540     
541     {
542         char proc_file_name[128];
543
544         memset(proc_file_name, 0, 128);
545         snprintf(proc_file_name, 128, "v3-mem%d", zone->node_id);
546
547         remove_proc_entry(proc_file_name, palacios_get_procdir());
548     }
549
550
551     palacios_free(zone->avail);
552     palacios_free(zone);
553
554     return;
555 }
556
557
558
559 /**
560  * Initializes a buddy system memory allocator object.
561  *
562  * Arguments:
563  *       [IN] base_addr:  Base address of the memory pool.
564  *       [IN] pool_order: Size of the memory pool (2^pool_order bytes).
565  *       [IN] min_order:  Minimum allocatable block size (2^min_order bytes).
566  *
567  * Returns:
568  *       Success: Pointer to an initialized buddy system memory allocator.
569  *       Failure: NULL
570  *
571  * NOTE: The min_order argument is provided as an optimization. Since one tag
572  *       bit is required for each minimum-sized block, large memory pools that
573  *       allow order 0 allocations will use large amounts of memory. Specifying
574  *       a min_order of 5 (32 bytes), for example, reduces the number of tag
575  *       bits by 32x.
576  */
577 struct buddy_memzone *
578 buddy_init(
579         unsigned long max_order,
580         unsigned long min_order,
581         unsigned int node_id
582 )
583 {
584     struct buddy_memzone * zone = NULL;
585     unsigned long i;
586
587     DEBUG("Initializing Memory zone with up to %lu bit blocks on Node %d\n", max_order, node_id);
588
589
590     /* Smallest block size must be big enough to hold a block structure */
591     if ((1UL << min_order) < sizeof(struct block))
592         min_order = ilog2( roundup_pow_of_two(sizeof(struct block)) );
593
594     /* The minimum block order must be smaller than the pool order */
595     if (min_order > max_order)
596         return NULL;
597
598     zone = palacios_alloc_extended(sizeof(struct buddy_memzone), GFP_KERNEL, node_id);
599         
600     INFO("Allocated zone at %p\n", zone);
601
602     if (!zone) {
603         ERROR("Could not allocate memzone\n");
604         return NULL;
605     }
606
607     memset(zone, 0, sizeof(struct buddy_memzone));
608
609     zone->max_order = max_order;
610     zone->min_order  = min_order;
611     zone->node_id = node_id;
612
613     /* Allocate a list for every order up to the maximum allowed order */
614     zone->avail = palacios_alloc_extended((max_order + 1) * sizeof(struct list_head), GFP_KERNEL, zone->node_id);
615
616     if (!(zone->avail)) { 
617         ERROR("Unable to allocate space for zone list\n");
618         palacios_free(zone);
619         return NULL;
620     }
621
622     INFO("Allocated free lists at %p\n", zone->avail);
623
624     /* Initially all lists are empty */
625     for (i = 0; i <= max_order; i++) {
626         INIT_LIST_HEAD(&zone->avail[i]);
627     }
628
629
630     palacios_spinlock_init(&(zone->lock));
631
632     zone->mempools.rb_node = NULL;
633
634     INFO("Allocated zone at %p\n", zone);
635
636     {
637         struct proc_dir_entry * zone_entry = NULL;
638         char proc_file_name[128];
639
640         memset(proc_file_name, 0, 128);
641         snprintf(proc_file_name, 128, "v3-mem%u", zone->node_id);
642
643         zone_entry = create_proc_entry(proc_file_name, 0444, palacios_get_procdir());
644         if (zone_entry) {
645             zone_entry->proc_fops = &zone_proc_ops;
646             zone_entry->data = zone;
647             INFO("Successfully created /proc/v3vee/v3-mem%d\n", zone->node_id);
648         } else {
649             ERROR("Cannot create /proc/v3vee/v3-mem%d\n", zone->node_id);
650         }
651
652     }
653
654     return zone;
655 }