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>
#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"
* See also: page_collection_lock().
*/
struct page_collection {
- GTree *tree;
+ QTree *tree;
struct page_entry *max;
};
struct page_entry *pe;
PageDesc *pd;
- pe = g_tree_lookup(set->tree, &index);
+ pe = q_tree_lookup(set->tree, &index);
if (pe) {
return false;
}
}
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
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;
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);
(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;
}
}
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);
}
#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"
struct tcg_region_tree {
QemuMutex lock;
- GTree *tree;
+ QTree *tree;
/* padding to avoid false sharing is computed at run-time */
};
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);
}
}
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);
}
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);
}
}
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;
}
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();
}
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;
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();
}
*
* 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;
}
/* internal insert routine */
-static QTreeNode *
+static QTreeNode * QEMU_DISABLE_CFI
q_tree_insert_internal(QTree *tree,
gpointer key,
gpointer value,
}
/* internal remove routine */
-static gboolean
+static gboolean QEMU_DISABLE_CFI
q_tree_remove_internal(QTree *tree,
gconstpointer key,
gboolean steal)
return node;
}
-static QTreeNode *
+static QTreeNode * QEMU_DISABLE_CFI
q_tree_find_node(QTree *tree,
gconstpointer key)
{