qemu/0001-Handle-CPU-interrupts-by-inline-che.patch

232 lines
7.8 KiB
Diff

From e076316356911dae5ae97af90710e28fabb416cd Mon Sep 17 00:00:00 2001
From: Peter Maydell <peter.maydell@linaro.org>
Date: Wed, 5 Oct 2011 10:04:02 +0100
Subject: [PATCH] Handle CPU interrupts by inline checking of a flag
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 :-)
[AF: CPUState -> CPUArchState]
[AF: Rebased onto exec.c/translate-all.c split]
---
cpu-exec.c | 11 ++++++++++-
exec.c | 6 +++++-
include/exec/gen-icount.h | 16 ++++++++++++++++
linux-user/main.c | 8 ++++++++
qemu-options.hx | 9 +++++++++
translate-all.c | 8 +++++++-
translate-all.h | 2 ++
vl.c | 5 +++++
8 Dateien geändert, 62 Zeilen hinzugefügt(+), 3 Zeilen entfernt(-)
diff --git a/cpu-exec.c b/cpu-exec.c
index 19ebb4a..02d8d69 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -597,7 +597,16 @@ int cpu_exec(CPUArchState *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 *)(next_tb & ~3);
diff --git a/exec.c b/exec.c
index b85508b..9458672 100644
--- a/exec.c
+++ b/exec.c
@@ -79,6 +79,8 @@ DEFINE_TLS(CPUArchState *,cpu_single_env);
1 = Precise instruction counting.
2 = Adaptive rate instruction counting. */
int use_icount;
+/* 1 to do cpu_exit by inline flag check rather than tb link breaking */
+int use_stopflag = 1;
#if !defined(CONFIG_USER_ONLY)
@@ -493,7 +495,9 @@ void cpu_reset_interrupt(CPUArchState *env, int mask)
void cpu_exit(CPUArchState *env)
{
env->exit_request = 1;
- cpu_unlink_tb(env);
+ if (!use_stopflag) {
+ cpu_unlink_tb(env);
+ }
}
void cpu_abort(CPUArchState *env, const char *fmt, ...)
diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h
index 8043b3b..c2e14d5 100644
--- a/include/exec/gen-icount.h
+++ b/include/exec/gen-icount.h
@@ -5,13 +5,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(CPUArchState, exit_request));
+ tcg_gen_brcondi_i32(TCG_COND_NE, flag, 0, stopflag_label);
+ tcg_temp_free_i32(flag);
+ }
+
if (!use_icount)
return;
@@ -29,6 +41,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 3df8aa2..d83f79f 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -69,6 +69,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;
@@ -3241,6 +3242,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;
@@ -3296,6 +3302,8 @@ static const struct qemu_argument arg_table[] = {
"options", "activate log"},
{"D", "QEMU_LOG_FILENAME", true, handle_arg_log_filename,
"logfile", "override default logfile location"},
+ {"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 9d7131a..6481175 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1266,6 +1266,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("acpitable", HAS_ARG, QEMU_OPTION_acpitable,
"-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n"
" ACPI table description\n", QEMU_ARCH_I386)
diff --git a/translate-all.c b/translate-all.c
index d367fc4..0d6c5a9 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -1476,7 +1476,13 @@ static void tcg_handle_interrupt(CPUArchState *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);
+ }
}
}
diff --git a/translate-all.h b/translate-all.h
index b181fb4..44736c5 100644
--- a/translate-all.h
+++ b/translate-all.h
@@ -26,6 +26,8 @@
#define P_L2_LEVELS \
(((TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS - 1) / L2_BITS) + 1)
+extern int use_stopflag;
+
/* translate-all.c */
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len);
void cpu_unlink_tb(CPUArchState *env);
diff --git a/vl.c b/vl.c
index 1355f69..7500e19 100644
--- a/vl.c
+++ b/vl.c
@@ -178,6 +178,8 @@ int main(int argc, char **argv)
#define MAX_VIRTIO_CONSOLES 1
#define MAX_SCLP_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;
@@ -3507,6 +3509,9 @@ int main(int argc, char **argv, char **envp)
qdev_prop_register_global_list(slew_lost_ticks);
break;
}
+ case QEMU_OPTION_no_stopflag:
+ use_stopflag = 0;
+ break;
case QEMU_OPTION_acpitable:
do_acpitable_option(optarg);
break;