aos/lib/mdb/mdb_tree.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

1386 lines
38 KiB
C

#include <mdb/mdb_tree.h>
#include <mdb/mdb.h>
#include <cap_predicates.h>
#include <barrelfish_kpi/capabilities.h>
#include <capabilities.h>
#include <assert.h>
#include <stdio.h>
#if IN_KERNEL
#include <kernel.h>
#include <kcb.h>
#endif
#ifndef MIN
#define MIN(a, b) ((a)<(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a, b) ((a)>(b)?(a):(b))
#endif
#ifdef N
#undef N
#endif
#define N(cte) (&(cte)->mdbnode)
#ifdef C
#undef C
#endif
#define C(cte) (&(cte)->cap)
// define panic() for user-land build
#ifndef IN_KERNEL
#define panic(msg...) \
do { \
printf(msg); \
abort(); \
}while(0)
#endif
// PP switch to change behaviour if invariants fail
#ifdef MDB_FAIL_INVARIANTS
// on failure, dump mdb and terminate
__attribute__((noreturn))
static void
mdb_dump_and_fail(struct cte *cte, enum mdb_invariant failure)
{
mdb_dump(cte, 0);
panic("failed on cte %p with failure %s (%d)\n",
cte, mdb_invariant_to_str(failure), failure);
}
#define MDB_RET_INVARIANT(cte, failure) mdb_dump_and_fail(cte, failure)
#else
#define MDB_RET_INVARIANT(cte, failure) return failure
#endif
// PP switch to toggle top-level checking of invariants
#ifdef MDB_CHECK_INVARIANTS
#define CHECK_INVARIANTS(root, cte, reach) \
do { \
if (mdb_is_reachable(root, cte) != reach) { \
panic("mdb_is_reachable(%p,%p) != %d", root, cte, reach); \
} \
mdb_check_subtree_invariants(cte); \
} while(0)
#else
#define CHECK_INVARIANTS(root, cte, reach) ((void)0)
#endif
// PP switch to toggle recursive checking of invariants by default
#ifdef MDB_RECHECK_INVARIANTS
// disable toplevel invariants checks except for the assertion clause as we're
// doing them anyway in CHECK_INVARIANTS_SUB
#undef CHECK_INVARIANTS
#define CHECK_INVARIANTS(root, cte, reach) \
do { \
if (mdb_is_reachable(root, cte) != reach) { \
panic("mdb_is_reachable(%p,%p) != %d", root, cte, reach); \
} \
} while(0)
#define CHECK_INVARIANTS_SUB(cte) mdb_check_subtree_invariants(cte)
#else
#define CHECK_INVARIANTS_SUB(cte) ((void)0)
#endif
// printf tracing and entry/exit invariant checking
#ifdef MDB_TRACE
#define MDB_TRACE_ENTER(valid_cte, args_fmt, ...) do { \
printf("enter %s(" args_fmt ")\n", __func__, __VA_ARGS__); \
CHECK_INVARIANTS_SUB(valid_cte); \
} while (0)
#define MDB_TRACE_LEAVE_SUB(valid_cte) do { \
CHECK_INVARIANTS_SUB(valid_cte); \
printf("leave %s\n", __func__); \
return; \
} while (0)
#define MDB_TRACE_LEAVE_SUB_RET(ret_fmt, ret, valid_cte) do { \
CHECK_INVARIANTS_SUB(valid_cte); \
printf("leave %s->" ret_fmt "\n", __func__, (ret)); \
return (ret); \
} while (0)
#else
#define MDB_TRACE_ENTER(valid_cte, args_fmt, ...) CHECK_INVARIANTS_SUB(valid_cte)
#define MDB_TRACE_LEAVE_SUB(valid_cte) do { \
CHECK_INVARIANTS_SUB(valid_cte); \
return; \
} while (0)
#define MDB_TRACE_LEAVE_SUB_RET(ret_fmt, ret, valid_cte) do { \
CHECK_INVARIANTS_SUB(valid_cte); \
return (ret); \
} while (0)
#endif
// Global for test_ops_with_root.c
struct cte *mdb_root = NULL;
#if IN_KERNEL
struct kcb *my_kcb = NULL;
#endif
static void set_root(struct cte *new_root)
{
mdb_root = new_root;
#if IN_KERNEL
my_kcb->mdb_root = (lvaddr_t) new_root;
#endif
}
/*
* (re)initialization
*/
errval_t
mdb_init(struct kcb *k)
{
assert (k != NULL);
#if IN_KERNEL
#if 0
//XXX: write two versions of this; so we can have full sanity checks for
//all scenarios -SG
if (my_kcb) {
printf("MDB has non-null kcb.\n");
return CAPS_ERR_MDB_ALREADY_INITIALIZED;
}
#endif
my_kcb = k;
if (!my_kcb->is_valid) {
// empty kcb, do nothing
return SYS_ERR_OK;
}
#endif
// set root
mdb_root = (struct cte *)k->mdb_root;
#if 0
// always check invariants here
int i = mdb_check_invariants();
if (i) {
printf("mdb invariant %s violated\n", mdb_invariant_to_str(i));
mdb_dump_all_the_things();
mdb_root = NULL;
return CAPS_ERR_MDB_INVARIANT_VIOLATION;
}
#endif
return SYS_ERR_OK;
}
/*
* Debug printing.
*/
void
mdb_dump_all_the_things(void)
{
mdb_dump(mdb_root, 0);
}
STATIC_ASSERT(68 == ObjType_Num, "Knowledge of all cap types");
static void print_cte(struct cte *cte, char *indent_buff)
{
struct mdbnode *node = N(cte);
char extra[255] = { 0 };
struct capability *cap = C(cte);
switch (cap->type) {
case ObjType_L1CNode:
snprintf(extra, 255,
"[allocated_bytes=%"PRIxGENSIZE",rightsmask=%"PRIu8"]",
cap->u.l1cnode.allocated_bytes, cap->u.l1cnode.rightsmask);
break;
case ObjType_L2CNode:
snprintf(extra, 255, "[rightsmask=%"PRIu8"]", cap->u.l2cnode.rightsmask);
break;
case ObjType_EndPointLMP:
snprintf(extra, 255,
"[listener=%p,epoffset=0x%08"PRIxLVADDR",epbuflen=%"PRIu32", if=%"PRIu32"]",
cap->u.endpointlmp.listener,cap->u.endpointlmp.epoffset,
cap->u.endpointlmp.epbuflen, cap->u.endpointlmp.iftype);
break;
case ObjType_EndPointUMP:
snprintf(extra, 255,
"[base=%" PRIxLPADDR ",if=%"PRIu32"]",
cap->u.endpointump.base, cap->u.endpointump.iftype);
break;
case ObjType_Dispatcher:
snprintf(extra, 255, "[dcb=%p]", cap->u.dispatcher.dcb);
break;
case ObjType_IO:
snprintf(extra, 255, "[start=0x%04"PRIx16",end=0x%04"PRIx16"]",
cap->u.io.start, cap->u.io.end);
break;
default:
break;
}
printf("%s%p{left=%p,right=%p,end=0x%08"PRIxGENPADDR",end_root=%"PRIu8","
"level=%"PRIu8",address=0x%08"PRIxGENPADDR",size=0x%08"PRIx64","
"type=%"PRIu8",remote_rels=%d%d%d,extra=%s}\n",
indent_buff,
cte, node->left, node->right, node->end, node->end_root,
node->level, get_address(C(cte)), get_size(C(cte)),
(uint8_t)C(cte)->type, node->remote_copies,
node->remote_ancs, node->remote_descs,extra);
return;
}
void
mdb_dump(struct cte *cte, int indent)
{
// Print a tree with root on the left, the smallest element at the top and the
// largest at the bottom.
/* make an indent buffer */
char indent_buff[indent+2];
for (int i=0; i < indent+1; i++) {
indent_buff[i]='\t';
}
indent_buff[indent+1] = '\0';
if (!cte) {
printf("NULL{}\n");
return;
}
struct mdbnode *node = N(cte);
assert(node);
if (node->left) {
if (node->left == cte) {
printf("%sSELF!!!!\n", indent_buff);
}
else {
mdb_dump(node->left, indent+1);
}
}
print_cte(cte, indent_buff);
if (node->right) {
if (node->right == cte) {
printf("%sSELF!!!!\n", indent_buff);
}
else {
mdb_dump(node->right, indent+1);
}
}
}
/*
* Invariant checking.
*/
static int
mdb_check_subtree_invariants(struct cte *cte)
{
if (!cte) {
return MDB_INVARIANT_OK;
}
if (C(cte)->type == 0) {
mdb_dump_all_the_things();
}
assert(C(cte)->type != 0);
int err;
struct mdbnode *node = N(cte);
if (node->level > 0 && !(node->left && node->right)) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_BOTHCHILDREN);
}
if (node->left && !(N(node->left)->level < node->level)) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_LEFT_LEVEL_LESS);
}
if (node->right && !(N(node->right)->level <= node->level)) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_RIGHT_LEVEL_LEQ);
}
if (node->right && N(node->right)->right &&
!(N(N(node->right)->right)->level < node->level))
{
MDB_RET_INVARIANT(cte, MDB_INVARIANT_RIGHTRIGHT_LEVEL_LESS);
}
if (node->right && N(node->right)->left &&
!(N(N(node->right)->left)->level < node->level))
{
MDB_RET_INVARIANT(cte, MDB_INVARIANT_RIGHTLEFT_LEVEL_LESS);
}
// build expected end root for current node
mdb_root_t expected_end_root = get_type_root(C(cte)->type);
if (node->left) {
expected_end_root = MAX(expected_end_root, N(node->left)->end_root);
}
if (node->right) {
expected_end_root = MAX(expected_end_root, N(node->right)->end_root);
}
if (node->end_root != expected_end_root) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_END_IS_MAX);
}
// build expected end for current node. this is complex because the root
// acts as an address prefix, so only ends where the corresponding root is
// the maximum may be considered.
genpaddr_t expected_end = 0;
if (get_type_root(C(cte)->type) == node->end_root) {
// only consider current cte's end if its root is node->end_root
expected_end = get_address(C(cte))+get_size(C(cte));
}
if (node->left && N(node->left)->end_root == node->end_root) {
// only consider left child end if its end_root is node->end_root
expected_end = MAX(expected_end, N(node->left)->end);
}
if (node->right && N(node->right)->end_root == node->end_root) {
// only consider right child end if its end_root is node->end_root
expected_end = MAX(expected_end, N(node->right)->end);
}
if (node->end != expected_end) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_END_IS_MAX);
}
if (node->left) {
assert(node->left != cte);
if (compare_caps(C(node->left), C(cte), true) >= 0) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_LEFT_SMALLER);
}
err = mdb_check_subtree_invariants(node->left);
if (err) {
return err;
}
}
if (node->right) {
assert(node->right != cte);
if (compare_caps(C(node->right), C(cte), true) <= 0) {
MDB_RET_INVARIANT(cte, MDB_INVARIANT_RIGHT_GREATER);
}
err = mdb_check_subtree_invariants(node->right);
if (err) {
return err;
}
}
return MDB_INVARIANT_OK;
}
int
mdb_check_invariants(void)
{
int res = mdb_check_subtree_invariants(mdb_root);
if (res != 0) {
printf("mdb_check_invariants() -> %d\n", res);
}
return res;
}
static bool
mdb_is_reachable(struct cte *root, struct cte *cte)
{
if (!root) {
return false;
}
if (root == cte) {
return true;
}
if (N(root)->left) {
if (mdb_is_reachable(N(root)->left, cte)) {
return true;
}
}
if (N(root)->right) {
if (mdb_is_reachable(N(root)->right, cte)) {
return true;
}
}
return false;
}
/*
* General internal helpers.
*/
static void
mdb_update_end(struct cte *cte)
{
if (!cte) {
return;
}
struct mdbnode *node = N(cte);
// build end root for current node
mdb_root_t end_root = get_type_root(C(cte)->type);
if (node->left) {
end_root = MAX(end_root, N(node->left)->end_root);
}
if (node->right) {
end_root = MAX(end_root, N(node->right)->end_root);
}
node->end_root = end_root;
// build end address for current node. this is complex because the root
// acts as an address prefix, so only ends where the corresponding root is
// the maximum may be considered.
genpaddr_t end = 0;
if (get_type_root(C(cte)->type) == node->end_root) {
// only consider current cte's end if its root is node->end_root
end = get_address(C(cte))+get_size(C(cte));
}
if (node->left && N(node->left)->end_root == node->end_root) {
// only consider left child end if its end_root is node->end_root
end = MAX(end, N(node->left)->end);
}
if (node->right && N(node->right)->end_root == node->end_root) {
// only consider right child end if its end_root is node->end_root
end = MAX(end, N(node->right)->end);
}
node->end = end;
}
static struct cte*
mdb_skew(struct cte *node)
{
/* transform invalid state
*
* |
* |L|<---|T|
* / \ \
* |A| |B| |R|
*
* to valid equivalent state
*
* |
* |L|--->|T|
* / / \
* |A| |B| |R|
*
*/
if (!node || !N(node)->left) {
return node;
}
else if (N(node)->level == N(N(node)->left)->level) {
struct cte *left = N(node)->left;
N(node)->left = N(left)->right;
N(left)->right = node;
mdb_update_end(node);
mdb_update_end(left);
// need to update mdb_root
if (mdb_root == node) {
set_root(left);
}
return left;
}
else {
return node;
}
}
static struct cte*
mdb_split(struct cte *node)
{
/* transform invalid state
*
* |
* |T|--->|R|-->|X|
* / /
* |A| |B|
*
* to valid equivalent state
*
* |
* |R|
* / \
* |T| |X|
* / \
* |A| |B|
*
*/
if (!node || !N(node)->right || !N(N(node)->right)->right) {
return node;
}
else if (N(node)->level == N(N(N(node)->right)->right)->level) {
struct cte *right = N(node)->right;
N(node)->right = N(right)->left;
N(right)->left = node;
N(right)->level += 1;
mdb_update_end(node);
mdb_update_end(right);
// need to update mdb_root
if (mdb_root == node) {
set_root(right);
}
return right;
}
else {
return node;
}
}
static void
mdb_decrease_level(struct cte *node)
{
assert(node);
mdb_level_t expected;
if (!N(node)->left || !N(node)->right) {
expected = 0;
}
else {
expected = MIN(N(N(node)->left)->level, N(N(node)->right)->level) + 1;
}
if (expected < N(node)->level) {
N(node)->level = expected;
if (N(node)->right && expected < N(N(node)->right)->level) {
N(N(node)->right)->level = expected;
}
}
}
static struct cte*
mdb_rebalance(struct cte *node)
{
assert(node);
mdb_update_end(node);
mdb_decrease_level(node);
node = mdb_skew(node);
N(node)->right = mdb_skew(N(node)->right);
if (N(node)->right) {
N(N(node)->right)->right = mdb_skew(N(N(node)->right)->right);
}
node = mdb_split(node);
N(node)->right = mdb_split(N(node)->right);
return node;
}
#ifndef NDEBUG
static bool
mdb_is_child(struct cte *child, struct cte *parent)
{
if (!parent) {
return mdb_root == child;
}
else {
return N(parent)->left == child || N(parent)->right == child;
}
}
#else
#define mdb_is_child(a, b) 0
#endif
static bool
mdb_is_inside(genpaddr_t outer_begin, genpaddr_t outer_end,
genpaddr_t inner_begin, genpaddr_t inner_end)
{
assert(outer_begin <= outer_end);
assert(inner_begin <= inner_end);
return
(inner_begin >= outer_begin && inner_end < outer_end) ||
(inner_begin > outer_begin && inner_end <= outer_end);
}
/*
* Operations and operation-specific helpers.
*/
static errval_t
mdb_sub_insert(struct cte *new_node, struct cte **current)
{
errval_t err;
assert(new_node);
assert(current);
MDB_TRACE_ENTER(*current, "%p, %p (*%p)", new_node, *current, current);
struct cte *current_ = *current;
if (!current_) {
// we've reached an empty leaf, insert here
*current = new_node;
mdb_update_end(new_node);
return SYS_ERR_OK;
}
int compare = compare_caps(C(new_node), C(current_), true);
if (compare < 0) {
// new_node < current
err = mdb_sub_insert(new_node, &N(current_)->left);
if (err_is_fail(err)) {
return err;
}
}
else if (compare > 0) {
// new_node > current
err = mdb_sub_insert(new_node, &N(current_)->right);
if (err_is_fail(err)) {
return err;
}
}
else {
return CAPS_ERR_MDB_DUPLICATE_ENTRY;
}
mdb_update_end(current_);
current_ = mdb_skew(current_);
current_ = mdb_split(current_);
*current = current_;
err = SYS_ERR_OK;
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, current_);
}
uint64_t mdb_insert_count;
errval_t
mdb_insert(struct cte *new_node)
{
MDB_TRACE_ENTER(mdb_root, "%p", new_node);
#ifdef IN_KERNEL
#ifdef MDB_TRACE_NO_RECURSIVE
char prefix[50];
snprintf(prefix, 50, "mdb_insert.%d: ", my_core_id);
print_cte(new_node, prefix);
#endif
#endif
mdb_insert_count ++;
errval_t ret = mdb_sub_insert(new_node, &mdb_root);
CHECK_INVARIANTS(mdb_root, new_node, true);
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, ret, mdb_root);
}
static void
mdb_exchange_child(struct cte *first, struct cte *first_parent,
struct cte *second)
{
assert(mdb_is_child(first, first_parent));
if (!first_parent) {
set_root(second);
}
else if (N(first_parent)->left == first) {
N(first_parent)->left = second;
}
else if (N(first_parent)->right == first) {
N(first_parent)->right = second;
}
else {
assert(!"first is not child of first_parent");
}
}
static void
mdb_exchange_nodes(struct cte *first, struct cte *first_parent,
struct cte *second, struct cte *second_parent)
{
struct cte *tmp_node;
mdb_level_t tmp_level;
mdb_exchange_child(first, first_parent, second);
mdb_exchange_child(second, second_parent, first);
tmp_node = N(first)->left;
N(first)->left = N(second)->left;
N(second)->left = tmp_node;
tmp_node = N(first)->right;
N(first)->right = N(second)->right;
N(second)->right = tmp_node;
tmp_level = N(first)->level;
N(first)->level = N(second)->level;
N(second)->level = tmp_level;
mdb_update_end(first);
mdb_update_end(second);
assert(mdb_is_reachable(mdb_root, first));
assert(mdb_is_reachable(mdb_root, second));
}
static void
mdb_exchange_remove(struct cte *target, struct cte *target_parent,
struct cte **current, struct cte *parent,
int dir, struct cte **ret_target)
{
assert(current);
MDB_TRACE_ENTER(*current, "%p, %p, %p (*%p), %p, %d", target, target_parent, *current, current, parent, dir);
assert(target);
assert(*current);
assert(parent);
assert(C(target)->type != 0);
assert(C(*current)->type != 0);
assert(C(parent)->type != 0);
assert(ret_target);
assert(!*ret_target);
assert(dir != 0);
assert(compare_caps(C(target), C(*current), true) != 0);
assert(mdb_is_child(target, target_parent));
assert(mdb_is_child(*current, parent));
assert(mdb_is_reachable(mdb_root, target));
struct cte *current_ = *current;
if (dir > 0) {
if (parent == target) {
assert(N(parent)->left == current_);
}
else {
assert(N(parent)->right == current_);
}
if (N(current_)->right) {
mdb_exchange_remove(target, target_parent, &N(current_)->right,
current_, dir, ret_target);
}
}
else if (dir < 0) {
if (parent == target) {
assert(N(parent)->right == current_);
}
else {
assert(N(parent)->left == current_);
}
if (N(current_)->left) {
mdb_exchange_remove(target, target_parent, &N(current_)->left,
current_, dir, ret_target);
}
else if (N(current_)->right) {
assert(N(current_)->level == 0);
// right is non-null, left null -> current is level 0 node with
// horizontal right link, and is also the successor of the target.
// in this case, exchange current and current right, then current
// (at its new position) and the target.
struct cte *new_current = N(current_)->right;
mdb_exchange_nodes(current_, parent, N(current_)->right, current_);
mdb_exchange_nodes(target, target_parent, current_, new_current);
// "current" is now located where the target was, further up in the
// tree. "new_current" is the node where current was. "target" is
// where current->right was, and is a leaf, so can be dropped.
assert(N(new_current)->right == target);
N(new_current)->right = NULL;
*ret_target = current_;
*current = new_current;
assert(!mdb_is_reachable(mdb_root, target));
MDB_TRACE_LEAVE_SUB(NULL);
}
}
if (*ret_target) {
assert(!mdb_is_reachable(mdb_root, target));
// implies we recursed further down to find a leaf. need to rebalance.
current_ = mdb_rebalance(current_);
*current = current_;
MDB_TRACE_LEAVE_SUB(current_);
}
else {
//printf("found leaf %p\n", current_);
// found successor/predecessor leaf, exchange with target
assert(!N(current_)->right && !N(current_)->left);
mdb_exchange_nodes(target, target_parent, current_, parent);
// "current" is now where target was, so set as ret_target
*ret_target = current_;
// target would be the new current, but we're removing it, so set
// current to null. This also sets parent's corresponding child to
// null by recursion.
*current = NULL;
MDB_TRACE_LEAVE_SUB(NULL);
}
}
static errval_t
mdb_subtree_remove(struct cte *target, struct cte **current, struct cte *parent)
{
assert(current);
MDB_TRACE_ENTER(*current, "%p, %p (*%p), %p", target, *current, current, parent);
errval_t err;
struct cte *current_ = *current;
if (!current_) {
err = CAPS_ERR_MDB_ENTRY_NOTFOUND;
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, current_);
}
int compare = compare_caps(C(target), C(current_), true);
if (compare > 0) {
err = mdb_subtree_remove(target, &N(current_)->right, current_);
if (err != SYS_ERR_OK) {
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, current_);
return err;
}
}
else if (compare < 0) {
err = mdb_subtree_remove(target, &N(current_)->left, current_);
if (err != SYS_ERR_OK) {
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, current_);
}
}
else {
assert(current_ == target);
if (!N(current_)->left && !N(current_)->right) {
// target is leaf, just remove
*current = NULL;
err = SYS_ERR_OK;
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, NULL);
}
else if (!N(current_)->left) {
// move to right child then go left (dir=-1)
// curr, new_right = xchg_rm(elem, parent, current.right, current, -1)
struct cte *new_current = NULL;
struct cte *new_right = N(current_)->right;
mdb_exchange_remove(target, parent, &new_right, current_, -1,
&new_current);
assert(new_current);
current_ = new_current;
N(current_)->right = new_right;
assert(!mdb_is_reachable(mdb_root, target));
}
else {
// move to left child then go right (dir=1)
// curr, new_left = xchg_rm(elem, parent, current.left, current, 1)
struct cte *new_current = NULL;
struct cte *new_left = N(current_)->left;
mdb_exchange_remove(target, parent, &new_left, current_, 1,
&new_current);
assert(new_current);
current_ = new_current;
N(current_)->left = new_left;
assert(!mdb_is_reachable(mdb_root, target));
}
}
// rebalance after remove from subtree
current_ = mdb_rebalance(current_);
*current = current_;
assert(C(target)->type != 0);
assert(!*current || C(*current)->type != 0);
err = SYS_ERR_OK;
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, current_);
}
uint64_t mdb_remove_count;
errval_t
mdb_remove(struct cte *target)
{
MDB_TRACE_ENTER(mdb_root, "%p", target);
CHECK_INVARIANTS(mdb_root, target, true);
#ifdef IN_KERNEL
#ifdef MDB_TRACE_NO_RECURSIVE
char prefix[50];
snprintf(prefix, 50, "mdb_remove.%d: ", my_core_id);
print_cte(target, prefix);
#endif
#endif
mdb_remove_count++;
errval_t err = mdb_subtree_remove(target, &mdb_root, NULL);
CHECK_INVARIANTS(mdb_root, target, false);
MDB_TRACE_LEAVE_SUB_RET("%"PRIuPTR, err, mdb_root);
}
/*
* Queries on the ordering.
*/
static struct cte*
mdb_sub_find_equal(struct capability *cap, struct cte *current)
{
if (!current) {
return NULL;
}
int compare = compare_caps(cap, C(current), false);
if (compare < 0) {
// current is gt key, look for smaller node
return mdb_sub_find_equal(cap, N(current)->left);
}
else if (compare > 0) {
// current is lt key, attempt to find bigger current
return mdb_sub_find_equal(cap, N(current)->right);
}
else {
return current;
}
}
uint64_t mdb_find_equal_count;
struct cte*
mdb_find_equal(struct capability *cap)
{
mdb_find_equal_count++;
return mdb_sub_find_equal(cap, mdb_root);
}
static struct cte*
mdb_sub_find_less(struct capability *cap, struct cte *current, bool equal_ok,
bool tiebreak)
{
if (!current) {
return NULL;
}
int compare = compare_caps(cap, C(current), tiebreak);
if (compare < 0) {
// current is gt key, look for smaller node
return mdb_sub_find_less(cap, N(current)->left, equal_ok, tiebreak);
}
else if (compare > 0) {
// current is lt key, attempt to find bigger current
struct cte *res = mdb_sub_find_less(cap, N(current)->right, equal_ok,
tiebreak);
if (res) {
return res;
}
// bigger child exceeded key
return current;
}
else {
// found equal element
if (equal_ok) {
return current;
}
else {
// look for smaller node
return mdb_sub_find_less(cap, N(current)->left, equal_ok,
tiebreak);
}
}
}
uint64_t mdb_find_less_count;
struct cte*
mdb_find_less(struct capability *cap, bool equal_ok)
{
mdb_find_less_count++;
return mdb_sub_find_less(cap, mdb_root, equal_ok, false);
}
static struct cte*
mdb_sub_find_greater(struct capability *cap, struct cte *current,
bool equal_ok, bool tiebreak)
{
if (!current) {
return NULL;
}
int compare = compare_caps(cap, C(current), tiebreak);
if (compare < 0) {
// current is gt key, attempt to find smaller node
struct cte *res = mdb_sub_find_greater(cap, N(current)->left, equal_ok,
tiebreak);
if (res) {
return res;
}
// smaller was lte key
return current;
}
else if (compare > 0) {
// current is lte key, look for greater node
return mdb_sub_find_greater(cap, N(current)->right, equal_ok,
tiebreak);
}
else {
// found equal element
if (equal_ok) {
return current;
}
else {
// look for greater node
return mdb_sub_find_greater(cap, N(current)->right, equal_ok,
tiebreak);
}
}
}
uint64_t mdb_find_greater_count;
struct cte*
mdb_find_greater(struct capability *cap, bool equal_ok)
{
mdb_find_greater_count++;
return mdb_sub_find_greater(cap, mdb_root, equal_ok, false);
}
uint64_t mdb_predecessor_count;
struct cte*
mdb_predecessor(struct cte *current)
{
mdb_predecessor_count++;
struct mdbnode *node = N(current);
if (node->left) {
// if possible, look just at children
current = node->left;
while ((node = N(current))->right) {
current = node->right;
}
return current;
}
// XXX: in lieu of a parent pointer that can be used to traverse upwards,
// we have to perform a search through the tree from the root. This makes
// "predecessor" into a O(log(n)) operation, instead of the expected O(1).
return mdb_sub_find_less(C(current), mdb_root, false, true);
}
uint64_t mdb_successor_count;
struct cte*
mdb_successor(struct cte *current)
{
mdb_successor_count++;
struct mdbnode *node = N(current);
if (node->right) {
// if possible, look just at children
current = node->right;
while ((node = N(current))->left) {
current = node->left;
}
return current;
}
// XXX: in lieu of a parent pointer that can be used to traverse upwards,
// we have perform a search through the tree from the root. This makes
// "successor" into a O(log(n)) operation, instead of the expected O(1).
return mdb_sub_find_greater(C(current), mdb_root, false, true);
}
/*
* The range query.
*/
static struct cte*
mdb_choose_surrounding(genpaddr_t address, size_t size, struct cte *first,
struct cte *second)
{
assert(first);
assert(second);
assert(get_type_root(C(first)->type) == get_type_root(C(second)->type));
#ifndef NDEBUG
genpaddr_t beg = address, end = address + size;
genpaddr_t fst_beg = get_address(C(first));
genpaddr_t snd_beg = get_address(C(second));
genpaddr_t fst_end = fst_beg + get_size(C(first));
genpaddr_t snd_end = snd_beg + get_size(C(second));
assert(fst_beg <= beg && fst_end >= end);
assert(snd_beg <= beg && snd_end >= end);
#endif
if (compare_caps(C(first), C(second), true) >= 0) {
return first;
}
else {
return second;
}
}
static struct cte*
mdb_choose_inner(genpaddr_t address, size_t size, struct cte *first,
struct cte *second)
{
assert(first);
assert(second);
assert(get_type_root(C(first)->type) == get_type_root(C(second)->type));
#ifndef NDEBUG
genpaddr_t end = address + size;
genpaddr_t fst_beg = get_address(C(first));
genpaddr_t snd_beg = get_address(C(second));
genpaddr_t fst_end = fst_beg + get_size(C(first));
genpaddr_t snd_end = snd_beg + get_size(C(second));
assert(mdb_is_inside(address, end, fst_beg, fst_end));
assert(mdb_is_inside(address, end, snd_beg, snd_end));
#endif
if (compare_caps(C(first), C(second), true) <= 0) {
return first;
}
else {
return second;
}
}
static struct cte*
mdb_choose_partial(genpaddr_t address, size_t size, struct cte *first,
struct cte *second)
{
assert(first);
assert(second);
assert(get_type_root(C(first)->type) == get_type_root(C(second)->type));
genpaddr_t beg = address;
genpaddr_t fst_beg = get_address(C(first));
genpaddr_t snd_beg = get_address(C(second));
#ifndef NDEBUG
genpaddr_t end = address + size;
genpaddr_t fst_end = fst_beg + get_size(C(first));
genpaddr_t snd_end = snd_beg + get_size(C(second));
assert(fst_beg < end);
assert(snd_beg < end);
assert(fst_end > beg);
assert(snd_end > beg);
assert(fst_beg != beg);
assert(snd_beg != beg);
assert(fst_end != end);
assert(snd_end != end);
assert((fst_beg < beg) == (fst_end < end));
assert((snd_beg < beg) == (snd_end < end));
#endif
if (fst_beg < beg && snd_beg > beg) {
return first;
}
else if (snd_beg < beg && fst_beg > beg) {
return second;
}
else {
if (compare_caps(C(first), C(second), true) >= 0) {
return first;
}
else {
return second;
}
}
}
static int
mdb_sub_find_range(mdb_root_t root, genpaddr_t address, size_t size,
int max_precision, struct cte *current,
/*out*/ struct cte **ret_node);
static void
mdb_sub_find_range_merge(mdb_root_t root, genpaddr_t address, size_t size,
int max_precision, struct cte *sub,
/*inout*/ int *ret, /*inout*/ struct cte **result)
{
assert(sub);
assert(ret);
assert(result);
assert(max_precision >= 0);
assert(*ret <= max_precision);
struct cte *sub_result = NULL;
int sub_ret = mdb_sub_find_range(root, address, size, max_precision, sub,
&sub_result);
if (sub_ret > max_precision) {
*result = sub_result;
*ret = sub_ret;
}
else if (sub_ret > *ret) {
*result = sub_result;
*ret = sub_ret;
}
else if (sub_ret == *ret) {
switch (sub_ret) {
case MDB_RANGE_NOT_FOUND:
break;
case MDB_RANGE_FOUND_SURROUNDING:
*result = mdb_choose_surrounding(address, size, *result, sub_result);
break;
case MDB_RANGE_FOUND_INNER:
*result = mdb_choose_inner(address, size, *result, sub_result);
break;
case MDB_RANGE_FOUND_PARTIAL:
*result = mdb_choose_partial(address, size, *result, sub_result);
break;
default:
assert(!"Unhandled enum value for mdb_find_range result");
break;
}
}
// else ret > sub_ret, keep ret & result as is
}
static int
mdb_sub_find_range(mdb_root_t root, genpaddr_t address, size_t size,
int max_precision, struct cte *current,
/*out*/ struct cte **ret_node)
{
assert(max_precision >= 0);
assert(ret_node);
if (!current) {
*ret_node = NULL;
return MDB_RANGE_NOT_FOUND;
}
if (N(current)->end_root < root) {
*ret_node = NULL;
return MDB_RANGE_NOT_FOUND;
}
if (N(current)->end_root == root && N(current)->end <= address) {
*ret_node = NULL;
return MDB_RANGE_NOT_FOUND;
}
mdb_root_t current_root = get_type_root(C(current)->type);
struct cte *result = NULL;
int ret = MDB_RANGE_NOT_FOUND;
genpaddr_t current_address = get_address(C(current));
genpaddr_t current_end = current_address + get_size(C(current));
genpaddr_t search_end = address + size;
if (current_root == root) {
if (ret < MDB_RANGE_FOUND_PARTIAL &&
current_address > address &&
current_address < search_end &&
current_end > search_end)
{
result = current;
ret = MDB_RANGE_FOUND_PARTIAL;
}
if (ret < MDB_RANGE_FOUND_PARTIAL &&
current_end > address &&
current_end < search_end &&
current_address < address)
{
result = current;
ret = MDB_RANGE_FOUND_PARTIAL;
}
if (ret < MDB_RANGE_FOUND_INNER &&
mdb_is_inside(address, search_end, current_address, current_end))
{
result = current;
ret = MDB_RANGE_FOUND_INNER;
}
if (ret < MDB_RANGE_FOUND_SURROUNDING &&
current_address <= address &&
// exclude 0-length match with curaddr==addr
current_address < search_end &&
current_end >= search_end &&
// exclude 0-length match with currend==addr
current_end > address)
{
result = current;
ret = MDB_RANGE_FOUND_SURROUNDING;
}
if (ret > max_precision) {
*ret_node = result;
return ret;
}
}
if (N(current)->left) {
mdb_sub_find_range_merge(root, address, size, max_precision,
N(current)->left, /*inout*/&ret,
/*inout*/&result);
if (ret > max_precision) {
*ret_node = result;
return ret;
}
}
if (N(current)->right && root >= current_root &&
(search_end > current_address || (search_end == current_address && size == 0))) {
mdb_sub_find_range_merge(root, address, size, max_precision,
N(current)->right, /*inout*/&ret,
/*inout*/&result);
if (ret > max_precision) {
*ret_node = result;
return ret;
}
}
*ret_node = result;
return ret;
}
uint64_t mdb_find_range_count;
errval_t
mdb_find_range(mdb_root_t root, genpaddr_t address, gensize_t size,
int max_result, /*out*/ struct cte **ret_node,
/*out*/ int *result)
{
mdb_find_range_count++;
if (max_result < MDB_RANGE_NOT_FOUND ||
max_result > MDB_RANGE_FOUND_PARTIAL)
{
return CAPS_ERR_INVALID_ARGS;
}
if (max_result > MDB_RANGE_NOT_FOUND && !ret_node) {
return CAPS_ERR_INVALID_ARGS;
}
if (!result) {
return CAPS_ERR_INVALID_ARGS;
}
struct cte *alt_ret_node;
if (!ret_node) {
ret_node = &alt_ret_node;
}
*result = mdb_sub_find_range(root, address, size, max_result, mdb_root, ret_node);
return SYS_ERR_OK;
}
uint64_t mdb_find_cap_for_address_count;
errval_t
mdb_find_cap_for_address(genpaddr_t address, struct cte **ret_node)
{
mdb_find_cap_for_address_count++;
int result;
errval_t err;
// query for size 1 to get the smallest cap that includes the byte at the
// given address
err = mdb_find_range(get_type_root(ObjType_RAM), address,
1, MDB_RANGE_FOUND_SURROUNDING, ret_node, &result);
if (err_is_fail(err)) {
return err;
}
if (result != MDB_RANGE_FOUND_SURROUNDING) {
return SYS_ERR_CAP_NOT_FOUND;
}
return SYS_ERR_OK;
}
bool mdb_reachable(struct cte *cte)
{
return mdb_is_reachable(mdb_root, cte);
}
errval_t
mdb_traverse(enum mdb_tree_traversal_order order, mdb_tree_traversal_fn cb, void *data)
{
return mdb_traverse_subtree(mdb_root, order, cb, data);
}
errval_t
mdb_traverse_subtree(struct cte *cte, enum mdb_tree_traversal_order order,
mdb_tree_traversal_fn cb, void *data)
{
struct mdbnode *node = N(cte);
assert(node);
struct cte *first, *second;
if (order == MDB_TRAVERSAL_ORDER_ASCENDING) {
first = node->left;
second = node->right;
} else {
first = node->right;
second = node->left;
}
errval_t err;
if (first) {
err = mdb_traverse_subtree(first, order, cb, data);
if (err_is_fail(err)) {
return err;
}
}
err = cb(cte, data);
if (err_is_fail(err)) {
return err;
}
if (second) {
err = mdb_traverse_subtree(second, order, cb, data);
if (err_is_fail(err)) {
return err;
}
}
return SYS_ERR_OK;
}
static errval_t count_nodes(struct cte *cte, void *data)
{
size_t *counter = data;
*counter = *counter + 1;
return SYS_ERR_OK;
}
errval_t
mdb_size(size_t *count)
{
return mdb_traverse(MDB_TRAVERSAL_ORDER_ASCENDING, count_nodes, count);
}