Skip to content

Commit

Permalink
Handle CPU interrupts by inline checking of a flag
Browse files Browse the repository at this point in the history
Fix some of 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 avoids crashes if a thread or signal handler calls cpu_exit()
while the execution thread is itself modifying the TB graph (which
may happen in system emulation mode as well as in linux-user mode
with a multithreaded guest binary).

This fixes the crashes seen in LP:668799; however there are another
class of crashes described in LP:1098729 which stem from the fact
that in linux-user with a multithreaded guest all threads will
use and modify the same global TCG date structures (including the
generated code buffer) without any kind of locking. This means that
multithreaded guest binaries are still in the "unsupported"
category.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
  • Loading branch information
pm215 authored and blueswirl committed Mar 3, 2013
1 parent 7721137 commit 378df4b
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 4 deletions.
25 changes: 24 additions & 1 deletion cpu-exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
TranslationBlock *tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
cpu_pc_from_tb(env, tb);
}
if ((next_tb & TB_EXIT_MASK) == TB_EXIT_REQUESTED) {
/* We were asked to stop executing TBs (probably a pending
* interrupt. We've now stopped, so clear the flag.
*/
cpu->tcg_exit_req = 0;
}
return next_tb;
}

Expand Down Expand Up @@ -608,7 +614,20 @@ int cpu_exec(CPUArchState *env)
tc_ptr = tb->tc_ptr;
/* execute the generated code */
next_tb = cpu_tb_exec(cpu, tc_ptr);
if ((next_tb & TB_EXIT_MASK) == TB_EXIT_ICOUNT_EXPIRED) {
switch (next_tb & TB_EXIT_MASK) {
case TB_EXIT_REQUESTED:
/* Something asked us to stop executing
* chained TBs; just continue round the main
* loop. Whatever requested the exit will also
* have set something else (eg exit_request or
* interrupt_request) which we will handle
* next time around the loop.
*/
tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
next_tb = 0;
break;
case TB_EXIT_ICOUNT_EXPIRED:
{
/* Instruction counter expired. */
int insns_left;
tb = (TranslationBlock *)(next_tb & ~TB_EXIT_MASK);
Expand All @@ -632,6 +651,10 @@ int cpu_exec(CPUArchState *env)
next_tb = 0;
cpu_loop_exit(env);
}
break;
}
default:
break;
}
}
cpu->current_tb = NULL;
Expand Down
2 changes: 1 addition & 1 deletion exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ void cpu_exit(CPUArchState *env)
CPUState *cpu = ENV_GET_CPU(env);

cpu->exit_request = 1;
cpu_unlink_tb(cpu);
cpu->tcg_exit_req = 1;
}

void cpu_abort(CPUArchState *env, const char *fmt, ...)
Expand Down
12 changes: 12 additions & 0 deletions include/exec/gen-icount.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@

static TCGArg *icount_arg;
static int icount_label;
static int exitreq_label;

static inline void gen_icount_start(void)
{
TCGv_i32 count;
TCGv_i32 flag;

exitreq_label = gen_new_label();
flag = tcg_temp_local_new_i32();
tcg_gen_ld_i32(flag, cpu_env,
offsetof(CPUState, tcg_exit_req) - ENV_OFFSET);
tcg_gen_brcondi_i32(TCG_COND_NE, flag, 0, exitreq_label);
tcg_temp_free_i32(flag);

if (!use_icount)
return;
Expand All @@ -29,6 +38,9 @@ static inline void gen_icount_start(void)

static void gen_icount_end(TranslationBlock *tb, int num_insns)
{
gen_set_label(exitreq_label);
tcg_gen_exit_tb((tcg_target_long)tb + TB_EXIT_REQUESTED);

if (use_icount) {
*icount_arg = num_insns;
gen_set_label(icount_label);
Expand Down
3 changes: 3 additions & 0 deletions include/qom/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ struct kvm_run;
* @created: Indicates whether the CPU thread has been successfully created.
* @stop: Indicates a pending stop request.
* @stopped: Indicates the CPU has been artificially stopped.
* @tcg_exit_req: Set to force TCG to stop executing linked TBs for this
* CPU and return to its top level loop.
* @env_ptr: Pointer to subclass-specific CPUArchState field.
* @current_tb: Currently executing TB.
* @kvm_fd: vCPU file descriptor for KVM.
Expand Down Expand Up @@ -100,6 +102,7 @@ struct CPUState {
bool stop;
bool stopped;
volatile sig_atomic_t exit_request;
volatile sig_atomic_t tcg_exit_req;

void *env_ptr; /* CPUArchState */
struct TranslationBlock *current_tb;
Expand Down
5 changes: 5 additions & 0 deletions tcg/tcg.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,10 @@ TCGv_i64 tcg_const_local_i64(int64_t val);
* would hit zero midway through it. In this case the next-TB pointer
* returned is the TB we were about to execute, and the caller must
* arrange to execute the remaining count of instructions.
* 3: we stopped because the CPU's exit_request flag was set
* (usually meaning that there is an interrupt that needs to be
* handled). The next-TB pointer returned is the TB we were
* about to execute when we noticed the pending exit request.
*
* If the bottom two bits indicate an exit-via-index then the CPU
* state is correctly synchronised and ready for execution of the next
Expand All @@ -719,6 +723,7 @@ TCGv_i64 tcg_const_local_i64(int64_t val);
#define TB_EXIT_IDX0 0
#define TB_EXIT_IDX1 1
#define TB_EXIT_ICOUNT_EXPIRED 2
#define TB_EXIT_REQUESTED 3

#if !defined(tcg_qemu_tb_exec)
# define tcg_qemu_tb_exec(env, tb_ptr) \
Expand Down
4 changes: 2 additions & 2 deletions translate-all.c
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,7 @@ static void tcg_handle_interrupt(CPUArchState *env, int mask)
cpu_abort(env, "Raised interrupt while not in I/O function");
}
} else {
cpu_unlink_tb(cpu);
cpu->tcg_exit_req = 1;
}
}

Expand Down Expand Up @@ -1626,7 +1626,7 @@ void cpu_interrupt(CPUArchState *env, int mask)
CPUState *cpu = ENV_GET_CPU(env);

env->interrupt_request |= mask;
cpu_unlink_tb(cpu);
cpu->tcg_exit_req = 1;
}

/*
Expand Down

0 comments on commit 378df4b

Please sign in to comment.