summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2011-10-05 10:04:02 +0100
committerAnas Nashif <anas.nashif@intel.com>2013-01-15 13:32:14 -0800
commit19b46ea555a4646cec73d070e38d0009aec5c1a9 (patch)
treefeb6a7b2af8c85de9e888a6a084a932dc4668a15
parent81b1816061f4ce34b33cf4448e6521f615bc4aae (diff)
downloadqemu-19b46ea555a4646cec73d070e38d0009aec5c1a9.tar.gz
qemu-19b46ea555a4646cec73d070e38d0009aec5c1a9.tar.bz2
qemu-19b46ea555a4646cec73d070e38d0009aec5c1a9.zip
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]
-rw-r--r--cpu-exec.c11
-rw-r--r--exec.c14
-rw-r--r--gen-icount.h16
-rw-r--r--linux-user/main.c8
-rw-r--r--qemu-options.hx9
-rw-r--r--vl.c5
6 files changed, 60 insertions, 3 deletions
diff --git a/cpu-exec.c b/cpu-exec.c
index 904ee73c7..b83d43ed7 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 8435de0bd..edb44fca7 100644
--- a/exec.c
+++ b/exec.c
@@ -119,6 +119,8 @@ DEFINE_TLS(CPUArchState *,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 */
@@ -1734,7 +1736,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);
+ }
}
}
@@ -1757,7 +1765,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/gen-icount.h b/gen-icount.h
index 248cf5b16..27ab04826 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(CPUArchState, 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 25e35cd3d..c339af83e 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -64,6 +64,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;
@@ -3192,6 +3193,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;
@@ -3247,6 +3253,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 de43b1b48..94a1c50b7 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1258,6 +1258,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/vl.c b/vl.c
index a3ab3841a..a13012e57 100644
--- a/vl.c
+++ b/vl.c
@@ -177,6 +177,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;
@@ -3226,6 +3228,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;