Commit 426eeecd authored by Peter Maydell's avatar Peter Maydell Committed by Richard Henderson
Browse files

cpu-exec: Exit exclusive region on longjmp from step_atomic



Commit ac03ee53 narrowed the scope of the exclusive
region so it only covers when we're executing the TB, not when
we're generating it. However it missed that there is more than
one execution path out of cpu_tb_exec -- if the atomic insn
causes an exception then the code will longjmp out, skipping
the code to end the exclusive region. This causes QEMU to hang
the next time the CPU calls start_exclusive(), waiting for
itself to exit the region.

Move the "end the region" code out to the end of the
function so that it is run for both normal exit and also
for exit-via-longjmp. We have to use a volatile bool flag
to decide whether we need to end the region, because we
can longjump out of the codegen as well as the execution.

(For some reason this only reproduces for me with a clang
optimized build, not a gcc debug build.)

Reviewed-by: default avatarEmilio G. Cota <cota@braap.org>
Reviewed-by: default avatarAlex Bennée <alex.bennee@linaro.org>
Reviewed-by: default avatarRichard Henderson <richard.henderson@linaro.org>
Fixes: ac03ee53
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
Message-Id: <1509640536-32160-1-git-send-email-peter.maydell@linaro.org>
Signed-off-by: default avatarRichard Henderson <richard.henderson@linaro.org>
parent ba2c7479
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -233,6 +233,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
    uint32_t flags;
    uint32_t cflags = 1;
    uint32_t cf_mask = cflags & CF_HASH_MASK;
    /* volatile because we modify it between setjmp and longjmp */
    volatile bool in_exclusive_region = false;

    if (sigsetjmp(cpu->jmp_env, 0) == 0) {
        tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
@@ -251,14 +253,12 @@ void cpu_exec_step_atomic(CPUState *cpu)

        /* Since we got here, we know that parallel_cpus must be true.  */
        parallel_cpus = false;
        in_exclusive_region = true;
        cc->cpu_exec_enter(cpu);
        /* execute the generated code */
        trace_exec_tb(tb, pc);
        cpu_tb_exec(cpu, tb);
        cc->cpu_exec_exit(cpu);
        parallel_cpus = true;

        end_exclusive();
    } else {
        /* We may have exited due to another problem here, so we need
         * to reset any tb_locks we may have taken but didn't release.
@@ -270,6 +270,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
#endif
        tb_lock_reset();
    }

    if (in_exclusive_region) {
        /* We might longjump out of either the codegen or the
         * execution, so must make sure we only end the exclusive
         * region if we started it.
         */
        parallel_cpus = true;
        end_exclusive();
    }
}

struct tb_desc {