Richard Henderson <richard.hender...@linaro.org> writes:
> Continue weaning user-only away from PageDesc. > > Use an interval tree to record target data. > Chunk the data, to minimize allocation overhead. > > Signed-off-by: Richard Henderson <richard.hender...@linaro.org> > --- > accel/tcg/internal.h | 1 - > accel/tcg/user-exec.c | 110 ++++++++++++++++++++++++++++++++---------- > 2 files changed, 85 insertions(+), 26 deletions(-) > > diff --git a/accel/tcg/internal.h b/accel/tcg/internal.h > index 1bd5a02911..8731dc52e2 100644 > --- a/accel/tcg/internal.h > +++ b/accel/tcg/internal.h > @@ -26,7 +26,6 @@ > typedef struct PageDesc { > #ifdef CONFIG_USER_ONLY > unsigned long flags; > - void *target_data; > #else > QemuSpin lock; > /* list of TBs intersecting this ram page */ > diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c > index fb7d6ee9e9..bce3d5f335 100644 > --- a/accel/tcg/user-exec.c > +++ b/accel/tcg/user-exec.c > @@ -210,47 +210,107 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState > *env, target_ulong addr, > return addr; > } > > +#ifdef TARGET_PAGE_DATA_SIZE > +/* > + * Allocate chunks of target data together. For the only current user, > + * if we allocate one hunk per page, we have overhead of 40/128 or 40%. > + * Therefore, allocate memory for 64 pages at a time for overhead < 1%. > + */ > +#define TPD_PAGES 64 > +#define TBD_MASK (TARGET_PAGE_MASK * TPD_PAGES) > + > +typedef struct TargetPageDataNode { > + IntervalTreeNode itree; > + char data[TPD_PAGES][TARGET_PAGE_DATA_SIZE] __attribute__((aligned)); > +} TargetPageDataNode; > + > +static IntervalTreeRoot targetdata_root; > + > void page_reset_target_data(target_ulong start, target_ulong end) > { > -#ifdef TARGET_PAGE_DATA_SIZE > - target_ulong addr, len; > + IntervalTreeNode *n, *next; > + target_ulong last; > > - /* > - * This function should never be called with addresses outside the > - * guest address space. If this assert fires, it probably indicates > - * a missing call to h2g_valid. > - */ > - assert(end - 1 <= GUEST_ADDR_MAX); > - assert(start < end); > assert_memory_lock(); > > start = start & TARGET_PAGE_MASK; > - end = TARGET_PAGE_ALIGN(end); > + last = TARGET_PAGE_ALIGN(end) - 1; > > - for (addr = start, len = end - start; > - len != 0; > - len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { > - PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1); > + for (n = interval_tree_iter_first(&targetdata_root, start, last), > + next = n ? interval_tree_iter_next(n, start, last) : NULL; > + n != NULL; > + n = next, > + next = next ? interval_tree_iter_next(n, start, last) : NULL) { > + target_ulong n_start, n_last, p_ofs, p_len; > + TargetPageDataNode *t; > > - g_free(p->target_data); > - p->target_data = NULL; > + if (n->start >= start && n->last <= last) { > + interval_tree_remove(n, &targetdata_root); > + g_free(n); > + continue; > + } > + > + if (n->start < start) { > + n_start = start; > + p_ofs = (start - n->start) >> TARGET_PAGE_BITS; > + } else { > + n_start = n->start; > + p_ofs = 0; > + } > + n_last = MIN(last, n->last); > + p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS; > + > + t = container_of(n, TargetPageDataNode, itree); > + memset(t->data[p_ofs], 0, p_len * TARGET_PAGE_DATA_SIZE); > } > -#endif > } > > -#ifdef TARGET_PAGE_DATA_SIZE > void *page_get_target_data(target_ulong address) > { > - PageDesc *p = page_find(address >> TARGET_PAGE_BITS); > - void *ret = p->target_data; > + IntervalTreeNode *n; > + TargetPageDataNode *t; > + target_ulong page, region; > + bool locked; > > - if (!ret) { > - ret = g_malloc0(TARGET_PAGE_DATA_SIZE); > - p->target_data = ret; > + page = address & TARGET_PAGE_MASK; > + region = address & TBD_MASK; > + > + n = interval_tree_iter_first(&targetdata_root, page, page); > + if (n) { > + goto found; > } > - return ret; > + > + /* > + * See util/interval-tree.c re lockless lookups: no false positives but > + * there are false negatives. If we find nothing, retry with the mmap > + * lock acquired. We also need the lock for the allocation + insert. > + */ > + locked = have_mmap_lock(); Are we expecting to already hold the lock here? > + if (!locked) { > + mmap_lock(); > + n = interval_tree_iter_first(&targetdata_root, page, page); So we only search if we haven't got a lock already. > + if (n) { > + mmap_unlock(); > + goto found; > + } > + } > + > + t = g_new0(TargetPageDataNode, 1); > + n = &t->itree; > + n->start = region; > + n->last = region | ~TBD_MASK; > + interval_tree_insert(n, &targetdata_root); > + if (!locked) { > + mmap_unlock(); > + } To be honest the mmap_lock safe to use recursively and wrapping the locked portion in a LOCK_GUARD would make me happier that we didn't cock up unwinding. > + > + found: > + t = container_of(n, TargetPageDataNode, itree); > + return t->data[(page - region) >> TARGET_PAGE_BITS]; > } > -#endif > +#else > +void page_reset_target_data(target_ulong start, target_ulong end) { } > +#endif /* TARGET_PAGE_DATA_SIZE */ > > /* The softmmu versions of these helpers are in cputlb.c. */ -- Alex Bennée