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 / 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_node_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_node_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  *
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)
271 {
272     unsigned long j;
273     struct buddy_mempool * mp = NULL;
274     struct list_head * list = NULL;
275     struct block * block = NULL;
276     struct block * buddy_block = NULL;
277     unsigned long flags = 0;
278
279     BUG_ON(zone == NULL);
280     BUG_ON(order > zone->max_order);
281
282     /* Fixup requested order to be at least the minimum supported */
283     if (order < zone->min_order) {
284         order = zone->min_order;
285     }
286
287     INFO("zone=%p, order=%lu\n", zone, order);
288
289     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
290
291     for (j = order; j <= zone->max_order; j++) {
292
293         INFO("Order iter=%lu\n", j);
294
295         /* Try to allocate the first block in the order j list */
296         list = &zone->avail[j];
297
298         if (list_empty(list)) 
299             continue;
300
301         block = list_entry(list->next, struct block, link);
302         list_del(&(block->link));
303
304         mp = block->mp;
305
306         mark_allocated(mp, block);
307
308         INFO("pool=%p, block=%p, order=%lu, j=%lu\n", mp, block, order, j);
309
310         /*
311         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
312         return 0;
313         */
314
315         /* Trim if a higher order block than necessary was allocated */
316         while (j > order) {
317             --j;
318             buddy_block = (struct block *)((unsigned long)block + (1UL << j));
319             buddy_block->mp = mp;
320             buddy_block->order = j;
321             mark_available(mp, buddy_block);
322             list_add(&(buddy_block->link), &(zone->avail[j]));
323         }
324
325         mp->num_free_blocks -= (1UL << (order - zone->min_order));
326
327         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
328
329         return __pa(block);
330     }
331
332     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
333
334     return (uintptr_t)NULL;
335 }
336
337
338 /**
339  * Returns a block of memory to the buddy system memory allocator.
340  */
341 void
342 buddy_free(
343         //!    Buddy system memory allocator object.
344         struct buddy_memzone *  zone,
345         //!  Address of memory block to free.
346         uintptr_t addr,
347         //! Size of the memory block (2^order bytes).
348         unsigned long           order
349
350 {
351     struct block * block  = NULL;
352     struct buddy_mempool * pool = NULL;
353     unsigned long flags = 0;
354
355     BUG_ON(zone == NULL);
356     BUG_ON(order > zone->max_order);
357
358
359     if ((addr & ((1UL << zone->min_order) - 1)) != 0) {
360         ERROR("Attempting to free an invalid memory address (%p)\n", (void *)addr);
361         BUG_ON(1);
362     }
363
364
365     /* Fixup requested order to be at least the minimum supported */
366     if (order < zone->min_order) {
367         order = zone->min_order;
368     }
369
370
371     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
372
373     pool = find_mempool(zone, addr);
374
375     if ((pool == NULL) || (order > pool->pool_order)) {
376         WARNING("Attempted to free an invalid page address (%p)\n", (void *)addr);
377         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
378         return;
379     }
380
381
382     /* Overlay block structure on the memory block being freed */
383     block = (struct block *) __va(addr);
384     
385     if (is_available(pool, block)) {
386         ERROR("Error: Freeing an available block\n");
387         palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
388         return;
389     }
390
391     pool->num_free_blocks += (1UL << (order - zone->min_order));
392
393     /* Coalesce as much as possible with adjacent free buddy blocks */
394     while (order < pool->pool_order) {
395         /* Determine our buddy block's address */
396         struct block * buddy = find_buddy(pool, block, order);
397
398         /* Make sure buddy is available and has the same size as us */
399         if (!is_available(pool, buddy))
400             break;
401         if (is_available(pool, buddy) && (buddy->order != order))
402             break;
403
404         /* OK, we're good to go... buddy merge! */
405         list_del(&buddy->link);
406         if (buddy < block)
407             block = buddy;
408         ++order;
409         block->order = order;
410     }
411
412     /* Add the (possibly coalesced) block to the appropriate free list */
413     block->order = order;
414     block->mp = pool;
415     mark_available(pool, block);
416     list_add(&(block->link), &(zone->avail[order]));
417
418     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
419 }
420
421
422
423
424 /**
425  * Dumps the state of a buddy system memory allocator object to the console.
426  */
427 static int 
428 zone_mem_show(struct seq_file * s, void * v) {
429     struct buddy_memzone * zone = s->private;
430     unsigned long i;
431     unsigned long num_blocks;
432     struct list_head * entry = NULL;
433     unsigned long flags = 0;
434
435
436     if (!zone) {
437         seq_printf(s, "Null Zone Pointer!!\n");
438         return 0;
439     }
440
441     seq_printf(s, "DUMP OF BUDDY MEMORY ZONE:\n");
442     seq_printf(s, "  Zone Max Order=%lu, Min Order=%lu\n", 
443            zone->max_order, zone->min_order);
444
445     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
446
447     for (i = zone->min_order; i <= zone->max_order; i++) {
448
449         /* Count the number of memory blocks in the list */
450         num_blocks = 0;
451         list_for_each(entry, &zone->avail[i]) {
452             ++num_blocks;
453         }
454
455         seq_printf(s, "  order %2lu: %lu free blocks\n", i, num_blocks);
456     }
457
458
459     seq_printf(s, " %lu memory pools\n", zone->num_pools);
460     // list pools in zone
461     {
462         struct rb_node * node = rb_first(&(zone->mempools));
463         struct buddy_mempool * pool = NULL;
464     
465         while (node) {
466             pool = rb_entry(node, struct buddy_mempool, tree_node);
467             
468             seq_printf(s, "    Base Addr=%p, order=%lu, size=%lu, free=%lu\n", 
469                        (void *)pool->base_addr, pool->pool_order, (1UL << pool->pool_order),
470                        pool->num_free_blocks << zone->min_order);
471
472
473             node = rb_next(node);
474         }
475     }
476
477     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
478
479     return 0;
480 }
481
482
483 static int zone_proc_open(struct inode * inode, struct file * filp) {
484     struct proc_dir_entry * proc_entry = PDE(inode);
485     INFO("proc_entry at %p, data at %p\n", proc_entry, proc_entry->data);
486     return single_open(filp, zone_mem_show, proc_entry->data);
487 }
488
489
490 static struct file_operations zone_proc_ops = {
491     .owner = THIS_MODULE,
492     .open = zone_proc_open, 
493     .read = seq_read,
494     .llseek = seq_lseek, 
495     .release = single_release,
496 };
497
498
499 void buddy_deinit(struct buddy_memzone * zone) {
500     unsigned long flags;
501
502     palacios_spinlock_lock_irqsave(&(zone->lock), flags);
503
504     // for each pool, free it
505 #warning We really need to free the memory pools here
506
507     palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
508     
509     {
510         char proc_file_name[128];
511
512         memset(proc_file_name, 0, 128);
513         snprintf(proc_file_name, 128, "v3-mem%d", zone->node_id);
514
515         remove_proc_entry(proc_file_name, palacios_get_procdir());
516     }
517
518
519     palacios_free(zone->avail);
520     palacios_free(zone);
521
522     return;
523 }
524
525
526
527 /**
528  * Initializes a buddy system memory allocator object.
529  *
530  * Arguments:
531  *       [IN] base_addr:  Base address of the memory pool.
532  *       [IN] pool_order: Size of the memory pool (2^pool_order bytes).
533  *       [IN] min_order:  Minimum allocatable block size (2^min_order bytes).
534  *
535  * Returns:
536  *       Success: Pointer to an initialized buddy system memory allocator.
537  *       Failure: NULL
538  *
539  * NOTE: The min_order argument is provided as an optimization. Since one tag
540  *       bit is required for each minimum-sized block, large memory pools that
541  *       allow order 0 allocations will use large amounts of memory. Specifying
542  *       a min_order of 5 (32 bytes), for example, reduces the number of tag
543  *       bits by 32x.
544  */
545 struct buddy_memzone *
546 buddy_init(
547         unsigned long max_order,
548         unsigned long min_order,
549         unsigned int node_id
550 )
551 {
552     struct buddy_memzone * zone = NULL;
553     unsigned long i;
554
555     DEBUG("Initializing Memory zone with up to %lu bit blocks on Node %d\n", max_order, node_id);
556
557
558     /* Smallest block size must be big enough to hold a block structure */
559     if ((1UL << min_order) < sizeof(struct block))
560         min_order = ilog2( roundup_pow_of_two(sizeof(struct block)) );
561
562     /* The minimum block order must be smaller than the pool order */
563     if (min_order > max_order)
564         return NULL;
565
566     zone = palacios_alloc_node_extended(sizeof(struct buddy_memzone), GFP_KERNEL, node_id);
567         
568     INFO("Allocated zone at %p\n", zone);
569
570     if (IS_ERR(zone)) {
571         ERROR("Could not allocate memzone\n");
572         return NULL;
573     }
574
575     memset(zone, 0, sizeof(struct buddy_memzone));
576
577     zone->max_order = max_order;
578     zone->min_order  = min_order;
579     zone->node_id = node_id;
580
581     /* Allocate a list for every order up to the maximum allowed order */
582     zone->avail = palacios_alloc_node_extended((max_order + 1) * sizeof(struct list_head), GFP_KERNEL, zone->node_id);
583
584     INFO("Allocated free lists at %p\n", zone->avail);
585
586     /* Initially all lists are empty */
587     for (i = 0; i <= max_order; i++) {
588         INIT_LIST_HEAD(&zone->avail[i]);
589     }
590
591
592     palacios_spinlock_init(&(zone->lock));
593
594     zone->mempools.rb_node = NULL;
595
596     INFO("Allocated zone at %p\n", zone);
597
598     {
599         struct proc_dir_entry * zone_entry = NULL;
600         char proc_file_name[128];
601
602         memset(proc_file_name, 0, 128);
603         snprintf(proc_file_name, 128, "v3-mem%u", zone->node_id);
604
605         zone_entry = create_proc_entry(proc_file_name, 0444, palacios_get_procdir());
606         if (zone_entry) {
607             zone_entry->proc_fops = &zone_proc_ops;
608             zone_entry->data = zone;
609             INFO("Successfully created /proc/v3vee/v3-mem%d\n", zone->node_id);
610         } else {
611             ERROR("Cannot create /proc/v3vee/v3-mem%d\n", zone->node_id);
612         }
613
614     }
615
616     return zone;
617 }