mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
accel/tcg: Use interval tree for TBs in user-only mode
Begin weaning user-only away from PageDesc. Since, for user-only, all TB (and page) manipulation is done with a single mutex, and there is no virtual/physical discontinuity to split a TB across discontinuous pages, place all of the TBs into a single IntervalTree. This makes it trivial to find all of the TBs intersecting a range. Retain the existing PageDesc + linked list implementation for system mode. Move the portion of the implementation that overlaps the new user-only code behind the common ifdef. Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
@@ -24,14 +24,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct PageDesc {
|
typedef struct PageDesc {
|
||||||
/* list of TBs intersecting this ram page */
|
|
||||||
uintptr_t first_tb;
|
|
||||||
#ifdef CONFIG_USER_ONLY
|
#ifdef CONFIG_USER_ONLY
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
void *target_data;
|
void *target_data;
|
||||||
#endif
|
#else
|
||||||
#ifdef CONFIG_SOFTMMU
|
|
||||||
QemuSpin lock;
|
QemuSpin lock;
|
||||||
|
/* list of TBs intersecting this ram page */
|
||||||
|
uintptr_t first_tb;
|
||||||
#endif
|
#endif
|
||||||
} PageDesc;
|
} PageDesc;
|
||||||
|
|
||||||
@@ -69,9 +68,6 @@ static inline PageDesc *page_find(tb_page_addr_t index)
|
|||||||
tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \
|
tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \
|
||||||
tb = (TranslationBlock *)((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) \
|
#define TB_FOR_EACH_JMP(head_tb, tb, n) \
|
||||||
TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next)
|
TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next)
|
||||||
|
|
||||||
@@ -89,6 +85,12 @@ void do_assert_page_locked(const PageDesc *pd, const char *file, int line);
|
|||||||
#endif
|
#endif
|
||||||
void page_lock(PageDesc *pd);
|
void page_lock(PageDesc *pd);
|
||||||
void page_unlock(PageDesc *pd);
|
void page_unlock(PageDesc *pd);
|
||||||
|
|
||||||
|
/* TODO: For now, still shared with translate-all.c for system mode. */
|
||||||
|
typedef int PageForEachNext;
|
||||||
|
#define PAGE_FOR_EACH_TB(start, end, pagedesc, tb, n) \
|
||||||
|
TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG)
|
#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG)
|
||||||
void assert_no_pages_locked(void);
|
void assert_no_pages_locked(void);
|
||||||
|
|||||||
+228
-159
File diff suppressed because it is too large
Load Diff
@@ -709,7 +709,7 @@ page_collection_lock(tb_page_addr_t start, tb_page_addr_t end)
|
|||||||
|
|
||||||
for (index = start; index <= end; index++) {
|
for (index = start; index <= end; index++) {
|
||||||
TranslationBlock *tb;
|
TranslationBlock *tb;
|
||||||
int n;
|
PageForEachNext n;
|
||||||
|
|
||||||
pd = page_find(index);
|
pd = page_find(index);
|
||||||
if (pd == NULL) {
|
if (pd == NULL) {
|
||||||
@@ -720,7 +720,7 @@ page_collection_lock(tb_page_addr_t start, tb_page_addr_t end)
|
|||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
assert_page_locked(pd);
|
assert_page_locked(pd);
|
||||||
PAGE_FOR_EACH_TB(pd, tb, n) {
|
PAGE_FOR_EACH_TB(unused, unused, pd, tb, n) {
|
||||||
if (page_trylock_add(set, tb_page_addr0(tb)) ||
|
if (page_trylock_add(set, tb_page_addr0(tb)) ||
|
||||||
(tb_page_addr1(tb) != -1 &&
|
(tb_page_addr1(tb) != -1 &&
|
||||||
page_trylock_add(set, tb_page_addr1(tb)))) {
|
page_trylock_add(set, tb_page_addr1(tb)))) {
|
||||||
|
|||||||
+40
-3
@@ -24,6 +24,7 @@
|
|||||||
#ifdef CONFIG_TCG
|
#ifdef CONFIG_TCG
|
||||||
#include "exec/cpu_ldst.h"
|
#include "exec/cpu_ldst.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "qemu/interval-tree.h"
|
||||||
|
|
||||||
/* allow to see translation results - the slowdown should be negligible, so we leave it */
|
/* allow to see translation results - the slowdown should be negligible, so we leave it */
|
||||||
#define DEBUG_DISAS
|
#define DEBUG_DISAS
|
||||||
@@ -559,11 +560,20 @@ struct TranslationBlock {
|
|||||||
|
|
||||||
struct tb_tc tc;
|
struct tb_tc tc;
|
||||||
|
|
||||||
/* first and second physical page containing code. The lower bit
|
/*
|
||||||
of the pointer tells the index in page_next[].
|
* Track tb_page_addr_t intervals that intersect this TB.
|
||||||
The list is protected by the TB's page('s) lock(s) */
|
* 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];
|
uintptr_t page_next[2];
|
||||||
tb_page_addr_t page_addr[2];
|
tb_page_addr_t page_addr[2];
|
||||||
|
#endif
|
||||||
|
|
||||||
/* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */
|
/* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */
|
||||||
QemuSpin jmp_lock;
|
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)
|
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];
|
return tb->page_addr[0];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline tb_page_addr_t tb_page_addr1(const TranslationBlock *tb)
|
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];
|
return tb->page_addr[1];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void tb_set_page_addr0(TranslationBlock *tb,
|
static inline void tb_set_page_addr0(TranslationBlock *tb,
|
||||||
tb_page_addr_t addr)
|
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;
|
tb->page_addr[0] = addr;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void tb_set_page_addr1(TranslationBlock *tb,
|
static inline void tb_set_page_addr1(TranslationBlock *tb,
|
||||||
tb_page_addr_t addr)
|
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;
|
tb->page_addr[1] = addr;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* current cflags for hashing/comparison */
|
/* current cflags for hashing/comparison */
|
||||||
|
|||||||
Reference in New Issue
Block a user