aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorEmilio G. Cota <cota@braap.org>2017-06-14 20:36:13 -0400
committerRichard Henderson <rth@twiddle.net>2017-06-30 11:40:59 -0700
commitf3ced3c59287dabc253f83f0c70aa4934470c15e (patch)
tree726cf8a382384bed4ca5379ec1027c754b40b706 /include
parent53f6672bcf57d82b794a2cc3a3469be7d35c8653 (diff)
tcg: consistently access cpu->tb_jmp_cache atomically
Some code paths can lead to atomic accesses racing with memset() on cpu->tb_jmp_cache, which can result in torn reads/writes and is undefined behaviour in C11. These torn accesses are unlikely to show up as bugs, but from code inspection they seem possible. For example, tb_phys_invalidate does: /* remove the TB from the hash list */ h = tb_jmp_cache_hash_func(tb->pc); CPU_FOREACH(cpu) { if (atomic_read(&cpu->tb_jmp_cache[h]) == tb) { atomic_set(&cpu->tb_jmp_cache[h], NULL); } } Here atomic_set might race with a concurrent memset (such as the ones scheduled via "unsafe" async work, e.g. tlb_flush_page) and therefore we might end up with a torn pointer (or who knows what, because we are under undefined behaviour). This patch converts parallel accesses to cpu->tb_jmp_cache to use atomic primitives, thereby bringing these accesses back to defined behaviour. The price to pay is to potentially execute more instructions when clearing cpu->tb_jmp_cache, but given how infrequently they happen and the small size of the cache, the performance impact I have measured is within noise range when booting debian-arm. Note that under "safe async" work (e.g. do_tb_flush) we could use memset because no other vcpus are running. However I'm keeping these accesses atomic as well to keep things simple and to avoid confusing analysis tools such as ThreadSanitizer. Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Emilio G. Cota <cota@braap.org> Message-Id: <1497486973-25845-1-git-send-email-cota@braap.org> Signed-off-by: Richard Henderson <rth@twiddle.net>
Diffstat (limited to 'include')
-rw-r--r--include/qom/cpu.h11
1 files changed, 10 insertions, 1 deletions
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 89ddb686fb..2fe7cff9fe 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -346,7 +346,7 @@ struct CPUState {
void *env_ptr; /* CPUArchState */
- /* Writes protected by tb_lock, reads not thread-safe */
+ /* Accessed in parallel; all accesses must be atomic */
struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE];
struct GDBRegisterState *gdb_regs;
@@ -422,6 +422,15 @@ extern struct CPUTailQ cpus;
extern __thread CPUState *current_cpu;
+static inline void cpu_tb_jmp_cache_clear(CPUState *cpu)
+{
+ unsigned int i;
+
+ for (i = 0; i < TB_JMP_CACHE_SIZE; i++) {
+ atomic_set(&cpu->tb_jmp_cache[i], NULL);
+ }
+}
+
/**
* qemu_tcg_mttcg_enabled:
* Check whether we are running MultiThread TCG or not.