Răsfoiți Sursa

[fix] mm bugs (#7010)

* [fix] implementation fault on avl

* [fix] mm may free varea allocated statically

* [test] add test and benchmark for avl
Shell 2 ani în urmă
părinte
comite
0de21341f9

+ 3 - 0
components/mm/avl_adpt.h

@@ -21,8 +21,11 @@
         ? rt_container_of(rt_container_of(pnode, struct _aspace_node, node),   \
                           struct rt_varea, node)                               \
         : 0
+
 #define ASPACE_VAREA_NEXT(pva) (VAREA_ENTRY(util_avl_next(&pva->node.node)))
 #define ASPACE_VAREA_FIRST(aspace) (VAREA_ENTRY(util_avl_first(&aspace->tree.tree)))
+#define ASPACE_VAREA_LAST(aspace) (VAREA_ENTRY(util_avl_last(&aspace->tree.tree)))
+#define ASPACE_VAREA_PREV(pva) (VAREA_ENTRY(util_avl_prev(&pva->node.node)))
 
 typedef struct _aspace_node
 {

+ 57 - 20
components/mm/mm_aspace.c

@@ -65,6 +65,24 @@ static inline void _varea_post_install(rt_varea_t varea, rt_aspace_t aspace,
         varea->mem_obj->on_varea_open(varea);
 }
 
+/* restore context modified by varea install */
+static inline void _varea_uninstall(rt_varea_t varea)
+{
+    rt_aspace_t aspace = varea->aspace;
+
+    if (varea->mem_obj && varea->mem_obj->on_varea_close)
+        varea->mem_obj->on_varea_close(varea);
+
+    rt_varea_free_pages(varea);
+
+    WR_LOCK(aspace);
+    _aspace_bst_remove(aspace, varea);
+    WR_UNLOCK(aspace);
+
+    rt_hw_mmu_unmap(aspace, varea->start, varea->size);
+    rt_hw_tlb_invalidate_range(aspace, varea->start, varea->size, ARCH_PAGE_SIZE);
+}
+
 int _init_lock(rt_aspace_t aspace)
 {
     MM_PGTBL_LOCK_INIT(aspace);
@@ -144,12 +162,18 @@ static int _do_named_map(rt_aspace_t aspace, void *vaddr, rt_size_t length,
     {
         /* TODO try to map with huge TLB, when flag & HUGEPAGE */
         rt_size_t pgsz = ARCH_PAGE_SIZE;
-        rt_hw_mmu_map(aspace, vaddr, phyaddr, pgsz, attr);
+        void *ret = rt_hw_mmu_map(aspace, vaddr, phyaddr, pgsz, attr);
+        if (ret == RT_NULL)
+        {
+            err = -RT_ERROR;
+            break;
+        }
         vaddr += pgsz;
         phyaddr += pgsz;
     }
 
-    rt_hw_tlb_invalidate_range(aspace, vaddr, length, ARCH_PAGE_SIZE);
+    if (err == RT_EOK)
+        rt_hw_tlb_invalidate_range(aspace, end - length, length, ARCH_PAGE_SIZE);
 
     return err;
 }
@@ -265,6 +289,7 @@ static int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t varea, rt_size_t attr,
 
     if (mem_obj->hint_free)
     {
+        /* mem object can control mapping range and so by modifing hint */
         mem_obj->hint_free(&hint);
     }
 
@@ -273,17 +298,27 @@ static int _mm_aspace_map(rt_aspace_t aspace, rt_varea_t varea, rt_size_t attr,
 
     if (err == RT_EOK)
     {
+        /* fill in varea data */
         _varea_post_install(varea, aspace, attr, flags, mem_obj, offset);
 
         if (MMF_TEST_CNTL(flags, MMF_PREFETCH))
         {
+            /* do the MMU & TLB business */
             err = _do_prefetch(aspace, varea, varea->start, varea->size);
+            if (err)
+            {
+                /* restore data structure and MMU */
+                _varea_uninstall(varea);
+            }
         }
     }
 
     return err;
 }
 
+#define _IS_OVERFLOW(start, length) ((length) > (0ul - (uintptr_t)(start)))
+#define _IS_OVERSIZE(start, length, limit_s, limit_sz) (((length) + (rt_size_t)((start) - (limit_start))) > (limit_size))
+
 static inline int _not_in_range(void *start, rt_size_t length,
                                 void *limit_start, rt_size_t limit_size)
 {
@@ -291,8 +326,8 @@ static inline int _not_in_range(void *start, rt_size_t length,
         LOG_D("%s: [%p : %p] [%p : %p]", __func__, start, length, limit_start, limit_size);
     /* assuming (base + length) will not overflow except (0) */
     return start != RT_NULL
-               ? ((length > (0ul - (uintptr_t)start)) || start < limit_start ||
-                  (length + (rt_size_t)(start - limit_start)) > limit_size)
+               ? (_IS_OVERFLOW(start, length) || start < limit_start ||
+                  _IS_OVERSIZE(start, length, limit_start, limit_size))
                : length > limit_size;
 }
 
@@ -334,6 +369,10 @@ int rt_aspace_map(rt_aspace_t aspace, void **addr, rt_size_t length,
         if (varea)
         {
             err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
+            if (err != RT_EOK)
+            {
+                rt_free(varea);
+            }
         }
         else
         {
@@ -374,6 +413,7 @@ int rt_aspace_map_static(rt_aspace_t aspace, rt_varea_t varea, void **addr,
     {
         varea->size = length;
         varea->start = *addr;
+        flags |= MMF_STATIC_ALLOC;
         err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
     }
 
@@ -430,8 +470,7 @@ int _mm_aspace_map_phy(rt_aspace_t aspace, rt_varea_t varea,
 
             if (err != RT_EOK)
             {
-                _aspace_unmap(aspace, varea->start, varea->size);
-                rt_free(varea);
+                _varea_uninstall(varea);
             }
         }
     }
@@ -486,7 +525,7 @@ int rt_aspace_map_phy_static(rt_aspace_t aspace, rt_varea_t varea,
     {
         varea->start = hint->prefer;
         varea->size = hint->map_size;
-        hint->flags |= MMF_MAP_FIXED;
+        hint->flags |= (MMF_MAP_FIXED | MMF_STATIC_ALLOC);
         LOG_D("%s: start %p size %p phy at %p", __func__, varea->start, varea->size, pa_off << MM_PAGE_SHIFT);
         err = _mm_aspace_map_phy(aspace, varea, hint, attr, pa_off, ret_va);
     }
@@ -502,21 +541,19 @@ void _aspace_unmap(rt_aspace_t aspace, void *addr, rt_size_t length)
 {
     struct _mm_range range = {addr, addr + length - 1};
     rt_varea_t varea = _aspace_bst_search_overlap(aspace, range);
-    while (varea)
-    {
-        if (varea->mem_obj && varea->mem_obj->on_varea_close)
-            varea->mem_obj->on_varea_close(varea);
-
-        rt_varea_free_pages(varea);
-
-        WR_LOCK(aspace);
-        _aspace_bst_remove(aspace, varea);
-        WR_UNLOCK(aspace);
 
-        rt_hw_mmu_unmap(aspace, varea->start, varea->size);
-        rt_hw_tlb_invalidate_range(aspace, varea->start, varea->size, ARCH_PAGE_SIZE);
+    if (varea == RT_NULL)
+    {
+        LOG_I("%s: No such entry found at %p with %lx bytes\n", __func__, addr, length);
+    }
 
-        rt_free(varea);
+    while (varea)
+    {
+        _varea_uninstall(varea);
+        if (!(varea->flag & MMF_STATIC_ALLOC))
+        {
+            rt_free(varea);
+        }
         varea = _aspace_bst_search_overlap(aspace, range);
     }
 }

+ 2 - 0
components/mm/mm_flag.h

@@ -51,6 +51,8 @@ enum mm_flag_cntl
 
     MMF_TEXT = _DEF_FLAG(5),
 
+    MMF_STATIC_ALLOC = _DEF_FLAG(6),
+
     /**
      * @brief a non-locked memory can be swapped out when required, this is
      * reserved for future

+ 18 - 7
components/utilities/libadt/avl.c

@@ -51,9 +51,11 @@ static inline void midmount_right(struct util_avl_struct *axis,
     lrchild->avl_left = lchild;
     lrchild->avl_right = axis;
 
+    lrchild->height = lchild->height;
     lchild->height = lrheight;
     axis->height = lrheight;
 
+    lrchild->parent = axis->parent;
     lchild->parent = lrchild;
     axis->parent = lrchild;
     if (lchild->avl_right != NULL)
@@ -70,7 +72,7 @@ static inline void rotate_left(struct util_avl_struct *axis,
                                size_t rlheight)
 {
     axis->avl_right = rlchild;
-    rchild->avl_right = axis;
+    rchild->avl_left = axis;
 
     axis->height = rlheight + 1;
     rchild->height = axis->height + 1;
@@ -94,9 +96,11 @@ static inline void midmount_left(struct util_avl_struct *axis,
     rlchild->avl_right = rchild;
     rlchild->avl_left = axis;
 
+    rlchild->height = rchild->height;
     rchild->height = rlheight;
     axis->height = rlheight;
 
+    rlchild->parent = axis->parent;
     rchild->parent = rlchild;
     axis->parent = rlchild;
     if (rchild->avl_left != NULL)
@@ -125,7 +129,7 @@ void util_avl_rebalance(struct util_avl_struct *node,
     {
         struct util_avl_struct *lchild = axis->avl_left;
         struct util_avl_struct *rchild = axis->avl_right;
-        nodeplace = node->parent ? NODE_PLACE(axis) : &root->root_node;
+        nodeplace = axis->parent ? NODE_PLACE(axis) : &root->root_node;
         int lheight = HEIGHT_OF(lchild);
         int rheight = HEIGHT_OF(rchild);
         if (rheight + 1 < lheight)
@@ -135,10 +139,12 @@ void util_avl_rebalance(struct util_avl_struct *node,
             if (HEIGHT_OF(lchild->avl_left) >= lrheight)
             {
                 rotate_right(axis, lchild, lrchild, nodeplace, lrheight);
+                axis = lchild->parent;
             }
             else
             {
                 midmount_right(axis, lchild, lrchild, nodeplace, lrheight);
+                axis = lrchild->parent;
             }
         }
         else if (lheight + 1 < rheight)
@@ -148,20 +154,23 @@ void util_avl_rebalance(struct util_avl_struct *node,
             if (HEIGHT_OF(rchild->avl_right) >= rlheight)
             {
                 rotate_left(axis, rchild, rlchild, nodeplace, rlheight);
+                axis = rchild->parent;
             }
             else
             {
                 midmount_left(axis, rchild, rlchild, nodeplace, rlheight);
+                axis = rlchild->parent;
             }
         }
         else
         {
-            int height = lheight < rheight ? rheight : lheight;
+            int height = (lheight < rheight ? rheight : lheight) + 1;
             if (height == axis->height)
                 break;
             axis->height = height;
+            axis = axis->parent;
         }
-    } while (nodeplace != &root->root_node);
+    } while (axis);
 }
 
 void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root)
@@ -196,9 +205,10 @@ void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root)
             *nodeplace = rchild;
             rchild->avl_left = node->avl_left;
             if (rchild->avl_left != NULL)
-                rchild->avl_left->parent = node->parent;
+                rchild->avl_left->parent = rchild;
             rchild->parent = node->parent;
-            node = rchild;
+            util_avl_rebalance(rchild, root);
+            node = rchild->parent;
         }
         else
         {
@@ -221,7 +231,8 @@ void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root)
             if (sparent->avl_left != NULL)
                 sparent->avl_left->parent = sparent;
             successor->parent = node->parent;
-            node = sparent;
+            util_avl_rebalance(sparent, root);
+            node = successor;
         }
     }
 

+ 585 - 0
examples/test/avl.c

@@ -0,0 +1,585 @@
+
+/**
+ * Here is the assertions to ensure rightness of bst maintenance
+ * After each insertion and delete, a tree must still be binary search tree,
+ * and still remain balanced
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <mm_aspace.h>
+#include <mm_private.h>
+
+#define BUF_SIZE 1000000
+static void *_start;
+static void *_boundary;
+static int _count;
+static rt_varea_t _buf[BUF_SIZE];
+#define RT_ASSERT assert
+
+static void _print_varea(rt_varea_t varea, int depth)
+{
+    if (depth == 0)
+    {
+        printf("%p ", varea->start);
+    }
+    else
+    {
+        rt_varea_t lchild = VAREA_ENTRY(varea->node.node.avl_left);
+        rt_varea_t rchild = VAREA_ENTRY(varea->node.node.avl_right);
+        depth--;
+        if (lchild)
+            _print_varea(lchild, depth);
+        else
+            printf("0x**** ");
+
+        if (rchild)
+            _print_varea(rchild, depth);
+        else
+            printf("0x**** ");
+    }
+}
+static void _print_tree(rt_aspace_t aspace)
+{
+    rt_varea_t varea = VAREA_ENTRY(aspace->tree.tree.root_node);
+    if (!varea)
+        return ;
+
+    for (size_t i = 0; i < aspace->tree.tree.root_node->height; i++) {
+        _print_varea(varea, i);
+        putchar('\n');
+    }
+
+    return ;
+}
+
+static int _is_bst(rt_varea_t varea)
+{
+    rt_varea_t lchild = VAREA_ENTRY(varea->node.node.avl_left);
+    rt_varea_t rchild = VAREA_ENTRY(varea->node.node.avl_right);
+    if (lchild)
+    {
+        RT_ASSERT(lchild->node.node.parent == &varea->node.node);
+        RT_ASSERT(varea->start > lchild->start);
+    }
+    if (rchild)
+    {
+        RT_ASSERT(rchild->node.node.parent == &varea->node.node);
+        if (varea->start >= rchild->start)
+        {
+            RT_ASSERT(0);
+        }
+    }
+    return 1;
+}
+
+/* return height of current varea */
+static int _is_balanced(rt_varea_t varea)
+{
+    if (!varea)
+    {
+        return 1;
+    }
+
+    rt_varea_t lchild = VAREA_ENTRY(varea->node.node.avl_left);
+    rt_varea_t rchild = VAREA_ENTRY(varea->node.node.avl_right);
+    int lbal = _is_balanced(lchild);
+    int rbal = _is_balanced(rchild);
+
+    if (lbal && rbal)
+    {
+        int diff = lbal - rbal;
+        if (diff > 1 || diff < -1)
+        {
+            printf("lbal %d, rbal %d\n", lbal, rbal);
+            return 0;
+        }
+        else
+        {
+            int height = lbal > rbal ? lbal : rbal;
+            return height + 1;
+        }
+    }
+}
+
+/* add bst assertion */
+static int _check_asc_before(rt_varea_t varea, void *arg)
+{
+    if (varea->start >= _start && (!_boundary || varea->start >= _boundary) && _is_bst(varea))
+    {
+        _buf[_count] = varea;
+        _start = varea->start;
+        _boundary = varea->start + varea->size;
+        _count++;
+        RT_ASSERT(_count < BUF_SIZE);
+    }
+    else
+    {
+        RT_ASSERT(0);
+    }
+    return 0;
+}
+
+static int _check_asc_before_rev(rt_varea_t varea, void *arg)
+{
+    _count--;
+    RT_ASSERT(varea == _buf[_count]);
+    return 0;
+}
+
+static int _check_asc_after(rt_varea_t varea, void *arg)
+{
+    rt_varea_t add_elem = (rt_varea_t)arg;
+    if (!_is_bst(varea))
+    {
+        RT_ASSERT(0);
+    }
+
+    if (varea == _buf[_count])
+    {
+        _buf[_count] = 0;
+        _count++;
+        RT_ASSERT(_count < BUF_SIZE);
+    }
+    else if (add_elem && add_elem == varea)
+    {
+        /* adding, skip adding elem */
+    }
+    else if (!add_elem && varea == _buf[_count + 1])
+    {
+        /* deleting */
+        _buf[_count] = 0;
+        _buf[_count] = 0;
+        _count++;
+        RT_ASSERT(_count < BUF_SIZE);
+    }
+    else
+    {
+        printf("add_elem %p, varea %p, _count %d, in buf %p\n",
+            add_elem->start, varea->start, _count, _buf[_count]);
+        RT_ASSERT(0);
+    }
+    return 0;
+}
+
+static int _aspace_traversal(rt_aspace_t aspace, int (*fn)(rt_varea_t varea, void *arg), void *arg)
+{
+    rt_varea_t varea = ASPACE_VAREA_FIRST(aspace);
+    while (varea)
+    {
+        fn(varea, arg);
+        varea = ASPACE_VAREA_NEXT(varea);
+    }
+
+    return 0;
+}
+
+static int _aspace_traversal_reverse(rt_aspace_t aspace, int (*fn)(rt_varea_t varea, void *arg), void *arg)
+{
+    rt_varea_t varea = ASPACE_VAREA_LAST(aspace);
+    while (varea)
+    {
+        fn(varea, arg);
+        varea = ASPACE_VAREA_PREV(varea);
+    }
+
+    return 0;
+}
+
+static int _check_bst_before(struct rt_aspace *aspace, struct rt_varea *varea)
+{
+    rt_varea_t root = VAREA_ENTRY(aspace->tree.tree.root_node);
+    int height = _is_balanced(root);
+
+    if (root)
+        RT_ASSERT(height);
+
+    memset(_buf, 0, sizeof(_buf)); // clear first avoiding none tree error
+    _start = 0;
+    _boundary = 0;
+    _count = 0;
+
+    _aspace_traversal(aspace, _check_asc_before, varea);
+    int saved = _count;
+    _aspace_traversal_reverse(aspace, _check_asc_before_rev, varea);
+    _count = saved;
+
+    return 1;
+}
+
+static int _check_bst_after(struct rt_aspace *aspace, struct rt_varea *varea, int isdel)
+{
+    rt_varea_t root = VAREA_ENTRY(aspace->tree.tree.root_node);
+    int height = _is_balanced(root);
+
+    if (root)
+        RT_ASSERT(height);
+
+    int prev_count = _count;
+    _start = 0;
+    _boundary = 0;
+    _count = 0;
+    _aspace_traversal(aspace, _check_asc_after, isdel ? NULL : varea);
+    _count = isdel ? _count : _count + 1;
+
+    if (isdel)
+    {
+        RT_ASSERT(prev_count - 1 == _count);
+    }
+    else
+    {
+        RT_ASSERT(prev_count + 1 == _count);
+    }
+
+    return 1;
+}
+
+/* test library */
+#define RANDOM(n) (xrand() % (n))
+static unsigned int xseed = 0x11223344;
+
+static inline unsigned int xrand(void)
+{
+    return (((xseed = xseed * 214013L + 2531011L) >> 16) & 0x7fffffff);
+}
+
+// generate keys
+static inline void init_random_keys(int *keys, int count, int seed)
+{
+    int save_seed = time(NULL);
+    int *array = (int*)malloc(sizeof(int) * count);
+    int length = count, i;
+    xseed = seed;
+    for (i = 0; i < count; i++) {
+        array[i] = i;
+    }
+    for (i = 0; i < length; i++) {
+        int pos = xrand() % count;
+        int key = array[pos];
+        keys[i] = key;
+        array[pos] = array[--count];
+    }
+    free(array);
+    xseed = save_seed;
+}
+
+// A utility function to swap to integers
+static inline void swap (int *a, int *b)
+{
+    int temp = *a;
+    *a = *b;
+    *b = temp;
+}
+
+// A function to generate a random permutation of arr[]
+static void randomize ( int arr[], int n )
+{
+    // Use a different seed value so that we don't get same
+    // result each time we run this program
+    srand ( time(NULL) );
+
+    // Start from the last element and swap one by one. We don't
+    // need to run for the first element that's why i > 0
+    for (int i = n-1; i > 0; i--)
+    {
+        // Pick a random index from 0 to i
+        int j = rand() % (i+1);
+
+        // Swap arr[i] with the element at random index
+        swap(&arr[i], &arr[j]);
+    }
+}
+
+/* time */
+#include <time.h>
+
+static int gettime(void)
+{
+    struct timespec ts;
+    clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+    time_t seconds = ts.tv_sec;
+    int millisecond = ts.tv_nsec / 1000000;
+    return millisecond + seconds * 1000;
+}
+
+/* Adapt Layer */
+
+/**
+ * @brief Adapter Layer for lwp AVL BST
+ */
+
+int _aspace_bst_init(struct rt_aspace *aspace)
+{
+    aspace->tree.tree.root_node = AVL_ROOT;
+    return 0;
+}
+
+static int compare_overlap(void *as, void *ae, void *bs, void *be)
+{
+    LOG_D("as %lx, ae %lx, bs %lx, be %lx", as, ae, bs, be);
+    int cmp;
+    if (as > be)
+    {
+        cmp = 1;
+    }
+    else if (ae < bs)
+    {
+        cmp = -1;
+    }
+    else
+    {
+        cmp = 0;
+    }
+    LOG_D("ret %d", cmp);
+    return cmp;
+}
+
+static int compare_exceed(void *as, void *ae, void *bs, void *be)
+{
+    LOG_D("as %lx, ae %lx, bs %lx, be %lx", as, ae, bs, be);
+    int cmp;
+    if (as > bs)
+    {
+        cmp = 1;
+    }
+    else if (as < bs)
+    {
+        cmp = -1;
+    }
+    else
+    {
+        cmp = 0;
+    }
+    LOG_D("ret %d", cmp);
+    return cmp;
+}
+
+static struct rt_varea *search(struct util_avl_root *root,
+                               struct _mm_range range,
+                               int (*compare)(void *as, void *ae, void *bs,
+                                              void *be))
+{
+    struct util_avl_struct *node = root->root_node;
+    while (node)
+    {
+        rt_varea_t varea = VAREA_ENTRY(node);
+        int cmp = compare(range.start, range.end, varea->start,
+                          varea->start + varea->size - 1);
+
+        if (cmp < 0)
+        {
+            node = node->avl_left;
+        }
+        else if (cmp > 0)
+        {
+            node = node->avl_right;
+        }
+        else
+        {
+            return varea;
+        }
+    }
+    return NULL;
+}
+
+struct rt_varea *_aspace_bst_search(struct rt_aspace *aspace, void *key)
+{
+    struct util_avl_root *root = &aspace->tree.tree;
+    struct _mm_range range = {key, key};
+    return search(root, range, compare_overlap);
+}
+
+rt_varea_t _aspace_bst_search_exceed(struct rt_aspace *aspace, void *start)
+{
+    struct util_avl_root *root = &aspace->tree.tree;
+    struct util_avl_struct *node = root->root_node;
+    rt_varea_t closest = NULL;
+    ptrdiff_t min_off = PTRDIFF_MAX;
+    while (node)
+    {
+        rt_varea_t varea = VAREA_ENTRY(node);
+        void *va_s = varea->start;
+        int cmp = compare_exceed(start, start, va_s, va_s);
+
+        if (cmp < 0)
+        {
+            ptrdiff_t off = va_s - start;
+            if (off < min_off)
+            {
+                min_off = off;
+                closest = varea;
+            }
+            node = node->avl_left;
+        }
+        else if (cmp > 0)
+        {
+            node = node->avl_right;
+        }
+        else
+        {
+            return varea;
+        }
+    }
+    return closest;
+}
+
+struct rt_varea *_aspace_bst_search_overlap(struct rt_aspace *aspace,
+                                            struct _mm_range range)
+{
+    struct util_avl_root *root = &aspace->tree.tree;
+    return search(root, range, compare_overlap);
+}
+
+#ifdef ENABLE_DEBUG
+#include "bst_assert.h"
+#else
+#define _check_bst_before(x, ...)
+#define _check_bst_after(x, ...)
+#endif
+
+void _aspace_bst_insert(struct rt_aspace *aspace, struct rt_varea *varea)
+{
+    struct util_avl_root *root = &aspace->tree.tree;
+    struct util_avl_struct *current = NULL;
+    struct util_avl_struct **next = &(root->root_node);
+    rt_ubase_t key = (rt_ubase_t)varea->start;
+
+    /* Figure out where to put new node */
+    while (*next)
+    {
+        current = *next;
+        struct rt_varea *data = VAREA_ENTRY(current);
+
+        if (key < (rt_ubase_t)data->start)
+            next = &(current->avl_left);
+        else if (key > (rt_ubase_t)data->start)
+            next = &(current->avl_right);
+        else
+            return;
+    }
+
+    /* Add new node and rebalance tree. */
+    _check_bst_before(aspace, varea);
+    util_avl_link(&varea->node.node, current, next);
+    util_avl_rebalance(current, root);
+    _check_bst_after(aspace, varea, 0);
+    return;
+}
+
+void _aspace_bst_remove(struct rt_aspace *aspace, struct rt_varea *varea)
+{
+    struct util_avl_struct *node = &varea->node.node;
+    _check_bst_before(aspace, varea);
+    util_avl_remove(node, &aspace->tree.tree);
+    _check_bst_after(aspace, varea, 1);
+}
+
+struct rt_aspace aspace;
+
+/**
+ * @brief Simulate environment of varea and BSTs
+ */
+
+/* test data set */
+int *dataset;
+int loop_count;
+
+/* preallocate varea to decrease influence by malloc routine */
+struct rt_varea *_varea_buf;
+
+#define STOPWATCH(fun, time) do {   \
+    unsigned int _time;             \
+    _time = gettime();              \
+    fun();                          \
+    _time = gettime()-_time;        \
+    time = _time;                   \
+    } while (0);
+
+static void init_test(void)
+{
+    _aspace_bst_init(&aspace);
+
+    dataset = malloc(loop_count * sizeof(*dataset));
+    assert(dataset);
+
+    _varea_buf = malloc(loop_count * sizeof(*_varea_buf));
+    assert(_varea_buf);
+
+    init_random_keys(dataset, loop_count, 0xabcdabcd);
+}
+
+static void insert_test(void)
+{
+    for (size_t i = 0; i < loop_count; i++)
+    {
+        struct rt_varea *varea;
+        varea = &_varea_buf[i];
+        varea->start = (void *)(uintptr_t)dataset[i];
+        varea->size = 1;
+        _aspace_bst_insert(&aspace, varea);
+    }
+}
+
+static void search_test(void)
+{
+    for (size_t i = 0; i < loop_count; i++)
+    {
+        void *start = (void *)(uintptr_t)dataset[i];
+        struct rt_varea *varea;
+        varea = _aspace_bst_search(&aspace, start);
+        assert(varea);
+        assert(varea->start == start);
+    }
+}
+
+static void delete_test(void)
+{
+    for (size_t i = 0; i < loop_count; i++)
+    {
+        void *start = (void *)(uintptr_t)dataset[i];
+        struct rt_varea *varea;
+        varea = _aspace_bst_search(&aspace, start);
+        _aspace_bst_remove(&aspace, varea);
+    }
+}
+
+static void cleanup(void)
+{
+    free(dataset);
+
+    free(_varea_buf);
+}
+
+int main(int argc, char *argv[])
+{
+    if (argc == 2)
+    {
+        sscanf(argv[1], "%d", &loop_count);
+    }
+    else
+    {
+        loop_count = 1000;
+    }
+
+    puts("Benchmark");
+    printf("looping times: %d\n", loop_count);
+
+    init_test();
+    int endurance;
+    STOPWATCH(insert_test, endurance);
+    printf("Insertion: %d ms\n", endurance);
+
+    randomize(dataset, loop_count);
+    STOPWATCH(search_test, endurance);
+    printf("Search: %d ms\n", endurance);
+
+    randomize(dataset, loop_count);
+    STOPWATCH(delete_test, endurance);
+    printf("Delete: %d ms\n", endurance);
+
+    cleanup();
+    puts("Benchmark exit");
+    return 0;
+}

+ 2 - 0
libcpu/aarch64/common/mmu.c

@@ -205,6 +205,8 @@ void *rt_hw_mmu_map(rt_aspace_t aspace, void *v_addr, void *p_addr, size_t size,
 
         if (ret != 0)
         {
+            /* other types of return value are taken as programming error */
+            RT_ASSERT(ret == MMU_MAP_ERROR_NOPAGE);
             /* error, undo map */
             while (unmap_va != v_addr)
             {