Commit ccbce7bb authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan
Browse files

host-resource-trees: Page alloc inserts into memory interval tree.

Microkernel's page allocation code will insert the memory object
that represents the allocated pages into the interval tree.

Fixes interval tree insertion to use memory object range (start
and last).

Adds locks for tree traversal and per-node locks.
parent 3c82ef28
......@@ -438,7 +438,35 @@ struct lcd_memory_object {
void *object;
unsigned int order;
};
/**
* __lcd_memory_object_start -- Starting address (memory-object dependent)
* @mo: the memory object
*
* This computes the starting address of the memory object. If the
* memory object is for physical memory, the address is a physical address.
* If for vmalloc memory, the address is a host virtual address.
*/
unsigned long __lcd_memory_object_start(struct lcd_memory_object *mo);
/**
* __lcd_memory_object_size -- Computes the size in bytes of the memory
* object
* @mo: the memory object
*
* For physical memory, this will be the amount of contiguous physical memory
* spanned by the memory object. For vmalloc memory, this will be the amount
* of *host virtual address space* spanned by the object.
*
* Memory objects are assumed to be page-aligned.
*/
unsigned long __lcd_memory_object_size(struct lcd_memory_object *mo);
/**
* __lcd_memory_object_last -- Computes the address of the last valid byte
* in the memory object
* @mo: the memory object
*
* This is just start + size - 1.
*/
unsigned long __lcd_memory_object_last(struct lcd_memory_object *mo);
/**
* __lcd_alloc_pages_exact_node -- Alloc host pages and insert into caller's
* cspace
......@@ -575,6 +603,7 @@ void __lcd_mem_exit(void)
*/
struct lcd_mem_itree {
struct rb_root root;
struct mutex lock;
};
/**
* struct lcd_mem_itree_node
......@@ -582,11 +611,16 @@ struct lcd_mem_itree {
* Node in the lcd_mem_itree. Contains pointer to the memory object
* itself (the physical pages, device memory address, vmalloc address,
* etc.).
*
* XXX: The lock here might be unnecessary (if we always use itree nodes
* under other related locks - like cnode locks). And it could potentially
* lead to deadlocks. (So many lockz ...)
*/
struct lcd_mem_itree_node {
struct interval_tree_node it_node;
struct lcd_memory_object *mo;
unsigned int flags;
struct mutex lock;
};
/**
* __lcd_mem_itree_insert -- Insert a memory object into the appropriate
......@@ -601,8 +635,8 @@ struct lcd_mem_itree_node {
*/
int __lcd_mem_itree_insert(struct lcd_memory_object *mo, unsigned int flags);
/**
* __lcd_mem_itree_lookup -- Search for a node in the appropriate tree
* for a memory object that contains address
* __lcd_mem_itree_get -- Search for a node in the appropriate tree
* for a memory object that contains address
* @addr: address to search for
* @type: the memory object type to search for
* @node_out: out param, the tree node, if found
......@@ -610,16 +644,25 @@ int __lcd_mem_itree_insert(struct lcd_memory_object *mo, unsigned int flags);
* If searching for physical memory (pages, device memory), @addr is
* a physical address. If searching for vmalloc memory, @addr is a host
* virtual address.
*
* IMPORTANT: Locks the node itself. You should call __lcd_mem_itree_put
* to release lock.
*/
int __lcd_mem_itree_lookup(unsigned long addr,
int __lcd_mem_itree_get(unsigned long addr,
enum lcd_microkernel_type_id type,
struct lcd_mem_itree_node **node_out);
/**
* __lcd_mem_itree_delete -- Delete a node from its containing memory interval
* tree
* @node: the node to delete
* __lcd_mem_itree_put -- Release lock on node
*/
int __lcd_mem_itree_put(struct lcd_mem_itree_node *node);
/**
* __lcd_mem_itree_delete -- Remove memory object from its containing memory
* interval tree
* @mo: the memory object to delete
*
* BUG_ON @mo is not found in the proper tree
*/
int __lcd_mem_itree_delete(struct lcd_mem_itree_node *node);
void __lcd_mem_itree_delete(struct lcd_memory_object *mo);
/**
* __lcd_mem_itree_init -- Call this when the microkernel initializes
*
......
......@@ -7,6 +7,36 @@
#include <linux/slab.h>
#include "internal.h"
/* MEMORY OBJECT HELPERS -------------------------------------------------- */
unsigned long __lcd_memory_object_start(struct lcd_memory_object *mo)
{
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
return va2hpa(page_address(mo->object));
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return (unsigned long)mo->object;
default:
LCD_ERR("unexpected memory object type %d",
mo->sub_type);
/* Return an invalid physical address */
return ~(0UL);
}
}
unsigned long __lcd_memory_object_size(struct lcd_memory_object *mo)
{
return (1UL << mo->order) << PAGE_SHIFT;
}
unsigned long __lcd_memory_object_last(struct lcd_memory_object *mo)
{
return __lcd_memory_object_start(mo) +
__lcd_memory_object_size(mo) - 1;
}
/* ALLOC -------------------------------------------------- */
static void do_insert_pages(struct lcd *caller, struct page *p,
......@@ -33,6 +63,14 @@ static void do_insert_pages(struct lcd *caller, struct page *p,
mo->sub_type = LCD_MICROKERNEL_TYPE_ID_PAGE;
mo->object = p;
mo->order = order;
/*
* Insert memory object into global interval tree
*/
ret = __lcd_mem_itree_insert(mo, 0);
if (ret) {
LCD_ERR("memory itree insert failed");
goto fail3;
}
/*
* Insert into caller's cspace
*/
......@@ -41,7 +79,7 @@ static void do_insert_pages(struct lcd *caller, struct page *p,
__lcd_get_libcap_type(LCD_MICROKERNEL_TYPE_ID_PAGE));
if (ret) {
LCD_ERR("insert");
goto fail3;
goto fail4;
}
/*
* ================================================
......@@ -70,6 +108,8 @@ static void do_insert_pages(struct lcd *caller, struct page *p,
*/
return 0;
fail4:
__lcd_mem_itree_delete(mo);
fail3:
kfree(meta);
fail2:
......
......@@ -11,13 +11,10 @@
#include <linux/mutex.h>
#include "internal.h"
static struct mutex lcd_physical_mem_itree_lock;
static struct mutex lcd_vmalloc_mem_itree_lock;
static struct lcd_mem_itree lcd_physical_mem_itree;
static struct lcd_mem_itree lcd_vmalloc_mem_itree;
static int do_mem_itree_insert(struct lcd_mem_itree *tree,
struct mutex *tree_lock,
struct lcd_memory_object *mo, unsigned int flags)
{
struct lcd_memory_itree_node *n;
......@@ -29,15 +26,20 @@ static int do_mem_itree_insert(struct lcd_mem_itree *tree,
LCD_ERR("kzalloc node");
return -ENOMEM;
}
/*
* Insert into interval tree
*/
mutex_init(&n->lock);
n->mo = mo;
n->flags = flags;
mutex_lock(tree_lock);
/*
* Set up the start and end address, using memory object
*/
n->it_node.start = __lcd_memory_object_start(mo);
n->it_node.last = __lcd_memory_object_last(mo);
/*
* Insert into tree
*/
mutex_lock(&tree->lock);
interval_tree_insert(&n->it_node, &tree->root);
mutex_unlock(tree_lock);
mutex_unlock(&tree->lock);
return 0;
}
......@@ -49,12 +51,10 @@ int __lcd_mem_itree_insert(struct lcd_memory_object *mo, unsigned int flags)
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return do_mem_itree_insert(&lcd_physical_mem_itree,
&lcd_physical_mem_itree_lock,
mo,
flags);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return do_mem_itree_insert(&lcd_vmalloc_mem_itree,
&lcd_vmalloc_mem_itree_lock,
mo,
flags);
default:
......@@ -63,8 +63,7 @@ int __lcd_mem_itree_insert(struct lcd_memory_object *mo, unsigned int flags)
}
}
static int do_mem_itree_lookup(struct lcd_mem_itree *tree,
struct mutex *tree_lock,
static int do_mem_itree_lookup_nolock(struct lcd_mem_itree *tree,
unsigned long addr,
struct lcd_mem_itree_node **node_out)
{
......@@ -75,19 +74,33 @@ static int do_mem_itree_lookup(struct lcd_mem_itree *tree,
*/
struct interval_tree_node *match;
mutex_lock(tree_lock);
match = interval_tree_first(&tree->root, addr, addr);
mutex_unlock(tree_lock);
if (match) {
n_out = container_of(match, struct lcd_mem_itree_node,
it_node);
node_out = container_of(match, struct lcd_mem_itree_node,
it_node);
mutex_lock(&(*node_out)->lock);
return 0;
} else {
return -1; /* not found */
}
}
int __lcd_mem_itree_lookup(unsigned long addr,
static int do_mem_itree_lookup(struct lcd_mem_itree *tree,
unsigned long addr,
struct lcd_mem_itree_node **node_out)
{
int ret;
mutex_lock(&tree->lock);
ret = do_mem_itree_lookup(tree, addr, node_out);
mutex_unlock(&tree->lock);
return ret;
}
int __lcd_mem_itree_get(unsigned long addr,
enum lcd_microkernel_type_id type,
struct lcd_mem_itree_node **node_out)
{
......@@ -96,12 +109,10 @@ int __lcd_mem_itree_lookup(unsigned long addr,
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return do_mem_itree_lookup(&lcd_physical_mem_itree,
&lcd_physical_mem_itree_lock,
addr,
node_out);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return do_mem_itree_lookup(&lcd_vmalloc_mem_itree,
&lcd_vmalloc_mem_itree_lock,
addr,
node_out);
default:
......@@ -110,31 +121,54 @@ int __lcd_mem_itree_lookup(unsigned long addr,
}
}
int __lcd_mem_itree_put(struct lcd_mem_itree_node *node)
{
mutex_unlock(&node->lock);
}
static void do_mem_itree_delete(struct lcd_mem_itree *tree,
struct mutex *tree_lock,
struct lcd_mem_itree_node *node)
struct lcd_memory_object *mo)
{
mutex_lock(tree_lock);
struct lcd_mem_itree_node *node;
int ret;
mutex_lock(&tree->lock);
/*
* Find the tree node that contains the memory object
*/
ret = do_mem_itree_lookup_nolock(tree,
__lcd_memory_object_start(mo),
&node);
if (ret) {
LCD_ERR("couldn't find memory object in tree, skipping delete");
mutex_unlock(&tree->lock);
BUG();
return;
}
/*
* We now have the node locked and the tree locked, so no one
* can have a reference. Free to unlock and delete tree node.
*/
interval_tree_remove(&node->it_node, &tree->root);
mutex_unlock(tree_lock);
kfree(node);
mutex_unlock(&tree->lock);
}
void __lcd_mem_itree_delete(struct lcd_mem_itree_node *node)
void __lcd_mem_itree_delete(struct lcd_memory_object *mo)
{
switch (node->mo->sub_type) {
switch (mo->sub_type) {
case LCD_MICROKERNEL_TYPE_ID_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_PAGE:
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_DEV_MEM:
return do_mem_itree_delete(&lcd_physical_mem_itree,
&lcd_physical_mem_itree_lock,
node);
return do_mem_itree_delete(&lcd_physical_mem_itree, mo);
case LCD_MICROKERNEL_TYPE_ID_VOLUNTEERED_VMALLOC_MEM:
return do_mem_itree_delete(&lcd_vmalloc_mem_itree,
&lcd_vmalloc_mem_itree_lock,
node);
return do_mem_itree_delete(&lcd_vmalloc_mem_itree, mo);
default:
LCD_ERR("unexpected memory object type %d",
node->mo->sub_type);
mo->sub_type);
}
}
......@@ -145,8 +179,8 @@ int __lcd_mem_itree_init(void)
*/
lcd_physical_mem_itree.root = RB_ROOT;
lcd_vmalloc_mem_itree.root = RB_ROOT;
mutex_init(&lcd_physical_mem_itree_lock);
mutex_init(&lcd_vmalloc_mem_itree_lock);
mutex_init(&lcd_physical_mem_itree.lock);
mutex_init(&lcd_vmalloc_mem_itree.lock);
return 0;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment