[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PULL 20/28] tcg/plugins: implement a qemu_plugin_user_exit helper
From: |
Alex Bennée |
Subject: |
[PULL 20/28] tcg/plugins: implement a qemu_plugin_user_exit helper |
Date: |
Fri, 23 Jul 2021 18:03:46 +0100 |
In user-mode emulation there is a small race between preexit_cleanup
and exit_group() which means we may end up calling instrumented
instructions before the kernel reaps child threads. To solve this we
implement a new helper which ensures the callbacks are flushed along
with any translations before we let the host do it's a thing.
While we are at it make the documentation of
qemu_plugin_register_atexit_cb clearer as to what the user can expect.
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Mahmoud Mandour <ma.mandourr@gmail.com>
Acked-by: Warner Losh <imp@bsdimp.com>
Message-Id: <20210720232703.10650-21-alex.bennee@linaro.org>
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 0fefbc6084..9a8438f683 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -190,6 +190,16 @@ void qemu_plugin_add_dyn_cb_arr(GArray *arr);
void qemu_plugin_disable_mem_helpers(CPUState *cpu);
+/**
+ * qemu_plugin_user_exit(): clean-up callbacks before calling exit callbacks
+ *
+ * This is a user-mode only helper that ensure we have fully cleared
+ * callbacks from all threads before calling the exit callbacks. This
+ * is so the plugins themselves don't have to jump through hoops to
+ * guard against race conditions.
+ */
+void qemu_plugin_user_exit(void);
+
#else /* !CONFIG_PLUGIN */
static inline void qemu_plugin_add_opts(void)
@@ -250,6 +260,8 @@ void qemu_plugin_add_dyn_cb_arr(GArray *arr)
static inline void qemu_plugin_disable_mem_helpers(CPUState *cpu)
{ }
+static inline void qemu_plugin_user_exit(void)
+{ }
#endif /* !CONFIG_PLUGIN */
#endif /* QEMU_PLUGIN_H */
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index dc3496f36c..e6e815abc5 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -549,6 +549,19 @@ void qemu_plugin_vcpu_for_each(qemu_plugin_id_t id,
void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
qemu_plugin_simple_cb_t cb);
+/**
+ * qemu_plugin_register_atexit_cb() - register exit callback
+ * @id: plugin ID
+ * @cb: callback
+ * @userdata: user data for callback
+ *
+ * The @cb function is called once execution has finished. Plugins
+ * should be able to free all their resources at this point much like
+ * after a reset/uninstall callback is called.
+ *
+ * In user-mode it is possible a few un-instrumented instructions from
+ * child threads may run before the host kernel reaps the threads.
+ */
void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
qemu_plugin_udata_cb_t cb, void *userdata);
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c
index 7d986e9700..3f44311396 100644
--- a/bsd-user/syscall.c
+++ b/bsd-user/syscall.c
@@ -335,7 +335,7 @@ abi_long do_freebsd_syscall(void *cpu_env, int num,
abi_long arg1,
_mcleanup();
#endif
gdb_exit(arg1);
- qemu_plugin_atexit_cb();
+ qemu_plugin_user_exit();
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -437,7 +437,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long
arg1,
_mcleanup();
#endif
gdb_exit(arg1);
- qemu_plugin_atexit_cb();
+ qemu_plugin_user_exit();
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
@@ -516,7 +516,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num,
abi_long arg1,
_mcleanup();
#endif
gdb_exit(arg1);
- qemu_plugin_atexit_cb();
+ qemu_plugin_user_exit();
/* XXX: should free thread stack and CPU env */
_exit(arg1);
ret = 0; /* avoid warning */
diff --git a/linux-user/exit.c b/linux-user/exit.c
index 70b344048c..527e29cbc1 100644
--- a/linux-user/exit.c
+++ b/linux-user/exit.c
@@ -35,5 +35,5 @@ void preexit_cleanup(CPUArchState *env, int code)
__gcov_dump();
#endif
gdb_exit(code);
- qemu_plugin_atexit_cb();
+ qemu_plugin_user_exit();
}
diff --git a/plugins/core.c b/plugins/core.c
index 474db287cb..6b2490f973 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -487,6 +487,45 @@ void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
plugin_register_cb_udata(id, QEMU_PLUGIN_EV_ATEXIT, cb, udata);
}
+/*
+ * Handle exit from linux-user. Unlike the normal atexit() mechanism
+ * we need to handle the clean-up manually as it's possible threads
+ * are still running. We need to remove all callbacks from code
+ * generation, flush the current translations and then we can safely
+ * trigger the exit callbacks.
+ */
+
+void qemu_plugin_user_exit(void)
+{
+ enum qemu_plugin_event ev;
+ CPUState *cpu;
+
+ QEMU_LOCK_GUARD(&plugin.lock);
+
+ start_exclusive();
+
+ /* un-register all callbacks except the final AT_EXIT one */
+ for (ev = 0; ev < QEMU_PLUGIN_EV_MAX; ev++) {
+ if (ev != QEMU_PLUGIN_EV_ATEXIT) {
+ struct qemu_plugin_ctx *ctx;
+ QTAILQ_FOREACH(ctx, &plugin.ctxs, entry) {
+ plugin_unregister_cb__locked(ctx, ev);
+ }
+ }
+ }
+
+ tb_flush(current_cpu);
+
+ CPU_FOREACH(cpu) {
+ qemu_plugin_disable_mem_helpers(cpu);
+ }
+
+ end_exclusive();
+
+ /* now it's safe to handle the exit case */
+ qemu_plugin_atexit_cb();
+}
+
/*
* Call this function after longjmp'ing to the main loop. It's possible that
the
* last instruction of a TB might have used helpers, and therefore the
--
2.20.1
- [PULL 04/28] configure: remove needless if leg, (continued)
- [PULL 04/28] configure: remove needless if leg, Alex Bennée, 2021/07/23
- [PULL 02/28] docs: collect the disparate device emulation docs into one section, Alex Bennée, 2021/07/23
- [PULL 07/28] contrib/gitdm: add domain-map for MontaVista, Alex Bennée, 2021/07/23
- [PULL 06/28] .mailmap: fix up some broken commit authors, Alex Bennée, 2021/07/23
- [PULL 09/28] gitdm.config: sort the corporate GroupMap entries, Alex Bennée, 2021/07/23
- [PULL 08/28] contrib/gitdm: add a group mapping for robot scanners, Alex Bennée, 2021/07/23
- [PULL 10/28] contrib/gitdm: add domain-map/group-map mappings for Samsung, Alex Bennée, 2021/07/23
- [PULL 05/28] contrib/gitdm: add some new aliases to fix up commits, Alex Bennée, 2021/07/23
- [PULL 11/28] contrib/gitdm: add domain-map for Eldorado, Alex Bennée, 2021/07/23
- [PULL 19/28] contrib/gitdm: add more individual contributor entries., Alex Bennée, 2021/07/23
- [PULL 20/28] tcg/plugins: implement a qemu_plugin_user_exit helper,
Alex Bennée <=
- [PULL 26/28] gitlab: enable a very minimal build with the tricore container, Alex Bennée, 2021/07/23
- [PULL 21/28] plugins/cache: Fixed a bug with destroying FIFO metadata, Alex Bennée, 2021/07/23
- [PULL 23/28] plugins/cache: Fixed "function decl. is not a prototype" warnings, Alex Bennée, 2021/07/23
- [PULL 15/28] contrib/gitdm: add domain-map for NVIDIA, Alex Bennée, 2021/07/23
- [PULL 13/28] contrib/gitdm: un-ironically add a mapping for LWN, Alex Bennée, 2021/07/23
- [PULL 12/28] contrib/gitdm: add domain-map/group-map for Wind River, Alex Bennée, 2021/07/23
- [PULL 22/28] plugins/cache: limited the scope of a mutex lock, Alex Bennée, 2021/07/23
- [PULL 24/28] plugins: Fix physical address calculation for IO regions, Alex Bennée, 2021/07/23
- [PULL 17/28] contrib/gitdm: add an explicit academic entry for BU, Alex Bennée, 2021/07/23
- [PULL 16/28] contrib/gitdm: add group-map for Netflix, Alex Bennée, 2021/07/23