Skip to content

Commit

Permalink
tcg: use QTree instead of GTree
Browse files Browse the repository at this point in the history
qemu-user can hang in a multi-threaded fork. One common
reason is that when creating a TB, between fork and exec
we manipulate a GTree whose memory allocator (GSlice) is
not fork-safe.

Although POSIX does not mandate it, the system's allocator
(e.g. tcmalloc, libc malloc) is probably fork-safe.

Fix some of these hangs by using QTree, which uses the system's
allocator regardless of the Glib version that we used at
configuration time.

Tested with the test program in the original bug report, i.e.:
```

void garble() {
  int pid = fork();
  if (pid == 0) {
    exit(0);
  } else {
    int wstatus;
    waitpid(pid, &wstatus, 0);
  }
}

void supragarble(unsigned depth) {
  if (depth == 0)
    return ;

  std::thread a(supragarble, depth-1);
  std::thread b(supragarble, depth-1);
  garble();
  a.join();
  b.join();
}

int main() {
  supragarble(10);
}
```

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/285
Reported-by: Valentin David <me@valentindavid.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Emilio Cota <cota@braap.org>
Message-Id: <20230205163758.416992-3-cota@braap.org>
[rth: Add QEMU_DISABLE_CFI for all callback using functions.]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
  • Loading branch information
cota authored and rth7680 committed Mar 28, 2023
1 parent e3feb2c commit 1ff4a81
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 21 deletions.
17 changes: 9 additions & 8 deletions accel/tcg/tb-maint.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "qemu/osdep.h"
#include "qemu/interval-tree.h"
#include "qemu/qtree.h"
#include "exec/cputlb.h"
#include "exec/log.h"
#include "exec/exec-all.h"
Expand Down Expand Up @@ -314,7 +315,7 @@ struct page_entry {
* See also: page_collection_lock().
*/
struct page_collection {
GTree *tree;
QTree *tree;
struct page_entry *max;
};

Expand Down Expand Up @@ -467,7 +468,7 @@ static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr)
struct page_entry *pe;
PageDesc *pd;

pe = g_tree_lookup(set->tree, &index);
pe = q_tree_lookup(set->tree, &index);
if (pe) {
return false;
}
Expand All @@ -478,7 +479,7 @@ static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr)
}

pe = page_entry_new(pd, index);
g_tree_insert(set->tree, &pe->index, pe);
q_tree_insert(set->tree, &pe->index, pe);

/*
* If this is either (1) the first insertion or (2) a page whose index
Expand Down Expand Up @@ -525,13 +526,13 @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
end >>= TARGET_PAGE_BITS;
g_assert(start <= end);

set->tree = g_tree_new_full(tb_page_addr_cmp, NULL, NULL,
set->tree = q_tree_new_full(tb_page_addr_cmp, NULL, NULL,
page_entry_destroy);
set->max = NULL;
assert_no_pages_locked();

retry:
g_tree_foreach(set->tree, page_entry_lock, NULL);
q_tree_foreach(set->tree, page_entry_lock, NULL);

for (index = start; index <= end; index++) {
TranslationBlock *tb;
Expand All @@ -542,7 +543,7 @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
continue;
}
if (page_trylock_add(set, index << TARGET_PAGE_BITS)) {
g_tree_foreach(set->tree, page_entry_unlock, NULL);
q_tree_foreach(set->tree, page_entry_unlock, NULL);
goto retry;
}
assert_page_locked(pd);
Expand All @@ -551,7 +552,7 @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
(tb_page_addr1(tb) != -1 &&
page_trylock_add(set, tb_page_addr1(tb)))) {
/* drop all locks, and reacquire in order */
g_tree_foreach(set->tree, page_entry_unlock, NULL);
q_tree_foreach(set->tree, page_entry_unlock, NULL);
goto retry;
}
}
Expand All @@ -562,7 +563,7 @@ static struct page_collection *page_collection_lock(tb_page_addr_t start,
static void page_collection_unlock(struct page_collection *set)
{
/* entries are unlocked and freed via page_entry_destroy */
g_tree_destroy(set->tree);
q_tree_destroy(set->tree);
g_free(set);
}

Expand Down
19 changes: 10 additions & 9 deletions tcg/region.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "qemu/mprotect.h"
#include "qemu/memalign.h"
#include "qemu/cacheinfo.h"
#include "qemu/qtree.h"
#include "qapi/error.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
Expand All @@ -36,7 +37,7 @@

struct tcg_region_tree {
QemuMutex lock;
GTree *tree;
QTree *tree;
/* padding to avoid false sharing is computed at run-time */
};

Expand Down Expand Up @@ -163,7 +164,7 @@ static void tcg_region_trees_init(void)
struct tcg_region_tree *rt = region_trees + i * tree_size;

qemu_mutex_init(&rt->lock);
rt->tree = g_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy);
rt->tree = q_tree_new_full(tb_tc_cmp, NULL, NULL, tb_destroy);
}
}

Expand Down Expand Up @@ -202,7 +203,7 @@ void tcg_tb_insert(TranslationBlock *tb)

g_assert(rt != NULL);
qemu_mutex_lock(&rt->lock);
g_tree_insert(rt->tree, &tb->tc, tb);
q_tree_insert(rt->tree, &tb->tc, tb);
qemu_mutex_unlock(&rt->lock);
}

Expand All @@ -212,7 +213,7 @@ void tcg_tb_remove(TranslationBlock *tb)

g_assert(rt != NULL);
qemu_mutex_lock(&rt->lock);
g_tree_remove(rt->tree, &tb->tc);
q_tree_remove(rt->tree, &tb->tc);
qemu_mutex_unlock(&rt->lock);
}

Expand All @@ -232,7 +233,7 @@ TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr)
}

qemu_mutex_lock(&rt->lock);
tb = g_tree_lookup(rt->tree, &s);
tb = q_tree_lookup(rt->tree, &s);
qemu_mutex_unlock(&rt->lock);
return tb;
}
Expand Down Expand Up @@ -267,7 +268,7 @@ void tcg_tb_foreach(GTraverseFunc func, gpointer user_data)
for (i = 0; i < region.n; i++) {
struct tcg_region_tree *rt = region_trees + i * tree_size;

g_tree_foreach(rt->tree, func, user_data);
q_tree_foreach(rt->tree, func, user_data);
}
tcg_region_tree_unlock_all();
}
Expand All @@ -281,7 +282,7 @@ size_t tcg_nb_tbs(void)
for (i = 0; i < region.n; i++) {
struct tcg_region_tree *rt = region_trees + i * tree_size;

nb_tbs += g_tree_nnodes(rt->tree);
nb_tbs += q_tree_nnodes(rt->tree);
}
tcg_region_tree_unlock_all();
return nb_tbs;
Expand All @@ -296,8 +297,8 @@ static void tcg_region_tree_reset_all(void)
struct tcg_region_tree *rt = region_trees + i * tree_size;

/* Increment the refcount first so that destroy acts as a reset */
g_tree_ref(rt->tree);
g_tree_destroy(rt->tree);
q_tree_ref(rt->tree);
q_tree_destroy(rt->tree);
}
tcg_region_tree_unlock_all();
}
Expand Down
8 changes: 4 additions & 4 deletions util/qtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ q_tree_node_next(QTreeNode *node)
*
* Since: 2.70 in GLib. Internal in Qtree, i.e. not in the public API.
*/
static void
static void QEMU_DISABLE_CFI
q_tree_remove_all(QTree *tree)
{
QTreeNode *node;
Expand Down Expand Up @@ -532,7 +532,7 @@ q_tree_replace(QTree *tree,
}

/* internal insert routine */
static QTreeNode *
static QTreeNode * QEMU_DISABLE_CFI
q_tree_insert_internal(QTree *tree,
gpointer key,
gpointer value,
Expand Down Expand Up @@ -721,7 +721,7 @@ q_tree_steal(QTree *tree,
}

/* internal remove routine */
static gboolean
static gboolean QEMU_DISABLE_CFI
q_tree_remove_internal(QTree *tree,
gconstpointer key,
gboolean steal)
Expand Down Expand Up @@ -1182,7 +1182,7 @@ q_tree_node_balance(QTreeNode *node)
return node;
}

static QTreeNode *
static QTreeNode * QEMU_DISABLE_CFI
q_tree_find_node(QTree *tree,
gconstpointer key)
{
Expand Down

0 comments on commit 1ff4a81

Please sign in to comment.