[fix] mm bugs (#7010)
* [fix] implementation fault on avl * [fix] mm may free varea allocated statically * [test] add test and benchmark for avl
This commit is contained in:
parent
b7c2d1ba42
commit
0de21341f9
|
@ -21,8 +21,11 @@
|
||||||
? rt_container_of(rt_container_of(pnode, struct _aspace_node, node), \
|
? rt_container_of(rt_container_of(pnode, struct _aspace_node, node), \
|
||||||
struct rt_varea, node) \
|
struct rt_varea, node) \
|
||||||
: 0
|
: 0
|
||||||
|
|
||||||
#define ASPACE_VAREA_NEXT(pva) (VAREA_ENTRY(util_avl_next(&pva->node.node)))
|
#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_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
|
typedef struct _aspace_node
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
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)
|
int _init_lock(rt_aspace_t aspace)
|
||||||
{
|
{
|
||||||
MM_PGTBL_LOCK_INIT(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 */
|
/* TODO try to map with huge TLB, when flag & HUGEPAGE */
|
||||||
rt_size_t pgsz = ARCH_PAGE_SIZE;
|
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;
|
vaddr += pgsz;
|
||||||
phyaddr += 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;
|
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)
|
if (mem_obj->hint_free)
|
||||||
{
|
{
|
||||||
|
/* mem object can control mapping range and so by modifing hint */
|
||||||
mem_obj->hint_free(&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)
|
if (err == RT_EOK)
|
||||||
{
|
{
|
||||||
|
/* fill in varea data */
|
||||||
_varea_post_install(varea, aspace, attr, flags, mem_obj, offset);
|
_varea_post_install(varea, aspace, attr, flags, mem_obj, offset);
|
||||||
|
|
||||||
if (MMF_TEST_CNTL(flags, MMF_PREFETCH))
|
if (MMF_TEST_CNTL(flags, MMF_PREFETCH))
|
||||||
{
|
{
|
||||||
|
/* do the MMU & TLB business */
|
||||||
err = _do_prefetch(aspace, varea, varea->start, varea->size);
|
err = _do_prefetch(aspace, varea, varea->start, varea->size);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
/* restore data structure and MMU */
|
||||||
|
_varea_uninstall(varea);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
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,
|
static inline int _not_in_range(void *start, rt_size_t length,
|
||||||
void *limit_start, rt_size_t limit_size)
|
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);
|
LOG_D("%s: [%p : %p] [%p : %p]", __func__, start, length, limit_start, limit_size);
|
||||||
/* assuming (base + length) will not overflow except (0) */
|
/* assuming (base + length) will not overflow except (0) */
|
||||||
return start != RT_NULL
|
return start != RT_NULL
|
||||||
? ((length > (0ul - (uintptr_t)start)) || start < limit_start ||
|
? (_IS_OVERFLOW(start, length) || start < limit_start ||
|
||||||
(length + (rt_size_t)(start - limit_start)) > limit_size)
|
_IS_OVERSIZE(start, length, limit_start, limit_size))
|
||||||
: length > 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)
|
if (varea)
|
||||||
{
|
{
|
||||||
err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
|
err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
|
||||||
|
if (err != RT_EOK)
|
||||||
|
{
|
||||||
|
rt_free(varea);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -374,6 +413,7 @@ int rt_aspace_map_static(rt_aspace_t aspace, rt_varea_t varea, void **addr,
|
||||||
{
|
{
|
||||||
varea->size = length;
|
varea->size = length;
|
||||||
varea->start = *addr;
|
varea->start = *addr;
|
||||||
|
flags |= MMF_STATIC_ALLOC;
|
||||||
err = _mm_aspace_map(aspace, varea, attr, flags, mem_obj, offset);
|
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)
|
if (err != RT_EOK)
|
||||||
{
|
{
|
||||||
_aspace_unmap(aspace, varea->start, varea->size);
|
_varea_uninstall(varea);
|
||||||
rt_free(varea);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -486,7 +525,7 @@ int rt_aspace_map_phy_static(rt_aspace_t aspace, rt_varea_t varea,
|
||||||
{
|
{
|
||||||
varea->start = hint->prefer;
|
varea->start = hint->prefer;
|
||||||
varea->size = hint->map_size;
|
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);
|
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);
|
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};
|
struct _mm_range range = {addr, addr + length - 1};
|
||||||
rt_varea_t varea = _aspace_bst_search_overlap(aspace, range);
|
rt_varea_t varea = _aspace_bst_search_overlap(aspace, range);
|
||||||
|
|
||||||
|
if (varea == RT_NULL)
|
||||||
|
{
|
||||||
|
LOG_I("%s: No such entry found at %p with %lx bytes\n", __func__, addr, length);
|
||||||
|
}
|
||||||
|
|
||||||
while (varea)
|
while (varea)
|
||||||
{
|
{
|
||||||
if (varea->mem_obj && varea->mem_obj->on_varea_close)
|
_varea_uninstall(varea);
|
||||||
varea->mem_obj->on_varea_close(varea);
|
if (!(varea->flag & MMF_STATIC_ALLOC))
|
||||||
|
{
|
||||||
rt_varea_free_pages(varea);
|
rt_free(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);
|
|
||||||
|
|
||||||
rt_free(varea);
|
|
||||||
varea = _aspace_bst_search_overlap(aspace, range);
|
varea = _aspace_bst_search_overlap(aspace, range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ enum mm_flag_cntl
|
||||||
|
|
||||||
MMF_TEXT = _DEF_FLAG(5),
|
MMF_TEXT = _DEF_FLAG(5),
|
||||||
|
|
||||||
|
MMF_STATIC_ALLOC = _DEF_FLAG(6),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief a non-locked memory can be swapped out when required, this is
|
* @brief a non-locked memory can be swapped out when required, this is
|
||||||
* reserved for future
|
* reserved for future
|
||||||
|
|
|
@ -51,9 +51,11 @@ static inline void midmount_right(struct util_avl_struct *axis,
|
||||||
lrchild->avl_left = lchild;
|
lrchild->avl_left = lchild;
|
||||||
lrchild->avl_right = axis;
|
lrchild->avl_right = axis;
|
||||||
|
|
||||||
|
lrchild->height = lchild->height;
|
||||||
lchild->height = lrheight;
|
lchild->height = lrheight;
|
||||||
axis->height = lrheight;
|
axis->height = lrheight;
|
||||||
|
|
||||||
|
lrchild->parent = axis->parent;
|
||||||
lchild->parent = lrchild;
|
lchild->parent = lrchild;
|
||||||
axis->parent = lrchild;
|
axis->parent = lrchild;
|
||||||
if (lchild->avl_right != NULL)
|
if (lchild->avl_right != NULL)
|
||||||
|
@ -70,7 +72,7 @@ static inline void rotate_left(struct util_avl_struct *axis,
|
||||||
size_t rlheight)
|
size_t rlheight)
|
||||||
{
|
{
|
||||||
axis->avl_right = rlchild;
|
axis->avl_right = rlchild;
|
||||||
rchild->avl_right = axis;
|
rchild->avl_left = axis;
|
||||||
|
|
||||||
axis->height = rlheight + 1;
|
axis->height = rlheight + 1;
|
||||||
rchild->height = axis->height + 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_right = rchild;
|
||||||
rlchild->avl_left = axis;
|
rlchild->avl_left = axis;
|
||||||
|
|
||||||
|
rlchild->height = rchild->height;
|
||||||
rchild->height = rlheight;
|
rchild->height = rlheight;
|
||||||
axis->height = rlheight;
|
axis->height = rlheight;
|
||||||
|
|
||||||
|
rlchild->parent = axis->parent;
|
||||||
rchild->parent = rlchild;
|
rchild->parent = rlchild;
|
||||||
axis->parent = rlchild;
|
axis->parent = rlchild;
|
||||||
if (rchild->avl_left != NULL)
|
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 *lchild = axis->avl_left;
|
||||||
struct util_avl_struct *rchild = axis->avl_right;
|
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 lheight = HEIGHT_OF(lchild);
|
||||||
int rheight = HEIGHT_OF(rchild);
|
int rheight = HEIGHT_OF(rchild);
|
||||||
if (rheight + 1 < lheight)
|
if (rheight + 1 < lheight)
|
||||||
|
@ -135,10 +139,12 @@ void util_avl_rebalance(struct util_avl_struct *node,
|
||||||
if (HEIGHT_OF(lchild->avl_left) >= lrheight)
|
if (HEIGHT_OF(lchild->avl_left) >= lrheight)
|
||||||
{
|
{
|
||||||
rotate_right(axis, lchild, lrchild, nodeplace, lrheight);
|
rotate_right(axis, lchild, lrchild, nodeplace, lrheight);
|
||||||
|
axis = lchild->parent;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
midmount_right(axis, lchild, lrchild, nodeplace, lrheight);
|
midmount_right(axis, lchild, lrchild, nodeplace, lrheight);
|
||||||
|
axis = lrchild->parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lheight + 1 < rheight)
|
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)
|
if (HEIGHT_OF(rchild->avl_right) >= rlheight)
|
||||||
{
|
{
|
||||||
rotate_left(axis, rchild, rlchild, nodeplace, rlheight);
|
rotate_left(axis, rchild, rlchild, nodeplace, rlheight);
|
||||||
|
axis = rchild->parent;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
midmount_left(axis, rchild, rlchild, nodeplace, rlheight);
|
midmount_left(axis, rchild, rlchild, nodeplace, rlheight);
|
||||||
|
axis = rlchild->parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int height = lheight < rheight ? rheight : lheight;
|
int height = (lheight < rheight ? rheight : lheight) + 1;
|
||||||
if (height == axis->height)
|
if (height == axis->height)
|
||||||
break;
|
break;
|
||||||
axis->height = height;
|
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)
|
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;
|
*nodeplace = rchild;
|
||||||
rchild->avl_left = node->avl_left;
|
rchild->avl_left = node->avl_left;
|
||||||
if (rchild->avl_left != NULL)
|
if (rchild->avl_left != NULL)
|
||||||
rchild->avl_left->parent = node->parent;
|
rchild->avl_left->parent = rchild;
|
||||||
rchild->parent = node->parent;
|
rchild->parent = node->parent;
|
||||||
node = rchild;
|
util_avl_rebalance(rchild, root);
|
||||||
|
node = rchild->parent;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -221,7 +231,8 @@ void util_avl_remove(struct util_avl_struct *node, struct util_avl_root *root)
|
||||||
if (sparent->avl_left != NULL)
|
if (sparent->avl_left != NULL)
|
||||||
sparent->avl_left->parent = sparent;
|
sparent->avl_left->parent = sparent;
|
||||||
successor->parent = node->parent;
|
successor->parent = node->parent;
|
||||||
node = sparent;
|
util_avl_rebalance(sparent, root);
|
||||||
|
node = successor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
|
/* other types of return value are taken as programming error */
|
||||||
|
RT_ASSERT(ret == MMU_MAP_ERROR_NOPAGE);
|
||||||
/* error, undo map */
|
/* error, undo map */
|
||||||
while (unmap_va != v_addr)
|
while (unmap_va != v_addr)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue