return -1;
}
- mp = palacios_alloc_node_extended(sizeof(struct buddy_mempool), GFP_KERNEL, zone->node_id);
+ mp = palacios_alloc_extended(sizeof(struct buddy_mempool), GFP_KERNEL, zone->node_id);
- if (IS_ERR(mp)) {
+ if (!mp) {
ERROR("Could not allocate mempool\n");
return -1;
}
/* Allocate a bitmap with 1 bit per minimum-sized block */
mp->num_blocks = (1UL << pool_order) / (1UL << zone->min_order);
- mp->tag_bits = palacios_alloc_node_extended(
+ mp->tag_bits = palacios_alloc_extended(
BITS_TO_LONGS(mp->num_blocks) * sizeof(long), GFP_KERNEL, zone->node_id
);
+ if (!(mp->tag_bits)) {
+ ERROR("Could not allocate tag_bits\n");
+ palacios_free(mp);
+ return -1;
+ }
+
+
/* Initially mark all minimum-sized blocks as allocated */
bitmap_zero(mp->tag_bits, mp->num_blocks);
struct buddy_mempool * pool = NULL;
struct block * block = NULL;
+
pool = find_mempool(zone, base_addr);
if (pool == NULL) {
return -1;
}
- if (!bitmap_empty(pool->tag_bits, pool->num_blocks)) {
- ERROR("Trying to remove an in use memory pool\n");
- return -1;
+ block = (struct block *)__va(pool->base_addr);
+
+ INFO("Removing Mempool %p, base=%p\n",pool,block);
+
+ // The largest order block in the memory pool must be free
+ if (!is_available(pool, block)) {
+ if (!force) {
+ ERROR("Trying to remove an in use memory pool\n");
+ *user_metadata=0;
+ return -1;
+ } else {
+ WARNING("Forcefully removing in use memory pool\n");
+ }
}
*user_metadata = pool->user_metadata;
- block = (struct block *)__va(pool->base_addr);
-
- list_del(&(block->link));
+ if (is_available(pool,block)) {
+ list_del(&(block->link));
+ } else {
+ // we may not be on the free list if we are being
+ // forcibly removed before all allocations are freed
+ }
+
rb_erase(&(pool->tree_node), &(zone->mempools));
palacios_free(pool->tag_bits);
unsigned long flags = 0;
int ret = 0;
+
palacios_spinlock_lock_irqsave(&(zone->lock), flags);
ret = __buddy_remove_mempool(zone, base_addr, force, user_metadata);
palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
+
/**
* Allocates a block of memory of the requested size (2^order bytes).
*
* Arguments:
* [IN] mp: Buddy system memory allocator object.
* [IN] order: Block size to allocate (2^order bytes).
- *
+ * [IN] filter_func: returns nonzero if given paddr is OK to use
+ * [IN] filter_state: opaque argument to filter_func
* Returns:
* Success: Pointer to the start of the allocated memory block.
* Failure: NULL
*/
uintptr_t
-buddy_alloc(struct buddy_memzone *zone, unsigned long order)
+buddy_alloc(struct buddy_memzone *zone, unsigned long order, int (*filter_func)(void *paddr, void *filter_state), void *filter_state)
{
unsigned long j;
struct buddy_mempool * mp = NULL;
struct list_head * list = NULL;
+ struct list_head * cur = NULL;
struct block * block = NULL;
struct block * buddy_block = NULL;
unsigned long flags = 0;
INFO("Order iter=%lu\n", j);
- /* Try to allocate the first block in the order j list */
- list = &zone->avail[j];
+ block=NULL;
+
+ list = &(zone->avail[j]);
+
+ if (list_empty(list)) {
+ continue;
+ }
+
+ list_for_each(cur, list) {
+ block = list_entry(cur, struct block, link);
+
+ if (!filter_func) {
+ // without a filter, we just want the first one
+ break;
+ } else {
+
+ void *block_pa = (void*)__pa(block);
- if (list_empty(list))
+ if (filter_func(block_pa,filter_state)) {
+ // this block will work
+ break;
+ } else {
+ // this block won't work
+ block=NULL;
+ continue;
+ }
+
+ }
+ }
+
+ if (!block) {
+ // uh oh, no block, look to next order
continue;
+ }
+
+ // have appropriate block, will allocate
- block = list_entry(list->next, struct block, link);
list_del(&(block->link));
mp = block->mp;
INFO("pool=%p, block=%p, order=%lu, j=%lu\n", mp, block, order, j);
- /*
- palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
- return 0;
- */
-
/* Trim if a higher order block than necessary was allocated */
while (j > order) {
--j;
/**
* Returns a block of memory to the buddy system memory allocator.
*/
-void
+int
buddy_free(
//! Buddy system memory allocator object.
struct buddy_memzone * zone,
pool = find_mempool(zone, addr);
if ((pool == NULL) || (order > pool->pool_order)) {
- WARNING("Attempted to free an invalid page address (%p)\n", (void *)addr);
+ WARNING("Attempted to free an invalid page address (%p) - pool=%p order=%lu\n", (void *)addr,pool,order);
palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
- return;
+ return -1;
}
if (is_available(pool, block)) {
ERROR("Error: Freeing an available block\n");
palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
- return;
+ return -1;
}
pool->num_free_blocks += (1UL << (order - zone->min_order));
list_add(&(block->link), &(zone->avail[order]));
palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
+
+ return 0;
}
};
-void buddy_deinit(struct buddy_memzone * zone) {
+void buddy_deinit(struct buddy_memzone * zone, int (*free_callback)(void *user_metadata)) {
unsigned long flags;
-
+ struct rb_node *node;
+ struct buddy_mempool **pools;
+ unsigned long long base_addr;
+ void *meta;
+ int i;
+ unsigned long num_in_tree;
+
+ pools = (struct buddy_mempool **) palacios_alloc(sizeof(struct buddy_mempool *)*zone->num_pools);
+ if (!pools) {
+ ERROR("Cannot allocate space for doing deinit of memory zone\n");
+ return ;
+ }
+
+ // We will lock only to build up the memory pool list
+ // when we free memory, we need to be able to support free callbacks
+ // that could block. This does leave a race with adds, allocs, and frees, however
+ // In Palacios, we expect a deinit will only really happen on the module unload
+ // so this should not present a problem
palacios_spinlock_lock_irqsave(&(zone->lock), flags);
- // for each pool, free it
-#warning We really need to free the memory pools here
+ // because it does not appear possible to erase while iterating
+ // over the rb tree, we do the following contorted mess
+ // get the pools
+ for (num_in_tree=0, node=rb_first(&(zone->mempools));
+ node && num_in_tree<zone->num_pools;
+ node=rb_next(node), num_in_tree++) {
+
+ pools[num_in_tree]=rb_entry(node,struct buddy_mempool, tree_node);
+ }
palacios_spinlock_unlock_irqrestore(&(zone->lock), flags);
-
+
+ if (num_in_tree != zone->num_pools) {
+ WARNING("Odd, the number of pools in the tree is %lu, but the zone reports %lu\n",
+ num_in_tree, zone->num_pools);
+ }
+
+ // now we'll free the memory
+ // note that buddy_remove_mempool also removes them
+ // from the rb tree, and frees them
+ for (i=0;i<num_in_tree;i++) {
+ base_addr = pools[i]->base_addr;
+
+ if (buddy_remove_pool(zone, base_addr, 1, &meta)) {
+ WARNING("Cannot remove memory pool at %p during zone deinit...\n",(void*)(base_addr));
+ continue;
+ }
+
+ // pool and node are now gone...
+
+ // invoke the callback to free the actual memory, if any
+ if (free_callback) {
+ free_callback(meta);
+ }
+ }
+
+
+ // get rid of /proc entry
{
char proc_file_name[128];
memset(proc_file_name, 0, 128);
- snprintf(proc_file_name, 128, "v3-mem%d", zone->node_id);
+ snprintf(proc_file_name, 128, "v3-mem%u", zone->node_id);
remove_proc_entry(proc_file_name, palacios_get_procdir());
}
+ palacios_free(pools);
palacios_free(zone->avail);
palacios_free(zone);
if (min_order > max_order)
return NULL;
- zone = palacios_alloc_node_extended(sizeof(struct buddy_memzone), GFP_KERNEL, node_id);
+ zone = palacios_alloc_extended(sizeof(struct buddy_memzone), GFP_KERNEL, node_id);
INFO("Allocated zone at %p\n", zone);
- if (IS_ERR(zone)) {
+ if (!zone) {
ERROR("Could not allocate memzone\n");
return NULL;
}
zone->node_id = node_id;
/* Allocate a list for every order up to the maximum allowed order */
- zone->avail = palacios_alloc_node_extended((max_order + 1) * sizeof(struct list_head), GFP_KERNEL, zone->node_id);
+ zone->avail = palacios_alloc_extended((max_order + 1) * sizeof(struct list_head), GFP_KERNEL, zone->node_id);
+
+ if (!(zone->avail)) {
+ ERROR("Unable to allocate space for zone list\n");
+ palacios_free(zone);
+ return NULL;
+ }
INFO("Allocated free lists at %p\n", zone->avail);