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 / 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
214     pool = find_mempool(zone, base_addr);
215
216     if (pool == NULL) {
217         ERROR("Could not find mempool with base address (%p)\n", (void *)base_addr);
218         return -1;
219     }
220
221     block = (struct block *)__va(pool->base_addr);
222
223     INFO("Removing Mempool %p, base=%p\n",pool,block);
224
225     // The largest order block in the memory pool must be free
226     if (!is_available(pool, block)) { 
227         if (!force) { 
228             ERROR("Trying to remove an in use memory pool\n");
229             *user_metadata=0;
230             return -1;
231         } else {
232             WARNING("Forcefully removing in use memory pool\n");
233         }
234     }
235
236     *user_metadata = pool->user_metadata;
237     
238     list_del(&(block->link));
239     rb_erase(&(pool->tree_node), &(zone->mempools));
240
241     palacios_free(pool->tag_bits);
242     palacios_free(pool);
243
244     zone->num_pools--;
245
246     return 0;
247 }
248
249 int buddy_remove_pool(struct buddy_memzone * zone,
250                       unsigned long base_addr, 
251                       unsigned char force,
252                       void          **user_metadata)
253 {
254     unsigned long flags = 0;
255     int ret = 0;
256
257     palacios_spinlock_lock_irqsave(&(zone->lock), flags);    
258     ret = __buddy_remove_mempool(zone, base_addr, force, user_metadata);
259     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
260
261     return ret;
262 }
263
264
265
266
267
268 /**
269  * Allocates a block of memory of the requested size (2^order bytes).
270  *
271  * Arguments:
272  *       [IN] mp:    Buddy system memory allocator object.
273  *       [IN] order: Block size to allocate (2^order bytes).
274  *       [IN] constraints: bitmmask showing restrictions for scan. currently: 0=none, or LWK_BUDDY_CONSTRAINT_4GB
275  * Returns:
276  *       Success: Pointer to the start of the allocated memory block.
277  *       Failure: NULL
278  */
279 uintptr_t
280 buddy_alloc(struct buddy_memzone *zone, unsigned long order, int constraints)
281 {
282     unsigned long j;
283     struct buddy_mempool * mp = NULL;
284     struct list_head * list = NULL;
285     struct list_head * cur = NULL;
286     struct block * block = NULL;
287     struct block * buddy_block = NULL;
288     unsigned long flags = 0;
289
290     if (constraints && constraints!=LWK_BUDDY_CONSTRAINT_4GB) { 
291         ERROR("Do not know how to satisfy constraint mask 0x%x\n", constraints);
292         return (uintptr_t) NULL;
293     }
294
295     BUG_ON(zone == NULL);
296     BUG_ON(order > zone->max_order);
297
298     /* Fixup requested order to be at least the minimum supported */
299     if (order < zone->min_order) {
300         order = zone->min_order;
301     }
302
303     INFO("zone=%p, order=%lu\n", zone, order);
304
305     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
306
307     for (j = order; j <= zone->max_order; j++) {
308
309         INFO("Order iter=%lu\n", j);
310
311         block=NULL;
312
313         list = &(zone->avail[j]);
314         
315         if (list_empty(list)) {
316             continue;
317         }
318
319         list_for_each(cur, list) {
320             block = list_entry(cur, struct block, link);
321
322             if (!constraints) {
323                 // without a constraint, we just want the first one
324                 break;
325             }
326             
327             if (constraints & LWK_BUDDY_CONSTRAINT_4GB) {
328                 // under this constraint, we will only use if the entirity
329                 // of the allocation within the block will be below 4 GB
330                 void *block_pa = (void*)__pa(block);
331                 if ((block_pa + (1ULL<<order)) <= (void*)(0x100000000ULL)) {
332                     // this block will work
333                     break;
334                 } else {
335                     // look for the next block
336                     block=NULL;
337                     continue;
338                 }
339             }
340         }
341         
342         if (!block) { 
343             // uh oh, no block, look to next order
344             continue;
345         }
346
347         // have appropriate block, will allocate
348
349         list_del(&(block->link));
350
351         mp = block->mp;
352
353         mark_allocated(mp, block);
354
355         INFO("pool=%p, block=%p, order=%lu, j=%lu\n", mp, block, order, j);
356
357         /* Trim if a higher order block than necessary was allocated */
358         while (j > order) {
359             --j;
360             buddy_block = (struct block *)((unsigned long)block + (1UL << j));
361             buddy_block->mp = mp;
362             buddy_block->order = j;
363             mark_available(mp, buddy_block);
364             list_add(&(buddy_block->link), &(zone->avail[j]));
365         }
366
367         mp->num_free_blocks -= (1UL << (order - zone->min_order));
368
369         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
370
371         return __pa(block);
372     }
373
374     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
375
376     return (uintptr_t)NULL;
377 }
378
379
380 /**
381  * Returns a block of memory to the buddy system memory allocator.
382  */
383 void
384 buddy_free(
385         //!    Buddy system memory allocator object.
386         struct buddy_memzone *  zone,
387         //!  Address of memory block to free.
388         uintptr_t addr,
389         //! Size of the memory block (2^order bytes).
390         unsigned long           order
391
392 {
393     struct block * block  = NULL;
394     struct buddy_mempool * pool = NULL;
395     unsigned long flags = 0;
396
397     BUG_ON(zone == NULL);
398     BUG_ON(order > zone->max_order);
399
400
401     if ((addr & ((1UL << zone->min_order) - 1)) != 0) {
402         ERROR("Attempting to free an invalid memory address (%p)\n", (void *)addr);
403         BUG_ON(1);
404     }
405
406
407     /* Fixup requested order to be at least the minimum supported */
408     if (order < zone->min_order) {
409         order = zone->min_order;
410     }
411
412
413     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
414
415     pool = find_mempool(zone, addr);
416
417     if ((pool == NULL) || (order > pool->pool_order)) {
418         WARNING("Attempted to free an invalid page address (%p)\n", (void *)addr);
419         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
420         return;
421     }
422
423
424     /* Overlay block structure on the memory block being freed */
425     block = (struct block *) __va(addr);
426     
427     if (is_available(pool, block)) {
428         ERROR("Error: Freeing an available block\n");
429         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
430         return;
431     }
432
433     pool->num_free_blocks += (1UL << (order - zone->min_order));
434
435     /* Coalesce as much as possible with adjacent free buddy blocks */
436     while (order < pool->pool_order) {
437         /* Determine our buddy block's address */
438         struct block * buddy = find_buddy(pool, block, order);
439
440         /* Make sure buddy is available and has the same size as us */
441         if (!is_available(pool, buddy))
442             break;
443         if (is_available(pool, buddy) && (buddy->order != order))
444             break;
445
446         /* OK, we're good to go... buddy merge! */
447         list_del(&buddy->link);
448         if (buddy < block)
449             block = buddy;
450         ++order;
451         block->order = order;
452     }
453
454     /* Add the (possibly coalesced) block to the appropriate free list */
455     block->order = order;
456     block->mp = pool;
457     mark_available(pool, block);
458     list_add(&(block->link), &(zone->avail[order]));
459
460     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
461 }
462
463
464
465
466 /**
467  * Dumps the state of a buddy system memory allocator object to the console.
468  */
469 static int 
470 zone_mem_show(struct seq_file * s, void * v) {
471     struct buddy_memzone * zone = s->private;
472     unsigned long i;
473     unsigned long num_blocks;
474     struct list_head * entry = NULL;
475     unsigned long flags = 0;
476
477
478     if (!zone) {
479         seq_printf(s, "Null Zone Pointer!!\n");
480         return 0;
481     }
482
483     seq_printf(s, "DUMP OF BUDDY MEMORY ZONE:\n");
484     seq_printf(s, "  Zone Max Order=%lu, Min Order=%lu\n", 
485            zone->max_order, zone->min_order);
486
487     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
488
489     for (i = zone->min_order; i <= zone->max_order; i++) {
490
491         /* Count the number of memory blocks in the list */
492         num_blocks = 0;
493         list_for_each(entry, &zone->avail[i]) {
494             ++num_blocks;
495         }
496
497         seq_printf(s, "  order %2lu: %lu free blocks\n", i, num_blocks);
498     }
499
500
501     seq_printf(s, " %lu memory pools\n", zone->num_pools);
502     // list pools in zone
503     {
504         struct rb_node * node = rb_first(&(zone->mempools));
505         struct buddy_mempool * pool = NULL;
506     
507         while (node) {
508             pool = rb_entry(node, struct buddy_mempool, tree_node);
509             
510             seq_printf(s, "    Base Addr=%p, order=%lu, size=%lu, free=%lu\n", 
511                        (void *)pool->base_addr, pool->pool_order, (1UL << pool->pool_order),
512                        pool->num_free_blocks << zone->min_order);
513
514
515             node = rb_next(node);
516         }
517     }
518
519     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
520
521     return 0;
522 }
523
524
525 static int zone_proc_open(struct inode * inode, struct file * filp) {
526     struct proc_dir_entry * proc_entry = PDE(inode);
527     INFO("proc_entry at %p, data at %p\n", proc_entry, proc_entry->data);
528     return single_open(filp, zone_mem_show, proc_entry->data);
529 }
530
531
532 static struct file_operations zone_proc_ops = {
533     .owner = THIS_MODULE,
534     .open = zone_proc_open, 
535     .read = seq_read,
536     .llseek = seq_lseek, 
537     .release = single_release,
538 };
539
540
541 void buddy_deinit(struct buddy_memzone * zone, int (*free_callback)(void *user_metadata)) {
542     unsigned long flags;
543     struct rb_node *node;
544     struct buddy_mempool **pools;
545     unsigned long long base_addr;
546     void *meta;
547     int i;
548     unsigned long num_in_tree;
549
550     pools = (struct buddy_mempool **) palacios_alloc(sizeof(struct buddy_mempool *)*zone->num_pools);
551     if (!pools) { 
552         ERROR("Cannot allocate space for doing deinit of memory zone\n");
553         return ;
554     }
555     
556     // We will lock only to build up the memory pool list
557     // when we free memory, we need to be able to support free callbacks
558     // that could block.  This does leave a race with adds, allocs, and frees, however
559     // In Palacios, we expect a deinit will only really happen on the module unload
560     // so this should not present a problem
561     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
562
563     // because it does not appear possible to erase while iterating
564     // over the rb tree, we do the following contorted mess
565     // get the pools 
566     for (num_in_tree=0, node=rb_first(&(zone->mempools));
567          node && num_in_tree<zone->num_pools;
568          node=rb_next(node), num_in_tree++) { 
569         
570         pools[num_in_tree]=rb_entry(node,struct buddy_mempool, tree_node);
571     }
572
573     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
574
575     if (num_in_tree != zone->num_pools) { 
576         WARNING("Odd, the number of pools in the tree is %lu, but the zone reports %lu\n",
577                 num_in_tree, zone->num_pools);
578     }
579
580     // now we'll free the memory
581     // note that buddy_remove_mempool also removes them
582     // from the rb tree, and frees them
583     for (i=0;i<num_in_tree;i++) { 
584         base_addr = pools[i]->base_addr;
585         
586         if (buddy_remove_pool(zone, base_addr, 1, &meta)) {
587             WARNING("Cannot remove memory pool at %p during zone deinit...\n",(void*)(base_addr));
588             continue;
589         }
590         
591         // pool and node are now gone... 
592
593         // invoke the callback to free the actual memory, if any
594         if (free_callback) { 
595             free_callback(meta);
596         }
597     }
598
599
600     // get rid of /proc entry
601     {
602         char proc_file_name[128];
603
604         memset(proc_file_name, 0, 128);
605         snprintf(proc_file_name, 128, "v3-mem%d", zone->node_id);
606
607         remove_proc_entry(proc_file_name, palacios_get_procdir());
608     }
609
610
611     palacios_free(pools);
612     palacios_free(zone->avail);
613     palacios_free(zone);
614
615     return;
616 }
617
618
619
620 /**
621  * Initializes a buddy system memory allocator object.
622  *
623  * Arguments:
624  *       [IN] base_addr:  Base address of the memory pool.
625  *       [IN] pool_order: Size of the memory pool (2^pool_order bytes).
626  *       [IN] min_order:  Minimum allocatable block size (2^min_order bytes).
627  *
628  * Returns:
629  *       Success: Pointer to an initialized buddy system memory allocator.
630  *       Failure: NULL
631  *
632  * NOTE: The min_order argument is provided as an optimization. Since one tag
633  *       bit is required for each minimum-sized block, large memory pools that
634  *       allow order 0 allocations will use large amounts of memory. Specifying
635  *       a min_order of 5 (32 bytes), for example, reduces the number of tag
636  *       bits by 32x.
637  */
638 struct buddy_memzone *
639 buddy_init(
640         unsigned long max_order,
641         unsigned long min_order,
642         unsigned int node_id
643 )
644 {
645     struct buddy_memzone * zone = NULL;
646     unsigned long i;
647
648     DEBUG("Initializing Memory zone with up to %lu bit blocks on Node %d\n", max_order, node_id);
649
650
651     /* Smallest block size must be big enough to hold a block structure */
652     if ((1UL << min_order) < sizeof(struct block))
653         min_order = ilog2( roundup_pow_of_two(sizeof(struct block)) );
654
655     /* The minimum block order must be smaller than the pool order */
656     if (min_order > max_order)
657         return NULL;
658
659     zone = palacios_alloc_extended(sizeof(struct buddy_memzone), GFP_KERNEL, node_id);
660         
661     INFO("Allocated zone at %p\n", zone);
662
663     if (!zone) {
664         ERROR("Could not allocate memzone\n");
665         return NULL;
666     }
667
668     memset(zone, 0, sizeof(struct buddy_memzone));
669
670     zone->max_order = max_order;
671     zone->min_order  = min_order;
672     zone->node_id = node_id;
673
674     /* Allocate a list for every order up to the maximum allowed order */
675     zone->avail = palacios_alloc_extended((max_order + 1) * sizeof(struct list_head), GFP_KERNEL, zone->node_id);
676
677     if (!(zone->avail)) { 
678         ERROR("Unable to allocate space for zone list\n");
679         palacios_free(zone);
680         return NULL;
681     }
682
683     INFO("Allocated free lists at %p\n", zone->avail);
684
685     /* Initially all lists are empty */
686     for (i = 0; i <= max_order; i++) {
687         INIT_LIST_HEAD(&zone->avail[i]);
688     }
689
690
691     palacios_spinlock_init(&(zone->lock));
692
693     zone->mempools.rb_node = NULL;
694
695     INFO("Allocated zone at %p\n", zone);
696
697     {
698         struct proc_dir_entry * zone_entry = NULL;
699         char proc_file_name[128];
700
701         memset(proc_file_name, 0, 128);
702         snprintf(proc_file_name, 128, "v3-mem%u", zone->node_id);
703
704         zone_entry = create_proc_entry(proc_file_name, 0444, palacios_get_procdir());
705         if (zone_entry) {
706             zone_entry->proc_fops = &zone_proc_ops;
707             zone_entry->data = zone;
708             INFO("Successfully created /proc/v3vee/v3-mem%d\n", zone->node_id);
709         } else {
710             ERROR("Cannot create /proc/v3vee/v3-mem%d\n", zone->node_id);
711         }
712
713     }
714
715     return zone;
716 }