mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-tcg-20221220' of https://gitlab.com/rth7680/qemu into staging
Use interval trees for user-only vma mappings. Assorted cleanups to page locking. # gpg: Signature made Wed 21 Dec 2022 05:00:30 GMT # gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F # gpg: issuer "richard.henderson@linaro.org" # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full] # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * tag 'pull-tcg-20221220' of https://gitlab.com/rth7680/qemu: accel/tcg: Restrict page_collection structure to system TB maintainance accel/tcg: Factor tb_invalidate_phys_range_fast() out accel/tcg: Rename tb_invalidate_phys_page_fast{,__locked}() accel/tcg: Remove trace events from trace-root.h accel/tcg: Restrict cpu_io_recompile() to system emulation accel/tcg: Move remainder of page locking to tb-maint.c accel/tcg: Move PageDesc tree into tb-maint.c for system accel/tcg: Use interval tree for user-only page tracking accel/tcg: Move page_{get,set}_flags to user-exec.c accel/tcg: Drop PAGE_RESERVED for CONFIG_BSD accel/tcg: Use interval tree for TARGET_PAGE_DATA_SIZE accel/tcg: Use interval tree for TBs in user-only mode accel/tcg: Rename page_flush_tb util: Add interval-tree.c Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+2
-5
@@ -33,7 +33,7 @@
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/atomic128.h"
|
||||
#include "exec/translate-all.h"
|
||||
#include "trace/trace-root.h"
|
||||
#include "trace.h"
|
||||
#include "tb-hash.h"
|
||||
#include "internal.h"
|
||||
#ifdef CONFIG_PLUGIN
|
||||
@@ -1508,10 +1508,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size,
|
||||
trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size);
|
||||
|
||||
if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) {
|
||||
struct page_collection *pages
|
||||
= page_collection_lock(ram_addr, ram_addr + size);
|
||||
tb_invalidate_phys_page_fast(pages, ram_addr, size, retaddr);
|
||||
page_collection_unlock(pages);
|
||||
tb_invalidate_phys_range_fast(ram_addr, size, retaddr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+14
-69
@@ -23,83 +23,28 @@
|
||||
#define assert_memory_lock() tcg_debug_assert(have_mmap_lock())
|
||||
#endif
|
||||
|
||||
typedef struct PageDesc {
|
||||
/* list of TBs intersecting this ram page */
|
||||
uintptr_t first_tb;
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
unsigned long flags;
|
||||
void *target_data;
|
||||
#endif
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
QemuSpin lock;
|
||||
#endif
|
||||
} PageDesc;
|
||||
|
||||
/* Size of the L2 (and L3, etc) page tables. */
|
||||
#define V_L2_BITS 10
|
||||
#define V_L2_SIZE (1 << V_L2_BITS)
|
||||
|
||||
/*
|
||||
* L1 Mapping properties
|
||||
*/
|
||||
extern int v_l1_size;
|
||||
extern int v_l1_shift;
|
||||
extern int v_l2_levels;
|
||||
|
||||
/*
|
||||
* The bottom level has pointers to PageDesc, and is indexed by
|
||||
* anything from 4 to (V_L2_BITS + 3) bits, depending on target page size.
|
||||
*/
|
||||
#define V_L1_MIN_BITS 4
|
||||
#define V_L1_MAX_BITS (V_L2_BITS + 3)
|
||||
#define V_L1_MAX_SIZE (1 << V_L1_MAX_BITS)
|
||||
|
||||
extern void *l1_map[V_L1_MAX_SIZE];
|
||||
|
||||
PageDesc *page_find_alloc(tb_page_addr_t index, bool alloc);
|
||||
|
||||
static inline PageDesc *page_find(tb_page_addr_t index)
|
||||
{
|
||||
return page_find_alloc(index, false);
|
||||
}
|
||||
|
||||
/* list iterators for lists of tagged pointers in TranslationBlock */
|
||||
#define TB_FOR_EACH_TAGGED(head, tb, n, field) \
|
||||
for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \
|
||||
tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \
|
||||
tb = (TranslationBlock *)((uintptr_t)tb & ~1))
|
||||
|
||||
#define PAGE_FOR_EACH_TB(pagedesc, tb, n) \
|
||||
TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next)
|
||||
|
||||
#define TB_FOR_EACH_JMP(head_tb, tb, n) \
|
||||
TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next)
|
||||
|
||||
/* In user-mode page locks aren't used; mmap_lock is enough */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#define assert_page_locked(pd) tcg_debug_assert(have_mmap_lock())
|
||||
static inline void page_lock(PageDesc *pd) { }
|
||||
static inline void page_unlock(PageDesc *pd) { }
|
||||
#else
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
void do_assert_page_locked(const PageDesc *pd, const char *file, int line);
|
||||
#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__)
|
||||
#else
|
||||
#define assert_page_locked(pd)
|
||||
#endif
|
||||
void page_lock(PageDesc *pd);
|
||||
void page_unlock(PageDesc *pd);
|
||||
#endif
|
||||
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG)
|
||||
#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG)
|
||||
void assert_no_pages_locked(void);
|
||||
#else
|
||||
static inline void assert_no_pages_locked(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
static inline void page_table_config_init(void) { }
|
||||
#else
|
||||
void page_table_config_init(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
void tb_invalidate_phys_range_fast(ram_addr_t ram_addr,
|
||||
unsigned size,
|
||||
uintptr_t retaddr);
|
||||
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
#endif /* CONFIG_SOFTMMU */
|
||||
|
||||
TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc,
|
||||
target_ulong cs_base, uint32_t flags,
|
||||
int cflags);
|
||||
G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
void page_init(void);
|
||||
void tb_htable_init(void);
|
||||
void tb_reset_jump(TranslationBlock *tb, int n);
|
||||
|
||||
+755
-239
File diff suppressed because it is too large
Load Diff
@@ -6,5 +6,9 @@ exec_tb(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
exec_tb_nocache(void *tb, uintptr_t pc) "tb:%p pc=0x%"PRIxPTR
|
||||
exec_tb_exit(void *last_tb, unsigned int flags) "tb:%p flags=0x%x"
|
||||
|
||||
# cputlb.c
|
||||
memory_notdirty_write_access(uint64_t vaddr, uint64_t ram_addr, unsigned size) "0x%" PRIx64 " ram_addr 0x%" PRIx64 " size %u"
|
||||
memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64
|
||||
|
||||
# translate-all.c
|
||||
translate_block(void *tb, uintptr_t pc, const void *tb_code) "tb:%p, pc:0x%"PRIxPTR", tb_code:%p"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+633
-25
File diff suppressed because it is too large
Load Diff
+40
-3
@@ -24,6 +24,7 @@
|
||||
#ifdef CONFIG_TCG
|
||||
#include "exec/cpu_ldst.h"
|
||||
#endif
|
||||
#include "qemu/interval-tree.h"
|
||||
|
||||
/* allow to see translation results - the slowdown should be negligible, so we leave it */
|
||||
#define DEBUG_DISAS
|
||||
@@ -559,11 +560,20 @@ struct TranslationBlock {
|
||||
|
||||
struct tb_tc tc;
|
||||
|
||||
/* first and second physical page containing code. The lower bit
|
||||
of the pointer tells the index in page_next[].
|
||||
The list is protected by the TB's page('s) lock(s) */
|
||||
/*
|
||||
* Track tb_page_addr_t intervals that intersect this TB.
|
||||
* For user-only, the virtual addresses are always contiguous,
|
||||
* and we use a unified interval tree. For system, we use a
|
||||
* linked list headed in each PageDesc. Within the list, the lsb
|
||||
* of the previous pointer tells the index of page_next[], and the
|
||||
* list is protected by the PageDesc lock(s).
|
||||
*/
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
IntervalTreeNode itree;
|
||||
#else
|
||||
uintptr_t page_next[2];
|
||||
tb_page_addr_t page_addr[2];
|
||||
#endif
|
||||
|
||||
/* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */
|
||||
QemuSpin jmp_lock;
|
||||
@@ -619,24 +629,51 @@ static inline uint32_t tb_cflags(const TranslationBlock *tb)
|
||||
|
||||
static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return tb->itree.start;
|
||||
#else
|
||||
return tb->page_addr[0];
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline tb_page_addr_t tb_page_addr1(const TranslationBlock *tb)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
tb_page_addr_t next = tb->itree.last & TARGET_PAGE_MASK;
|
||||
return next == (tb->itree.start & TARGET_PAGE_MASK) ? -1 : next;
|
||||
#else
|
||||
return tb->page_addr[1];
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void tb_set_page_addr0(TranslationBlock *tb,
|
||||
tb_page_addr_t addr)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
tb->itree.start = addr;
|
||||
/*
|
||||
* To begin, we record an interval of one byte. When the translation
|
||||
* loop encounters a second page, the interval will be extended to
|
||||
* include the first byte of the second page, which is sufficient to
|
||||
* allow tb_page_addr1() above to work properly. The final corrected
|
||||
* interval will be set by tb_page_add() from tb->size before the
|
||||
* node is added to the interval tree.
|
||||
*/
|
||||
tb->itree.last = addr;
|
||||
#else
|
||||
tb->page_addr[0] = addr;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void tb_set_page_addr1(TranslationBlock *tb,
|
||||
tb_page_addr_t addr)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Extend the interval to the first byte of the second page. See above. */
|
||||
tb->itree.last = addr;
|
||||
#else
|
||||
tb->page_addr[1] = addr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* current cflags for hashing/comparison */
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
|
||||
|
||||
/* translate-all.c */
|
||||
struct page_collection *page_collection_lock(tb_page_addr_t start,
|
||||
tb_page_addr_t end);
|
||||
void page_collection_unlock(struct page_collection *set);
|
||||
void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
tb_page_addr_t start, int len,
|
||||
uintptr_t retaddr);
|
||||
void tb_invalidate_phys_page(tb_page_addr_t addr);
|
||||
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Interval trees.
|
||||
*
|
||||
* Derived from include/linux/interval_tree.h and its dependencies.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_INTERVAL_TREE_H
|
||||
#define QEMU_INTERVAL_TREE_H
|
||||
|
||||
/*
|
||||
* For now, don't expose Linux Red-Black Trees separately, but retain the
|
||||
* separate type definitions to keep the implementation sane, and allow
|
||||
* the possibility of disentangling them later.
|
||||
*/
|
||||
typedef struct RBNode
|
||||
{
|
||||
/* Encodes parent with color in the lsb. */
|
||||
uintptr_t rb_parent_color;
|
||||
struct RBNode *rb_right;
|
||||
struct RBNode *rb_left;
|
||||
} RBNode;
|
||||
|
||||
typedef struct RBRoot
|
||||
{
|
||||
RBNode *rb_node;
|
||||
} RBRoot;
|
||||
|
||||
typedef struct RBRootLeftCached {
|
||||
RBRoot rb_root;
|
||||
RBNode *rb_leftmost;
|
||||
} RBRootLeftCached;
|
||||
|
||||
typedef struct IntervalTreeNode
|
||||
{
|
||||
RBNode rb;
|
||||
|
||||
uint64_t start; /* Start of interval */
|
||||
uint64_t last; /* Last location _in_ interval */
|
||||
uint64_t subtree_last;
|
||||
} IntervalTreeNode;
|
||||
|
||||
typedef RBRootLeftCached IntervalTreeRoot;
|
||||
|
||||
/**
|
||||
* interval_tree_is_empty
|
||||
* @root: root of the tree.
|
||||
*
|
||||
* Returns true if the tree contains no nodes.
|
||||
*/
|
||||
static inline bool interval_tree_is_empty(const IntervalTreeRoot *root)
|
||||
{
|
||||
return root->rb_root.rb_node == NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* interval_tree_insert
|
||||
* @node: node to insert,
|
||||
* @root: root of the tree.
|
||||
*
|
||||
* Insert @node into @root, and rebalance.
|
||||
*/
|
||||
void interval_tree_insert(IntervalTreeNode *node, IntervalTreeRoot *root);
|
||||
|
||||
/**
|
||||
* interval_tree_remove
|
||||
* @node: node to remove,
|
||||
* @root: root of the tree.
|
||||
*
|
||||
* Remove @node from @root, and rebalance.
|
||||
*/
|
||||
void interval_tree_remove(IntervalTreeNode *node, IntervalTreeRoot *root);
|
||||
|
||||
/**
|
||||
* interval_tree_iter_first:
|
||||
* @root: root of the tree,
|
||||
* @start, @last: the inclusive interval [start, last].
|
||||
*
|
||||
* Locate the "first" of a set of nodes within the tree at @root
|
||||
* that overlap the interval, where "first" is sorted by start.
|
||||
* Returns NULL if no overlap found.
|
||||
*/
|
||||
IntervalTreeNode *interval_tree_iter_first(IntervalTreeRoot *root,
|
||||
uint64_t start, uint64_t last);
|
||||
|
||||
/**
|
||||
* interval_tree_iter_next:
|
||||
* @node: previous search result
|
||||
* @start, @last: the inclusive interval [start, last].
|
||||
*
|
||||
* Locate the "next" of a set of nodes within the tree that overlap the
|
||||
* interval; @next is the result of a previous call to
|
||||
* interval_tree_iter_{first,next}. Returns NULL if @next was the last
|
||||
* node in the set.
|
||||
*/
|
||||
IntervalTreeNode *interval_tree_iter_next(IntervalTreeNode *node,
|
||||
uint64_t start, uint64_t last);
|
||||
|
||||
#endif /* QEMU_INTERVAL_TREE_H */
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Test very large vma allocations.
|
||||
* The qemu out-of-memory condition was within the mmap syscall itself.
|
||||
* If the syscall actually returns with MAP_FAILED, the test succeeded.
|
||||
*/
|
||||
#include <sys/mman.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int n = sizeof(size_t) == 4 ? 32 : 45;
|
||||
|
||||
for (int i = 28; i < n; i++) {
|
||||
size_t l = (size_t)1 << i;
|
||||
void *p = mmap(0, l, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
break;
|
||||
}
|
||||
munmap(p, l);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -47,6 +47,7 @@ tests = {
|
||||
'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'],
|
||||
'test-qapi-util': [],
|
||||
'test-smp-parse': [qom, meson.project_source_root() / 'hw/core/machine-smp.c'],
|
||||
'test-interval-tree': [],
|
||||
}
|
||||
|
||||
if have_system or have_tools
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Test interval trees
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/interval-tree.h"
|
||||
|
||||
static IntervalTreeNode nodes[20];
|
||||
static IntervalTreeRoot root;
|
||||
|
||||
static void rand_interval(IntervalTreeNode *n, uint64_t start, uint64_t last)
|
||||
{
|
||||
gint32 s_ofs, l_ofs, l_max;
|
||||
|
||||
if (last - start > INT32_MAX) {
|
||||
l_max = INT32_MAX;
|
||||
} else {
|
||||
l_max = last - start;
|
||||
}
|
||||
s_ofs = g_test_rand_int_range(0, l_max);
|
||||
l_ofs = g_test_rand_int_range(s_ofs, l_max);
|
||||
|
||||
n->start = start + s_ofs;
|
||||
n->last = start + l_ofs;
|
||||
}
|
||||
|
||||
static void test_empty(void)
|
||||
{
|
||||
g_assert(root.rb_root.rb_node == NULL);
|
||||
g_assert(root.rb_leftmost == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 0, UINT64_MAX) == NULL);
|
||||
}
|
||||
|
||||
static void test_find_one_point(void)
|
||||
{
|
||||
/* Create a tree of a single node, which is the point [1,1]. */
|
||||
nodes[0].start = 1;
|
||||
nodes[0].last = 1;
|
||||
|
||||
interval_tree_insert(&nodes[0], &root);
|
||||
|
||||
g_assert(interval_tree_iter_first(&root, 0, 9) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_next(&nodes[0], 0, 9) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 0, 0) == NULL);
|
||||
g_assert(interval_tree_iter_next(&nodes[0], 0, 0) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 0, 1) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 1, 1) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 1, 2) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 2, 2) == NULL);
|
||||
|
||||
interval_tree_remove(&nodes[0], &root);
|
||||
g_assert(root.rb_root.rb_node == NULL);
|
||||
g_assert(root.rb_leftmost == NULL);
|
||||
}
|
||||
|
||||
static void test_find_two_point(void)
|
||||
{
|
||||
IntervalTreeNode *find0, *find1;
|
||||
|
||||
/* Create a tree of a two nodes, which are both the point [1,1]. */
|
||||
nodes[0].start = 1;
|
||||
nodes[0].last = 1;
|
||||
nodes[1] = nodes[0];
|
||||
|
||||
interval_tree_insert(&nodes[0], &root);
|
||||
interval_tree_insert(&nodes[1], &root);
|
||||
|
||||
find0 = interval_tree_iter_first(&root, 0, 9);
|
||||
g_assert(find0 == &nodes[0] || find0 == &nodes[1]);
|
||||
|
||||
find1 = interval_tree_iter_next(find0, 0, 9);
|
||||
g_assert(find1 == &nodes[0] || find1 == &nodes[1]);
|
||||
g_assert(find0 != find1);
|
||||
|
||||
interval_tree_remove(&nodes[1], &root);
|
||||
|
||||
g_assert(interval_tree_iter_first(&root, 0, 9) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_next(&nodes[0], 0, 9) == NULL);
|
||||
|
||||
interval_tree_remove(&nodes[0], &root);
|
||||
}
|
||||
|
||||
static void test_find_one_range(void)
|
||||
{
|
||||
/* Create a tree of a single node, which is the range [1,8]. */
|
||||
nodes[0].start = 1;
|
||||
nodes[0].last = 8;
|
||||
|
||||
interval_tree_insert(&nodes[0], &root);
|
||||
|
||||
g_assert(interval_tree_iter_first(&root, 0, 9) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_next(&nodes[0], 0, 9) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 0, 0) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 0, 1) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 1, 1) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 4, 6) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 8, 8) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 9, 9) == NULL);
|
||||
|
||||
interval_tree_remove(&nodes[0], &root);
|
||||
}
|
||||
|
||||
static void test_find_one_range_many(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Create a tree of many nodes in [0,99] and [200,299],
|
||||
* but only one node with exactly [110,190].
|
||||
*/
|
||||
nodes[0].start = 110;
|
||||
nodes[0].last = 190;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(nodes) / 2; ++i) {
|
||||
rand_interval(&nodes[i], 0, 99);
|
||||
}
|
||||
for (; i < ARRAY_SIZE(nodes); ++i) {
|
||||
rand_interval(&nodes[i], 200, 299);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nodes); ++i) {
|
||||
interval_tree_insert(&nodes[i], &root);
|
||||
}
|
||||
|
||||
/* Test that we find exactly the one node. */
|
||||
g_assert(interval_tree_iter_first(&root, 100, 199) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_next(&nodes[0], 100, 199) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 100, 109) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 100, 110) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 111, 120) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 111, 199) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 190, 199) == &nodes[0]);
|
||||
g_assert(interval_tree_iter_first(&root, 192, 199) == NULL);
|
||||
|
||||
/*
|
||||
* Test that if there are multiple matches, we return the one
|
||||
* with the minimal start.
|
||||
*/
|
||||
g_assert(interval_tree_iter_first(&root, 100, 300) == &nodes[0]);
|
||||
|
||||
/* Test that we don't find it after it is removed. */
|
||||
interval_tree_remove(&nodes[0], &root);
|
||||
g_assert(interval_tree_iter_first(&root, 100, 199) == NULL);
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(nodes); ++i) {
|
||||
interval_tree_remove(&nodes[i], &root);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_find_many_range(void)
|
||||
{
|
||||
IntervalTreeNode *find;
|
||||
int i, n;
|
||||
|
||||
n = g_test_rand_int_range(ARRAY_SIZE(nodes) / 3, ARRAY_SIZE(nodes) / 2);
|
||||
|
||||
/*
|
||||
* Create a fair few nodes in [2000,2999], with the others
|
||||
* distributed around.
|
||||
*/
|
||||
for (i = 0; i < n; ++i) {
|
||||
rand_interval(&nodes[i], 2000, 2999);
|
||||
}
|
||||
for (; i < ARRAY_SIZE(nodes) * 2 / 3; ++i) {
|
||||
rand_interval(&nodes[i], 1000, 1899);
|
||||
}
|
||||
for (; i < ARRAY_SIZE(nodes); ++i) {
|
||||
rand_interval(&nodes[i], 3100, 3999);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nodes); ++i) {
|
||||
interval_tree_insert(&nodes[i], &root);
|
||||
}
|
||||
|
||||
/* Test that we find all of the nodes. */
|
||||
find = interval_tree_iter_first(&root, 2000, 2999);
|
||||
for (i = 0; find != NULL; i++) {
|
||||
find = interval_tree_iter_next(find, 2000, 2999);
|
||||
}
|
||||
g_assert_cmpint(i, ==, n);
|
||||
|
||||
g_assert(interval_tree_iter_first(&root, 0, 999) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 1900, 1999) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 3000, 3099) == NULL);
|
||||
g_assert(interval_tree_iter_first(&root, 4000, UINT64_MAX) == NULL);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nodes); ++i) {
|
||||
interval_tree_remove(&nodes[i], &root);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func("/interval-tree/empty", test_empty);
|
||||
g_test_add_func("/interval-tree/find-one-point", test_find_one_point);
|
||||
g_test_add_func("/interval-tree/find-two-point", test_find_two_point);
|
||||
g_test_add_func("/interval-tree/find-one-range", test_find_one_range);
|
||||
g_test_add_func("/interval-tree/find-one-range-many",
|
||||
test_find_one_range_many);
|
||||
g_test_add_func("/interval-tree/find-many-range", test_find_many_range);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
@@ -42,10 +42,6 @@ find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx
|
||||
find_ram_offset_loop(uint64_t size, uint64_t candidate, uint64_t offset, uint64_t next, uint64_t mingap) "trying size: 0x%" PRIx64 " @ 0x%" PRIx64 ", offset: 0x%" PRIx64" next: 0x%" PRIx64 " mingap: 0x%" PRIx64
|
||||
ram_block_discard_range(const char *rbname, void *hva, size_t length, bool need_madvise, bool need_fallocate, int ret) "%s@%p + 0x%zx: madvise: %d fallocate: %d ret: %d"
|
||||
|
||||
# accel/tcg/cputlb.c
|
||||
memory_notdirty_write_access(uint64_t vaddr, uint64_t ram_addr, unsigned size) "0x%" PRIx64 " ram_addr 0x%" PRIx64 " size %u"
|
||||
memory_notdirty_set_dirty(uint64_t vaddr) "0x%" PRIx64
|
||||
|
||||
# job.c
|
||||
job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
|
||||
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@ util_ss.add(files('guest-random.c'))
|
||||
util_ss.add(files('yank.c'))
|
||||
util_ss.add(files('int128.c'))
|
||||
util_ss.add(files('memalign.c'))
|
||||
util_ss.add(files('interval-tree.c'))
|
||||
|
||||
if have_user
|
||||
util_ss.add(files('selfmap.c'))
|
||||
|
||||
Reference in New Issue
Block a user