2011-12-11 03:42:09 +01:00
|
|
|
From 4cc09e9530fb08123594be6c72dfc381df5dcddc Mon Sep 17 00:00:00 2001
|
|
|
|
From: Peter Maydell <peter.maydell@linaro.org>
|
|
|
|
Date: Wed, 5 Oct 2011 10:04:02 +0100
|
2012-02-01 00:10:40 +01:00
|
|
|
Subject: [PATCH] Handle CPU interrupts by inline checking of a flag
|
2011-12-11 03:42:09 +01:00
|
|
|
|
|
|
|
Fix the nasty TCG race conditions and crashes by implementing cpu_exit
|
|
|
|
as setting a flag which is checked at the start of each TB. This is
|
|
|
|
slightly slower than the attempt to have cpu_exit alter the graph of
|
|
|
|
TBs, but it doesn't crash if a thread or signal handler calls cpu_exit
|
|
|
|
while the execution thread is itself modifying the TB graph.
|
|
|
|
|
|
|
|
This version of the patch includes command line option "-no-stopflag"
|
|
|
|
which reverts to the previous racy behaviour. This is intended for
|
|
|
|
convenience in testing and comparative benchmarking and won't be
|
|
|
|
in the final patch.
|
|
|
|
|
|
|
|
It's probably worth experimenting with whether the flag-testing
|
|
|
|
code has the branch in a sense which confuses branch-prediction
|
|
|
|
and thus whether flipping it might change performance.
|
|
|
|
|
|
|
|
Mostly this needs benchmarking to determine what the actual speed
|
|
|
|
hit is, which I never got round to. Feel free to do some :-)
|
|
|
|
---
|
|
|
|
cpu-exec.c | 11 ++++++++++-
|
|
|
|
exec.c | 14 ++++++++++++--
|
|
|
|
gen-icount.h | 16 ++++++++++++++++
|
|
|
|
linux-user/main.c | 8 ++++++++
|
|
|
|
qemu-options.hx | 9 +++++++++
|
|
|
|
vl.c | 5 +++++
|
|
|
|
6 files changed, 60 insertions(+), 3 deletions(-)
|
|
|
|
|
|
|
|
diff --git a/cpu-exec.c b/cpu-exec.c
|
|
|
|
index a9fa608..5f7982f 100644
|
|
|
|
--- a/cpu-exec.c
|
|
|
|
+++ b/cpu-exec.c
|
|
|
|
@@ -564,7 +564,16 @@ int cpu_exec(CPUState *env)
|
|
|
|
tc_ptr = tb->tc_ptr;
|
|
|
|
/* execute the generated code */
|
|
|
|
next_tb = tcg_qemu_tb_exec(env, tc_ptr);
|
|
|
|
- if ((next_tb & 3) == 2) {
|
|
|
|
+ if ((next_tb & 3) == 3) {
|
|
|
|
+ /* hit stopflag check */
|
|
|
|
+ tb = (TranslationBlock *)(long)(next_tb & ~3);
|
|
|
|
+ /* Restore PC. */
|
|
|
|
+ cpu_pc_from_tb(env, tb);
|
|
|
|
+ next_tb = 0;
|
|
|
|
+ env->exit_request = 0;
|
|
|
|
+ env->exception_index = EXCP_INTERRUPT;
|
|
|
|
+ cpu_loop_exit(env);
|
|
|
|
+ } else if ((next_tb & 3) == 2) {
|
|
|
|
/* Instruction counter expired. */
|
|
|
|
int insns_left;
|
|
|
|
tb = (TranslationBlock *)(long)(next_tb & ~3);
|
|
|
|
diff --git a/exec.c b/exec.c
|
|
|
|
index 6b92198..6c923f2 100644
|
|
|
|
--- a/exec.c
|
|
|
|
+++ b/exec.c
|
|
|
|
@@ -125,6 +125,8 @@ DEFINE_TLS(CPUState *,cpu_single_env);
|
|
|
|
1 = Precise instruction counting.
|
|
|
|
2 = Adaptive rate instruction counting. */
|
|
|
|
int use_icount = 0;
|
|
|
|
+/* 1 to do cpu_exit by inline flag check rather than tb link breaking */
|
|
|
|
+int use_stopflag = 1;
|
|
|
|
|
|
|
|
typedef struct PageDesc {
|
|
|
|
/* list of TBs intersecting this ram page */
|
|
|
|
@@ -1670,7 +1672,13 @@ static void tcg_handle_interrupt(CPUState *env, int mask)
|
|
|
|
cpu_abort(env, "Raised interrupt while not in I/O function");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
- cpu_unlink_tb(env);
|
|
|
|
+ // XXX just call cpu_exit ?
|
|
|
|
+ if (use_stopflag) {
|
|
|
|
+ // XXX is this OK?
|
|
|
|
+ env->exit_request = 1;
|
|
|
|
+ } else {
|
|
|
|
+ cpu_unlink_tb(env);
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1693,7 +1701,9 @@ void cpu_reset_interrupt(CPUState *env, int mask)
|
|
|
|
void cpu_exit(CPUState *env)
|
|
|
|
{
|
|
|
|
env->exit_request = 1;
|
|
|
|
- cpu_unlink_tb(env);
|
|
|
|
+ if (!use_stopflag) {
|
|
|
|
+ cpu_unlink_tb(env);
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
const CPULogItem cpu_log_items[] = {
|
|
|
|
diff --git a/gen-icount.h b/gen-icount.h
|
|
|
|
index 5fb3829..060f814 100644
|
|
|
|
--- a/gen-icount.h
|
|
|
|
+++ b/gen-icount.h
|
|
|
|
@@ -2,13 +2,25 @@
|
|
|
|
|
|
|
|
/* Helpers for instruction counting code generation. */
|
|
|
|
|
|
|
|
+extern int use_stopflag;
|
|
|
|
+
|
|
|
|
static TCGArg *icount_arg;
|
|
|
|
static int icount_label;
|
|
|
|
+static int stopflag_label;
|
|
|
|
|
|
|
|
static inline void gen_icount_start(void)
|
|
|
|
{
|
|
|
|
TCGv_i32 count;
|
|
|
|
|
|
|
|
+ if (use_stopflag) {
|
|
|
|
+ TCGv_i32 flag;
|
|
|
|
+ stopflag_label = gen_new_label();
|
|
|
|
+ flag = tcg_temp_local_new_i32();
|
|
|
|
+ tcg_gen_ld_i32(flag, cpu_env, offsetof(CPUState, exit_request));
|
|
|
|
+ tcg_gen_brcondi_i32(TCG_COND_NE, flag, 0, stopflag_label);
|
|
|
|
+ tcg_temp_free_i32(flag);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (!use_icount)
|
|
|
|
return;
|
|
|
|
|
|
|
|
@@ -26,6 +38,10 @@ static inline void gen_icount_start(void)
|
|
|
|
|
|
|
|
static void gen_icount_end(TranslationBlock *tb, int num_insns)
|
|
|
|
{
|
|
|
|
+ if (use_stopflag) {
|
|
|
|
+ gen_set_label(stopflag_label);
|
|
|
|
+ tcg_gen_exit_tb((long)tb + 3); // XXX
|
|
|
|
+ }
|
|
|
|
if (use_icount) {
|
|
|
|
*icount_arg = num_insns;
|
|
|
|
gen_set_label(icount_label);
|
|
|
|
diff --git a/linux-user/main.c b/linux-user/main.c
|
|
|
|
index d1bbc57..1cd8eb7 100644
|
|
|
|
--- a/linux-user/main.c
|
|
|
|
+++ b/linux-user/main.c
|
|
|
|
@@ -52,6 +52,7 @@ unsigned long reserved_va;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void usage(void);
|
|
|
|
+extern int use_stopflag;
|
|
|
|
|
|
|
|
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
|
|
|
|
const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
|
|
|
|
@@ -3072,6 +3073,11 @@ static void handle_arg_reserved_va(const char *arg)
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
+static void handle_arg_nostopflag(const char *arg)
|
|
|
|
+{
|
|
|
|
+ use_stopflag = 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static void handle_arg_singlestep(const char *arg)
|
|
|
|
{
|
|
|
|
singlestep = 1;
|
|
|
|
@@ -3125,6 +3131,8 @@ struct qemu_argument arg_table[] = {
|
|
|
|
#endif
|
|
|
|
{"d", "QEMU_LOG", true, handle_arg_log,
|
|
|
|
"options", "activate log"},
|
|
|
|
+ {"no-stopflag", "QEMU_NOSTOPFLAG", false, handle_arg_nostopflag,
|
|
|
|
+ "", "run in singlestep mode"},
|
|
|
|
{"p", "QEMU_PAGESIZE", true, handle_arg_pagesize,
|
|
|
|
"pagesize", "set the host page size to 'pagesize'"},
|
|
|
|
{"singlestep", "QEMU_SINGLESTEP", false, handle_arg_singlestep,
|
|
|
|
diff --git a/qemu-options.hx b/qemu-options.hx
|
|
|
|
index 681eaf1..83b1f38 100644
|
|
|
|
--- a/qemu-options.hx
|
|
|
|
+++ b/qemu-options.hx
|
|
|
|
@@ -1112,6 +1112,15 @@ STEXI
|
|
|
|
Disable HPET support.
|
|
|
|
ETEXI
|
|
|
|
|
|
|
|
+DEF("no-stopflag", 0, QEMU_OPTION_no_stopflag,
|
|
|
|
+ "-no-stopflag use old behaviour, not inline stopflag checks\n", QEMU_ARCH_ALL)
|
|
|
|
+STEXI
|
|
|
|
+@item -no-stopflag
|
|
|
|
+@findex -no-stopflag
|
|
|
|
+Implement cpu-exit by the old tb link breaking method rather than inline checks
|
|
|
|
+(this is slightly faster but racy!)
|
|
|
|
+ETEXI
|
|
|
|
+
|
|
|
|
DEF("balloon", HAS_ARG, QEMU_OPTION_balloon,
|
|
|
|
"-balloon none disable balloon device\n"
|
|
|
|
"-balloon virtio[,addr=str]\n"
|
|
|
|
diff --git a/vl.c b/vl.c
|
|
|
|
index a50842b..7fdd80f 100644
|
|
|
|
--- a/vl.c
|
|
|
|
+++ b/vl.c
|
|
|
|
@@ -174,6 +174,8 @@ int main(int argc, char **argv)
|
|
|
|
|
|
|
|
#define MAX_VIRTIO_CONSOLES 1
|
|
|
|
|
|
|
|
+extern int use_stopflag;
|
|
|
|
+
|
|
|
|
static const char *data_dir;
|
|
|
|
const char *bios_name = NULL;
|
|
|
|
enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB;
|
|
|
|
@@ -2819,6 +2821,9 @@ int main(int argc, char **argv, char **envp)
|
|
|
|
case QEMU_OPTION_rtc_td_hack:
|
|
|
|
rtc_td_hack = 1;
|
|
|
|
break;
|
|
|
|
+ case QEMU_OPTION_no_stopflag:
|
|
|
|
+ use_stopflag = 0;
|
|
|
|
+ break;
|
|
|
|
case QEMU_OPTION_acpitable:
|
|
|
|
do_acpitable_option(optarg);
|
|
|
|
break;
|
|
|
|
--
|
|
|
|
1.6.0.2
|
|
|
|
|