1386 lines
38 KiB
C
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);
|
|
}
|