qemu-arm.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PULL 08/24] tcg: drop global lock during TCG code execution
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
@ 2017-02-24 11:20 ` Alex Bennée
  2017-02-27 12:48   ` [Qemu-devel] " Laurent Desnogues
  2017-02-24 11:21 ` [PULL 16/24] cputlb and arm/sparc targets: convert mmuidx flushes from varg to bitmap Alex Bennée
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:20 UTC (permalink / raw)
  To: peter.maydell
  Cc: qemu-devel, Jan Kiszka, KONRAD Frederic, Emilio G . Cota,
	Alex Bennée, Paolo Bonzini, Peter Crosthwaite,
	Richard Henderson, Eduardo Habkost, Michael S. Tsirkin,
	David Gibson, Alexander Graf, open list:ARM cores,
	open list:PowerPC

From: Jan Kiszka <jan.kiszka@siemens.com>

This finally allows TCG to benefit from the iothread introduction: Drop
the global mutex while running pure TCG CPU code. Reacquire the lock
when entering MMIO or PIO emulation, or when leaving the TCG loop.

We have to revert a few optimization for the current TCG threading
model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
kicking it in qemu_cpu_kick. We also need to disable RAM block
reordering until we have a more efficient locking mechanism at hand.

Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
These numbers demonstrate where we gain something:

20338 jan       20   0  331m  75m 6904 R   99  0.9   0:50.95 qemu-system-arm
20337 jan       20   0  331m  75m 6904 S   20  0.9   0:26.50 qemu-system-arm

The guest CPU was fully loaded, but the iothread could still run mostly
independent on a second core. Without the patch we don't get beyond

32206 jan       20   0  330m  73m 7036 R   82  0.9   1:06.00 qemu-system-arm
32204 jan       20   0  330m  73m 7036 S   21  0.9   0:17.03 qemu-system-arm

We don't benefit significantly, though, when the guest is not fully
loading a host CPU.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
[FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
[EGC: fixed iothread lock for cpu-exec IRQ handling]
Signed-off-by: Emilio G. Cota <cota@braap.org>
[AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
[PM: target-arm changes]
Acked-by: Peter Maydell <peter.maydell@linaro.org>
---
 cpu-exec.c                 | 23 +++++++++++++++++++++--
 cpus.c                     | 28 +++++-----------------------
 cputlb.c                   | 21 ++++++++++++++++++++-
 exec.c                     | 12 +++++++++---
 hw/core/irq.c              |  1 +
 hw/i386/kvmvapic.c         |  4 ++--
 hw/intc/arm_gicv3_cpuif.c  |  3 +++
 hw/ppc/ppc.c               | 16 +++++++++++++++-
 hw/ppc/spapr.c             |  3 +++
 include/qom/cpu.h          |  1 +
 memory.c                   |  2 ++
 qom/cpu.c                  | 10 ++++++++++
 target/arm/helper.c        |  6 ++++++
 target/arm/op_helper.c     | 43 +++++++++++++++++++++++++++++++++++++++----
 target/i386/smm_helper.c   |  7 +++++++
 target/s390x/misc_helper.c |  5 ++++-
 translate-all.c            |  9 +++++++--
 translate-common.c         | 21 +++++++++++----------
 18 files changed, 166 insertions(+), 49 deletions(-)

diff --git a/cpu-exec.c b/cpu-exec.c
index 06a6b25564..1bd3d72002 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -29,6 +29,7 @@
 #include "qemu/rcu.h"
 #include "exec/tb-hash.h"
 #include "exec/log.h"
+#include "qemu/main-loop.h"
 #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
 #include "hw/i386/apic.h"
 #endif
@@ -388,8 +389,10 @@ static inline bool cpu_handle_halt(CPUState *cpu)
         if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
             && replay_interrupt()) {
             X86CPU *x86_cpu = X86_CPU(cpu);
+            qemu_mutex_lock_iothread();
             apic_poll_irq(x86_cpu->apic_state);
             cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
+            qemu_mutex_unlock_iothread();
         }
 #endif
         if (!cpu_has_work(cpu)) {
@@ -443,7 +446,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
 #else
             if (replay_exception()) {
                 CPUClass *cc = CPU_GET_CLASS(cpu);
+                qemu_mutex_lock_iothread();
                 cc->do_interrupt(cpu);
+                qemu_mutex_unlock_iothread();
                 cpu->exception_index = -1;
             } else if (!replay_has_interrupt()) {
                 /* give a chance to iothread in replay mode */
@@ -469,9 +474,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
                                         TranslationBlock **last_tb)
 {
     CPUClass *cc = CPU_GET_CLASS(cpu);
-    int interrupt_request = cpu->interrupt_request;
 
-    if (unlikely(interrupt_request)) {
+    if (unlikely(atomic_read(&cpu->interrupt_request))) {
+        int interrupt_request;
+        qemu_mutex_lock_iothread();
+        interrupt_request = cpu->interrupt_request;
         if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
             /* Mask out external interrupts for this step. */
             interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
@@ -479,6 +486,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
         if (interrupt_request & CPU_INTERRUPT_DEBUG) {
             cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
             cpu->exception_index = EXCP_DEBUG;
+            qemu_mutex_unlock_iothread();
             return true;
         }
         if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
@@ -488,6 +496,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
             cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
             cpu->halted = 1;
             cpu->exception_index = EXCP_HLT;
+            qemu_mutex_unlock_iothread();
             return true;
         }
 #if defined(TARGET_I386)
@@ -498,12 +507,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
             cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
             do_cpu_init(x86_cpu);
             cpu->exception_index = EXCP_HALTED;
+            qemu_mutex_unlock_iothread();
             return true;
         }
 #else
         else if (interrupt_request & CPU_INTERRUPT_RESET) {
             replay_interrupt();
             cpu_reset(cpu);
+            qemu_mutex_unlock_iothread();
             return true;
         }
 #endif
@@ -526,7 +537,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
                the program flow was changed */
             *last_tb = NULL;
         }
+
+        /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
+        qemu_mutex_unlock_iothread();
     }
+
+
     if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
         atomic_set(&cpu->exit_request, 0);
         cpu->exception_index = EXCP_INTERRUPT;
@@ -643,6 +659,9 @@ int cpu_exec(CPUState *cpu)
 #endif /* buggy compiler */
         cpu->can_do_io = 1;
         tb_lock_reset();
+        if (qemu_mutex_iothread_locked()) {
+            qemu_mutex_unlock_iothread();
+        }
     }
 
     /* if an exception is pending, we execute it here */
diff --git a/cpus.c b/cpus.c
index 860034a794..0ae8f69be5 100644
--- a/cpus.c
+++ b/cpus.c
@@ -1027,8 +1027,6 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
 #endif /* _WIN32 */
 
 static QemuMutex qemu_global_mutex;
-static QemuCond qemu_io_proceeded_cond;
-static unsigned iothread_requesting_mutex;
 
 static QemuThread io_thread;
 
@@ -1042,7 +1040,6 @@ void qemu_init_cpu_loop(void)
     qemu_init_sigbus();
     qemu_cond_init(&qemu_cpu_cond);
     qemu_cond_init(&qemu_pause_cond);
-    qemu_cond_init(&qemu_io_proceeded_cond);
     qemu_mutex_init(&qemu_global_mutex);
 
     qemu_thread_get_self(&io_thread);
@@ -1085,10 +1082,6 @@ static void qemu_tcg_wait_io_event(CPUState *cpu)
 
     start_tcg_kick_timer();
 
-    while (iothread_requesting_mutex) {
-        qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex);
-    }
-
     CPU_FOREACH(cpu) {
         qemu_wait_io_event_common(cpu);
     }
@@ -1249,9 +1242,11 @@ static int tcg_cpu_exec(CPUState *cpu)
         cpu->icount_decr.u16.low = decr;
         cpu->icount_extra = count;
     }
+    qemu_mutex_unlock_iothread();
     cpu_exec_start(cpu);
     ret = cpu_exec(cpu);
     cpu_exec_end(cpu);
+    qemu_mutex_lock_iothread();
 #ifdef CONFIG_PROFILER
     tcg_time += profile_getclock() - ti;
 #endif
@@ -1479,27 +1474,14 @@ bool qemu_mutex_iothread_locked(void)
 
 void qemu_mutex_lock_iothread(void)
 {
-    atomic_inc(&iothread_requesting_mutex);
-    /* In the simple case there is no need to bump the VCPU thread out of
-     * TCG code execution.
-     */
-    if (!tcg_enabled() || qemu_in_vcpu_thread() ||
-        !first_cpu || !first_cpu->created) {
-        qemu_mutex_lock(&qemu_global_mutex);
-        atomic_dec(&iothread_requesting_mutex);
-    } else {
-        if (qemu_mutex_trylock(&qemu_global_mutex)) {
-            qemu_cpu_kick_rr_cpu();
-            qemu_mutex_lock(&qemu_global_mutex);
-        }
-        atomic_dec(&iothread_requesting_mutex);
-        qemu_cond_broadcast(&qemu_io_proceeded_cond);
-    }
+    g_assert(!qemu_mutex_iothread_locked());
+    qemu_mutex_lock(&qemu_global_mutex);
     iothread_locked = true;
 }
 
 void qemu_mutex_unlock_iothread(void)
 {
+    g_assert(qemu_mutex_iothread_locked());
     iothread_locked = false;
     qemu_mutex_unlock(&qemu_global_mutex);
 }
diff --git a/cputlb.c b/cputlb.c
index 6c39927455..1cc9d9da51 100644
--- a/cputlb.c
+++ b/cputlb.c
@@ -18,6 +18,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 #include "cpu.h"
 #include "exec/exec-all.h"
 #include "exec/memory.h"
@@ -495,6 +496,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
     hwaddr physaddr = iotlbentry->addr;
     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
     uint64_t val;
+    bool locked = false;
 
     physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
     cpu->mem_io_pc = retaddr;
@@ -503,7 +505,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
     }
 
     cpu->mem_io_vaddr = addr;
+
+    if (mr->global_locking) {
+        qemu_mutex_lock_iothread();
+        locked = true;
+    }
     memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs);
+    if (locked) {
+        qemu_mutex_unlock_iothread();
+    }
+
     return val;
 }
 
@@ -514,15 +525,23 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
     CPUState *cpu = ENV_GET_CPU(env);
     hwaddr physaddr = iotlbentry->addr;
     MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
+    bool locked = false;
 
     physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
     if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
         cpu_io_recompile(cpu, retaddr);
     }
-
     cpu->mem_io_vaddr = addr;
     cpu->mem_io_pc = retaddr;
+
+    if (mr->global_locking) {
+        qemu_mutex_lock_iothread();
+        locked = true;
+    }
     memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs);
+    if (locked) {
+        qemu_mutex_unlock_iothread();
+    }
 }
 
 /* Return true if ADDR is present in the victim tlb, and has been copied
diff --git a/exec.c b/exec.c
index 865a1e8295..3adf2b1861 100644
--- a/exec.c
+++ b/exec.c
@@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
                 }
                 cpu->watchpoint_hit = wp;
 
-                /* The tb_lock will be reset when cpu_loop_exit or
-                 * cpu_loop_exit_noexc longjmp back into the cpu_exec
-                 * main loop.
+                /* Both tb_lock and iothread_mutex will be reset when
+                 * cpu_loop_exit or cpu_loop_exit_noexc longjmp
+                 * back into the cpu_exec main loop.
                  */
                 tb_lock();
                 tb_check_watchpoint(cpu);
@@ -2371,8 +2371,14 @@ static void io_mem_init(void)
     memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
     memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
                           NULL, UINT64_MAX);
+
+    /* io_mem_notdirty calls tb_invalidate_phys_page_fast,
+     * which can be called without the iothread mutex.
+     */
     memory_region_init_io(&io_mem_notdirty, NULL, &notdirty_mem_ops, NULL,
                           NULL, UINT64_MAX);
+    memory_region_clear_global_locking(&io_mem_notdirty);
+
     memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
                           NULL, UINT64_MAX);
 }
diff --git a/hw/core/irq.c b/hw/core/irq.c
index 49ff2e64fe..b98d1d69f5 100644
--- a/hw/core/irq.c
+++ b/hw/core/irq.c
@@ -22,6 +22,7 @@
  * THE SOFTWARE.
  */
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 #include "qemu-common.h"
 #include "hw/irq.h"
 #include "qom/object.h"
diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index 7135633863..82a49556af 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -457,8 +457,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
     resume_all_vcpus();
 
     if (!kvm_enabled()) {
-        /* tb_lock will be reset when cpu_loop_exit_noexc longjmps
-         * back into the cpu_exec loop. */
+        /* Both tb_lock and iothread_mutex will be reset when
+         *  longjmps back into the cpu_exec loop. */
         tb_lock();
         tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1);
         cpu_loop_exit_noexc(cs);
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index c25ee03556..f775aba507 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -14,6 +14,7 @@
 
 #include "qemu/osdep.h"
 #include "qemu/bitops.h"
+#include "qemu/main-loop.h"
 #include "trace.h"
 #include "gicv3_internal.h"
 #include "cpu.h"
@@ -733,6 +734,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
     ARMCPU *cpu = ARM_CPU(cs->cpu);
     CPUARMState *env = &cpu->env;
 
+    g_assert(qemu_mutex_iothread_locked());
+
     trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
                              cs->hppi.grp, cs->hppi.prio);
 
diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
index d171e60b5c..5f93083d4a 100644
--- a/hw/ppc/ppc.c
+++ b/hw/ppc/ppc.c
@@ -62,7 +62,16 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
 {
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
-    unsigned int old_pending = env->pending_interrupts;
+    unsigned int old_pending;
+    bool locked = false;
+
+    /* We may already have the BQL if coming from the reset path */
+    if (!qemu_mutex_iothread_locked()) {
+        locked = true;
+        qemu_mutex_lock_iothread();
+    }
+
+    old_pending = env->pending_interrupts;
 
     if (level) {
         env->pending_interrupts |= 1 << n_IRQ;
@@ -80,9 +89,14 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
 #endif
     }
 
+
     LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
                 "req %08x\n", __func__, env, n_IRQ, level,
                 env->pending_interrupts, CPU(cpu)->interrupt_request);
+
+    if (locked) {
+        qemu_mutex_unlock_iothread();
+    }
 }
 
 /* PowerPC 6xx / 7xx internal IRQ controller */
diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
index e465d7ac98..b1e374f3f9 100644
--- a/hw/ppc/spapr.c
+++ b/hw/ppc/spapr.c
@@ -1010,6 +1010,9 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
 {
     CPUPPCState *env = &cpu->env;
 
+    /* The TCG path should also be holding the BQL at this point */
+    g_assert(qemu_mutex_iothread_locked());
+
     if (msr_pr) {
         hcall_dprintf("Hypercall made with MSR[PR]=1\n");
         env->gpr[3] = H_PRIVILEGE;
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 2cf4ecf144..10db89b16a 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -329,6 +329,7 @@ struct CPUState {
     bool unplug;
     bool crash_occurred;
     bool exit_request;
+    /* updates protected by BQL */
     uint32_t interrupt_request;
     int singlestep_enabled;
     int64_t icount_extra;
diff --git a/memory.c b/memory.c
index ed8b5aa83e..d61caee867 100644
--- a/memory.c
+++ b/memory.c
@@ -917,6 +917,8 @@ void memory_region_transaction_commit(void)
     AddressSpace *as;
 
     assert(memory_region_transaction_depth);
+    assert(qemu_mutex_iothread_locked());
+
     --memory_region_transaction_depth;
     if (!memory_region_transaction_depth) {
         if (memory_region_update_pending) {
diff --git a/qom/cpu.c b/qom/cpu.c
index ed87c50cea..58784bcbea 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -113,9 +113,19 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
     error_setg(errp, "Obtaining memory mappings is unsupported on this CPU.");
 }
 
+/* Resetting the IRQ comes from across the code base so we take the
+ * BQL here if we need to.  cpu_interrupt assumes it is held.*/
 void cpu_reset_interrupt(CPUState *cpu, int mask)
 {
+    bool need_lock = !qemu_mutex_iothread_locked();
+
+    if (need_lock) {
+        qemu_mutex_lock_iothread();
+    }
     cpu->interrupt_request &= ~mask;
+    if (need_lock) {
+        qemu_mutex_unlock_iothread();
+    }
 }
 
 void cpu_exit(CPUState *cpu)
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 47250bcf16..753a69d40d 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6769,6 +6769,12 @@ void arm_cpu_do_interrupt(CPUState *cs)
         arm_cpu_do_interrupt_aarch32(cs);
     }
 
+    /* Hooks may change global state so BQL should be held, also the
+     * BQL needs to be held for any modification of
+     * cs->interrupt_request.
+     */
+    g_assert(qemu_mutex_iothread_locked());
+
     arm_call_el_change_hook(cpu);
 
     if (!kvm_enabled()) {
diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index fb366fdc35..5f3e3bdae2 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -18,6 +18,7 @@
  */
 #include "qemu/osdep.h"
 #include "qemu/log.h"
+#include "qemu/main-loop.h"
 #include "cpu.h"
 #include "exec/helper-proto.h"
 #include "internals.h"
@@ -487,7 +488,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
      */
     env->regs[15] &= (env->thumb ? ~1 : ~3);
 
+    qemu_mutex_lock_iothread();
     arm_call_el_change_hook(arm_env_get_cpu(env));
+    qemu_mutex_unlock_iothread();
 }
 
 /* Access to user mode registers from privileged modes.  */
@@ -735,28 +738,58 @@ void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
 {
     const ARMCPRegInfo *ri = rip;
 
-    ri->writefn(env, ri, value);
+    if (ri->type & ARM_CP_IO) {
+        qemu_mutex_lock_iothread();
+        ri->writefn(env, ri, value);
+        qemu_mutex_unlock_iothread();
+    } else {
+        ri->writefn(env, ri, value);
+    }
 }
 
 uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip)
 {
     const ARMCPRegInfo *ri = rip;
+    uint32_t res;
 
-    return ri->readfn(env, ri);
+    if (ri->type & ARM_CP_IO) {
+        qemu_mutex_lock_iothread();
+        res = ri->readfn(env, ri);
+        qemu_mutex_unlock_iothread();
+    } else {
+        res = ri->readfn(env, ri);
+    }
+
+    return res;
 }
 
 void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value)
 {
     const ARMCPRegInfo *ri = rip;
 
-    ri->writefn(env, ri, value);
+    if (ri->type & ARM_CP_IO) {
+        qemu_mutex_lock_iothread();
+        ri->writefn(env, ri, value);
+        qemu_mutex_unlock_iothread();
+    } else {
+        ri->writefn(env, ri, value);
+    }
 }
 
 uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
 {
     const ARMCPRegInfo *ri = rip;
+    uint64_t res;
+
+    if (ri->type & ARM_CP_IO) {
+        qemu_mutex_lock_iothread();
+        res = ri->readfn(env, ri);
+        qemu_mutex_unlock_iothread();
+    } else {
+        res = ri->readfn(env, ri);
+    }
 
-    return ri->readfn(env, ri);
+    return res;
 }
 
 void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
@@ -989,7 +1022,9 @@ void HELPER(exception_return)(CPUARMState *env)
                       cur_el, new_el, env->pc);
     }
 
+    qemu_mutex_lock_iothread();
     arm_call_el_change_hook(arm_env_get_cpu(env));
+    qemu_mutex_unlock_iothread();
 
     return;
 
diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c
index 4dd6a2c544..f051a77c4a 100644
--- a/target/i386/smm_helper.c
+++ b/target/i386/smm_helper.c
@@ -18,6 +18,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 #include "cpu.h"
 #include "exec/helper-proto.h"
 #include "exec/log.h"
@@ -42,11 +43,14 @@ void helper_rsm(CPUX86State *env)
 #define SMM_REVISION_ID 0x00020000
 #endif
 
+/* Called with iothread lock taken */
 void cpu_smm_update(X86CPU *cpu)
 {
     CPUX86State *env = &cpu->env;
     bool smm_enabled = (env->hflags & HF_SMM_MASK);
 
+    g_assert(qemu_mutex_iothread_locked());
+
     if (cpu->smram) {
         memory_region_set_enabled(cpu->smram, smm_enabled);
     }
@@ -333,7 +337,10 @@ void helper_rsm(CPUX86State *env)
     }
     env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK;
     env->hflags &= ~HF_SMM_MASK;
+
+    qemu_mutex_lock_iothread();
     cpu_smm_update(cpu);
+    qemu_mutex_unlock_iothread();
 
     qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
     log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
index c9604ea9c7..3cb942e8bb 100644
--- a/target/s390x/misc_helper.c
+++ b/target/s390x/misc_helper.c
@@ -25,6 +25,7 @@
 #include "exec/helper-proto.h"
 #include "sysemu/kvm.h"
 #include "qemu/timer.h"
+#include "qemu/main-loop.h"
 #include "exec/address-spaces.h"
 #ifdef CONFIG_KVM
 #include <linux/kvm.h>
@@ -109,11 +110,13 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
 /* SCLP service call */
 uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
 {
+    qemu_mutex_lock_iothread();
     int r = sclp_service_call(env, r1, r2);
     if (r < 0) {
         program_interrupt(env, -r, 4);
-        return 0;
+        r = 0;
     }
+    qemu_mutex_unlock_iothread();
     return r;
 }
 
diff --git a/translate-all.c b/translate-all.c
index 8a861cb583..f810259c41 100644
--- a/translate-all.c
+++ b/translate-all.c
@@ -55,6 +55,7 @@
 #include "translate-all.h"
 #include "qemu/bitmap.h"
 #include "qemu/timer.h"
+#include "qemu/main-loop.h"
 #include "exec/log.h"
 
 /* #define DEBUG_TB_INVALIDATE */
@@ -1523,7 +1524,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
 #ifdef CONFIG_SOFTMMU
 /* len must be <= 8 and start must be a multiple of len.
  * Called via softmmu_template.h when code areas are written to with
- * tb_lock held.
+ * iothread mutex not held.
  */
 void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
 {
@@ -1725,7 +1726,10 @@ void tb_check_watchpoint(CPUState *cpu)
 
 #ifndef CONFIG_USER_ONLY
 /* in deterministic execution mode, instructions doing device I/Os
-   must be at the end of the TB */
+ * must be at the end of the TB.
+ *
+ * Called by softmmu_template.h, with iothread mutex not held.
+ */
 void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
 {
 #if defined(TARGET_MIPS) || defined(TARGET_SH4)
@@ -1937,6 +1941,7 @@ void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
 
 void cpu_interrupt(CPUState *cpu, int mask)
 {
+    g_assert(qemu_mutex_iothread_locked());
     cpu->interrupt_request |= mask;
     cpu->tcg_exit_req = 1;
 }
diff --git a/translate-common.c b/translate-common.c
index 5e989cdf70..d504dd0d33 100644
--- a/translate-common.c
+++ b/translate-common.c
@@ -21,6 +21,7 @@
 #include "qemu-common.h"
 #include "qom/cpu.h"
 #include "sysemu/cpus.h"
+#include "qemu/main-loop.h"
 
 uintptr_t qemu_real_host_page_size;
 intptr_t qemu_real_host_page_mask;
@@ -30,6 +31,7 @@ intptr_t qemu_real_host_page_mask;
 static void tcg_handle_interrupt(CPUState *cpu, int mask)
 {
     int old_mask;
+    g_assert(qemu_mutex_iothread_locked());
 
     old_mask = cpu->interrupt_request;
     cpu->interrupt_request |= mask;
@@ -40,17 +42,16 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
      */
     if (!qemu_cpu_is_self(cpu)) {
         qemu_cpu_kick(cpu);
-        return;
-    }
-
-    if (use_icount) {
-        cpu->icount_decr.u16.high = 0xffff;
-        if (!cpu->can_do_io
-            && (mask & ~old_mask) != 0) {
-            cpu_abort(cpu, "Raised interrupt while not in I/O function");
-        }
     } else {
-        cpu->tcg_exit_req = 1;
+        if (use_icount) {
+            cpu->icount_decr.u16.high = 0xffff;
+            if (!cpu->can_do_io
+                && (mask & ~old_mask) != 0) {
+                cpu_abort(cpu, "Raised interrupt while not in I/O function");
+            }
+        } else {
+            cpu->tcg_exit_req = 1;
+        }
     }
 }
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PULL 16/24] cputlb and arm/sparc targets: convert mmuidx flushes from varg to bitmap
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
  2017-02-24 11:20 ` [PULL 08/24] tcg: drop global lock during TCG code execution Alex Bennée
@ 2017-02-24 11:21 ` Alex Bennée
  2017-02-24 11:21 ` [PULL 20/24] target-arm/powerctl: defer cpu reset work to CPU context Alex Bennée
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:21 UTC (permalink / raw)
  To: peter.maydell
  Cc: qemu-devel, Alex Bennée, Paolo Bonzini, Peter Crosthwaite,
	Richard Henderson, Mark Cave-Ayland, Artyom Tarasenko,
	open list:ARM

While the vargs approach was flexible the original MTTCG ended up
having munge the bits to a bitmap so the data could be used in
deferred work helpers. Instead of hiding that in cputlb we push the
change to the API to make it take a bitmap of MMU indexes instead.

For ARM some the resulting flushes end up being quite long so to aid
readability I've tended to move the index shifting to a new line so
all the bits being or-ed together line up nicely, for example:

    tlb_flush_page_by_mmuidx(other_cs, pageaddr,
                             (1 << ARMMMUIdx_S1SE1) |
                             (1 << ARMMMUIdx_S1SE0));

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
[AT: SPARC parts only]
Reviewed-by: Artyom Tarasenko <atar4qemu@gmail.com>
Reviewed-by: Richard Henderson <rth@twiddle.net>
[PM: ARM parts only]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 cputlb.c                   |  60 +++++++++--------------
 include/exec/exec-all.h    |  13 ++---
 target/arm/helper.c        | 116 ++++++++++++++++++++++++++++-----------------
 target/sparc/ldst_helper.c |   8 ++--
 4 files changed, 107 insertions(+), 90 deletions(-)

diff --git a/cputlb.c b/cputlb.c
index 5dfd3c3ba9..97e5c12de8 100644
--- a/cputlb.c
+++ b/cputlb.c
@@ -122,26 +122,25 @@ void tlb_flush(CPUState *cpu)
     }
 }
 
-static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, va_list argp)
+static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
 {
     CPUArchState *env = cpu->env_ptr;
+    unsigned long mmu_idx_bitmask = idxmap;
+    int mmu_idx;
 
     assert_cpu_is_self(cpu);
     tlb_debug("start\n");
 
     tb_lock();
 
-    for (;;) {
-        int mmu_idx = va_arg(argp, int);
-
-        if (mmu_idx < 0) {
-            break;
-        }
+    for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
 
-        tlb_debug("%d\n", mmu_idx);
+        if (test_bit(mmu_idx, &mmu_idx_bitmask)) {
+            tlb_debug("%d\n", mmu_idx);
 
-        memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0]));
-        memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
+            memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0]));
+            memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
+        }
     }
 
     memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache));
@@ -149,12 +148,9 @@ static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, va_list argp)
     tb_unlock();
 }
 
-void tlb_flush_by_mmuidx(CPUState *cpu, ...)
+void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
 {
-    va_list argp;
-    va_start(argp, cpu);
-    v_tlb_flush_by_mmuidx(cpu, argp);
-    va_end(argp);
+    v_tlb_flush_by_mmuidx(cpu, idxmap);
 }
 
 static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
@@ -219,13 +215,11 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
     }
 }
 
-void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...)
+void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap)
 {
     CPUArchState *env = cpu->env_ptr;
-    int i, k;
-    va_list argp;
-
-    va_start(argp, addr);
+    unsigned long mmu_idx_bitmap = idxmap;
+    int i, page, mmu_idx;
 
     assert_cpu_is_self(cpu);
     tlb_debug("addr "TARGET_FMT_lx"\n", addr);
@@ -236,31 +230,23 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...)
                   TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
                   env->tlb_flush_addr, env->tlb_flush_mask);
 
-        v_tlb_flush_by_mmuidx(cpu, argp);
-        va_end(argp);
+        v_tlb_flush_by_mmuidx(cpu, idxmap);
         return;
     }
 
     addr &= TARGET_PAGE_MASK;
-    i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
-
-    for (;;) {
-        int mmu_idx = va_arg(argp, int);
+    page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
 
-        if (mmu_idx < 0) {
-            break;
-        }
-
-        tlb_debug("idx %d\n", mmu_idx);
-
-        tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
+    for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
+        if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
+            tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr);
 
-        /* check whether there are vltb entries that need to be flushed */
-        for (k = 0; k < CPU_VTLB_SIZE; k++) {
-            tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr);
+            /* check whether there are vltb entries that need to be flushed */
+            for (i = 0; i < CPU_VTLB_SIZE; i++) {
+                tlb_flush_entry(&env->tlb_v_table[mmu_idx][i], addr);
+            }
         }
     }
-    va_end(argp);
 
     tb_flush_jmp_cache(cpu, addr);
 }
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index c694e3482b..e94e6849dd 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -106,21 +106,22 @@ void tlb_flush(CPUState *cpu);
  * tlb_flush_page_by_mmuidx:
  * @cpu: CPU whose TLB should be flushed
  * @addr: virtual address of page to be flushed
- * @...: list of MMU indexes to flush, terminated by a negative value
+ * @idxmap: bitmap of MMU indexes to flush
  *
  * Flush one page from the TLB of the specified CPU, for the specified
  * MMU indexes.
  */
-void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...);
+void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr,
+                              uint16_t idxmap);
 /**
  * tlb_flush_by_mmuidx:
  * @cpu: CPU whose TLB should be flushed
- * @...: list of MMU indexes to flush, terminated by a negative value
+ * @idxmap: bitmap of MMU indexes to flush
  *
  * Flush all entries from the TLB of the specified CPU, for the specified
  * MMU indexes.
  */
-void tlb_flush_by_mmuidx(CPUState *cpu, ...);
+void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap);
 /**
  * tlb_set_page_with_attrs:
  * @cpu: CPU to add this TLB entry for
@@ -169,11 +170,11 @@ static inline void tlb_flush(CPUState *cpu)
 }
 
 static inline void tlb_flush_page_by_mmuidx(CPUState *cpu,
-                                            target_ulong addr, ...)
+                                            target_ulong addr, uint16_t idxmap)
 {
 }
 
-static inline void tlb_flush_by_mmuidx(CPUState *cpu, ...)
+static inline void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
 {
 }
 #endif
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 753a69d40d..b41d0494d1 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -578,8 +578,10 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
 {
     CPUState *cs = ENV_GET_CPU(env);
 
-    tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0,
-                        ARMMMUIdx_S2NS, -1);
+    tlb_flush_by_mmuidx(cs,
+                        (1 << ARMMMUIdx_S12NSE1) |
+                        (1 << ARMMMUIdx_S12NSE0) |
+                        (1 << ARMMMUIdx_S2NS));
 }
 
 static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -588,8 +590,10 @@ static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *other_cs;
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1,
-                            ARMMMUIdx_S12NSE0, ARMMMUIdx_S2NS, -1);
+        tlb_flush_by_mmuidx(other_cs,
+                            (1 << ARMMMUIdx_S12NSE1) |
+                            (1 << ARMMMUIdx_S12NSE0) |
+                            (1 << ARMMMUIdx_S2NS));
     }
 }
 
@@ -611,7 +615,7 @@ static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     pageaddr = sextract64(value << 12, 0, 40);
 
-    tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S2NS, -1);
+    tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S2NS));
 }
 
 static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -627,7 +631,7 @@ static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     pageaddr = sextract64(value << 12, 0, 40);
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S2NS, -1);
+        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
     }
 }
 
@@ -636,7 +640,7 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
 {
     CPUState *cs = ENV_GET_CPU(env);
 
-    tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1E2, -1);
+    tlb_flush_by_mmuidx(cs, (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -645,7 +649,7 @@ static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *other_cs;
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1E2, -1);
+        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
     }
 }
 
@@ -655,7 +659,7 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *cs = ENV_GET_CPU(env);
     uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
 
-    tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1E2, -1);
+    tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -665,7 +669,7 @@ static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1E2, -1);
+        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
     }
 }
 
@@ -2542,8 +2546,10 @@ static void vttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     /* Accesses to VTTBR may change the VMID so we must flush the TLB.  */
     if (raw_read(env, ri) != value) {
-        tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0,
-                            ARMMMUIdx_S2NS, -1);
+        tlb_flush_by_mmuidx(cs,
+                            (1 << ARMMMUIdx_S12NSE1) |
+                            (1 << ARMMMUIdx_S12NSE0) |
+                            (1 << ARMMMUIdx_S2NS));
         raw_write(env, ri, value);
     }
 }
@@ -2902,9 +2908,13 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *cs = CPU(cpu);
 
     if (arm_is_secure_below_el3(env)) {
-        tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1);
+        tlb_flush_by_mmuidx(cs,
+                            (1 << ARMMMUIdx_S1SE1) |
+                            (1 << ARMMMUIdx_S1SE0));
     } else {
-        tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, -1);
+        tlb_flush_by_mmuidx(cs,
+                            (1 << ARMMMUIdx_S12NSE1) |
+                            (1 << ARMMMUIdx_S12NSE0));
     }
 }
 
@@ -2916,10 +2926,13 @@ static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     CPU_FOREACH(other_cs) {
         if (sec) {
-            tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1);
+            tlb_flush_by_mmuidx(other_cs,
+                                (1 << ARMMMUIdx_S1SE1) |
+                                (1 << ARMMMUIdx_S1SE0));
         } else {
-            tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1,
-                                ARMMMUIdx_S12NSE0, -1);
+            tlb_flush_by_mmuidx(other_cs,
+                                (1 << ARMMMUIdx_S12NSE1) |
+                                (1 << ARMMMUIdx_S12NSE0));
         }
     }
 }
@@ -2935,13 +2948,19 @@ static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *cs = CPU(cpu);
 
     if (arm_is_secure_below_el3(env)) {
-        tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1);
+        tlb_flush_by_mmuidx(cs,
+                            (1 << ARMMMUIdx_S1SE1) |
+                            (1 << ARMMMUIdx_S1SE0));
     } else {
         if (arm_feature(env, ARM_FEATURE_EL2)) {
-            tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0,
-                                ARMMMUIdx_S2NS, -1);
+            tlb_flush_by_mmuidx(cs,
+                                (1 << ARMMMUIdx_S12NSE1) |
+                                (1 << ARMMMUIdx_S12NSE0) |
+                                (1 << ARMMMUIdx_S2NS));
         } else {
-            tlb_flush_by_mmuidx(cs, ARMMMUIdx_S12NSE1, ARMMMUIdx_S12NSE0, -1);
+            tlb_flush_by_mmuidx(cs,
+                                (1 << ARMMMUIdx_S12NSE1) |
+                                (1 << ARMMMUIdx_S12NSE0));
         }
     }
 }
@@ -2952,7 +2971,7 @@ static void tlbi_aa64_alle2_write(CPUARMState *env, const ARMCPRegInfo *ri,
     ARMCPU *cpu = arm_env_get_cpu(env);
     CPUState *cs = CPU(cpu);
 
-    tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1E2, -1);
+    tlb_flush_by_mmuidx(cs, (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -2961,7 +2980,7 @@ static void tlbi_aa64_alle3_write(CPUARMState *env, const ARMCPRegInfo *ri,
     ARMCPU *cpu = arm_env_get_cpu(env);
     CPUState *cs = CPU(cpu);
 
-    tlb_flush_by_mmuidx(cs, ARMMMUIdx_S1E3, -1);
+    tlb_flush_by_mmuidx(cs, (1 << ARMMMUIdx_S1E3));
 }
 
 static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -2977,13 +2996,18 @@ static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     CPU_FOREACH(other_cs) {
         if (sec) {
-            tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1SE1, ARMMMUIdx_S1SE0, -1);
+            tlb_flush_by_mmuidx(other_cs,
+                                (1 << ARMMMUIdx_S1SE1) |
+                                (1 << ARMMMUIdx_S1SE0));
         } else if (has_el2) {
-            tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1,
-                                ARMMMUIdx_S12NSE0, ARMMMUIdx_S2NS, -1);
+            tlb_flush_by_mmuidx(other_cs,
+                                (1 << ARMMMUIdx_S12NSE1) |
+                                (1 << ARMMMUIdx_S12NSE0) |
+                                (1 << ARMMMUIdx_S2NS));
         } else {
-            tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S12NSE1,
-                                ARMMMUIdx_S12NSE0, -1);
+            tlb_flush_by_mmuidx(other_cs,
+                                (1 << ARMMMUIdx_S12NSE1) |
+                                (1 << ARMMMUIdx_S12NSE0));
         }
     }
 }
@@ -2994,7 +3018,7 @@ static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *other_cs;
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1E2, -1);
+        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
     }
 }
 
@@ -3004,7 +3028,7 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *other_cs;
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, ARMMMUIdx_S1E3, -1);
+        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E3));
     }
 }
 
@@ -3021,11 +3045,13 @@ static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri,
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
     if (arm_is_secure_below_el3(env)) {
-        tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1SE1,
-                                 ARMMMUIdx_S1SE0, -1);
+        tlb_flush_page_by_mmuidx(cs, pageaddr,
+                                 (1 << ARMMMUIdx_S1SE1) |
+                                 (1 << ARMMMUIdx_S1SE0));
     } else {
-        tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S12NSE1,
-                                 ARMMMUIdx_S12NSE0, -1);
+        tlb_flush_page_by_mmuidx(cs, pageaddr,
+                                 (1 << ARMMMUIdx_S12NSE1) |
+                                 (1 << ARMMMUIdx_S12NSE0));
     }
 }
 
@@ -3040,7 +3066,7 @@ static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *cs = CPU(cpu);
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
-    tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1E2, -1);
+    tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -3054,7 +3080,7 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri,
     CPUState *cs = CPU(cpu);
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
-    tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S1E3, -1);
+    tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S1E3));
 }
 
 static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -3066,11 +3092,13 @@ static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     CPU_FOREACH(other_cs) {
         if (sec) {
-            tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1SE1,
-                                     ARMMMUIdx_S1SE0, -1);
+            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
+                                     (1 << ARMMMUIdx_S1SE1) |
+                                     (1 << ARMMMUIdx_S1SE0));
         } else {
-            tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S12NSE1,
-                                     ARMMMUIdx_S12NSE0, -1);
+            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
+                                     (1 << ARMMMUIdx_S12NSE1) |
+                                     (1 << ARMMMUIdx_S12NSE0));
         }
     }
 }
@@ -3082,7 +3110,7 @@ static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1E2, -1);
+        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
     }
 }
 
@@ -3093,7 +3121,7 @@ static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S1E3, -1);
+        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E3));
     }
 }
 
@@ -3116,7 +3144,7 @@ static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     pageaddr = sextract64(value << 12, 0, 48);
 
-    tlb_flush_page_by_mmuidx(cs, pageaddr, ARMMMUIdx_S2NS, -1);
+    tlb_flush_page_by_mmuidx(cs, pageaddr, (1 << ARMMMUIdx_S2NS));
 }
 
 static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -3132,7 +3160,7 @@ static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
     pageaddr = sextract64(value << 12, 0, 48);
 
     CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, ARMMMUIdx_S2NS, -1);
+        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
     }
 }
 
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index 2c05d6af75..57968d9143 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -1768,13 +1768,15 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val,
           case 1:
               env->dmmu.mmu_primary_context = val;
               env->immu.mmu_primary_context = val;
-              tlb_flush_by_mmuidx(CPU(cpu), MMU_USER_IDX, MMU_KERNEL_IDX, -1);
+              tlb_flush_by_mmuidx(CPU(cpu),
+                                  (1 << MMU_USER_IDX) | (1 << MMU_KERNEL_IDX));
               break;
           case 2:
               env->dmmu.mmu_secondary_context = val;
               env->immu.mmu_secondary_context = val;
-              tlb_flush_by_mmuidx(CPU(cpu), MMU_USER_SECONDARY_IDX,
-                                  MMU_KERNEL_SECONDARY_IDX, -1);
+              tlb_flush_by_mmuidx(CPU(cpu),
+                                  (1 << MMU_USER_SECONDARY_IDX) |
+                                  (1 << MMU_KERNEL_SECONDARY_IDX));
               break;
           default:
               cpu_unassigned_access(cs, addr, true, false, 1, size);
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PULL 20/24] target-arm/powerctl: defer cpu reset work to CPU context
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
  2017-02-24 11:20 ` [PULL 08/24] tcg: drop global lock during TCG code execution Alex Bennée
  2017-02-24 11:21 ` [PULL 16/24] cputlb and arm/sparc targets: convert mmuidx flushes from varg to bitmap Alex Bennée
@ 2017-02-24 11:21 ` Alex Bennée
  2017-02-24 11:21 ` [PULL 21/24] target-arm: don't generate WFE/YIELD calls for MTTCG Alex Bennée
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:21 UTC (permalink / raw)
  To: peter.maydell; +Cc: qemu-devel, Alex Bennée, open list:ARM

When switching a new vCPU on we want to complete a bunch of the setup
work before we start scheduling the vCPU thread. To do this cleanly we
defer vCPU setup to async work which will run the vCPUs execution
context as the thread is woken up. The scheduling of the work will kick
the vCPU awake.

This avoids potential races in MTTCG system emulation.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 target/arm/arm-powerctl.c | 202 +++++++++++++++++++++++++++++++---------------
 target/arm/arm-powerctl.h |   2 +
 target/arm/cpu.c          |   4 +-
 target/arm/cpu.h          |  15 +++-
 target/arm/kvm.c          |   7 +-
 target/arm/machine.c      |  41 +++++++++-
 target/arm/psci.c         |   4 +-
 7 files changed, 201 insertions(+), 74 deletions(-)

diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index fbb7a15daa..25207cb850 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -14,6 +14,7 @@
 #include "internals.h"
 #include "arm-powerctl.h"
 #include "qemu/log.h"
+#include "qemu/main-loop.h"
 #include "exec/exec-all.h"
 
 #ifndef DEBUG_ARM_POWERCTL
@@ -48,11 +49,93 @@ CPUState *arm_get_cpu_by_id(uint64_t id)
     return NULL;
 }
 
+struct CpuOnInfo {
+    uint64_t entry;
+    uint64_t context_id;
+    uint32_t target_el;
+    bool target_aa64;
+};
+
+
+static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
+                                      run_on_cpu_data data)
+{
+    ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
+    struct CpuOnInfo *info = (struct CpuOnInfo *) data.host_ptr;
+
+    /* Initialize the cpu we are turning on */
+    cpu_reset(target_cpu_state);
+    target_cpu_state->halted = 0;
+
+    if (info->target_aa64) {
+        if ((info->target_el < 3) && arm_feature(&target_cpu->env,
+                                                 ARM_FEATURE_EL3)) {
+            /*
+             * As target mode is AArch64, we need to set lower
+             * exception level (the requested level 2) to AArch64
+             */
+            target_cpu->env.cp15.scr_el3 |= SCR_RW;
+        }
+
+        if ((info->target_el < 2) && arm_feature(&target_cpu->env,
+                                                 ARM_FEATURE_EL2)) {
+            /*
+             * As target mode is AArch64, we need to set lower
+             * exception level (the requested level 1) to AArch64
+             */
+            target_cpu->env.cp15.hcr_el2 |= HCR_RW;
+        }
+
+        target_cpu->env.pstate = aarch64_pstate_mode(info->target_el, true);
+    } else {
+        /* We are requested to boot in AArch32 mode */
+        static const uint32_t mode_for_el[] = { 0,
+                                                ARM_CPU_MODE_SVC,
+                                                ARM_CPU_MODE_HYP,
+                                                ARM_CPU_MODE_SVC };
+
+        cpsr_write(&target_cpu->env, mode_for_el[info->target_el], CPSR_M,
+                   CPSRWriteRaw);
+    }
+
+    if (info->target_el == 3) {
+        /* Processor is in secure mode */
+        target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
+    } else {
+        /* Processor is not in secure mode */
+        target_cpu->env.cp15.scr_el3 |= SCR_NS;
+    }
+
+    /* We check if the started CPU is now at the correct level */
+    assert(info->target_el == arm_current_el(&target_cpu->env));
+
+    if (info->target_aa64) {
+        target_cpu->env.xregs[0] = info->context_id;
+        target_cpu->env.thumb = false;
+    } else {
+        target_cpu->env.regs[0] = info->context_id;
+        target_cpu->env.thumb = info->entry & 1;
+        info->entry &= 0xfffffffe;
+    }
+
+    /* Start the new CPU at the requested address */
+    cpu_set_pc(target_cpu_state, info->entry);
+
+    g_free(info);
+
+    /* Finally set the power status */
+    assert(qemu_mutex_iothread_locked());
+    target_cpu->power_state = PSCI_ON;
+}
+
 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
                    uint32_t target_el, bool target_aa64)
 {
     CPUState *target_cpu_state;
     ARMCPU *target_cpu;
+    struct CpuOnInfo *info;
+
+    assert(qemu_mutex_iothread_locked());
 
     DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64
             "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,
@@ -77,7 +160,7 @@ int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
     }
 
     target_cpu = ARM_CPU(target_cpu_state);
-    if (!target_cpu->powered_off) {
+    if (target_cpu->power_state == PSCI_ON) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "[ARM]%s: CPU %" PRId64 " is already on\n",
                       __func__, cpuid);
@@ -109,74 +192,54 @@ int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
         return QEMU_ARM_POWERCTL_INVALID_PARAM;
     }
 
-    /* Initialize the cpu we are turning on */
-    cpu_reset(target_cpu_state);
-    target_cpu->powered_off = false;
-    target_cpu_state->halted = 0;
-
-    if (target_aa64) {
-        if ((target_el < 3) && arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {
-            /*
-             * As target mode is AArch64, we need to set lower
-             * exception level (the requested level 2) to AArch64
-             */
-            target_cpu->env.cp15.scr_el3 |= SCR_RW;
-        }
-
-        if ((target_el < 2) && arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {
-            /*
-             * As target mode is AArch64, we need to set lower
-             * exception level (the requested level 1) to AArch64
-             */
-            target_cpu->env.cp15.hcr_el2 |= HCR_RW;
-        }
-
-        target_cpu->env.pstate = aarch64_pstate_mode(target_el, true);
-    } else {
-        /* We are requested to boot in AArch32 mode */
-        static uint32_t mode_for_el[] = { 0,
-                                          ARM_CPU_MODE_SVC,
-                                          ARM_CPU_MODE_HYP,
-                                          ARM_CPU_MODE_SVC };
-
-        cpsr_write(&target_cpu->env, mode_for_el[target_el], CPSR_M,
-                   CPSRWriteRaw);
-    }
-
-    if (target_el == 3) {
-        /* Processor is in secure mode */
-        target_cpu->env.cp15.scr_el3 &= ~SCR_NS;
-    } else {
-        /* Processor is not in secure mode */
-        target_cpu->env.cp15.scr_el3 |= SCR_NS;
-    }
-
-    /* We check if the started CPU is now at the correct level */
-    assert(target_el == arm_current_el(&target_cpu->env));
-
-    if (target_aa64) {
-        target_cpu->env.xregs[0] = context_id;
-        target_cpu->env.thumb = false;
-    } else {
-        target_cpu->env.regs[0] = context_id;
-        target_cpu->env.thumb = entry & 1;
-        entry &= 0xfffffffe;
+    /*
+     * If another CPU has powered the target on we are in the state
+     * ON_PENDING and additional attempts to power on the CPU should
+     * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI
+     * spec)
+     */
+    if (target_cpu->power_state == PSCI_ON_PENDING) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "[ARM]%s: CPU %" PRId64 " is already powering on\n",
+                      __func__, cpuid);
+        return QEMU_ARM_POWERCTL_ON_PENDING;
     }
 
-    /* Start the new CPU at the requested address */
-    cpu_set_pc(target_cpu_state, entry);
+    /* To avoid racing with a CPU we are just kicking off we do the
+     * final bit of preparation for the work in the target CPUs
+     * context.
+     */
+    info = g_new(struct CpuOnInfo, 1);
+    info->entry = entry;
+    info->context_id = context_id;
+    info->target_el = target_el;
+    info->target_aa64 = target_aa64;
 
-    qemu_cpu_kick(target_cpu_state);
+    async_run_on_cpu(target_cpu_state, arm_set_cpu_on_async_work,
+                     RUN_ON_CPU_HOST_PTR(info));
 
     /* We are good to go */
     return QEMU_ARM_POWERCTL_RET_SUCCESS;
 }
 
+static void arm_set_cpu_off_async_work(CPUState *target_cpu_state,
+                                       run_on_cpu_data data)
+{
+    ARMCPU *target_cpu = ARM_CPU(target_cpu_state);
+
+    assert(qemu_mutex_iothread_locked());
+    target_cpu->power_state = PSCI_OFF;
+    target_cpu_state->halted = 1;
+    target_cpu_state->exception_index = EXCP_HLT;
+}
+
 int arm_set_cpu_off(uint64_t cpuid)
 {
     CPUState *target_cpu_state;
     ARMCPU *target_cpu;
 
+    assert(qemu_mutex_iothread_locked());
+
     DPRINTF("cpu %" PRId64 "\n", cpuid);
 
     /* change to the cpu we are powering up */
@@ -185,27 +248,34 @@ int arm_set_cpu_off(uint64_t cpuid)
         return QEMU_ARM_POWERCTL_INVALID_PARAM;
     }
     target_cpu = ARM_CPU(target_cpu_state);
-    if (target_cpu->powered_off) {
+    if (target_cpu->power_state == PSCI_OFF) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "[ARM]%s: CPU %" PRId64 " is already off\n",
                       __func__, cpuid);
         return QEMU_ARM_POWERCTL_IS_OFF;
     }
 
-    target_cpu->powered_off = true;
-    target_cpu_state->halted = 1;
-    target_cpu_state->exception_index = EXCP_HLT;
-    cpu_loop_exit(target_cpu_state);
-    /* notreached */
+    /* Queue work to run under the target vCPUs context */
+    async_run_on_cpu(target_cpu_state, arm_set_cpu_off_async_work,
+                     RUN_ON_CPU_NULL);
 
     return QEMU_ARM_POWERCTL_RET_SUCCESS;
 }
 
+static void arm_reset_cpu_async_work(CPUState *target_cpu_state,
+                                     run_on_cpu_data data)
+{
+    /* Reset the cpu */
+    cpu_reset(target_cpu_state);
+}
+
 int arm_reset_cpu(uint64_t cpuid)
 {
     CPUState *target_cpu_state;
     ARMCPU *target_cpu;
 
+    assert(qemu_mutex_iothread_locked());
+
     DPRINTF("cpu %" PRId64 "\n", cpuid);
 
     /* change to the cpu we are resetting */
@@ -214,15 +284,17 @@ int arm_reset_cpu(uint64_t cpuid)
         return QEMU_ARM_POWERCTL_INVALID_PARAM;
     }
     target_cpu = ARM_CPU(target_cpu_state);
-    if (target_cpu->powered_off) {
+
+    if (target_cpu->power_state == PSCI_OFF) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "[ARM]%s: CPU %" PRId64 " is off\n",
                       __func__, cpuid);
         return QEMU_ARM_POWERCTL_IS_OFF;
     }
 
-    /* Reset the cpu */
-    cpu_reset(target_cpu_state);
+    /* Queue work to run under the target vCPUs context */
+    async_run_on_cpu(target_cpu_state, arm_reset_cpu_async_work,
+                     RUN_ON_CPU_NULL);
 
     return QEMU_ARM_POWERCTL_RET_SUCCESS;
 }
diff --git a/target/arm/arm-powerctl.h b/target/arm/arm-powerctl.h
index 98ee04989b..04353923c0 100644
--- a/target/arm/arm-powerctl.h
+++ b/target/arm/arm-powerctl.h
@@ -17,6 +17,7 @@
 #define QEMU_ARM_POWERCTL_INVALID_PARAM QEMU_PSCI_RET_INVALID_PARAMS
 #define QEMU_ARM_POWERCTL_ALREADY_ON QEMU_PSCI_RET_ALREADY_ON
 #define QEMU_ARM_POWERCTL_IS_OFF QEMU_PSCI_RET_DENIED
+#define QEMU_ARM_POWERCTL_ON_PENDING QEMU_PSCI_RET_ON_PENDING
 
 /*
  * arm_get_cpu_by_id:
@@ -43,6 +44,7 @@ CPUState *arm_get_cpu_by_id(uint64_t cpuid);
  * Returns: QEMU_ARM_POWERCTL_RET_SUCCESS on success.
  * QEMU_ARM_POWERCTL_INVALID_PARAM if bad parameters are provided.
  * QEMU_ARM_POWERCTL_ALREADY_ON if the CPU was already started.
+ * QEMU_ARM_POWERCTL_ON_PENDING if the CPU is still powering up
  */
 int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,
                    uint32_t target_el, bool target_aa64);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 4a069f6985..f7157dc0e5 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -45,7 +45,7 @@ static bool arm_cpu_has_work(CPUState *cs)
 {
     ARMCPU *cpu = ARM_CPU(cs);
 
-    return !cpu->powered_off
+    return (cpu->power_state != PSCI_OFF)
         && cs->interrupt_request &
         (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD
          | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ
@@ -132,7 +132,7 @@ static void arm_cpu_reset(CPUState *s)
     env->vfp.xregs[ARM_VFP_MVFR1] = cpu->mvfr1;
     env->vfp.xregs[ARM_VFP_MVFR2] = cpu->mvfr2;
 
-    cpu->powered_off = cpu->start_powered_off;
+    cpu->power_state = cpu->start_powered_off ? PSCI_OFF : PSCI_ON;
     s->halted = cpu->start_powered_off;
 
     if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 0956a54e89..e285ba3b4b 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -526,6 +526,15 @@ typedef struct CPUARMState {
  */
 typedef void ARMELChangeHook(ARMCPU *cpu, void *opaque);
 
+
+/* These values map onto the return values for
+ * QEMU_PSCI_0_2_FN_AFFINITY_INFO */
+typedef enum ARMPSCIState {
+    PSCI_OFF = 0,
+    PSCI_ON = 1,
+    PSCI_ON_PENDING = 2
+} ARMPSCIState;
+
 /**
  * ARMCPU:
  * @env: #CPUARMState
@@ -582,8 +591,10 @@ struct ARMCPU {
 
     /* Should CPU start in PSCI powered-off state? */
     bool start_powered_off;
-    /* CPU currently in PSCI powered-off state */
-    bool powered_off;
+
+    /* Current power state, access guarded by BQL */
+    ARMPSCIState power_state;
+
     /* CPU has virtualization extension */
     bool has_el2;
     /* CPU has security extension */
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index c00b94e42a..395e986973 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -488,8 +488,8 @@ int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu)
 {
     if (cap_has_mp_state) {
         struct kvm_mp_state mp_state = {
-            .mp_state =
-            cpu->powered_off ? KVM_MP_STATE_STOPPED : KVM_MP_STATE_RUNNABLE
+            .mp_state = (cpu->power_state == PSCI_OFF) ?
+            KVM_MP_STATE_STOPPED : KVM_MP_STATE_RUNNABLE
         };
         int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
         if (ret) {
@@ -515,7 +515,8 @@ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu)
                     __func__, ret, strerror(-ret));
             abort();
         }
-        cpu->powered_off = (mp_state.mp_state == KVM_MP_STATE_STOPPED);
+        cpu->power_state = (mp_state.mp_state == KVM_MP_STATE_STOPPED) ?
+            PSCI_OFF : PSCI_ON;
     }
 
     return 0;
diff --git a/target/arm/machine.c b/target/arm/machine.c
index fa5ec76090..d8094a840b 100644
--- a/target/arm/machine.c
+++ b/target/arm/machine.c
@@ -211,6 +211,38 @@ static const VMStateInfo vmstate_cpsr = {
     .put = put_cpsr,
 };
 
+static int get_power(QEMUFile *f, void *opaque, size_t size,
+                    VMStateField *field)
+{
+    ARMCPU *cpu = opaque;
+    bool powered_off = qemu_get_byte(f);
+    cpu->power_state = powered_off ? PSCI_OFF : PSCI_ON;
+    return 0;
+}
+
+static int put_power(QEMUFile *f, void *opaque, size_t size,
+                    VMStateField *field, QJSON *vmdesc)
+{
+    ARMCPU *cpu = opaque;
+
+    /* Migration should never happen while we transition power states */
+
+    if (cpu->power_state == PSCI_ON ||
+        cpu->power_state == PSCI_OFF) {
+        bool powered_off = (cpu->power_state == PSCI_OFF) ? true : false;
+        qemu_put_byte(f, powered_off);
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+static const VMStateInfo vmstate_powered_off = {
+    .name = "powered_off",
+    .get = get_power,
+    .put = put_power,
+};
+
 static void cpu_pre_save(void *opaque)
 {
     ARMCPU *cpu = opaque;
@@ -329,7 +361,14 @@ const VMStateDescription vmstate_arm_cpu = {
         VMSTATE_UINT64(env.exception.vaddress, ARMCPU),
         VMSTATE_TIMER_PTR(gt_timer[GTIMER_PHYS], ARMCPU),
         VMSTATE_TIMER_PTR(gt_timer[GTIMER_VIRT], ARMCPU),
-        VMSTATE_BOOL(powered_off, ARMCPU),
+        {
+            .name = "power_state",
+            .version_id = 0,
+            .size = sizeof(bool),
+            .info = &vmstate_powered_off,
+            .flags = VMS_SINGLE,
+            .offset = 0,
+        },
         VMSTATE_END_OF_LIST()
     },
     .subsections = (const VMStateDescription*[]) {
diff --git a/target/arm/psci.c b/target/arm/psci.c
index 64bf82eea1..ade9fe2ede 100644
--- a/target/arm/psci.c
+++ b/target/arm/psci.c
@@ -127,7 +127,9 @@ void arm_handle_psci_call(ARMCPU *cpu)
                 break;
             }
             target_cpu = ARM_CPU(target_cpu_state);
-            ret = target_cpu->powered_off ? 1 : 0;
+
+            g_assert(qemu_mutex_iothread_locked());
+            ret = target_cpu->power_state;
             break;
         default:
             /* Everything above affinity level 0 is always on. */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PULL 21/24] target-arm: don't generate WFE/YIELD calls for MTTCG
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
                   ` (2 preceding siblings ...)
  2017-02-24 11:21 ` [PULL 20/24] target-arm/powerctl: defer cpu reset work to CPU context Alex Bennée
@ 2017-02-24 11:21 ` Alex Bennée
  2017-02-24 11:21 ` [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete Alex Bennée
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:21 UTC (permalink / raw)
  To: peter.maydell; +Cc: qemu-devel, Alex Bennée, open list:ARM

The WFE and YIELD instructions are really only hints and in TCG's case
they were useful to move the scheduling on from one vCPU to the next. In
the parallel context (MTTCG) this just causes an unnecessary cpu_exit
and contention of the BQL.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 target/arm/op_helper.c     |  7 +++++++
 target/arm/translate-a64.c |  8 ++++++--
 target/arm/translate.c     | 20 ++++++++++++++++----
 3 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
index 5f3e3bdae2..d64c8670fa 100644
--- a/target/arm/op_helper.c
+++ b/target/arm/op_helper.c
@@ -436,6 +436,13 @@ void HELPER(yield)(CPUARMState *env)
     ARMCPU *cpu = arm_env_get_cpu(env);
     CPUState *cs = CPU(cpu);
 
+    /* When running in MTTCG we don't generate jumps to the yield and
+     * WFE helpers as it won't affect the scheduling of other vCPUs.
+     * If we wanted to more completely model WFE/SEV so we don't busy
+     * spin unnecessarily we would need to do something more involved.
+     */
+    g_assert(!parallel_cpus);
+
     /* This is a non-trappable hint instruction that generally indicates
      * that the guest is currently busy-looping. Yield control back to the
      * top level loop so that a more deserving VCPU has a chance to run.
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index e61bbd6b3b..e15eae6d41 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -1328,10 +1328,14 @@ static void handle_hint(DisasContext *s, uint32_t insn,
         s->is_jmp = DISAS_WFI;
         return;
     case 1: /* YIELD */
-        s->is_jmp = DISAS_YIELD;
+        if (!parallel_cpus) {
+            s->is_jmp = DISAS_YIELD;
+        }
         return;
     case 2: /* WFE */
-        s->is_jmp = DISAS_WFE;
+        if (!parallel_cpus) {
+            s->is_jmp = DISAS_WFE;
+        }
         return;
     case 4: /* SEV */
     case 5: /* SEVL */
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 4436d8f3a2..abc1f77ee4 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -4404,20 +4404,32 @@ static void gen_exception_return(DisasContext *s, TCGv_i32 pc)
     gen_rfe(s, pc, load_cpu_field(spsr));
 }
 
+/*
+ * For WFI we will halt the vCPU until an IRQ. For WFE and YIELD we
+ * only call the helper when running single threaded TCG code to ensure
+ * the next round-robin scheduled vCPU gets a crack. In MTTCG mode we
+ * just skip this instruction. Currently the SEV/SEVL instructions
+ * which are *one* of many ways to wake the CPU from WFE are not
+ * implemented so we can't sleep like WFI does.
+ */
 static void gen_nop_hint(DisasContext *s, int val)
 {
     switch (val) {
     case 1: /* yield */
-        gen_set_pc_im(s, s->pc);
-        s->is_jmp = DISAS_YIELD;
+        if (!parallel_cpus) {
+            gen_set_pc_im(s, s->pc);
+            s->is_jmp = DISAS_YIELD;
+        }
         break;
     case 3: /* wfi */
         gen_set_pc_im(s, s->pc);
         s->is_jmp = DISAS_WFI;
         break;
     case 2: /* wfe */
-        gen_set_pc_im(s, s->pc);
-        s->is_jmp = DISAS_WFE;
+        if (!parallel_cpus) {
+            gen_set_pc_im(s, s->pc);
+            s->is_jmp = DISAS_WFE;
+        }
         break;
     case 4: /* sev */
     case 5: /* sevl */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
                   ` (3 preceding siblings ...)
  2017-02-24 11:21 ` [PULL 21/24] target-arm: don't generate WFE/YIELD calls for MTTCG Alex Bennée
@ 2017-02-24 11:21 ` Alex Bennée
  2017-09-17 13:07   ` Dmitry Osipenko
  2017-02-24 11:21 ` [PULL 23/24] hw/misc/imx6_src: defer clearing of SRC_SCR reset bits Alex Bennée
  2017-02-24 11:21 ` [PULL 24/24] tcg: enable MTTCG by default for ARM on x86 hosts Alex Bennée
  6 siblings, 1 reply; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:21 UTC (permalink / raw)
  To: peter.maydell; +Cc: qemu-devel, Alex Bennée, open list:ARM

Previously flushes on other vCPUs would only get serviced when they
exited their TranslationBlocks. While this isn't overly problematic it
violates the semantics of TLB flush from the point of view of source
vCPU.

To solve this we call the cputlb *_all_cpus_synced() functions to do
the flushes which ensures all flushes are completed by the time the
vCPU next schedules its own work. As the TLB instructions are modelled
as CP writes the TB ends at this point meaning cpu->exit_request will
be checked before the next instruction is executed.

Deferring the work until the architectural sync point is a possible
future optimisation.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
 1 file changed, 69 insertions(+), 96 deletions(-)

diff --git a/target/arm/helper.c b/target/arm/helper.c
index b41d0494d1..bcedb4a808 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -536,41 +536,33 @@ static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                              uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush(other_cs);
-    }
+    tlb_flush_all_cpus_synced(cs);
 }
 
 static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                              uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush(other_cs);
-    }
+    tlb_flush_all_cpus_synced(cs);
 }
 
 static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                              uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page(other_cs, value & TARGET_PAGE_MASK);
-    }
+    tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
 }
 
 static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                              uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page(other_cs, value & TARGET_PAGE_MASK);
-    }
+    tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
 }
 
 static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -587,14 +579,12 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                   uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs,
-                            (1 << ARMMMUIdx_S12NSE1) |
-                            (1 << ARMMMUIdx_S12NSE0) |
-                            (1 << ARMMMUIdx_S2NS));
-    }
+    tlb_flush_by_mmuidx_all_cpus_synced(cs,
+                                        (1 << ARMMMUIdx_S12NSE1) |
+                                        (1 << ARMMMUIdx_S12NSE0) |
+                                        (1 << ARMMMUIdx_S2NS));
 }
 
 static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -621,7 +611,7 @@ static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
     uint64_t pageaddr;
 
     if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) {
@@ -630,9 +620,8 @@ static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     pageaddr = sextract64(value << 12, 0, 40);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
-    }
+    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                             (1 << ARMMMUIdx_S2NS));
 }
 
 static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -646,11 +635,9 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                  uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
-    }
+    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -665,12 +652,11 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                  uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
     uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
-    }
+    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                             (1 << ARMMMUIdx_S1E2));
 }
 
 static const ARMCPRegInfo cp_reginfo[] = {
@@ -2904,8 +2890,7 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env,
 static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                     uint64_t value)
 {
-    ARMCPU *cpu = arm_env_get_cpu(env);
-    CPUState *cs = CPU(cpu);
+    CPUState *cs = ENV_GET_CPU(env);
 
     if (arm_is_secure_below_el3(env)) {
         tlb_flush_by_mmuidx(cs,
@@ -2921,19 +2906,17 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                       uint64_t value)
 {
+    CPUState *cs = ENV_GET_CPU(env);
     bool sec = arm_is_secure_below_el3(env);
-    CPUState *other_cs;
 
-    CPU_FOREACH(other_cs) {
-        if (sec) {
-            tlb_flush_by_mmuidx(other_cs,
-                                (1 << ARMMMUIdx_S1SE1) |
-                                (1 << ARMMMUIdx_S1SE0));
-        } else {
-            tlb_flush_by_mmuidx(other_cs,
-                                (1 << ARMMMUIdx_S12NSE1) |
-                                (1 << ARMMMUIdx_S12NSE0));
-        }
+    if (sec) {
+        tlb_flush_by_mmuidx_all_cpus_synced(cs,
+                                            (1 << ARMMMUIdx_S1SE1) |
+                                            (1 << ARMMMUIdx_S1SE0));
+    } else {
+        tlb_flush_by_mmuidx_all_cpus_synced(cs,
+                                            (1 << ARMMMUIdx_S12NSE1) |
+                                            (1 << ARMMMUIdx_S12NSE0));
     }
 }
 
@@ -2990,46 +2973,40 @@ static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
      * stage 2 translations, whereas most other scopes only invalidate
      * stage 1 translations.
      */
+    CPUState *cs = ENV_GET_CPU(env);
     bool sec = arm_is_secure_below_el3(env);
     bool has_el2 = arm_feature(env, ARM_FEATURE_EL2);
-    CPUState *other_cs;
-
-    CPU_FOREACH(other_cs) {
-        if (sec) {
-            tlb_flush_by_mmuidx(other_cs,
-                                (1 << ARMMMUIdx_S1SE1) |
-                                (1 << ARMMMUIdx_S1SE0));
-        } else if (has_el2) {
-            tlb_flush_by_mmuidx(other_cs,
-                                (1 << ARMMMUIdx_S12NSE1) |
-                                (1 << ARMMMUIdx_S12NSE0) |
-                                (1 << ARMMMUIdx_S2NS));
-        } else {
-            tlb_flush_by_mmuidx(other_cs,
-                                (1 << ARMMMUIdx_S12NSE1) |
-                                (1 << ARMMMUIdx_S12NSE0));
-        }
+
+    if (sec) {
+        tlb_flush_by_mmuidx_all_cpus_synced(cs,
+                                            (1 << ARMMMUIdx_S1SE1) |
+                                            (1 << ARMMMUIdx_S1SE0));
+    } else if (has_el2) {
+        tlb_flush_by_mmuidx_all_cpus_synced(cs,
+                                            (1 << ARMMMUIdx_S12NSE1) |
+                                            (1 << ARMMMUIdx_S12NSE0) |
+                                            (1 << ARMMMUIdx_S2NS));
+    } else {
+          tlb_flush_by_mmuidx_all_cpus_synced(cs,
+                                              (1 << ARMMMUIdx_S12NSE1) |
+                                              (1 << ARMMMUIdx_S12NSE0));
     }
 }
 
 static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                     uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
-    }
+    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                     uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E3));
-    }
+    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E3));
 }
 
 static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -3086,43 +3063,40 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                    uint64_t value)
 {
+    ARMCPU *cpu = arm_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
     bool sec = arm_is_secure_below_el3(env);
-    CPUState *other_cs;
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
-    CPU_FOREACH(other_cs) {
-        if (sec) {
-            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
-                                     (1 << ARMMMUIdx_S1SE1) |
-                                     (1 << ARMMMUIdx_S1SE0));
-        } else {
-            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
-                                     (1 << ARMMMUIdx_S12NSE1) |
-                                     (1 << ARMMMUIdx_S12NSE0));
-        }
+    if (sec) {
+        tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                                 (1 << ARMMMUIdx_S1SE1) |
+                                                 (1 << ARMMMUIdx_S1SE0));
+    } else {
+        tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                                 (1 << ARMMMUIdx_S12NSE1) |
+                                                 (1 << ARMMMUIdx_S12NSE0));
     }
 }
 
 static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                    uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
-    }
+    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                             (1 << ARMMMUIdx_S1E2));
 }
 
 static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                    uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
     uint64_t pageaddr = sextract64(value << 12, 0, 56);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E3));
-    }
+    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                             (1 << ARMMMUIdx_S1E3));
 }
 
 static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -3150,7 +3124,7 @@ static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
 static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
                                       uint64_t value)
 {
-    CPUState *other_cs;
+    CPUState *cs = ENV_GET_CPU(env);
     uint64_t pageaddr;
 
     if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) {
@@ -3159,9 +3133,8 @@ static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     pageaddr = sextract64(value << 12, 0, 48);
 
-    CPU_FOREACH(other_cs) {
-        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
-    }
+    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
+                                             (1 << ARMMMUIdx_S2NS));
 }
 
 static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri,
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PULL 23/24] hw/misc/imx6_src: defer clearing of SRC_SCR reset bits
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
                   ` (4 preceding siblings ...)
  2017-02-24 11:21 ` [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete Alex Bennée
@ 2017-02-24 11:21 ` Alex Bennée
  2017-02-24 11:21 ` [PULL 24/24] tcg: enable MTTCG by default for ARM on x86 hosts Alex Bennée
  6 siblings, 0 replies; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:21 UTC (permalink / raw)
  To: peter.maydell; +Cc: qemu-devel, Alex Bennée, Peter Chubb, open list:i.MX31

The arm_reset_cpu/set_cpu_on/set_cpu_off() functions do their work
asynchronously in the target vCPUs context. As a result we need to
ensure the SRC_SCR reset bits correctly report the reset status at the
right time. To do this we defer the clearing of the bit with an async
job which will run after the work queued by ARM powerctl functions.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/misc/imx6_src.c | 58 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 49 insertions(+), 9 deletions(-)

diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c
index 55b817b8d7..edbb756c36 100644
--- a/hw/misc/imx6_src.c
+++ b/hw/misc/imx6_src.c
@@ -14,6 +14,7 @@
 #include "qemu/bitops.h"
 #include "qemu/log.h"
 #include "arm-powerctl.h"
+#include "qom/cpu.h"
 
 #ifndef DEBUG_IMX6_SRC
 #define DEBUG_IMX6_SRC 0
@@ -113,6 +114,45 @@ static uint64_t imx6_src_read(void *opaque, hwaddr offset, unsigned size)
     return value;
 }
 
+
+/* The reset is asynchronous so we need to defer clearing the reset
+ * bit until the work is completed.
+ */
+
+struct SRCSCRResetInfo {
+    IMX6SRCState *s;
+    int reset_bit;
+};
+
+static void imx6_clear_reset_bit(CPUState *cpu, run_on_cpu_data data)
+{
+    struct SRCSCRResetInfo *ri = data.host_ptr;
+    IMX6SRCState *s = ri->s;
+
+    assert(qemu_mutex_iothread_locked());
+
+    s->regs[SRC_SCR] = deposit32(s->regs[SRC_SCR], ri->reset_bit, 1, 0);
+    DPRINTF("reg[%s] <= 0x%" PRIx32 "\n",
+            imx6_src_reg_name(SRC_SCR), s->regs[SRC_SCR]);
+
+    g_free(ri);
+}
+
+static void imx6_defer_clear_reset_bit(int cpuid,
+                                       IMX6SRCState *s,
+                                       unsigned long reset_shift)
+{
+    struct SRCSCRResetInfo *ri;
+
+    ri = g_malloc(sizeof(struct SRCSCRResetInfo));
+    ri->s = s;
+    ri->reset_bit = reset_shift;
+
+    async_run_on_cpu(arm_get_cpu_by_id(cpuid), imx6_clear_reset_bit,
+                     RUN_ON_CPU_HOST_PTR(ri));
+}
+
+
 static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
                            unsigned size)
 {
@@ -153,7 +193,7 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
                 arm_set_cpu_off(3);
             }
             /* We clear the reset bits as the processor changed state */
-            clear_bit(CORE3_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(3, s, CORE3_RST_SHIFT);
             clear_bit(CORE3_RST_SHIFT, &change_mask);
         }
         if (EXTRACT(change_mask, CORE2_ENABLE)) {
@@ -162,11 +202,11 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
                 arm_set_cpu_on(2, s->regs[SRC_GPR5], s->regs[SRC_GPR6],
                                3, false);
             } else {
-                /* CORE 3 is shut down */
+                /* CORE 2 is shut down */
                 arm_set_cpu_off(2);
             }
             /* We clear the reset bits as the processor changed state */
-            clear_bit(CORE2_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(2, s, CORE2_RST_SHIFT);
             clear_bit(CORE2_RST_SHIFT, &change_mask);
         }
         if (EXTRACT(change_mask, CORE1_ENABLE)) {
@@ -175,28 +215,28 @@ static void imx6_src_write(void *opaque, hwaddr offset, uint64_t value,
                 arm_set_cpu_on(1, s->regs[SRC_GPR3], s->regs[SRC_GPR4],
                                3, false);
             } else {
-                /* CORE 3 is shut down */
+                /* CORE 1 is shut down */
                 arm_set_cpu_off(1);
             }
             /* We clear the reset bits as the processor changed state */
-            clear_bit(CORE1_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(1, s, CORE1_RST_SHIFT);
             clear_bit(CORE1_RST_SHIFT, &change_mask);
         }
         if (EXTRACT(change_mask, CORE0_RST)) {
             arm_reset_cpu(0);
-            clear_bit(CORE0_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(0, s, CORE0_RST_SHIFT);
         }
         if (EXTRACT(change_mask, CORE1_RST)) {
             arm_reset_cpu(1);
-            clear_bit(CORE1_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(1, s, CORE1_RST_SHIFT);
         }
         if (EXTRACT(change_mask, CORE2_RST)) {
             arm_reset_cpu(2);
-            clear_bit(CORE2_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(2, s, CORE2_RST_SHIFT);
         }
         if (EXTRACT(change_mask, CORE3_RST)) {
             arm_reset_cpu(3);
-            clear_bit(CORE3_RST_SHIFT, &current_value);
+            imx6_defer_clear_reset_bit(3, s, CORE3_RST_SHIFT);
         }
         if (EXTRACT(change_mask, SW_IPU2_RST)) {
             /* We pretend the IPU2 is reset */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PULL 24/24] tcg: enable MTTCG by default for ARM on x86 hosts
       [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
                   ` (5 preceding siblings ...)
  2017-02-24 11:21 ` [PULL 23/24] hw/misc/imx6_src: defer clearing of SRC_SCR reset bits Alex Bennée
@ 2017-02-24 11:21 ` Alex Bennée
  6 siblings, 0 replies; 18+ messages in thread
From: Alex Bennée @ 2017-02-24 11:21 UTC (permalink / raw)
  To: peter.maydell
  Cc: qemu-devel, Alex Bennée, Richard Henderson, open list:ARM

This enables the multi-threaded system emulation by default for ARMv7
and ARMv8 guests using the x86_64 TCG backend. This is because on the
guest side:

  - The ARM translate.c/translate-64.c have been converted to
    - use MTTCG safe atomic primitives
    - emit the appropriate barrier ops
  - The ARM machine has been updated to
    - hold the BQL when modifying shared cross-vCPU state
    - defer powerctl changes to async safe work

All the host backends support the barrier and atomic primitives but
need to provide same-or-better support for normal load/store
operations.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Acked-by: Peter Maydell <peter.maydell@linaro.org>
Tested-by: Pranith Kumar <bobby.prani@gmail.com>
Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
---
 configure             |  6 ++++++
 target/arm/cpu.h      |  3 +++
 tcg/i386/tcg-target.h | 11 +++++++++++
 3 files changed, 20 insertions(+)

diff --git a/configure b/configure
index 4b68861992..44ecbe6f74 100755
--- a/configure
+++ b/configure
@@ -5879,6 +5879,7 @@ mkdir -p $target_dir
 echo "# Automatically generated by configure - do not modify" > $config_target_mak
 
 bflt="no"
+mttcg="no"
 interp_prefix1=$(echo "$interp_prefix" | sed "s/%M/$target_name/g")
 gdb_xml_files=""
 
@@ -5897,11 +5898,13 @@ case "$target_name" in
   arm|armeb)
     TARGET_ARCH=arm
     bflt="yes"
+    mttcg="yes"
     gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
   ;;
   aarch64)
     TARGET_BASE_ARCH=arm
     bflt="yes"
+    mttcg="yes"
     gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
   ;;
   cris)
@@ -6066,6 +6069,9 @@ if test "$target_bigendian" = "yes" ; then
 fi
 if test "$target_softmmu" = "yes" ; then
   echo "CONFIG_SOFTMMU=y" >> $config_target_mak
+  if test "$mttcg" = "yes" ; then
+    echo "TARGET_SUPPORTS_MTTCG=y" >> $config_target_mak
+  fi
 fi
 if test "$target_user_only" = "yes" ; then
   echo "CONFIG_USER_ONLY=y" >> $config_target_mak
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index e285ba3b4b..38a8e00908 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -30,6 +30,9 @@
 #  define TARGET_LONG_BITS 32
 #endif
 
+/* ARM processors have a weak memory model */
+#define TCG_GUEST_DEFAULT_MO      (0)
+
 #define CPUArchState struct CPUARMState
 
 #include "qemu-common.h"
diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h
index 21d96ec35c..4275787db9 100644
--- a/tcg/i386/tcg-target.h
+++ b/tcg/i386/tcg-target.h
@@ -165,4 +165,15 @@ static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
 {
 }
 
+/* This defines the natural memory order supported by this
+ * architecture before guarantees made by various barrier
+ * instructions.
+ *
+ * The x86 has a pretty strong memory ordering which only really
+ * allows for some stores to be re-ordered after loads.
+ */
+#include "tcg-mo.h"
+
+#define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD)
+
 #endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PULL 08/24] tcg: drop global lock during TCG code execution
  2017-02-24 11:20 ` [PULL 08/24] tcg: drop global lock during TCG code execution Alex Bennée
@ 2017-02-27 12:48   ` Laurent Desnogues
  2017-02-27 14:39     ` Alex Bennée
  0 siblings, 1 reply; 18+ messages in thread
From: Laurent Desnogues @ 2017-02-27 12:48 UTC (permalink / raw)
  To: Alex Bennée, Jan Kiszka; +Cc: qemu-devel@nongnu.org, open list:ARM cores

Hello,

On Fri, Feb 24, 2017 at 12:20 PM, Alex Bennée <alex.bennee@linaro.org> wrote:
> From: Jan Kiszka <jan.kiszka@siemens.com>
>
> This finally allows TCG to benefit from the iothread introduction: Drop
> the global mutex while running pure TCG CPU code. Reacquire the lock
> when entering MMIO or PIO emulation, or when leaving the TCG loop.
>
> We have to revert a few optimization for the current TCG threading
> model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
> kicking it in qemu_cpu_kick. We also need to disable RAM block
> reordering until we have a more efficient locking mechanism at hand.
>
> Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
> These numbers demonstrate where we gain something:
>
> 20338 jan       20   0  331m  75m 6904 R   99  0.9   0:50.95 qemu-system-arm
> 20337 jan       20   0  331m  75m 6904 S   20  0.9   0:26.50 qemu-system-arm
>
> The guest CPU was fully loaded, but the iothread could still run mostly
> independent on a second core. Without the patch we don't get beyond
>
> 32206 jan       20   0  330m  73m 7036 R   82  0.9   1:06.00 qemu-system-arm
> 32204 jan       20   0  330m  73m 7036 S   21  0.9   0:17.03 qemu-system-arm
>
> We don't benefit significantly, though, when the guest is not fully
> loading a host CPU.

I tried this patch (8d04fb55 in the repository) with the following image:

   http://wiki.qemu.org/download/arm-test-0.2.tar.gz

Running the image with no option works fine.  But specifying '-icount
1' results in a (guest) deadlock. Enabling some heavy logging (-d
in_asm,exec) sometimes results in a 'Bad ram offset'.

Is it expected that this patch breaks -icount?

Thanks,

Laurent

PS - To clarify 791158d9 works.

> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
> [FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
> Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
> [EGC: fixed iothread lock for cpu-exec IRQ handling]
> Signed-off-by: Emilio G. Cota <cota@braap.org>
> [AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Reviewed-by: Richard Henderson <rth@twiddle.net>
> Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
> [PM: target-arm changes]
> Acked-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  cpu-exec.c                 | 23 +++++++++++++++++++++--
>  cpus.c                     | 28 +++++-----------------------
>  cputlb.c                   | 21 ++++++++++++++++++++-
>  exec.c                     | 12 +++++++++---
>  hw/core/irq.c              |  1 +
>  hw/i386/kvmvapic.c         |  4 ++--
>  hw/intc/arm_gicv3_cpuif.c  |  3 +++
>  hw/ppc/ppc.c               | 16 +++++++++++++++-
>  hw/ppc/spapr.c             |  3 +++
>  include/qom/cpu.h          |  1 +
>  memory.c                   |  2 ++
>  qom/cpu.c                  | 10 ++++++++++
>  target/arm/helper.c        |  6 ++++++
>  target/arm/op_helper.c     | 43 +++++++++++++++++++++++++++++++++++++++----
>  target/i386/smm_helper.c   |  7 +++++++
>  target/s390x/misc_helper.c |  5 ++++-
>  translate-all.c            |  9 +++++++--
>  translate-common.c         | 21 +++++++++++----------
>  18 files changed, 166 insertions(+), 49 deletions(-)
>
> diff --git a/cpu-exec.c b/cpu-exec.c
> index 06a6b25564..1bd3d72002 100644
> --- a/cpu-exec.c
> +++ b/cpu-exec.c
> @@ -29,6 +29,7 @@
>  #include "qemu/rcu.h"
>  #include "exec/tb-hash.h"
>  #include "exec/log.h"
> +#include "qemu/main-loop.h"
>  #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
>  #include "hw/i386/apic.h"
>  #endif
> @@ -388,8 +389,10 @@ static inline bool cpu_handle_halt(CPUState *cpu)
>          if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
>              && replay_interrupt()) {
>              X86CPU *x86_cpu = X86_CPU(cpu);
> +            qemu_mutex_lock_iothread();
>              apic_poll_irq(x86_cpu->apic_state);
>              cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
> +            qemu_mutex_unlock_iothread();
>          }
>  #endif
>          if (!cpu_has_work(cpu)) {
> @@ -443,7 +446,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
>  #else
>              if (replay_exception()) {
>                  CPUClass *cc = CPU_GET_CLASS(cpu);
> +                qemu_mutex_lock_iothread();
>                  cc->do_interrupt(cpu);
> +                qemu_mutex_unlock_iothread();
>                  cpu->exception_index = -1;
>              } else if (!replay_has_interrupt()) {
>                  /* give a chance to iothread in replay mode */
> @@ -469,9 +474,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>                                          TranslationBlock **last_tb)
>  {
>      CPUClass *cc = CPU_GET_CLASS(cpu);
> -    int interrupt_request = cpu->interrupt_request;
>
> -    if (unlikely(interrupt_request)) {
> +    if (unlikely(atomic_read(&cpu->interrupt_request))) {
> +        int interrupt_request;
> +        qemu_mutex_lock_iothread();
> +        interrupt_request = cpu->interrupt_request;
>          if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
>              /* Mask out external interrupts for this step. */
>              interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
> @@ -479,6 +486,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>          if (interrupt_request & CPU_INTERRUPT_DEBUG) {
>              cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
>              cpu->exception_index = EXCP_DEBUG;
> +            qemu_mutex_unlock_iothread();
>              return true;
>          }
>          if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
> @@ -488,6 +496,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>              cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
>              cpu->halted = 1;
>              cpu->exception_index = EXCP_HLT;
> +            qemu_mutex_unlock_iothread();
>              return true;
>          }
>  #if defined(TARGET_I386)
> @@ -498,12 +507,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>              cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
>              do_cpu_init(x86_cpu);
>              cpu->exception_index = EXCP_HALTED;
> +            qemu_mutex_unlock_iothread();
>              return true;
>          }
>  #else
>          else if (interrupt_request & CPU_INTERRUPT_RESET) {
>              replay_interrupt();
>              cpu_reset(cpu);
> +            qemu_mutex_unlock_iothread();
>              return true;
>          }
>  #endif
> @@ -526,7 +537,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>                 the program flow was changed */
>              *last_tb = NULL;
>          }
> +
> +        /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
> +        qemu_mutex_unlock_iothread();
>      }
> +
> +
>      if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
>          atomic_set(&cpu->exit_request, 0);
>          cpu->exception_index = EXCP_INTERRUPT;
> @@ -643,6 +659,9 @@ int cpu_exec(CPUState *cpu)
>  #endif /* buggy compiler */
>          cpu->can_do_io = 1;
>          tb_lock_reset();
> +        if (qemu_mutex_iothread_locked()) {
> +            qemu_mutex_unlock_iothread();
> +        }
>      }
>
>      /* if an exception is pending, we execute it here */
> diff --git a/cpus.c b/cpus.c
> index 860034a794..0ae8f69be5 100644
> --- a/cpus.c
> +++ b/cpus.c
> @@ -1027,8 +1027,6 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
>  #endif /* _WIN32 */
>
>  static QemuMutex qemu_global_mutex;
> -static QemuCond qemu_io_proceeded_cond;
> -static unsigned iothread_requesting_mutex;
>
>  static QemuThread io_thread;
>
> @@ -1042,7 +1040,6 @@ void qemu_init_cpu_loop(void)
>      qemu_init_sigbus();
>      qemu_cond_init(&qemu_cpu_cond);
>      qemu_cond_init(&qemu_pause_cond);
> -    qemu_cond_init(&qemu_io_proceeded_cond);
>      qemu_mutex_init(&qemu_global_mutex);
>
>      qemu_thread_get_self(&io_thread);
> @@ -1085,10 +1082,6 @@ static void qemu_tcg_wait_io_event(CPUState *cpu)
>
>      start_tcg_kick_timer();
>
> -    while (iothread_requesting_mutex) {
> -        qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex);
> -    }
> -
>      CPU_FOREACH(cpu) {
>          qemu_wait_io_event_common(cpu);
>      }
> @@ -1249,9 +1242,11 @@ static int tcg_cpu_exec(CPUState *cpu)
>          cpu->icount_decr.u16.low = decr;
>          cpu->icount_extra = count;
>      }
> +    qemu_mutex_unlock_iothread();
>      cpu_exec_start(cpu);
>      ret = cpu_exec(cpu);
>      cpu_exec_end(cpu);
> +    qemu_mutex_lock_iothread();
>  #ifdef CONFIG_PROFILER
>      tcg_time += profile_getclock() - ti;
>  #endif
> @@ -1479,27 +1474,14 @@ bool qemu_mutex_iothread_locked(void)
>
>  void qemu_mutex_lock_iothread(void)
>  {
> -    atomic_inc(&iothread_requesting_mutex);
> -    /* In the simple case there is no need to bump the VCPU thread out of
> -     * TCG code execution.
> -     */
> -    if (!tcg_enabled() || qemu_in_vcpu_thread() ||
> -        !first_cpu || !first_cpu->created) {
> -        qemu_mutex_lock(&qemu_global_mutex);
> -        atomic_dec(&iothread_requesting_mutex);
> -    } else {
> -        if (qemu_mutex_trylock(&qemu_global_mutex)) {
> -            qemu_cpu_kick_rr_cpu();
> -            qemu_mutex_lock(&qemu_global_mutex);
> -        }
> -        atomic_dec(&iothread_requesting_mutex);
> -        qemu_cond_broadcast(&qemu_io_proceeded_cond);
> -    }
> +    g_assert(!qemu_mutex_iothread_locked());
> +    qemu_mutex_lock(&qemu_global_mutex);
>      iothread_locked = true;
>  }
>
>  void qemu_mutex_unlock_iothread(void)
>  {
> +    g_assert(qemu_mutex_iothread_locked());
>      iothread_locked = false;
>      qemu_mutex_unlock(&qemu_global_mutex);
>  }
> diff --git a/cputlb.c b/cputlb.c
> index 6c39927455..1cc9d9da51 100644
> --- a/cputlb.c
> +++ b/cputlb.c
> @@ -18,6 +18,7 @@
>   */
>
>  #include "qemu/osdep.h"
> +#include "qemu/main-loop.h"
>  #include "cpu.h"
>  #include "exec/exec-all.h"
>  #include "exec/memory.h"
> @@ -495,6 +496,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>      hwaddr physaddr = iotlbentry->addr;
>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
>      uint64_t val;
> +    bool locked = false;
>
>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
>      cpu->mem_io_pc = retaddr;
> @@ -503,7 +505,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>      }
>
>      cpu->mem_io_vaddr = addr;
> +
> +    if (mr->global_locking) {
> +        qemu_mutex_lock_iothread();
> +        locked = true;
> +    }
>      memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs);
> +    if (locked) {
> +        qemu_mutex_unlock_iothread();
> +    }
> +
>      return val;
>  }
>
> @@ -514,15 +525,23 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>      CPUState *cpu = ENV_GET_CPU(env);
>      hwaddr physaddr = iotlbentry->addr;
>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
> +    bool locked = false;
>
>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
>      if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
>          cpu_io_recompile(cpu, retaddr);
>      }
> -
>      cpu->mem_io_vaddr = addr;
>      cpu->mem_io_pc = retaddr;
> +
> +    if (mr->global_locking) {
> +        qemu_mutex_lock_iothread();
> +        locked = true;
> +    }
>      memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs);
> +    if (locked) {
> +        qemu_mutex_unlock_iothread();
> +    }
>  }
>
>  /* Return true if ADDR is present in the victim tlb, and has been copied
> diff --git a/exec.c b/exec.c
> index 865a1e8295..3adf2b1861 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
>                  }
>                  cpu->watchpoint_hit = wp;
>
> -                /* The tb_lock will be reset when cpu_loop_exit or
> -                 * cpu_loop_exit_noexc longjmp back into the cpu_exec
> -                 * main loop.
> +                /* Both tb_lock and iothread_mutex will be reset when
> +                 * cpu_loop_exit or cpu_loop_exit_noexc longjmp
> +                 * back into the cpu_exec main loop.
>                   */
>                  tb_lock();
>                  tb_check_watchpoint(cpu);
> @@ -2371,8 +2371,14 @@ static void io_mem_init(void)
>      memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
>      memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
>                            NULL, UINT64_MAX);
> +
> +    /* io_mem_notdirty calls tb_invalidate_phys_page_fast,
> +     * which can be called without the iothread mutex.
> +     */
>      memory_region_init_io(&io_mem_notdirty, NULL, &notdirty_mem_ops, NULL,
>                            NULL, UINT64_MAX);
> +    memory_region_clear_global_locking(&io_mem_notdirty);
> +
>      memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
>                            NULL, UINT64_MAX);
>  }
> diff --git a/hw/core/irq.c b/hw/core/irq.c
> index 49ff2e64fe..b98d1d69f5 100644
> --- a/hw/core/irq.c
> +++ b/hw/core/irq.c
> @@ -22,6 +22,7 @@
>   * THE SOFTWARE.
>   */
>  #include "qemu/osdep.h"
> +#include "qemu/main-loop.h"
>  #include "qemu-common.h"
>  #include "hw/irq.h"
>  #include "qom/object.h"
> diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
> index 7135633863..82a49556af 100644
> --- a/hw/i386/kvmvapic.c
> +++ b/hw/i386/kvmvapic.c
> @@ -457,8 +457,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
>      resume_all_vcpus();
>
>      if (!kvm_enabled()) {
> -        /* tb_lock will be reset when cpu_loop_exit_noexc longjmps
> -         * back into the cpu_exec loop. */
> +        /* Both tb_lock and iothread_mutex will be reset when
> +         *  longjmps back into the cpu_exec loop. */
>          tb_lock();
>          tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1);
>          cpu_loop_exit_noexc(cs);
> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> index c25ee03556..f775aba507 100644
> --- a/hw/intc/arm_gicv3_cpuif.c
> +++ b/hw/intc/arm_gicv3_cpuif.c
> @@ -14,6 +14,7 @@
>
>  #include "qemu/osdep.h"
>  #include "qemu/bitops.h"
> +#include "qemu/main-loop.h"
>  #include "trace.h"
>  #include "gicv3_internal.h"
>  #include "cpu.h"
> @@ -733,6 +734,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
>      ARMCPU *cpu = ARM_CPU(cs->cpu);
>      CPUARMState *env = &cpu->env;
>
> +    g_assert(qemu_mutex_iothread_locked());
> +
>      trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
>                               cs->hppi.grp, cs->hppi.prio);
>
> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
> index d171e60b5c..5f93083d4a 100644
> --- a/hw/ppc/ppc.c
> +++ b/hw/ppc/ppc.c
> @@ -62,7 +62,16 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
>  {
>      CPUState *cs = CPU(cpu);
>      CPUPPCState *env = &cpu->env;
> -    unsigned int old_pending = env->pending_interrupts;
> +    unsigned int old_pending;
> +    bool locked = false;
> +
> +    /* We may already have the BQL if coming from the reset path */
> +    if (!qemu_mutex_iothread_locked()) {
> +        locked = true;
> +        qemu_mutex_lock_iothread();
> +    }
> +
> +    old_pending = env->pending_interrupts;
>
>      if (level) {
>          env->pending_interrupts |= 1 << n_IRQ;
> @@ -80,9 +89,14 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
>  #endif
>      }
>
> +
>      LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
>                  "req %08x\n", __func__, env, n_IRQ, level,
>                  env->pending_interrupts, CPU(cpu)->interrupt_request);
> +
> +    if (locked) {
> +        qemu_mutex_unlock_iothread();
> +    }
>  }
>
>  /* PowerPC 6xx / 7xx internal IRQ controller */
> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> index e465d7ac98..b1e374f3f9 100644
> --- a/hw/ppc/spapr.c
> +++ b/hw/ppc/spapr.c
> @@ -1010,6 +1010,9 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
>  {
>      CPUPPCState *env = &cpu->env;
>
> +    /* The TCG path should also be holding the BQL at this point */
> +    g_assert(qemu_mutex_iothread_locked());
> +
>      if (msr_pr) {
>          hcall_dprintf("Hypercall made with MSR[PR]=1\n");
>          env->gpr[3] = H_PRIVILEGE;
> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
> index 2cf4ecf144..10db89b16a 100644
> --- a/include/qom/cpu.h
> +++ b/include/qom/cpu.h
> @@ -329,6 +329,7 @@ struct CPUState {
>      bool unplug;
>      bool crash_occurred;
>      bool exit_request;
> +    /* updates protected by BQL */
>      uint32_t interrupt_request;
>      int singlestep_enabled;
>      int64_t icount_extra;
> diff --git a/memory.c b/memory.c
> index ed8b5aa83e..d61caee867 100644
> --- a/memory.c
> +++ b/memory.c
> @@ -917,6 +917,8 @@ void memory_region_transaction_commit(void)
>      AddressSpace *as;
>
>      assert(memory_region_transaction_depth);
> +    assert(qemu_mutex_iothread_locked());
> +
>      --memory_region_transaction_depth;
>      if (!memory_region_transaction_depth) {
>          if (memory_region_update_pending) {
> diff --git a/qom/cpu.c b/qom/cpu.c
> index ed87c50cea..58784bcbea 100644
> --- a/qom/cpu.c
> +++ b/qom/cpu.c
> @@ -113,9 +113,19 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
>      error_setg(errp, "Obtaining memory mappings is unsupported on this CPU.");
>  }
>
> +/* Resetting the IRQ comes from across the code base so we take the
> + * BQL here if we need to.  cpu_interrupt assumes it is held.*/
>  void cpu_reset_interrupt(CPUState *cpu, int mask)
>  {
> +    bool need_lock = !qemu_mutex_iothread_locked();
> +
> +    if (need_lock) {
> +        qemu_mutex_lock_iothread();
> +    }
>      cpu->interrupt_request &= ~mask;
> +    if (need_lock) {
> +        qemu_mutex_unlock_iothread();
> +    }
>  }
>
>  void cpu_exit(CPUState *cpu)
> diff --git a/target/arm/helper.c b/target/arm/helper.c
> index 47250bcf16..753a69d40d 100644
> --- a/target/arm/helper.c
> +++ b/target/arm/helper.c
> @@ -6769,6 +6769,12 @@ void arm_cpu_do_interrupt(CPUState *cs)
>          arm_cpu_do_interrupt_aarch32(cs);
>      }
>
> +    /* Hooks may change global state so BQL should be held, also the
> +     * BQL needs to be held for any modification of
> +     * cs->interrupt_request.
> +     */
> +    g_assert(qemu_mutex_iothread_locked());
> +
>      arm_call_el_change_hook(cpu);
>
>      if (!kvm_enabled()) {
> diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
> index fb366fdc35..5f3e3bdae2 100644
> --- a/target/arm/op_helper.c
> +++ b/target/arm/op_helper.c
> @@ -18,6 +18,7 @@
>   */
>  #include "qemu/osdep.h"
>  #include "qemu/log.h"
> +#include "qemu/main-loop.h"
>  #include "cpu.h"
>  #include "exec/helper-proto.h"
>  #include "internals.h"
> @@ -487,7 +488,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
>       */
>      env->regs[15] &= (env->thumb ? ~1 : ~3);
>
> +    qemu_mutex_lock_iothread();
>      arm_call_el_change_hook(arm_env_get_cpu(env));
> +    qemu_mutex_unlock_iothread();
>  }
>
>  /* Access to user mode registers from privileged modes.  */
> @@ -735,28 +738,58 @@ void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
>  {
>      const ARMCPRegInfo *ri = rip;
>
> -    ri->writefn(env, ri, value);
> +    if (ri->type & ARM_CP_IO) {
> +        qemu_mutex_lock_iothread();
> +        ri->writefn(env, ri, value);
> +        qemu_mutex_unlock_iothread();
> +    } else {
> +        ri->writefn(env, ri, value);
> +    }
>  }
>
>  uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip)
>  {
>      const ARMCPRegInfo *ri = rip;
> +    uint32_t res;
>
> -    return ri->readfn(env, ri);
> +    if (ri->type & ARM_CP_IO) {
> +        qemu_mutex_lock_iothread();
> +        res = ri->readfn(env, ri);
> +        qemu_mutex_unlock_iothread();
> +    } else {
> +        res = ri->readfn(env, ri);
> +    }
> +
> +    return res;
>  }
>
>  void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value)
>  {
>      const ARMCPRegInfo *ri = rip;
>
> -    ri->writefn(env, ri, value);
> +    if (ri->type & ARM_CP_IO) {
> +        qemu_mutex_lock_iothread();
> +        ri->writefn(env, ri, value);
> +        qemu_mutex_unlock_iothread();
> +    } else {
> +        ri->writefn(env, ri, value);
> +    }
>  }
>
>  uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
>  {
>      const ARMCPRegInfo *ri = rip;
> +    uint64_t res;
> +
> +    if (ri->type & ARM_CP_IO) {
> +        qemu_mutex_lock_iothread();
> +        res = ri->readfn(env, ri);
> +        qemu_mutex_unlock_iothread();
> +    } else {
> +        res = ri->readfn(env, ri);
> +    }
>
> -    return ri->readfn(env, ri);
> +    return res;
>  }
>
>  void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
> @@ -989,7 +1022,9 @@ void HELPER(exception_return)(CPUARMState *env)
>                        cur_el, new_el, env->pc);
>      }
>
> +    qemu_mutex_lock_iothread();
>      arm_call_el_change_hook(arm_env_get_cpu(env));
> +    qemu_mutex_unlock_iothread();
>
>      return;
>
> diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c
> index 4dd6a2c544..f051a77c4a 100644
> --- a/target/i386/smm_helper.c
> +++ b/target/i386/smm_helper.c
> @@ -18,6 +18,7 @@
>   */
>
>  #include "qemu/osdep.h"
> +#include "qemu/main-loop.h"
>  #include "cpu.h"
>  #include "exec/helper-proto.h"
>  #include "exec/log.h"
> @@ -42,11 +43,14 @@ void helper_rsm(CPUX86State *env)
>  #define SMM_REVISION_ID 0x00020000
>  #endif
>
> +/* Called with iothread lock taken */
>  void cpu_smm_update(X86CPU *cpu)
>  {
>      CPUX86State *env = &cpu->env;
>      bool smm_enabled = (env->hflags & HF_SMM_MASK);
>
> +    g_assert(qemu_mutex_iothread_locked());
> +
>      if (cpu->smram) {
>          memory_region_set_enabled(cpu->smram, smm_enabled);
>      }
> @@ -333,7 +337,10 @@ void helper_rsm(CPUX86State *env)
>      }
>      env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK;
>      env->hflags &= ~HF_SMM_MASK;
> +
> +    qemu_mutex_lock_iothread();
>      cpu_smm_update(cpu);
> +    qemu_mutex_unlock_iothread();
>
>      qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
>      log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
> diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
> index c9604ea9c7..3cb942e8bb 100644
> --- a/target/s390x/misc_helper.c
> +++ b/target/s390x/misc_helper.c
> @@ -25,6 +25,7 @@
>  #include "exec/helper-proto.h"
>  #include "sysemu/kvm.h"
>  #include "qemu/timer.h"
> +#include "qemu/main-loop.h"
>  #include "exec/address-spaces.h"
>  #ifdef CONFIG_KVM
>  #include <linux/kvm.h>
> @@ -109,11 +110,13 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
>  /* SCLP service call */
>  uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
>  {
> +    qemu_mutex_lock_iothread();
>      int r = sclp_service_call(env, r1, r2);
>      if (r < 0) {
>          program_interrupt(env, -r, 4);
> -        return 0;
> +        r = 0;
>      }
> +    qemu_mutex_unlock_iothread();
>      return r;
>  }
>
> diff --git a/translate-all.c b/translate-all.c
> index 8a861cb583..f810259c41 100644
> --- a/translate-all.c
> +++ b/translate-all.c
> @@ -55,6 +55,7 @@
>  #include "translate-all.h"
>  #include "qemu/bitmap.h"
>  #include "qemu/timer.h"
> +#include "qemu/main-loop.h"
>  #include "exec/log.h"
>
>  /* #define DEBUG_TB_INVALIDATE */
> @@ -1523,7 +1524,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
>  #ifdef CONFIG_SOFTMMU
>  /* len must be <= 8 and start must be a multiple of len.
>   * Called via softmmu_template.h when code areas are written to with
> - * tb_lock held.
> + * iothread mutex not held.
>   */
>  void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
>  {
> @@ -1725,7 +1726,10 @@ void tb_check_watchpoint(CPUState *cpu)
>
>  #ifndef CONFIG_USER_ONLY
>  /* in deterministic execution mode, instructions doing device I/Os
> -   must be at the end of the TB */
> + * must be at the end of the TB.
> + *
> + * Called by softmmu_template.h, with iothread mutex not held.
> + */
>  void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
>  {
>  #if defined(TARGET_MIPS) || defined(TARGET_SH4)
> @@ -1937,6 +1941,7 @@ void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
>
>  void cpu_interrupt(CPUState *cpu, int mask)
>  {
> +    g_assert(qemu_mutex_iothread_locked());
>      cpu->interrupt_request |= mask;
>      cpu->tcg_exit_req = 1;
>  }
> diff --git a/translate-common.c b/translate-common.c
> index 5e989cdf70..d504dd0d33 100644
> --- a/translate-common.c
> +++ b/translate-common.c
> @@ -21,6 +21,7 @@
>  #include "qemu-common.h"
>  #include "qom/cpu.h"
>  #include "sysemu/cpus.h"
> +#include "qemu/main-loop.h"
>
>  uintptr_t qemu_real_host_page_size;
>  intptr_t qemu_real_host_page_mask;
> @@ -30,6 +31,7 @@ intptr_t qemu_real_host_page_mask;
>  static void tcg_handle_interrupt(CPUState *cpu, int mask)
>  {
>      int old_mask;
> +    g_assert(qemu_mutex_iothread_locked());
>
>      old_mask = cpu->interrupt_request;
>      cpu->interrupt_request |= mask;
> @@ -40,17 +42,16 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
>       */
>      if (!qemu_cpu_is_self(cpu)) {
>          qemu_cpu_kick(cpu);
> -        return;
> -    }
> -
> -    if (use_icount) {
> -        cpu->icount_decr.u16.high = 0xffff;
> -        if (!cpu->can_do_io
> -            && (mask & ~old_mask) != 0) {
> -            cpu_abort(cpu, "Raised interrupt while not in I/O function");
> -        }
>      } else {
> -        cpu->tcg_exit_req = 1;
> +        if (use_icount) {
> +            cpu->icount_decr.u16.high = 0xffff;
> +            if (!cpu->can_do_io
> +                && (mask & ~old_mask) != 0) {
> +                cpu_abort(cpu, "Raised interrupt while not in I/O function");
> +            }
> +        } else {
> +            cpu->tcg_exit_req = 1;
> +        }
>      }
>  }
>
> --
> 2.11.0
>
>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PULL 08/24] tcg: drop global lock during TCG code execution
  2017-02-27 12:48   ` [Qemu-devel] " Laurent Desnogues
@ 2017-02-27 14:39     ` Alex Bennée
  2017-03-03 20:59       ` Aaron Lindsay
  0 siblings, 1 reply; 18+ messages in thread
From: Alex Bennée @ 2017-02-27 14:39 UTC (permalink / raw)
  To: Laurent Desnogues; +Cc: Jan Kiszka, qemu-devel@nongnu.org, open list:ARM cores


Laurent Desnogues <laurent.desnogues@gmail.com> writes:

> Hello,
>
> On Fri, Feb 24, 2017 at 12:20 PM, Alex Bennée <alex.bennee@linaro.org> wrote:
>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>
>> This finally allows TCG to benefit from the iothread introduction: Drop
>> the global mutex while running pure TCG CPU code. Reacquire the lock
>> when entering MMIO or PIO emulation, or when leaving the TCG loop.
>>
>> We have to revert a few optimization for the current TCG threading
>> model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
>> kicking it in qemu_cpu_kick. We also need to disable RAM block
>> reordering until we have a more efficient locking mechanism at hand.
>>
>> Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
>> These numbers demonstrate where we gain something:
>>
>> 20338 jan       20   0  331m  75m 6904 R   99  0.9   0:50.95 qemu-system-arm
>> 20337 jan       20   0  331m  75m 6904 S   20  0.9   0:26.50 qemu-system-arm
>>
>> The guest CPU was fully loaded, but the iothread could still run mostly
>> independent on a second core. Without the patch we don't get beyond
>>
>> 32206 jan       20   0  330m  73m 7036 R   82  0.9   1:06.00 qemu-system-arm
>> 32204 jan       20   0  330m  73m 7036 S   21  0.9   0:17.03 qemu-system-arm
>>
>> We don't benefit significantly, though, when the guest is not fully
>> loading a host CPU.
>
> I tried this patch (8d04fb55 in the repository) with the following image:
>
>    http://wiki.qemu.org/download/arm-test-0.2.tar.gz
>
> Running the image with no option works fine.  But specifying '-icount
> 1' results in a (guest) deadlock. Enabling some heavy logging (-d
> in_asm,exec) sometimes results in a 'Bad ram offset'.
>
> Is it expected that this patch breaks -icount?

Not really. Using icount will disable MTTCG and run single threaded as
before. Paolo reported another icount failure so they may be related. I
shall have a look at it.

Thanks for the report.

>
> Thanks,
>
> Laurent
>
> PS - To clarify 791158d9 works.
>
>> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>> Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
>> [FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
>> Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
>> [EGC: fixed iothread lock for cpu-exec IRQ handling]
>> Signed-off-by: Emilio G. Cota <cota@braap.org>
>> [AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>> Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
>> [PM: target-arm changes]
>> Acked-by: Peter Maydell <peter.maydell@linaro.org>
>> ---
>>  cpu-exec.c                 | 23 +++++++++++++++++++++--
>>  cpus.c                     | 28 +++++-----------------------
>>  cputlb.c                   | 21 ++++++++++++++++++++-
>>  exec.c                     | 12 +++++++++---
>>  hw/core/irq.c              |  1 +
>>  hw/i386/kvmvapic.c         |  4 ++--
>>  hw/intc/arm_gicv3_cpuif.c  |  3 +++
>>  hw/ppc/ppc.c               | 16 +++++++++++++++-
>>  hw/ppc/spapr.c             |  3 +++
>>  include/qom/cpu.h          |  1 +
>>  memory.c                   |  2 ++
>>  qom/cpu.c                  | 10 ++++++++++
>>  target/arm/helper.c        |  6 ++++++
>>  target/arm/op_helper.c     | 43 +++++++++++++++++++++++++++++++++++++++----
>>  target/i386/smm_helper.c   |  7 +++++++
>>  target/s390x/misc_helper.c |  5 ++++-
>>  translate-all.c            |  9 +++++++--
>>  translate-common.c         | 21 +++++++++++----------
>>  18 files changed, 166 insertions(+), 49 deletions(-)
>>
>> diff --git a/cpu-exec.c b/cpu-exec.c
>> index 06a6b25564..1bd3d72002 100644
>> --- a/cpu-exec.c
>> +++ b/cpu-exec.c
>> @@ -29,6 +29,7 @@
>>  #include "qemu/rcu.h"
>>  #include "exec/tb-hash.h"
>>  #include "exec/log.h"
>> +#include "qemu/main-loop.h"
>>  #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
>>  #include "hw/i386/apic.h"
>>  #endif
>> @@ -388,8 +389,10 @@ static inline bool cpu_handle_halt(CPUState *cpu)
>>          if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
>>              && replay_interrupt()) {
>>              X86CPU *x86_cpu = X86_CPU(cpu);
>> +            qemu_mutex_lock_iothread();
>>              apic_poll_irq(x86_cpu->apic_state);
>>              cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
>> +            qemu_mutex_unlock_iothread();
>>          }
>>  #endif
>>          if (!cpu_has_work(cpu)) {
>> @@ -443,7 +446,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
>>  #else
>>              if (replay_exception()) {
>>                  CPUClass *cc = CPU_GET_CLASS(cpu);
>> +                qemu_mutex_lock_iothread();
>>                  cc->do_interrupt(cpu);
>> +                qemu_mutex_unlock_iothread();
>>                  cpu->exception_index = -1;
>>              } else if (!replay_has_interrupt()) {
>>                  /* give a chance to iothread in replay mode */
>> @@ -469,9 +474,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>>                                          TranslationBlock **last_tb)
>>  {
>>      CPUClass *cc = CPU_GET_CLASS(cpu);
>> -    int interrupt_request = cpu->interrupt_request;
>>
>> -    if (unlikely(interrupt_request)) {
>> +    if (unlikely(atomic_read(&cpu->interrupt_request))) {
>> +        int interrupt_request;
>> +        qemu_mutex_lock_iothread();
>> +        interrupt_request = cpu->interrupt_request;
>>          if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
>>              /* Mask out external interrupts for this step. */
>>              interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
>> @@ -479,6 +486,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>>          if (interrupt_request & CPU_INTERRUPT_DEBUG) {
>>              cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
>>              cpu->exception_index = EXCP_DEBUG;
>> +            qemu_mutex_unlock_iothread();
>>              return true;
>>          }
>>          if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
>> @@ -488,6 +496,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>>              cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
>>              cpu->halted = 1;
>>              cpu->exception_index = EXCP_HLT;
>> +            qemu_mutex_unlock_iothread();
>>              return true;
>>          }
>>  #if defined(TARGET_I386)
>> @@ -498,12 +507,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>>              cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
>>              do_cpu_init(x86_cpu);
>>              cpu->exception_index = EXCP_HALTED;
>> +            qemu_mutex_unlock_iothread();
>>              return true;
>>          }
>>  #else
>>          else if (interrupt_request & CPU_INTERRUPT_RESET) {
>>              replay_interrupt();
>>              cpu_reset(cpu);
>> +            qemu_mutex_unlock_iothread();
>>              return true;
>>          }
>>  #endif
>> @@ -526,7 +537,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>>                 the program flow was changed */
>>              *last_tb = NULL;
>>          }
>> +
>> +        /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
>> +        qemu_mutex_unlock_iothread();
>>      }
>> +
>> +
>>      if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
>>          atomic_set(&cpu->exit_request, 0);
>>          cpu->exception_index = EXCP_INTERRUPT;
>> @@ -643,6 +659,9 @@ int cpu_exec(CPUState *cpu)
>>  #endif /* buggy compiler */
>>          cpu->can_do_io = 1;
>>          tb_lock_reset();
>> +        if (qemu_mutex_iothread_locked()) {
>> +            qemu_mutex_unlock_iothread();
>> +        }
>>      }
>>
>>      /* if an exception is pending, we execute it here */
>> diff --git a/cpus.c b/cpus.c
>> index 860034a794..0ae8f69be5 100644
>> --- a/cpus.c
>> +++ b/cpus.c
>> @@ -1027,8 +1027,6 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
>>  #endif /* _WIN32 */
>>
>>  static QemuMutex qemu_global_mutex;
>> -static QemuCond qemu_io_proceeded_cond;
>> -static unsigned iothread_requesting_mutex;
>>
>>  static QemuThread io_thread;
>>
>> @@ -1042,7 +1040,6 @@ void qemu_init_cpu_loop(void)
>>      qemu_init_sigbus();
>>      qemu_cond_init(&qemu_cpu_cond);
>>      qemu_cond_init(&qemu_pause_cond);
>> -    qemu_cond_init(&qemu_io_proceeded_cond);
>>      qemu_mutex_init(&qemu_global_mutex);
>>
>>      qemu_thread_get_self(&io_thread);
>> @@ -1085,10 +1082,6 @@ static void qemu_tcg_wait_io_event(CPUState *cpu)
>>
>>      start_tcg_kick_timer();
>>
>> -    while (iothread_requesting_mutex) {
>> -        qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex);
>> -    }
>> -
>>      CPU_FOREACH(cpu) {
>>          qemu_wait_io_event_common(cpu);
>>      }
>> @@ -1249,9 +1242,11 @@ static int tcg_cpu_exec(CPUState *cpu)
>>          cpu->icount_decr.u16.low = decr;
>>          cpu->icount_extra = count;
>>      }
>> +    qemu_mutex_unlock_iothread();
>>      cpu_exec_start(cpu);
>>      ret = cpu_exec(cpu);
>>      cpu_exec_end(cpu);
>> +    qemu_mutex_lock_iothread();
>>  #ifdef CONFIG_PROFILER
>>      tcg_time += profile_getclock() - ti;
>>  #endif
>> @@ -1479,27 +1474,14 @@ bool qemu_mutex_iothread_locked(void)
>>
>>  void qemu_mutex_lock_iothread(void)
>>  {
>> -    atomic_inc(&iothread_requesting_mutex);
>> -    /* In the simple case there is no need to bump the VCPU thread out of
>> -     * TCG code execution.
>> -     */
>> -    if (!tcg_enabled() || qemu_in_vcpu_thread() ||
>> -        !first_cpu || !first_cpu->created) {
>> -        qemu_mutex_lock(&qemu_global_mutex);
>> -        atomic_dec(&iothread_requesting_mutex);
>> -    } else {
>> -        if (qemu_mutex_trylock(&qemu_global_mutex)) {
>> -            qemu_cpu_kick_rr_cpu();
>> -            qemu_mutex_lock(&qemu_global_mutex);
>> -        }
>> -        atomic_dec(&iothread_requesting_mutex);
>> -        qemu_cond_broadcast(&qemu_io_proceeded_cond);
>> -    }
>> +    g_assert(!qemu_mutex_iothread_locked());
>> +    qemu_mutex_lock(&qemu_global_mutex);
>>      iothread_locked = true;
>>  }
>>
>>  void qemu_mutex_unlock_iothread(void)
>>  {
>> +    g_assert(qemu_mutex_iothread_locked());
>>      iothread_locked = false;
>>      qemu_mutex_unlock(&qemu_global_mutex);
>>  }
>> diff --git a/cputlb.c b/cputlb.c
>> index 6c39927455..1cc9d9da51 100644
>> --- a/cputlb.c
>> +++ b/cputlb.c
>> @@ -18,6 +18,7 @@
>>   */
>>
>>  #include "qemu/osdep.h"
>> +#include "qemu/main-loop.h"
>>  #include "cpu.h"
>>  #include "exec/exec-all.h"
>>  #include "exec/memory.h"
>> @@ -495,6 +496,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>>      hwaddr physaddr = iotlbentry->addr;
>>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
>>      uint64_t val;
>> +    bool locked = false;
>>
>>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
>>      cpu->mem_io_pc = retaddr;
>> @@ -503,7 +505,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>>      }
>>
>>      cpu->mem_io_vaddr = addr;
>> +
>> +    if (mr->global_locking) {
>> +        qemu_mutex_lock_iothread();
>> +        locked = true;
>> +    }
>>      memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs);
>> +    if (locked) {
>> +        qemu_mutex_unlock_iothread();
>> +    }
>> +
>>      return val;
>>  }
>>
>> @@ -514,15 +525,23 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>>      CPUState *cpu = ENV_GET_CPU(env);
>>      hwaddr physaddr = iotlbentry->addr;
>>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
>> +    bool locked = false;
>>
>>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
>>      if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
>>          cpu_io_recompile(cpu, retaddr);
>>      }
>> -
>>      cpu->mem_io_vaddr = addr;
>>      cpu->mem_io_pc = retaddr;
>> +
>> +    if (mr->global_locking) {
>> +        qemu_mutex_lock_iothread();
>> +        locked = true;
>> +    }
>>      memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs);
>> +    if (locked) {
>> +        qemu_mutex_unlock_iothread();
>> +    }
>>  }
>>
>>  /* Return true if ADDR is present in the victim tlb, and has been copied
>> diff --git a/exec.c b/exec.c
>> index 865a1e8295..3adf2b1861 100644
>> --- a/exec.c
>> +++ b/exec.c
>> @@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
>>                  }
>>                  cpu->watchpoint_hit = wp;
>>
>> -                /* The tb_lock will be reset when cpu_loop_exit or
>> -                 * cpu_loop_exit_noexc longjmp back into the cpu_exec
>> -                 * main loop.
>> +                /* Both tb_lock and iothread_mutex will be reset when
>> +                 * cpu_loop_exit or cpu_loop_exit_noexc longjmp
>> +                 * back into the cpu_exec main loop.
>>                   */
>>                  tb_lock();
>>                  tb_check_watchpoint(cpu);
>> @@ -2371,8 +2371,14 @@ static void io_mem_init(void)
>>      memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
>>      memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
>>                            NULL, UINT64_MAX);
>> +
>> +    /* io_mem_notdirty calls tb_invalidate_phys_page_fast,
>> +     * which can be called without the iothread mutex.
>> +     */
>>      memory_region_init_io(&io_mem_notdirty, NULL, &notdirty_mem_ops, NULL,
>>                            NULL, UINT64_MAX);
>> +    memory_region_clear_global_locking(&io_mem_notdirty);
>> +
>>      memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
>>                            NULL, UINT64_MAX);
>>  }
>> diff --git a/hw/core/irq.c b/hw/core/irq.c
>> index 49ff2e64fe..b98d1d69f5 100644
>> --- a/hw/core/irq.c
>> +++ b/hw/core/irq.c
>> @@ -22,6 +22,7 @@
>>   * THE SOFTWARE.
>>   */
>>  #include "qemu/osdep.h"
>> +#include "qemu/main-loop.h"
>>  #include "qemu-common.h"
>>  #include "hw/irq.h"
>>  #include "qom/object.h"
>> diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
>> index 7135633863..82a49556af 100644
>> --- a/hw/i386/kvmvapic.c
>> +++ b/hw/i386/kvmvapic.c
>> @@ -457,8 +457,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
>>      resume_all_vcpus();
>>
>>      if (!kvm_enabled()) {
>> -        /* tb_lock will be reset when cpu_loop_exit_noexc longjmps
>> -         * back into the cpu_exec loop. */
>> +        /* Both tb_lock and iothread_mutex will be reset when
>> +         *  longjmps back into the cpu_exec loop. */
>>          tb_lock();
>>          tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1);
>>          cpu_loop_exit_noexc(cs);
>> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
>> index c25ee03556..f775aba507 100644
>> --- a/hw/intc/arm_gicv3_cpuif.c
>> +++ b/hw/intc/arm_gicv3_cpuif.c
>> @@ -14,6 +14,7 @@
>>
>>  #include "qemu/osdep.h"
>>  #include "qemu/bitops.h"
>> +#include "qemu/main-loop.h"
>>  #include "trace.h"
>>  #include "gicv3_internal.h"
>>  #include "cpu.h"
>> @@ -733,6 +734,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
>>      ARMCPU *cpu = ARM_CPU(cs->cpu);
>>      CPUARMState *env = &cpu->env;
>>
>> +    g_assert(qemu_mutex_iothread_locked());
>> +
>>      trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
>>                               cs->hppi.grp, cs->hppi.prio);
>>
>> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
>> index d171e60b5c..5f93083d4a 100644
>> --- a/hw/ppc/ppc.c
>> +++ b/hw/ppc/ppc.c
>> @@ -62,7 +62,16 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
>>  {
>>      CPUState *cs = CPU(cpu);
>>      CPUPPCState *env = &cpu->env;
>> -    unsigned int old_pending = env->pending_interrupts;
>> +    unsigned int old_pending;
>> +    bool locked = false;
>> +
>> +    /* We may already have the BQL if coming from the reset path */
>> +    if (!qemu_mutex_iothread_locked()) {
>> +        locked = true;
>> +        qemu_mutex_lock_iothread();
>> +    }
>> +
>> +    old_pending = env->pending_interrupts;
>>
>>      if (level) {
>>          env->pending_interrupts |= 1 << n_IRQ;
>> @@ -80,9 +89,14 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
>>  #endif
>>      }
>>
>> +
>>      LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
>>                  "req %08x\n", __func__, env, n_IRQ, level,
>>                  env->pending_interrupts, CPU(cpu)->interrupt_request);
>> +
>> +    if (locked) {
>> +        qemu_mutex_unlock_iothread();
>> +    }
>>  }
>>
>>  /* PowerPC 6xx / 7xx internal IRQ controller */
>> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> index e465d7ac98..b1e374f3f9 100644
>> --- a/hw/ppc/spapr.c
>> +++ b/hw/ppc/spapr.c
>> @@ -1010,6 +1010,9 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
>>  {
>>      CPUPPCState *env = &cpu->env;
>>
>> +    /* The TCG path should also be holding the BQL at this point */
>> +    g_assert(qemu_mutex_iothread_locked());
>> +
>>      if (msr_pr) {
>>          hcall_dprintf("Hypercall made with MSR[PR]=1\n");
>>          env->gpr[3] = H_PRIVILEGE;
>> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>> index 2cf4ecf144..10db89b16a 100644
>> --- a/include/qom/cpu.h
>> +++ b/include/qom/cpu.h
>> @@ -329,6 +329,7 @@ struct CPUState {
>>      bool unplug;
>>      bool crash_occurred;
>>      bool exit_request;
>> +    /* updates protected by BQL */
>>      uint32_t interrupt_request;
>>      int singlestep_enabled;
>>      int64_t icount_extra;
>> diff --git a/memory.c b/memory.c
>> index ed8b5aa83e..d61caee867 100644
>> --- a/memory.c
>> +++ b/memory.c
>> @@ -917,6 +917,8 @@ void memory_region_transaction_commit(void)
>>      AddressSpace *as;
>>
>>      assert(memory_region_transaction_depth);
>> +    assert(qemu_mutex_iothread_locked());
>> +
>>      --memory_region_transaction_depth;
>>      if (!memory_region_transaction_depth) {
>>          if (memory_region_update_pending) {
>> diff --git a/qom/cpu.c b/qom/cpu.c
>> index ed87c50cea..58784bcbea 100644
>> --- a/qom/cpu.c
>> +++ b/qom/cpu.c
>> @@ -113,9 +113,19 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
>>      error_setg(errp, "Obtaining memory mappings is unsupported on this CPU.");
>>  }
>>
>> +/* Resetting the IRQ comes from across the code base so we take the
>> + * BQL here if we need to.  cpu_interrupt assumes it is held.*/
>>  void cpu_reset_interrupt(CPUState *cpu, int mask)
>>  {
>> +    bool need_lock = !qemu_mutex_iothread_locked();
>> +
>> +    if (need_lock) {
>> +        qemu_mutex_lock_iothread();
>> +    }
>>      cpu->interrupt_request &= ~mask;
>> +    if (need_lock) {
>> +        qemu_mutex_unlock_iothread();
>> +    }
>>  }
>>
>>  void cpu_exit(CPUState *cpu)
>> diff --git a/target/arm/helper.c b/target/arm/helper.c
>> index 47250bcf16..753a69d40d 100644
>> --- a/target/arm/helper.c
>> +++ b/target/arm/helper.c
>> @@ -6769,6 +6769,12 @@ void arm_cpu_do_interrupt(CPUState *cs)
>>          arm_cpu_do_interrupt_aarch32(cs);
>>      }
>>
>> +    /* Hooks may change global state so BQL should be held, also the
>> +     * BQL needs to be held for any modification of
>> +     * cs->interrupt_request.
>> +     */
>> +    g_assert(qemu_mutex_iothread_locked());
>> +
>>      arm_call_el_change_hook(cpu);
>>
>>      if (!kvm_enabled()) {
>> diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
>> index fb366fdc35..5f3e3bdae2 100644
>> --- a/target/arm/op_helper.c
>> +++ b/target/arm/op_helper.c
>> @@ -18,6 +18,7 @@
>>   */
>>  #include "qemu/osdep.h"
>>  #include "qemu/log.h"
>> +#include "qemu/main-loop.h"
>>  #include "cpu.h"
>>  #include "exec/helper-proto.h"
>>  #include "internals.h"
>> @@ -487,7 +488,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
>>       */
>>      env->regs[15] &= (env->thumb ? ~1 : ~3);
>>
>> +    qemu_mutex_lock_iothread();
>>      arm_call_el_change_hook(arm_env_get_cpu(env));
>> +    qemu_mutex_unlock_iothread();
>>  }
>>
>>  /* Access to user mode registers from privileged modes.  */
>> @@ -735,28 +738,58 @@ void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
>>  {
>>      const ARMCPRegInfo *ri = rip;
>>
>> -    ri->writefn(env, ri, value);
>> +    if (ri->type & ARM_CP_IO) {
>> +        qemu_mutex_lock_iothread();
>> +        ri->writefn(env, ri, value);
>> +        qemu_mutex_unlock_iothread();
>> +    } else {
>> +        ri->writefn(env, ri, value);
>> +    }
>>  }
>>
>>  uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip)
>>  {
>>      const ARMCPRegInfo *ri = rip;
>> +    uint32_t res;
>>
>> -    return ri->readfn(env, ri);
>> +    if (ri->type & ARM_CP_IO) {
>> +        qemu_mutex_lock_iothread();
>> +        res = ri->readfn(env, ri);
>> +        qemu_mutex_unlock_iothread();
>> +    } else {
>> +        res = ri->readfn(env, ri);
>> +    }
>> +
>> +    return res;
>>  }
>>
>>  void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value)
>>  {
>>      const ARMCPRegInfo *ri = rip;
>>
>> -    ri->writefn(env, ri, value);
>> +    if (ri->type & ARM_CP_IO) {
>> +        qemu_mutex_lock_iothread();
>> +        ri->writefn(env, ri, value);
>> +        qemu_mutex_unlock_iothread();
>> +    } else {
>> +        ri->writefn(env, ri, value);
>> +    }
>>  }
>>
>>  uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
>>  {
>>      const ARMCPRegInfo *ri = rip;
>> +    uint64_t res;
>> +
>> +    if (ri->type & ARM_CP_IO) {
>> +        qemu_mutex_lock_iothread();
>> +        res = ri->readfn(env, ri);
>> +        qemu_mutex_unlock_iothread();
>> +    } else {
>> +        res = ri->readfn(env, ri);
>> +    }
>>
>> -    return ri->readfn(env, ri);
>> +    return res;
>>  }
>>
>>  void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
>> @@ -989,7 +1022,9 @@ void HELPER(exception_return)(CPUARMState *env)
>>                        cur_el, new_el, env->pc);
>>      }
>>
>> +    qemu_mutex_lock_iothread();
>>      arm_call_el_change_hook(arm_env_get_cpu(env));
>> +    qemu_mutex_unlock_iothread();
>>
>>      return;
>>
>> diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c
>> index 4dd6a2c544..f051a77c4a 100644
>> --- a/target/i386/smm_helper.c
>> +++ b/target/i386/smm_helper.c
>> @@ -18,6 +18,7 @@
>>   */
>>
>>  #include "qemu/osdep.h"
>> +#include "qemu/main-loop.h"
>>  #include "cpu.h"
>>  #include "exec/helper-proto.h"
>>  #include "exec/log.h"
>> @@ -42,11 +43,14 @@ void helper_rsm(CPUX86State *env)
>>  #define SMM_REVISION_ID 0x00020000
>>  #endif
>>
>> +/* Called with iothread lock taken */
>>  void cpu_smm_update(X86CPU *cpu)
>>  {
>>      CPUX86State *env = &cpu->env;
>>      bool smm_enabled = (env->hflags & HF_SMM_MASK);
>>
>> +    g_assert(qemu_mutex_iothread_locked());
>> +
>>      if (cpu->smram) {
>>          memory_region_set_enabled(cpu->smram, smm_enabled);
>>      }
>> @@ -333,7 +337,10 @@ void helper_rsm(CPUX86State *env)
>>      }
>>      env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK;
>>      env->hflags &= ~HF_SMM_MASK;
>> +
>> +    qemu_mutex_lock_iothread();
>>      cpu_smm_update(cpu);
>> +    qemu_mutex_unlock_iothread();
>>
>>      qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
>>      log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
>> diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
>> index c9604ea9c7..3cb942e8bb 100644
>> --- a/target/s390x/misc_helper.c
>> +++ b/target/s390x/misc_helper.c
>> @@ -25,6 +25,7 @@
>>  #include "exec/helper-proto.h"
>>  #include "sysemu/kvm.h"
>>  #include "qemu/timer.h"
>> +#include "qemu/main-loop.h"
>>  #include "exec/address-spaces.h"
>>  #ifdef CONFIG_KVM
>>  #include <linux/kvm.h>
>> @@ -109,11 +110,13 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
>>  /* SCLP service call */
>>  uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
>>  {
>> +    qemu_mutex_lock_iothread();
>>      int r = sclp_service_call(env, r1, r2);
>>      if (r < 0) {
>>          program_interrupt(env, -r, 4);
>> -        return 0;
>> +        r = 0;
>>      }
>> +    qemu_mutex_unlock_iothread();
>>      return r;
>>  }
>>
>> diff --git a/translate-all.c b/translate-all.c
>> index 8a861cb583..f810259c41 100644
>> --- a/translate-all.c
>> +++ b/translate-all.c
>> @@ -55,6 +55,7 @@
>>  #include "translate-all.h"
>>  #include "qemu/bitmap.h"
>>  #include "qemu/timer.h"
>> +#include "qemu/main-loop.h"
>>  #include "exec/log.h"
>>
>>  /* #define DEBUG_TB_INVALIDATE */
>> @@ -1523,7 +1524,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
>>  #ifdef CONFIG_SOFTMMU
>>  /* len must be <= 8 and start must be a multiple of len.
>>   * Called via softmmu_template.h when code areas are written to with
>> - * tb_lock held.
>> + * iothread mutex not held.
>>   */
>>  void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
>>  {
>> @@ -1725,7 +1726,10 @@ void tb_check_watchpoint(CPUState *cpu)
>>
>>  #ifndef CONFIG_USER_ONLY
>>  /* in deterministic execution mode, instructions doing device I/Os
>> -   must be at the end of the TB */
>> + * must be at the end of the TB.
>> + *
>> + * Called by softmmu_template.h, with iothread mutex not held.
>> + */
>>  void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
>>  {
>>  #if defined(TARGET_MIPS) || defined(TARGET_SH4)
>> @@ -1937,6 +1941,7 @@ void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
>>
>>  void cpu_interrupt(CPUState *cpu, int mask)
>>  {
>> +    g_assert(qemu_mutex_iothread_locked());
>>      cpu->interrupt_request |= mask;
>>      cpu->tcg_exit_req = 1;
>>  }
>> diff --git a/translate-common.c b/translate-common.c
>> index 5e989cdf70..d504dd0d33 100644
>> --- a/translate-common.c
>> +++ b/translate-common.c
>> @@ -21,6 +21,7 @@
>>  #include "qemu-common.h"
>>  #include "qom/cpu.h"
>>  #include "sysemu/cpus.h"
>> +#include "qemu/main-loop.h"
>>
>>  uintptr_t qemu_real_host_page_size;
>>  intptr_t qemu_real_host_page_mask;
>> @@ -30,6 +31,7 @@ intptr_t qemu_real_host_page_mask;
>>  static void tcg_handle_interrupt(CPUState *cpu, int mask)
>>  {
>>      int old_mask;
>> +    g_assert(qemu_mutex_iothread_locked());
>>
>>      old_mask = cpu->interrupt_request;
>>      cpu->interrupt_request |= mask;
>> @@ -40,17 +42,16 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
>>       */
>>      if (!qemu_cpu_is_self(cpu)) {
>>          qemu_cpu_kick(cpu);
>> -        return;
>> -    }
>> -
>> -    if (use_icount) {
>> -        cpu->icount_decr.u16.high = 0xffff;
>> -        if (!cpu->can_do_io
>> -            && (mask & ~old_mask) != 0) {
>> -            cpu_abort(cpu, "Raised interrupt while not in I/O function");
>> -        }
>>      } else {
>> -        cpu->tcg_exit_req = 1;
>> +        if (use_icount) {
>> +            cpu->icount_decr.u16.high = 0xffff;
>> +            if (!cpu->can_do_io
>> +                && (mask & ~old_mask) != 0) {
>> +                cpu_abort(cpu, "Raised interrupt while not in I/O function");
>> +            }
>> +        } else {
>> +            cpu->tcg_exit_req = 1;
>> +        }
>>      }
>>  }
>>
>> --
>> 2.11.0
>>
>>


--
Alex Bennée

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PULL 08/24] tcg: drop global lock during TCG code execution
  2017-02-27 14:39     ` Alex Bennée
@ 2017-03-03 20:59       ` Aaron Lindsay
  2017-03-03 21:08         ` Alex Bennée
  0 siblings, 1 reply; 18+ messages in thread
From: Aaron Lindsay @ 2017-03-03 20:59 UTC (permalink / raw)
  To: Alex Bennée
  Cc: Laurent Desnogues, Jan Kiszka, open list:ARM cores,
	qemu-devel@nongnu.org

On Feb 27 14:39, Alex Bennée wrote:
> 
> Laurent Desnogues <laurent.desnogues@gmail.com> writes:
> 
> > Hello,
> >
> > On Fri, Feb 24, 2017 at 12:20 PM, Alex Bennée <alex.bennee@linaro.org> wrote:
> >> From: Jan Kiszka <jan.kiszka@siemens.com>
> >>
> >> This finally allows TCG to benefit from the iothread introduction: Drop
> >> the global mutex while running pure TCG CPU code. Reacquire the lock
> >> when entering MMIO or PIO emulation, or when leaving the TCG loop.
> >>
> >> We have to revert a few optimization for the current TCG threading
> >> model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
> >> kicking it in qemu_cpu_kick. We also need to disable RAM block
> >> reordering until we have a more efficient locking mechanism at hand.
> >>
> >> Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
> >> These numbers demonstrate where we gain something:
> >>
> >> 20338 jan       20   0  331m  75m 6904 R   99  0.9   0:50.95 qemu-system-arm
> >> 20337 jan       20   0  331m  75m 6904 S   20  0.9   0:26.50 qemu-system-arm
> >>
> >> The guest CPU was fully loaded, but the iothread could still run mostly
> >> independent on a second core. Without the patch we don't get beyond
> >>
> >> 32206 jan       20   0  330m  73m 7036 R   82  0.9   1:06.00 qemu-system-arm
> >> 32204 jan       20   0  330m  73m 7036 S   21  0.9   0:17.03 qemu-system-arm
> >>
> >> We don't benefit significantly, though, when the guest is not fully
> >> loading a host CPU.
> >
> > I tried this patch (8d04fb55 in the repository) with the following image:
> >
> >    http://wiki.qemu.org/download/arm-test-0.2.tar.gz
> >
> > Running the image with no option works fine.  But specifying '-icount
> > 1' results in a (guest) deadlock. Enabling some heavy logging (-d
> > in_asm,exec) sometimes results in a 'Bad ram offset'.
> >
> > Is it expected that this patch breaks -icount?
> 
> Not really. Using icount will disable MTTCG and run single threaded as
> before. Paolo reported another icount failure so they may be related. I
> shall have a look at it.
> 
> Thanks for the report.

I have not experienced a guest deadlock, but for me this patch makes
booting a simple Linux distribution take about an order of magnitude
longer when using '-icount 0' (from about 1.6 seconds to 17.9). It was
slow enough to get to the printk the first time after recompiling that I
killed it thinking it *had* deadlocked.

`perf report` from before this patch (snipped to >1%):
 23.81%  qemu-system-aar  perf-9267.map        [.] 0x0000000041a5cc9e
  7.15%  qemu-system-aar  [kernel.kallsyms]    [k] 0xffffffff8172bc82
  6.29%  qemu-system-aar  qemu-system-aarch64  [.] cpu_exec
  4.99%  qemu-system-aar  qemu-system-aarch64  [.] tcg_gen_code
  4.71%  qemu-system-aar  qemu-system-aarch64  [.] cpu_get_tb_cpu_state
  4.39%  qemu-system-aar  qemu-system-aarch64  [.] tcg_optimize
  3.28%  qemu-system-aar  qemu-system-aarch64  [.] helper_dc_zva
  2.66%  qemu-system-aar  qemu-system-aarch64  [.] liveness_pass_1
  1.98%  qemu-system-aar  qemu-system-aarch64  [.] qht_lookup
  1.93%  qemu-system-aar  qemu-system-aarch64  [.] tcg_out_opc
  1.81%  qemu-system-aar  qemu-system-aarch64  [.] get_phys_addr_lpae
  1.71%  qemu-system-aar  qemu-system-aarch64  [.] object_class_dynamic_cast_assert
  1.38%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi1
  1.10%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi0

and after this patch:
 20.10%  qemu-system-aar  perf-3285.map        [.] 0x0000000040a3b690
*18.08%  qemu-system-aar  [kernel.kallsyms]    [k] 0xffffffff81371865
  7.87%  qemu-system-aar  qemu-system-aarch64  [.] cpu_exec
  4.70%  qemu-system-aar  qemu-system-aarch64  [.] cpu_get_tb_cpu_state
* 2.64%  qemu-system-aar  qemu-system-aarch64  [.] g_mutex_get_impl
  2.39%  qemu-system-aar  qemu-system-aarch64  [.] gic_update
* 1.89%  qemu-system-aar  qemu-system-aarch64  [.] pthread_mutex_unlock
  1.61%  qemu-system-aar  qemu-system-aarch64  [.] object_class_dynamic_cast_assert
* 1.55%  qemu-system-aar  qemu-system-aarch64  [.] pthread_mutex_lock
  1.31%  qemu-system-aar  qemu-system-aarch64  [.] get_phys_addr_lpae
  1.21%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi0
  1.13%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi1

I've put asterisks by a few suspicious mutex-related functions, though I wonder
if the slowdowns are also partially inlined into some of the other functions.
The kernel also jumps up, presumably from handling more mutexes?

I confess I'm not familiar enough with this code to suggest optimizations, but
I'll be glad to test any.

-Aaron

> 
> >
> > Thanks,
> >
> > Laurent
> >
> > PS - To clarify 791158d9 works.
> >
> >> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> >> Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
> >> [FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
> >> Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
> >> [EGC: fixed iothread lock for cpu-exec IRQ handling]
> >> Signed-off-by: Emilio G. Cota <cota@braap.org>
> >> [AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
> >> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> >> Reviewed-by: Richard Henderson <rth@twiddle.net>
> >> Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
> >> [PM: target-arm changes]
> >> Acked-by: Peter Maydell <peter.maydell@linaro.org>
> >> ---
> >>  cpu-exec.c                 | 23 +++++++++++++++++++++--
> >>  cpus.c                     | 28 +++++-----------------------
> >>  cputlb.c                   | 21 ++++++++++++++++++++-
> >>  exec.c                     | 12 +++++++++---
> >>  hw/core/irq.c              |  1 +
> >>  hw/i386/kvmvapic.c         |  4 ++--
> >>  hw/intc/arm_gicv3_cpuif.c  |  3 +++
> >>  hw/ppc/ppc.c               | 16 +++++++++++++++-
> >>  hw/ppc/spapr.c             |  3 +++
> >>  include/qom/cpu.h          |  1 +
> >>  memory.c                   |  2 ++
> >>  qom/cpu.c                  | 10 ++++++++++
> >>  target/arm/helper.c        |  6 ++++++
> >>  target/arm/op_helper.c     | 43 +++++++++++++++++++++++++++++++++++++++----
> >>  target/i386/smm_helper.c   |  7 +++++++
> >>  target/s390x/misc_helper.c |  5 ++++-
> >>  translate-all.c            |  9 +++++++--
> >>  translate-common.c         | 21 +++++++++++----------
> >>  18 files changed, 166 insertions(+), 49 deletions(-)
> >>
> >> diff --git a/cpu-exec.c b/cpu-exec.c
> >> index 06a6b25564..1bd3d72002 100644
> >> --- a/cpu-exec.c
> >> +++ b/cpu-exec.c
> >> @@ -29,6 +29,7 @@
> >>  #include "qemu/rcu.h"
> >>  #include "exec/tb-hash.h"
> >>  #include "exec/log.h"
> >> +#include "qemu/main-loop.h"
> >>  #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
> >>  #include "hw/i386/apic.h"
> >>  #endif
> >> @@ -388,8 +389,10 @@ static inline bool cpu_handle_halt(CPUState *cpu)
> >>          if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
> >>              && replay_interrupt()) {
> >>              X86CPU *x86_cpu = X86_CPU(cpu);
> >> +            qemu_mutex_lock_iothread();
> >>              apic_poll_irq(x86_cpu->apic_state);
> >>              cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
> >> +            qemu_mutex_unlock_iothread();
> >>          }
> >>  #endif
> >>          if (!cpu_has_work(cpu)) {
> >> @@ -443,7 +446,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
> >>  #else
> >>              if (replay_exception()) {
> >>                  CPUClass *cc = CPU_GET_CLASS(cpu);
> >> +                qemu_mutex_lock_iothread();
> >>                  cc->do_interrupt(cpu);
> >> +                qemu_mutex_unlock_iothread();
> >>                  cpu->exception_index = -1;
> >>              } else if (!replay_has_interrupt()) {
> >>                  /* give a chance to iothread in replay mode */
> >> @@ -469,9 +474,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
> >>                                          TranslationBlock **last_tb)
> >>  {
> >>      CPUClass *cc = CPU_GET_CLASS(cpu);
> >> -    int interrupt_request = cpu->interrupt_request;
> >>
> >> -    if (unlikely(interrupt_request)) {
> >> +    if (unlikely(atomic_read(&cpu->interrupt_request))) {
> >> +        int interrupt_request;
> >> +        qemu_mutex_lock_iothread();
> >> +        interrupt_request = cpu->interrupt_request;
> >>          if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
> >>              /* Mask out external interrupts for this step. */
> >>              interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
> >> @@ -479,6 +486,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
> >>          if (interrupt_request & CPU_INTERRUPT_DEBUG) {
> >>              cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
> >>              cpu->exception_index = EXCP_DEBUG;
> >> +            qemu_mutex_unlock_iothread();
> >>              return true;
> >>          }
> >>          if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
> >> @@ -488,6 +496,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
> >>              cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
> >>              cpu->halted = 1;
> >>              cpu->exception_index = EXCP_HLT;
> >> +            qemu_mutex_unlock_iothread();
> >>              return true;
> >>          }
> >>  #if defined(TARGET_I386)
> >> @@ -498,12 +507,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
> >>              cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
> >>              do_cpu_init(x86_cpu);
> >>              cpu->exception_index = EXCP_HALTED;
> >> +            qemu_mutex_unlock_iothread();
> >>              return true;
> >>          }
> >>  #else
> >>          else if (interrupt_request & CPU_INTERRUPT_RESET) {
> >>              replay_interrupt();
> >>              cpu_reset(cpu);
> >> +            qemu_mutex_unlock_iothread();
> >>              return true;
> >>          }
> >>  #endif
> >> @@ -526,7 +537,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
> >>                 the program flow was changed */
> >>              *last_tb = NULL;
> >>          }
> >> +
> >> +        /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
> >> +        qemu_mutex_unlock_iothread();
> >>      }
> >> +
> >> +
> >>      if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
> >>          atomic_set(&cpu->exit_request, 0);
> >>          cpu->exception_index = EXCP_INTERRUPT;
> >> @@ -643,6 +659,9 @@ int cpu_exec(CPUState *cpu)
> >>  #endif /* buggy compiler */
> >>          cpu->can_do_io = 1;
> >>          tb_lock_reset();
> >> +        if (qemu_mutex_iothread_locked()) {
> >> +            qemu_mutex_unlock_iothread();
> >> +        }
> >>      }
> >>
> >>      /* if an exception is pending, we execute it here */
> >> diff --git a/cpus.c b/cpus.c
> >> index 860034a794..0ae8f69be5 100644
> >> --- a/cpus.c
> >> +++ b/cpus.c
> >> @@ -1027,8 +1027,6 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
> >>  #endif /* _WIN32 */
> >>
> >>  static QemuMutex qemu_global_mutex;
> >> -static QemuCond qemu_io_proceeded_cond;
> >> -static unsigned iothread_requesting_mutex;
> >>
> >>  static QemuThread io_thread;
> >>
> >> @@ -1042,7 +1040,6 @@ void qemu_init_cpu_loop(void)
> >>      qemu_init_sigbus();
> >>      qemu_cond_init(&qemu_cpu_cond);
> >>      qemu_cond_init(&qemu_pause_cond);
> >> -    qemu_cond_init(&qemu_io_proceeded_cond);
> >>      qemu_mutex_init(&qemu_global_mutex);
> >>
> >>      qemu_thread_get_self(&io_thread);
> >> @@ -1085,10 +1082,6 @@ static void qemu_tcg_wait_io_event(CPUState *cpu)
> >>
> >>      start_tcg_kick_timer();
> >>
> >> -    while (iothread_requesting_mutex) {
> >> -        qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex);
> >> -    }
> >> -
> >>      CPU_FOREACH(cpu) {
> >>          qemu_wait_io_event_common(cpu);
> >>      }
> >> @@ -1249,9 +1242,11 @@ static int tcg_cpu_exec(CPUState *cpu)
> >>          cpu->icount_decr.u16.low = decr;
> >>          cpu->icount_extra = count;
> >>      }
> >> +    qemu_mutex_unlock_iothread();
> >>      cpu_exec_start(cpu);
> >>      ret = cpu_exec(cpu);
> >>      cpu_exec_end(cpu);
> >> +    qemu_mutex_lock_iothread();
> >>  #ifdef CONFIG_PROFILER
> >>      tcg_time += profile_getclock() - ti;
> >>  #endif
> >> @@ -1479,27 +1474,14 @@ bool qemu_mutex_iothread_locked(void)
> >>
> >>  void qemu_mutex_lock_iothread(void)
> >>  {
> >> -    atomic_inc(&iothread_requesting_mutex);
> >> -    /* In the simple case there is no need to bump the VCPU thread out of
> >> -     * TCG code execution.
> >> -     */
> >> -    if (!tcg_enabled() || qemu_in_vcpu_thread() ||
> >> -        !first_cpu || !first_cpu->created) {
> >> -        qemu_mutex_lock(&qemu_global_mutex);
> >> -        atomic_dec(&iothread_requesting_mutex);
> >> -    } else {
> >> -        if (qemu_mutex_trylock(&qemu_global_mutex)) {
> >> -            qemu_cpu_kick_rr_cpu();
> >> -            qemu_mutex_lock(&qemu_global_mutex);
> >> -        }
> >> -        atomic_dec(&iothread_requesting_mutex);
> >> -        qemu_cond_broadcast(&qemu_io_proceeded_cond);
> >> -    }
> >> +    g_assert(!qemu_mutex_iothread_locked());
> >> +    qemu_mutex_lock(&qemu_global_mutex);
> >>      iothread_locked = true;
> >>  }
> >>
> >>  void qemu_mutex_unlock_iothread(void)
> >>  {
> >> +    g_assert(qemu_mutex_iothread_locked());
> >>      iothread_locked = false;
> >>      qemu_mutex_unlock(&qemu_global_mutex);
> >>  }
> >> diff --git a/cputlb.c b/cputlb.c
> >> index 6c39927455..1cc9d9da51 100644
> >> --- a/cputlb.c
> >> +++ b/cputlb.c
> >> @@ -18,6 +18,7 @@
> >>   */
> >>
> >>  #include "qemu/osdep.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "cpu.h"
> >>  #include "exec/exec-all.h"
> >>  #include "exec/memory.h"
> >> @@ -495,6 +496,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
> >>      hwaddr physaddr = iotlbentry->addr;
> >>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
> >>      uint64_t val;
> >> +    bool locked = false;
> >>
> >>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
> >>      cpu->mem_io_pc = retaddr;
> >> @@ -503,7 +505,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
> >>      }
> >>
> >>      cpu->mem_io_vaddr = addr;
> >> +
> >> +    if (mr->global_locking) {
> >> +        qemu_mutex_lock_iothread();
> >> +        locked = true;
> >> +    }
> >>      memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs);
> >> +    if (locked) {
> >> +        qemu_mutex_unlock_iothread();
> >> +    }
> >> +
> >>      return val;
> >>  }
> >>
> >> @@ -514,15 +525,23 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
> >>      CPUState *cpu = ENV_GET_CPU(env);
> >>      hwaddr physaddr = iotlbentry->addr;
> >>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
> >> +    bool locked = false;
> >>
> >>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
> >>      if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
> >>          cpu_io_recompile(cpu, retaddr);
> >>      }
> >> -
> >>      cpu->mem_io_vaddr = addr;
> >>      cpu->mem_io_pc = retaddr;
> >> +
> >> +    if (mr->global_locking) {
> >> +        qemu_mutex_lock_iothread();
> >> +        locked = true;
> >> +    }
> >>      memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs);
> >> +    if (locked) {
> >> +        qemu_mutex_unlock_iothread();
> >> +    }
> >>  }
> >>
> >>  /* Return true if ADDR is present in the victim tlb, and has been copied
> >> diff --git a/exec.c b/exec.c
> >> index 865a1e8295..3adf2b1861 100644
> >> --- a/exec.c
> >> +++ b/exec.c
> >> @@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
> >>                  }
> >>                  cpu->watchpoint_hit = wp;
> >>
> >> -                /* The tb_lock will be reset when cpu_loop_exit or
> >> -                 * cpu_loop_exit_noexc longjmp back into the cpu_exec
> >> -                 * main loop.
> >> +                /* Both tb_lock and iothread_mutex will be reset when
> >> +                 * cpu_loop_exit or cpu_loop_exit_noexc longjmp
> >> +                 * back into the cpu_exec main loop.
> >>                   */
> >>                  tb_lock();
> >>                  tb_check_watchpoint(cpu);
> >> @@ -2371,8 +2371,14 @@ static void io_mem_init(void)
> >>      memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
> >>      memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
> >>                            NULL, UINT64_MAX);
> >> +
> >> +    /* io_mem_notdirty calls tb_invalidate_phys_page_fast,
> >> +     * which can be called without the iothread mutex.
> >> +     */
> >>      memory_region_init_io(&io_mem_notdirty, NULL, &notdirty_mem_ops, NULL,
> >>                            NULL, UINT64_MAX);
> >> +    memory_region_clear_global_locking(&io_mem_notdirty);
> >> +
> >>      memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
> >>                            NULL, UINT64_MAX);
> >>  }
> >> diff --git a/hw/core/irq.c b/hw/core/irq.c
> >> index 49ff2e64fe..b98d1d69f5 100644
> >> --- a/hw/core/irq.c
> >> +++ b/hw/core/irq.c
> >> @@ -22,6 +22,7 @@
> >>   * THE SOFTWARE.
> >>   */
> >>  #include "qemu/osdep.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "qemu-common.h"
> >>  #include "hw/irq.h"
> >>  #include "qom/object.h"
> >> diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
> >> index 7135633863..82a49556af 100644
> >> --- a/hw/i386/kvmvapic.c
> >> +++ b/hw/i386/kvmvapic.c
> >> @@ -457,8 +457,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
> >>      resume_all_vcpus();
> >>
> >>      if (!kvm_enabled()) {
> >> -        /* tb_lock will be reset when cpu_loop_exit_noexc longjmps
> >> -         * back into the cpu_exec loop. */
> >> +        /* Both tb_lock and iothread_mutex will be reset when
> >> +         *  longjmps back into the cpu_exec loop. */
> >>          tb_lock();
> >>          tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1);
> >>          cpu_loop_exit_noexc(cs);
> >> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
> >> index c25ee03556..f775aba507 100644
> >> --- a/hw/intc/arm_gicv3_cpuif.c
> >> +++ b/hw/intc/arm_gicv3_cpuif.c
> >> @@ -14,6 +14,7 @@
> >>
> >>  #include "qemu/osdep.h"
> >>  #include "qemu/bitops.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "trace.h"
> >>  #include "gicv3_internal.h"
> >>  #include "cpu.h"
> >> @@ -733,6 +734,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
> >>      ARMCPU *cpu = ARM_CPU(cs->cpu);
> >>      CPUARMState *env = &cpu->env;
> >>
> >> +    g_assert(qemu_mutex_iothread_locked());
> >> +
> >>      trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
> >>                               cs->hppi.grp, cs->hppi.prio);
> >>
> >> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
> >> index d171e60b5c..5f93083d4a 100644
> >> --- a/hw/ppc/ppc.c
> >> +++ b/hw/ppc/ppc.c
> >> @@ -62,7 +62,16 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
> >>  {
> >>      CPUState *cs = CPU(cpu);
> >>      CPUPPCState *env = &cpu->env;
> >> -    unsigned int old_pending = env->pending_interrupts;
> >> +    unsigned int old_pending;
> >> +    bool locked = false;
> >> +
> >> +    /* We may already have the BQL if coming from the reset path */
> >> +    if (!qemu_mutex_iothread_locked()) {
> >> +        locked = true;
> >> +        qemu_mutex_lock_iothread();
> >> +    }
> >> +
> >> +    old_pending = env->pending_interrupts;
> >>
> >>      if (level) {
> >>          env->pending_interrupts |= 1 << n_IRQ;
> >> @@ -80,9 +89,14 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
> >>  #endif
> >>      }
> >>
> >> +
> >>      LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
> >>                  "req %08x\n", __func__, env, n_IRQ, level,
> >>                  env->pending_interrupts, CPU(cpu)->interrupt_request);
> >> +
> >> +    if (locked) {
> >> +        qemu_mutex_unlock_iothread();
> >> +    }
> >>  }
> >>
> >>  /* PowerPC 6xx / 7xx internal IRQ controller */
> >> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
> >> index e465d7ac98..b1e374f3f9 100644
> >> --- a/hw/ppc/spapr.c
> >> +++ b/hw/ppc/spapr.c
> >> @@ -1010,6 +1010,9 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
> >>  {
> >>      CPUPPCState *env = &cpu->env;
> >>
> >> +    /* The TCG path should also be holding the BQL at this point */
> >> +    g_assert(qemu_mutex_iothread_locked());
> >> +
> >>      if (msr_pr) {
> >>          hcall_dprintf("Hypercall made with MSR[PR]=1\n");
> >>          env->gpr[3] = H_PRIVILEGE;
> >> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
> >> index 2cf4ecf144..10db89b16a 100644
> >> --- a/include/qom/cpu.h
> >> +++ b/include/qom/cpu.h
> >> @@ -329,6 +329,7 @@ struct CPUState {
> >>      bool unplug;
> >>      bool crash_occurred;
> >>      bool exit_request;
> >> +    /* updates protected by BQL */
> >>      uint32_t interrupt_request;
> >>      int singlestep_enabled;
> >>      int64_t icount_extra;
> >> diff --git a/memory.c b/memory.c
> >> index ed8b5aa83e..d61caee867 100644
> >> --- a/memory.c
> >> +++ b/memory.c
> >> @@ -917,6 +917,8 @@ void memory_region_transaction_commit(void)
> >>      AddressSpace *as;
> >>
> >>      assert(memory_region_transaction_depth);
> >> +    assert(qemu_mutex_iothread_locked());
> >> +
> >>      --memory_region_transaction_depth;
> >>      if (!memory_region_transaction_depth) {
> >>          if (memory_region_update_pending) {
> >> diff --git a/qom/cpu.c b/qom/cpu.c
> >> index ed87c50cea..58784bcbea 100644
> >> --- a/qom/cpu.c
> >> +++ b/qom/cpu.c
> >> @@ -113,9 +113,19 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
> >>      error_setg(errp, "Obtaining memory mappings is unsupported on this CPU.");
> >>  }
> >>
> >> +/* Resetting the IRQ comes from across the code base so we take the
> >> + * BQL here if we need to.  cpu_interrupt assumes it is held.*/
> >>  void cpu_reset_interrupt(CPUState *cpu, int mask)
> >>  {
> >> +    bool need_lock = !qemu_mutex_iothread_locked();
> >> +
> >> +    if (need_lock) {
> >> +        qemu_mutex_lock_iothread();
> >> +    }
> >>      cpu->interrupt_request &= ~mask;
> >> +    if (need_lock) {
> >> +        qemu_mutex_unlock_iothread();
> >> +    }
> >>  }
> >>
> >>  void cpu_exit(CPUState *cpu)
> >> diff --git a/target/arm/helper.c b/target/arm/helper.c
> >> index 47250bcf16..753a69d40d 100644
> >> --- a/target/arm/helper.c
> >> +++ b/target/arm/helper.c
> >> @@ -6769,6 +6769,12 @@ void arm_cpu_do_interrupt(CPUState *cs)
> >>          arm_cpu_do_interrupt_aarch32(cs);
> >>      }
> >>
> >> +    /* Hooks may change global state so BQL should be held, also the
> >> +     * BQL needs to be held for any modification of
> >> +     * cs->interrupt_request.
> >> +     */
> >> +    g_assert(qemu_mutex_iothread_locked());
> >> +
> >>      arm_call_el_change_hook(cpu);
> >>
> >>      if (!kvm_enabled()) {
> >> diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
> >> index fb366fdc35..5f3e3bdae2 100644
> >> --- a/target/arm/op_helper.c
> >> +++ b/target/arm/op_helper.c
> >> @@ -18,6 +18,7 @@
> >>   */
> >>  #include "qemu/osdep.h"
> >>  #include "qemu/log.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "cpu.h"
> >>  #include "exec/helper-proto.h"
> >>  #include "internals.h"
> >> @@ -487,7 +488,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
> >>       */
> >>      env->regs[15] &= (env->thumb ? ~1 : ~3);
> >>
> >> +    qemu_mutex_lock_iothread();
> >>      arm_call_el_change_hook(arm_env_get_cpu(env));
> >> +    qemu_mutex_unlock_iothread();
> >>  }
> >>
> >>  /* Access to user mode registers from privileged modes.  */
> >> @@ -735,28 +738,58 @@ void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
> >>  {
> >>      const ARMCPRegInfo *ri = rip;
> >>
> >> -    ri->writefn(env, ri, value);
> >> +    if (ri->type & ARM_CP_IO) {
> >> +        qemu_mutex_lock_iothread();
> >> +        ri->writefn(env, ri, value);
> >> +        qemu_mutex_unlock_iothread();
> >> +    } else {
> >> +        ri->writefn(env, ri, value);
> >> +    }
> >>  }
> >>
> >>  uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip)
> >>  {
> >>      const ARMCPRegInfo *ri = rip;
> >> +    uint32_t res;
> >>
> >> -    return ri->readfn(env, ri);
> >> +    if (ri->type & ARM_CP_IO) {
> >> +        qemu_mutex_lock_iothread();
> >> +        res = ri->readfn(env, ri);
> >> +        qemu_mutex_unlock_iothread();
> >> +    } else {
> >> +        res = ri->readfn(env, ri);
> >> +    }
> >> +
> >> +    return res;
> >>  }
> >>
> >>  void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value)
> >>  {
> >>      const ARMCPRegInfo *ri = rip;
> >>
> >> -    ri->writefn(env, ri, value);
> >> +    if (ri->type & ARM_CP_IO) {
> >> +        qemu_mutex_lock_iothread();
> >> +        ri->writefn(env, ri, value);
> >> +        qemu_mutex_unlock_iothread();
> >> +    } else {
> >> +        ri->writefn(env, ri, value);
> >> +    }
> >>  }
> >>
> >>  uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
> >>  {
> >>      const ARMCPRegInfo *ri = rip;
> >> +    uint64_t res;
> >> +
> >> +    if (ri->type & ARM_CP_IO) {
> >> +        qemu_mutex_lock_iothread();
> >> +        res = ri->readfn(env, ri);
> >> +        qemu_mutex_unlock_iothread();
> >> +    } else {
> >> +        res = ri->readfn(env, ri);
> >> +    }
> >>
> >> -    return ri->readfn(env, ri);
> >> +    return res;
> >>  }
> >>
> >>  void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
> >> @@ -989,7 +1022,9 @@ void HELPER(exception_return)(CPUARMState *env)
> >>                        cur_el, new_el, env->pc);
> >>      }
> >>
> >> +    qemu_mutex_lock_iothread();
> >>      arm_call_el_change_hook(arm_env_get_cpu(env));
> >> +    qemu_mutex_unlock_iothread();
> >>
> >>      return;
> >>
> >> diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c
> >> index 4dd6a2c544..f051a77c4a 100644
> >> --- a/target/i386/smm_helper.c
> >> +++ b/target/i386/smm_helper.c
> >> @@ -18,6 +18,7 @@
> >>   */
> >>
> >>  #include "qemu/osdep.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "cpu.h"
> >>  #include "exec/helper-proto.h"
> >>  #include "exec/log.h"
> >> @@ -42,11 +43,14 @@ void helper_rsm(CPUX86State *env)
> >>  #define SMM_REVISION_ID 0x00020000
> >>  #endif
> >>
> >> +/* Called with iothread lock taken */
> >>  void cpu_smm_update(X86CPU *cpu)
> >>  {
> >>      CPUX86State *env = &cpu->env;
> >>      bool smm_enabled = (env->hflags & HF_SMM_MASK);
> >>
> >> +    g_assert(qemu_mutex_iothread_locked());
> >> +
> >>      if (cpu->smram) {
> >>          memory_region_set_enabled(cpu->smram, smm_enabled);
> >>      }
> >> @@ -333,7 +337,10 @@ void helper_rsm(CPUX86State *env)
> >>      }
> >>      env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK;
> >>      env->hflags &= ~HF_SMM_MASK;
> >> +
> >> +    qemu_mutex_lock_iothread();
> >>      cpu_smm_update(cpu);
> >> +    qemu_mutex_unlock_iothread();
> >>
> >>      qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
> >>      log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
> >> diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
> >> index c9604ea9c7..3cb942e8bb 100644
> >> --- a/target/s390x/misc_helper.c
> >> +++ b/target/s390x/misc_helper.c
> >> @@ -25,6 +25,7 @@
> >>  #include "exec/helper-proto.h"
> >>  #include "sysemu/kvm.h"
> >>  #include "qemu/timer.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "exec/address-spaces.h"
> >>  #ifdef CONFIG_KVM
> >>  #include <linux/kvm.h>
> >> @@ -109,11 +110,13 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
> >>  /* SCLP service call */
> >>  uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
> >>  {
> >> +    qemu_mutex_lock_iothread();
> >>      int r = sclp_service_call(env, r1, r2);
> >>      if (r < 0) {
> >>          program_interrupt(env, -r, 4);
> >> -        return 0;
> >> +        r = 0;
> >>      }
> >> +    qemu_mutex_unlock_iothread();
> >>      return r;
> >>  }
> >>
> >> diff --git a/translate-all.c b/translate-all.c
> >> index 8a861cb583..f810259c41 100644
> >> --- a/translate-all.c
> >> +++ b/translate-all.c
> >> @@ -55,6 +55,7 @@
> >>  #include "translate-all.h"
> >>  #include "qemu/bitmap.h"
> >>  #include "qemu/timer.h"
> >> +#include "qemu/main-loop.h"
> >>  #include "exec/log.h"
> >>
> >>  /* #define DEBUG_TB_INVALIDATE */
> >> @@ -1523,7 +1524,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
> >>  #ifdef CONFIG_SOFTMMU
> >>  /* len must be <= 8 and start must be a multiple of len.
> >>   * Called via softmmu_template.h when code areas are written to with
> >> - * tb_lock held.
> >> + * iothread mutex not held.
> >>   */
> >>  void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
> >>  {
> >> @@ -1725,7 +1726,10 @@ void tb_check_watchpoint(CPUState *cpu)
> >>
> >>  #ifndef CONFIG_USER_ONLY
> >>  /* in deterministic execution mode, instructions doing device I/Os
> >> -   must be at the end of the TB */
> >> + * must be at the end of the TB.
> >> + *
> >> + * Called by softmmu_template.h, with iothread mutex not held.
> >> + */
> >>  void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
> >>  {
> >>  #if defined(TARGET_MIPS) || defined(TARGET_SH4)
> >> @@ -1937,6 +1941,7 @@ void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
> >>
> >>  void cpu_interrupt(CPUState *cpu, int mask)
> >>  {
> >> +    g_assert(qemu_mutex_iothread_locked());
> >>      cpu->interrupt_request |= mask;
> >>      cpu->tcg_exit_req = 1;
> >>  }
> >> diff --git a/translate-common.c b/translate-common.c
> >> index 5e989cdf70..d504dd0d33 100644
> >> --- a/translate-common.c
> >> +++ b/translate-common.c
> >> @@ -21,6 +21,7 @@
> >>  #include "qemu-common.h"
> >>  #include "qom/cpu.h"
> >>  #include "sysemu/cpus.h"
> >> +#include "qemu/main-loop.h"
> >>
> >>  uintptr_t qemu_real_host_page_size;
> >>  intptr_t qemu_real_host_page_mask;
> >> @@ -30,6 +31,7 @@ intptr_t qemu_real_host_page_mask;
> >>  static void tcg_handle_interrupt(CPUState *cpu, int mask)
> >>  {
> >>      int old_mask;
> >> +    g_assert(qemu_mutex_iothread_locked());
> >>
> >>      old_mask = cpu->interrupt_request;
> >>      cpu->interrupt_request |= mask;
> >> @@ -40,17 +42,16 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
> >>       */
> >>      if (!qemu_cpu_is_self(cpu)) {
> >>          qemu_cpu_kick(cpu);
> >> -        return;
> >> -    }
> >> -
> >> -    if (use_icount) {
> >> -        cpu->icount_decr.u16.high = 0xffff;
> >> -        if (!cpu->can_do_io
> >> -            && (mask & ~old_mask) != 0) {
> >> -            cpu_abort(cpu, "Raised interrupt while not in I/O function");
> >> -        }
> >>      } else {
> >> -        cpu->tcg_exit_req = 1;
> >> +        if (use_icount) {
> >> +            cpu->icount_decr.u16.high = 0xffff;
> >> +            if (!cpu->can_do_io
> >> +                && (mask & ~old_mask) != 0) {
> >> +                cpu_abort(cpu, "Raised interrupt while not in I/O function");
> >> +            }
> >> +        } else {
> >> +            cpu->tcg_exit_req = 1;
> >> +        }
> >>      }
> >>  }
> >>
> >> --
> >> 2.11.0
> >>
> >>
> 
> 
> --
> Alex Bennée
> 

-- 
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [Qemu-devel] [PULL 08/24] tcg: drop global lock during TCG code execution
  2017-03-03 20:59       ` Aaron Lindsay
@ 2017-03-03 21:08         ` Alex Bennée
  0 siblings, 0 replies; 18+ messages in thread
From: Alex Bennée @ 2017-03-03 21:08 UTC (permalink / raw)
  To: Aaron Lindsay
  Cc: Laurent Desnogues, Jan Kiszka, open list:ARM cores,
	qemu-devel@nongnu.org


Aaron Lindsay <alindsay@codeaurora.org> writes:

> On Feb 27 14:39, Alex Bennée wrote:
>>
>> Laurent Desnogues <laurent.desnogues@gmail.com> writes:
>>
>> > Hello,
>> >
>> > On Fri, Feb 24, 2017 at 12:20 PM, Alex Bennée <alex.bennee@linaro.org> wrote:
>> >> From: Jan Kiszka <jan.kiszka@siemens.com>
>> >>
>> >> This finally allows TCG to benefit from the iothread introduction: Drop
>> >> the global mutex while running pure TCG CPU code. Reacquire the lock
>> >> when entering MMIO or PIO emulation, or when leaving the TCG loop.
>> >>
>> >> We have to revert a few optimization for the current TCG threading
>> >> model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not
>> >> kicking it in qemu_cpu_kick. We also need to disable RAM block
>> >> reordering until we have a more efficient locking mechanism at hand.
>> >>
>> >> Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here.
>> >> These numbers demonstrate where we gain something:
>> >>
>> >> 20338 jan       20   0  331m  75m 6904 R   99  0.9   0:50.95 qemu-system-arm
>> >> 20337 jan       20   0  331m  75m 6904 S   20  0.9   0:26.50 qemu-system-arm
>> >>
>> >> The guest CPU was fully loaded, but the iothread could still run mostly
>> >> independent on a second core. Without the patch we don't get beyond
>> >>
>> >> 32206 jan       20   0  330m  73m 7036 R   82  0.9   1:06.00 qemu-system-arm
>> >> 32204 jan       20   0  330m  73m 7036 S   21  0.9   0:17.03 qemu-system-arm
>> >>
>> >> We don't benefit significantly, though, when the guest is not fully
>> >> loading a host CPU.
>> >
>> > I tried this patch (8d04fb55 in the repository) with the following image:
>> >
>> >    http://wiki.qemu.org/download/arm-test-0.2.tar.gz
>> >
>> > Running the image with no option works fine.  But specifying '-icount
>> > 1' results in a (guest) deadlock. Enabling some heavy logging (-d
>> > in_asm,exec) sometimes results in a 'Bad ram offset'.
>> >
>> > Is it expected that this patch breaks -icount?
>>
>> Not really. Using icount will disable MTTCG and run single threaded as
>> before. Paolo reported another icount failure so they may be related. I
>> shall have a look at it.
>>
>> Thanks for the report.
>
> I have not experienced a guest deadlock, but for me this patch makes
> booting a simple Linux distribution take about an order of magnitude
> longer when using '-icount 0' (from about 1.6 seconds to 17.9). It was
> slow enough to get to the printk the first time after recompiling that I
> killed it thinking it *had* deadlocked.
>
> `perf report` from before this patch (snipped to >1%):
>  23.81%  qemu-system-aar  perf-9267.map        [.] 0x0000000041a5cc9e
>   7.15%  qemu-system-aar  [kernel.kallsyms]    [k] 0xffffffff8172bc82
>   6.29%  qemu-system-aar  qemu-system-aarch64  [.] cpu_exec
>   4.99%  qemu-system-aar  qemu-system-aarch64  [.] tcg_gen_code
>   4.71%  qemu-system-aar  qemu-system-aarch64  [.] cpu_get_tb_cpu_state
>   4.39%  qemu-system-aar  qemu-system-aarch64  [.] tcg_optimize
>   3.28%  qemu-system-aar  qemu-system-aarch64  [.] helper_dc_zva
>   2.66%  qemu-system-aar  qemu-system-aarch64  [.] liveness_pass_1
>   1.98%  qemu-system-aar  qemu-system-aarch64  [.] qht_lookup
>   1.93%  qemu-system-aar  qemu-system-aarch64  [.] tcg_out_opc
>   1.81%  qemu-system-aar  qemu-system-aarch64  [.] get_phys_addr_lpae
>   1.71%  qemu-system-aar  qemu-system-aarch64  [.] object_class_dynamic_cast_assert
>   1.38%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi1
>   1.10%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi0
>
> and after this patch:
>  20.10%  qemu-system-aar  perf-3285.map        [.] 0x0000000040a3b690
> *18.08%  qemu-system-aar  [kernel.kallsyms]    [k] 0xffffffff81371865
>   7.87%  qemu-system-aar  qemu-system-aarch64  [.] cpu_exec
>   4.70%  qemu-system-aar  qemu-system-aarch64  [.] cpu_get_tb_cpu_state
> * 2.64%  qemu-system-aar  qemu-system-aarch64  [.] g_mutex_get_impl
>   2.39%  qemu-system-aar  qemu-system-aarch64  [.] gic_update
> * 1.89%  qemu-system-aar  qemu-system-aarch64  [.] pthread_mutex_unlock
>   1.61%  qemu-system-aar  qemu-system-aarch64  [.] object_class_dynamic_cast_assert
> * 1.55%  qemu-system-aar  qemu-system-aarch64  [.] pthread_mutex_lock
>   1.31%  qemu-system-aar  qemu-system-aarch64  [.] get_phys_addr_lpae
>   1.21%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi0
>   1.13%  qemu-system-aar  qemu-system-aarch64  [.] arm_regime_tbi1
>
> I've put asterisks by a few suspicious mutex-related functions, though I wonder
> if the slowdowns are also partially inlined into some of the other functions.
> The kernel also jumps up, presumably from handling more mutexes?
>
> I confess I'm not familiar enough with this code to suggest optimizations, but
> I'll be glad to test any.

Please see the series Paolo posted:

  Subject: [PATCH 0/6] tcg: fix icount super slowdown
  Date: Fri,  3 Mar 2017 14:11:08 +0100
  Message-Id: <20170303131113.25898-1-pbonzini@redhat.com>

>
> -Aaron
>
>>
>> >
>> > Thanks,
>> >
>> > Laurent
>> >
>> > PS - To clarify 791158d9 works.
>> >
>> >> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
>> >> Message-Id: <1439220437-23957-10-git-send-email-fred.konrad@greensocs.com>
>> >> [FK: Rebase, fix qemu_devices_reset deadlock, rm address_space_* mutex]
>> >> Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
>> >> [EGC: fixed iothread lock for cpu-exec IRQ handling]
>> >> Signed-off-by: Emilio G. Cota <cota@braap.org>
>> >> [AJB: -smp single-threaded fix, clean commit msg, BQL fixes]
>> >> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>> >> Reviewed-by: Richard Henderson <rth@twiddle.net>
>> >> Reviewed-by: Pranith Kumar <bobby.prani@gmail.com>
>> >> [PM: target-arm changes]
>> >> Acked-by: Peter Maydell <peter.maydell@linaro.org>
>> >> ---
>> >>  cpu-exec.c                 | 23 +++++++++++++++++++++--
>> >>  cpus.c                     | 28 +++++-----------------------
>> >>  cputlb.c                   | 21 ++++++++++++++++++++-
>> >>  exec.c                     | 12 +++++++++---
>> >>  hw/core/irq.c              |  1 +
>> >>  hw/i386/kvmvapic.c         |  4 ++--
>> >>  hw/intc/arm_gicv3_cpuif.c  |  3 +++
>> >>  hw/ppc/ppc.c               | 16 +++++++++++++++-
>> >>  hw/ppc/spapr.c             |  3 +++
>> >>  include/qom/cpu.h          |  1 +
>> >>  memory.c                   |  2 ++
>> >>  qom/cpu.c                  | 10 ++++++++++
>> >>  target/arm/helper.c        |  6 ++++++
>> >>  target/arm/op_helper.c     | 43 +++++++++++++++++++++++++++++++++++++++----
>> >>  target/i386/smm_helper.c   |  7 +++++++
>> >>  target/s390x/misc_helper.c |  5 ++++-
>> >>  translate-all.c            |  9 +++++++--
>> >>  translate-common.c         | 21 +++++++++++----------
>> >>  18 files changed, 166 insertions(+), 49 deletions(-)
>> >>
>> >> diff --git a/cpu-exec.c b/cpu-exec.c
>> >> index 06a6b25564..1bd3d72002 100644
>> >> --- a/cpu-exec.c
>> >> +++ b/cpu-exec.c
>> >> @@ -29,6 +29,7 @@
>> >>  #include "qemu/rcu.h"
>> >>  #include "exec/tb-hash.h"
>> >>  #include "exec/log.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #if defined(TARGET_I386) && !defined(CONFIG_USER_ONLY)
>> >>  #include "hw/i386/apic.h"
>> >>  #endif
>> >> @@ -388,8 +389,10 @@ static inline bool cpu_handle_halt(CPUState *cpu)
>> >>          if ((cpu->interrupt_request & CPU_INTERRUPT_POLL)
>> >>              && replay_interrupt()) {
>> >>              X86CPU *x86_cpu = X86_CPU(cpu);
>> >> +            qemu_mutex_lock_iothread();
>> >>              apic_poll_irq(x86_cpu->apic_state);
>> >>              cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
>> >> +            qemu_mutex_unlock_iothread();
>> >>          }
>> >>  #endif
>> >>          if (!cpu_has_work(cpu)) {
>> >> @@ -443,7 +446,9 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
>> >>  #else
>> >>              if (replay_exception()) {
>> >>                  CPUClass *cc = CPU_GET_CLASS(cpu);
>> >> +                qemu_mutex_lock_iothread();
>> >>                  cc->do_interrupt(cpu);
>> >> +                qemu_mutex_unlock_iothread();
>> >>                  cpu->exception_index = -1;
>> >>              } else if (!replay_has_interrupt()) {
>> >>                  /* give a chance to iothread in replay mode */
>> >> @@ -469,9 +474,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>> >>                                          TranslationBlock **last_tb)
>> >>  {
>> >>      CPUClass *cc = CPU_GET_CLASS(cpu);
>> >> -    int interrupt_request = cpu->interrupt_request;
>> >>
>> >> -    if (unlikely(interrupt_request)) {
>> >> +    if (unlikely(atomic_read(&cpu->interrupt_request))) {
>> >> +        int interrupt_request;
>> >> +        qemu_mutex_lock_iothread();
>> >> +        interrupt_request = cpu->interrupt_request;
>> >>          if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) {
>> >>              /* Mask out external interrupts for this step. */
>> >>              interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK;
>> >> @@ -479,6 +486,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>> >>          if (interrupt_request & CPU_INTERRUPT_DEBUG) {
>> >>              cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
>> >>              cpu->exception_index = EXCP_DEBUG;
>> >> +            qemu_mutex_unlock_iothread();
>> >>              return true;
>> >>          }
>> >>          if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) {
>> >> @@ -488,6 +496,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>> >>              cpu->interrupt_request &= ~CPU_INTERRUPT_HALT;
>> >>              cpu->halted = 1;
>> >>              cpu->exception_index = EXCP_HLT;
>> >> +            qemu_mutex_unlock_iothread();
>> >>              return true;
>> >>          }
>> >>  #if defined(TARGET_I386)
>> >> @@ -498,12 +507,14 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>> >>              cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0);
>> >>              do_cpu_init(x86_cpu);
>> >>              cpu->exception_index = EXCP_HALTED;
>> >> +            qemu_mutex_unlock_iothread();
>> >>              return true;
>> >>          }
>> >>  #else
>> >>          else if (interrupt_request & CPU_INTERRUPT_RESET) {
>> >>              replay_interrupt();
>> >>              cpu_reset(cpu);
>> >> +            qemu_mutex_unlock_iothread();
>> >>              return true;
>> >>          }
>> >>  #endif
>> >> @@ -526,7 +537,12 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
>> >>                 the program flow was changed */
>> >>              *last_tb = NULL;
>> >>          }
>> >> +
>> >> +        /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */
>> >> +        qemu_mutex_unlock_iothread();
>> >>      }
>> >> +
>> >> +
>> >>      if (unlikely(atomic_read(&cpu->exit_request) || replay_has_interrupt())) {
>> >>          atomic_set(&cpu->exit_request, 0);
>> >>          cpu->exception_index = EXCP_INTERRUPT;
>> >> @@ -643,6 +659,9 @@ int cpu_exec(CPUState *cpu)
>> >>  #endif /* buggy compiler */
>> >>          cpu->can_do_io = 1;
>> >>          tb_lock_reset();
>> >> +        if (qemu_mutex_iothread_locked()) {
>> >> +            qemu_mutex_unlock_iothread();
>> >> +        }
>> >>      }
>> >>
>> >>      /* if an exception is pending, we execute it here */
>> >> diff --git a/cpus.c b/cpus.c
>> >> index 860034a794..0ae8f69be5 100644
>> >> --- a/cpus.c
>> >> +++ b/cpus.c
>> >> @@ -1027,8 +1027,6 @@ static void qemu_kvm_init_cpu_signals(CPUState *cpu)
>> >>  #endif /* _WIN32 */
>> >>
>> >>  static QemuMutex qemu_global_mutex;
>> >> -static QemuCond qemu_io_proceeded_cond;
>> >> -static unsigned iothread_requesting_mutex;
>> >>
>> >>  static QemuThread io_thread;
>> >>
>> >> @@ -1042,7 +1040,6 @@ void qemu_init_cpu_loop(void)
>> >>      qemu_init_sigbus();
>> >>      qemu_cond_init(&qemu_cpu_cond);
>> >>      qemu_cond_init(&qemu_pause_cond);
>> >> -    qemu_cond_init(&qemu_io_proceeded_cond);
>> >>      qemu_mutex_init(&qemu_global_mutex);
>> >>
>> >>      qemu_thread_get_self(&io_thread);
>> >> @@ -1085,10 +1082,6 @@ static void qemu_tcg_wait_io_event(CPUState *cpu)
>> >>
>> >>      start_tcg_kick_timer();
>> >>
>> >> -    while (iothread_requesting_mutex) {
>> >> -        qemu_cond_wait(&qemu_io_proceeded_cond, &qemu_global_mutex);
>> >> -    }
>> >> -
>> >>      CPU_FOREACH(cpu) {
>> >>          qemu_wait_io_event_common(cpu);
>> >>      }
>> >> @@ -1249,9 +1242,11 @@ static int tcg_cpu_exec(CPUState *cpu)
>> >>          cpu->icount_decr.u16.low = decr;
>> >>          cpu->icount_extra = count;
>> >>      }
>> >> +    qemu_mutex_unlock_iothread();
>> >>      cpu_exec_start(cpu);
>> >>      ret = cpu_exec(cpu);
>> >>      cpu_exec_end(cpu);
>> >> +    qemu_mutex_lock_iothread();
>> >>  #ifdef CONFIG_PROFILER
>> >>      tcg_time += profile_getclock() - ti;
>> >>  #endif
>> >> @@ -1479,27 +1474,14 @@ bool qemu_mutex_iothread_locked(void)
>> >>
>> >>  void qemu_mutex_lock_iothread(void)
>> >>  {
>> >> -    atomic_inc(&iothread_requesting_mutex);
>> >> -    /* In the simple case there is no need to bump the VCPU thread out of
>> >> -     * TCG code execution.
>> >> -     */
>> >> -    if (!tcg_enabled() || qemu_in_vcpu_thread() ||
>> >> -        !first_cpu || !first_cpu->created) {
>> >> -        qemu_mutex_lock(&qemu_global_mutex);
>> >> -        atomic_dec(&iothread_requesting_mutex);
>> >> -    } else {
>> >> -        if (qemu_mutex_trylock(&qemu_global_mutex)) {
>> >> -            qemu_cpu_kick_rr_cpu();
>> >> -            qemu_mutex_lock(&qemu_global_mutex);
>> >> -        }
>> >> -        atomic_dec(&iothread_requesting_mutex);
>> >> -        qemu_cond_broadcast(&qemu_io_proceeded_cond);
>> >> -    }
>> >> +    g_assert(!qemu_mutex_iothread_locked());
>> >> +    qemu_mutex_lock(&qemu_global_mutex);
>> >>      iothread_locked = true;
>> >>  }
>> >>
>> >>  void qemu_mutex_unlock_iothread(void)
>> >>  {
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >>      iothread_locked = false;
>> >>      qemu_mutex_unlock(&qemu_global_mutex);
>> >>  }
>> >> diff --git a/cputlb.c b/cputlb.c
>> >> index 6c39927455..1cc9d9da51 100644
>> >> --- a/cputlb.c
>> >> +++ b/cputlb.c
>> >> @@ -18,6 +18,7 @@
>> >>   */
>> >>
>> >>  #include "qemu/osdep.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "cpu.h"
>> >>  #include "exec/exec-all.h"
>> >>  #include "exec/memory.h"
>> >> @@ -495,6 +496,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>> >>      hwaddr physaddr = iotlbentry->addr;
>> >>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
>> >>      uint64_t val;
>> >> +    bool locked = false;
>> >>
>> >>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
>> >>      cpu->mem_io_pc = retaddr;
>> >> @@ -503,7 +505,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>> >>      }
>> >>
>> >>      cpu->mem_io_vaddr = addr;
>> >> +
>> >> +    if (mr->global_locking) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +        locked = true;
>> >> +    }
>> >>      memory_region_dispatch_read(mr, physaddr, &val, size, iotlbentry->attrs);
>> >> +    if (locked) {
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    }
>> >> +
>> >>      return val;
>> >>  }
>> >>
>> >> @@ -514,15 +525,23 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
>> >>      CPUState *cpu = ENV_GET_CPU(env);
>> >>      hwaddr physaddr = iotlbentry->addr;
>> >>      MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
>> >> +    bool locked = false;
>> >>
>> >>      physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
>> >>      if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
>> >>          cpu_io_recompile(cpu, retaddr);
>> >>      }
>> >> -
>> >>      cpu->mem_io_vaddr = addr;
>> >>      cpu->mem_io_pc = retaddr;
>> >> +
>> >> +    if (mr->global_locking) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +        locked = true;
>> >> +    }
>> >>      memory_region_dispatch_write(mr, physaddr, val, size, iotlbentry->attrs);
>> >> +    if (locked) {
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    }
>> >>  }
>> >>
>> >>  /* Return true if ADDR is present in the victim tlb, and has been copied
>> >> diff --git a/exec.c b/exec.c
>> >> index 865a1e8295..3adf2b1861 100644
>> >> --- a/exec.c
>> >> +++ b/exec.c
>> >> @@ -2134,9 +2134,9 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags)
>> >>                  }
>> >>                  cpu->watchpoint_hit = wp;
>> >>
>> >> -                /* The tb_lock will be reset when cpu_loop_exit or
>> >> -                 * cpu_loop_exit_noexc longjmp back into the cpu_exec
>> >> -                 * main loop.
>> >> +                /* Both tb_lock and iothread_mutex will be reset when
>> >> +                 * cpu_loop_exit or cpu_loop_exit_noexc longjmp
>> >> +                 * back into the cpu_exec main loop.
>> >>                   */
>> >>                  tb_lock();
>> >>                  tb_check_watchpoint(cpu);
>> >> @@ -2371,8 +2371,14 @@ static void io_mem_init(void)
>> >>      memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
>> >>      memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
>> >>                            NULL, UINT64_MAX);
>> >> +
>> >> +    /* io_mem_notdirty calls tb_invalidate_phys_page_fast,
>> >> +     * which can be called without the iothread mutex.
>> >> +     */
>> >>      memory_region_init_io(&io_mem_notdirty, NULL, &notdirty_mem_ops, NULL,
>> >>                            NULL, UINT64_MAX);
>> >> +    memory_region_clear_global_locking(&io_mem_notdirty);
>> >> +
>> >>      memory_region_init_io(&io_mem_watch, NULL, &watch_mem_ops, NULL,
>> >>                            NULL, UINT64_MAX);
>> >>  }
>> >> diff --git a/hw/core/irq.c b/hw/core/irq.c
>> >> index 49ff2e64fe..b98d1d69f5 100644
>> >> --- a/hw/core/irq.c
>> >> +++ b/hw/core/irq.c
>> >> @@ -22,6 +22,7 @@
>> >>   * THE SOFTWARE.
>> >>   */
>> >>  #include "qemu/osdep.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "qemu-common.h"
>> >>  #include "hw/irq.h"
>> >>  #include "qom/object.h"
>> >> diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
>> >> index 7135633863..82a49556af 100644
>> >> --- a/hw/i386/kvmvapic.c
>> >> +++ b/hw/i386/kvmvapic.c
>> >> @@ -457,8 +457,8 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip)
>> >>      resume_all_vcpus();
>> >>
>> >>      if (!kvm_enabled()) {
>> >> -        /* tb_lock will be reset when cpu_loop_exit_noexc longjmps
>> >> -         * back into the cpu_exec loop. */
>> >> +        /* Both tb_lock and iothread_mutex will be reset when
>> >> +         *  longjmps back into the cpu_exec loop. */
>> >>          tb_lock();
>> >>          tb_gen_code(cs, current_pc, current_cs_base, current_flags, 1);
>> >>          cpu_loop_exit_noexc(cs);
>> >> diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
>> >> index c25ee03556..f775aba507 100644
>> >> --- a/hw/intc/arm_gicv3_cpuif.c
>> >> +++ b/hw/intc/arm_gicv3_cpuif.c
>> >> @@ -14,6 +14,7 @@
>> >>
>> >>  #include "qemu/osdep.h"
>> >>  #include "qemu/bitops.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "trace.h"
>> >>  #include "gicv3_internal.h"
>> >>  #include "cpu.h"
>> >> @@ -733,6 +734,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs)
>> >>      ARMCPU *cpu = ARM_CPU(cs->cpu);
>> >>      CPUARMState *env = &cpu->env;
>> >>
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >> +
>> >>      trace_gicv3_cpuif_update(gicv3_redist_affid(cs), cs->hppi.irq,
>> >>                               cs->hppi.grp, cs->hppi.prio);
>> >>
>> >> diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c
>> >> index d171e60b5c..5f93083d4a 100644
>> >> --- a/hw/ppc/ppc.c
>> >> +++ b/hw/ppc/ppc.c
>> >> @@ -62,7 +62,16 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
>> >>  {
>> >>      CPUState *cs = CPU(cpu);
>> >>      CPUPPCState *env = &cpu->env;
>> >> -    unsigned int old_pending = env->pending_interrupts;
>> >> +    unsigned int old_pending;
>> >> +    bool locked = false;
>> >> +
>> >> +    /* We may already have the BQL if coming from the reset path */
>> >> +    if (!qemu_mutex_iothread_locked()) {
>> >> +        locked = true;
>> >> +        qemu_mutex_lock_iothread();
>> >> +    }
>> >> +
>> >> +    old_pending = env->pending_interrupts;
>> >>
>> >>      if (level) {
>> >>          env->pending_interrupts |= 1 << n_IRQ;
>> >> @@ -80,9 +89,14 @@ void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
>> >>  #endif
>> >>      }
>> >>
>> >> +
>> >>      LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
>> >>                  "req %08x\n", __func__, env, n_IRQ, level,
>> >>                  env->pending_interrupts, CPU(cpu)->interrupt_request);
>> >> +
>> >> +    if (locked) {
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    }
>> >>  }
>> >>
>> >>  /* PowerPC 6xx / 7xx internal IRQ controller */
>> >> diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c
>> >> index e465d7ac98..b1e374f3f9 100644
>> >> --- a/hw/ppc/spapr.c
>> >> +++ b/hw/ppc/spapr.c
>> >> @@ -1010,6 +1010,9 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp,
>> >>  {
>> >>      CPUPPCState *env = &cpu->env;
>> >>
>> >> +    /* The TCG path should also be holding the BQL at this point */
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >> +
>> >>      if (msr_pr) {
>> >>          hcall_dprintf("Hypercall made with MSR[PR]=1\n");
>> >>          env->gpr[3] = H_PRIVILEGE;
>> >> diff --git a/include/qom/cpu.h b/include/qom/cpu.h
>> >> index 2cf4ecf144..10db89b16a 100644
>> >> --- a/include/qom/cpu.h
>> >> +++ b/include/qom/cpu.h
>> >> @@ -329,6 +329,7 @@ struct CPUState {
>> >>      bool unplug;
>> >>      bool crash_occurred;
>> >>      bool exit_request;
>> >> +    /* updates protected by BQL */
>> >>      uint32_t interrupt_request;
>> >>      int singlestep_enabled;
>> >>      int64_t icount_extra;
>> >> diff --git a/memory.c b/memory.c
>> >> index ed8b5aa83e..d61caee867 100644
>> >> --- a/memory.c
>> >> +++ b/memory.c
>> >> @@ -917,6 +917,8 @@ void memory_region_transaction_commit(void)
>> >>      AddressSpace *as;
>> >>
>> >>      assert(memory_region_transaction_depth);
>> >> +    assert(qemu_mutex_iothread_locked());
>> >> +
>> >>      --memory_region_transaction_depth;
>> >>      if (!memory_region_transaction_depth) {
>> >>          if (memory_region_update_pending) {
>> >> diff --git a/qom/cpu.c b/qom/cpu.c
>> >> index ed87c50cea..58784bcbea 100644
>> >> --- a/qom/cpu.c
>> >> +++ b/qom/cpu.c
>> >> @@ -113,9 +113,19 @@ static void cpu_common_get_memory_mapping(CPUState *cpu,
>> >>      error_setg(errp, "Obtaining memory mappings is unsupported on this CPU.");
>> >>  }
>> >>
>> >> +/* Resetting the IRQ comes from across the code base so we take the
>> >> + * BQL here if we need to.  cpu_interrupt assumes it is held.*/
>> >>  void cpu_reset_interrupt(CPUState *cpu, int mask)
>> >>  {
>> >> +    bool need_lock = !qemu_mutex_iothread_locked();
>> >> +
>> >> +    if (need_lock) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +    }
>> >>      cpu->interrupt_request &= ~mask;
>> >> +    if (need_lock) {
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    }
>> >>  }
>> >>
>> >>  void cpu_exit(CPUState *cpu)
>> >> diff --git a/target/arm/helper.c b/target/arm/helper.c
>> >> index 47250bcf16..753a69d40d 100644
>> >> --- a/target/arm/helper.c
>> >> +++ b/target/arm/helper.c
>> >> @@ -6769,6 +6769,12 @@ void arm_cpu_do_interrupt(CPUState *cs)
>> >>          arm_cpu_do_interrupt_aarch32(cs);
>> >>      }
>> >>
>> >> +    /* Hooks may change global state so BQL should be held, also the
>> >> +     * BQL needs to be held for any modification of
>> >> +     * cs->interrupt_request.
>> >> +     */
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >> +
>> >>      arm_call_el_change_hook(cpu);
>> >>
>> >>      if (!kvm_enabled()) {
>> >> diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c
>> >> index fb366fdc35..5f3e3bdae2 100644
>> >> --- a/target/arm/op_helper.c
>> >> +++ b/target/arm/op_helper.c
>> >> @@ -18,6 +18,7 @@
>> >>   */
>> >>  #include "qemu/osdep.h"
>> >>  #include "qemu/log.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "cpu.h"
>> >>  #include "exec/helper-proto.h"
>> >>  #include "internals.h"
>> >> @@ -487,7 +488,9 @@ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val)
>> >>       */
>> >>      env->regs[15] &= (env->thumb ? ~1 : ~3);
>> >>
>> >> +    qemu_mutex_lock_iothread();
>> >>      arm_call_el_change_hook(arm_env_get_cpu(env));
>> >> +    qemu_mutex_unlock_iothread();
>> >>  }
>> >>
>> >>  /* Access to user mode registers from privileged modes.  */
>> >> @@ -735,28 +738,58 @@ void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value)
>> >>  {
>> >>      const ARMCPRegInfo *ri = rip;
>> >>
>> >> -    ri->writefn(env, ri, value);
>> >> +    if (ri->type & ARM_CP_IO) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +        ri->writefn(env, ri, value);
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    } else {
>> >> +        ri->writefn(env, ri, value);
>> >> +    }
>> >>  }
>> >>
>> >>  uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip)
>> >>  {
>> >>      const ARMCPRegInfo *ri = rip;
>> >> +    uint32_t res;
>> >>
>> >> -    return ri->readfn(env, ri);
>> >> +    if (ri->type & ARM_CP_IO) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +        res = ri->readfn(env, ri);
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    } else {
>> >> +        res = ri->readfn(env, ri);
>> >> +    }
>> >> +
>> >> +    return res;
>> >>  }
>> >>
>> >>  void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value)
>> >>  {
>> >>      const ARMCPRegInfo *ri = rip;
>> >>
>> >> -    ri->writefn(env, ri, value);
>> >> +    if (ri->type & ARM_CP_IO) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +        ri->writefn(env, ri, value);
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    } else {
>> >> +        ri->writefn(env, ri, value);
>> >> +    }
>> >>  }
>> >>
>> >>  uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip)
>> >>  {
>> >>      const ARMCPRegInfo *ri = rip;
>> >> +    uint64_t res;
>> >> +
>> >> +    if (ri->type & ARM_CP_IO) {
>> >> +        qemu_mutex_lock_iothread();
>> >> +        res = ri->readfn(env, ri);
>> >> +        qemu_mutex_unlock_iothread();
>> >> +    } else {
>> >> +        res = ri->readfn(env, ri);
>> >> +    }
>> >>
>> >> -    return ri->readfn(env, ri);
>> >> +    return res;
>> >>  }
>> >>
>> >>  void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm)
>> >> @@ -989,7 +1022,9 @@ void HELPER(exception_return)(CPUARMState *env)
>> >>                        cur_el, new_el, env->pc);
>> >>      }
>> >>
>> >> +    qemu_mutex_lock_iothread();
>> >>      arm_call_el_change_hook(arm_env_get_cpu(env));
>> >> +    qemu_mutex_unlock_iothread();
>> >>
>> >>      return;
>> >>
>> >> diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c
>> >> index 4dd6a2c544..f051a77c4a 100644
>> >> --- a/target/i386/smm_helper.c
>> >> +++ b/target/i386/smm_helper.c
>> >> @@ -18,6 +18,7 @@
>> >>   */
>> >>
>> >>  #include "qemu/osdep.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "cpu.h"
>> >>  #include "exec/helper-proto.h"
>> >>  #include "exec/log.h"
>> >> @@ -42,11 +43,14 @@ void helper_rsm(CPUX86State *env)
>> >>  #define SMM_REVISION_ID 0x00020000
>> >>  #endif
>> >>
>> >> +/* Called with iothread lock taken */
>> >>  void cpu_smm_update(X86CPU *cpu)
>> >>  {
>> >>      CPUX86State *env = &cpu->env;
>> >>      bool smm_enabled = (env->hflags & HF_SMM_MASK);
>> >>
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >> +
>> >>      if (cpu->smram) {
>> >>          memory_region_set_enabled(cpu->smram, smm_enabled);
>> >>      }
>> >> @@ -333,7 +337,10 @@ void helper_rsm(CPUX86State *env)
>> >>      }
>> >>      env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK;
>> >>      env->hflags &= ~HF_SMM_MASK;
>> >> +
>> >> +    qemu_mutex_lock_iothread();
>> >>      cpu_smm_update(cpu);
>> >> +    qemu_mutex_unlock_iothread();
>> >>
>> >>      qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n");
>> >>      log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP);
>> >> diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c
>> >> index c9604ea9c7..3cb942e8bb 100644
>> >> --- a/target/s390x/misc_helper.c
>> >> +++ b/target/s390x/misc_helper.c
>> >> @@ -25,6 +25,7 @@
>> >>  #include "exec/helper-proto.h"
>> >>  #include "sysemu/kvm.h"
>> >>  #include "qemu/timer.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "exec/address-spaces.h"
>> >>  #ifdef CONFIG_KVM
>> >>  #include <linux/kvm.h>
>> >> @@ -109,11 +110,13 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
>> >>  /* SCLP service call */
>> >>  uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2)
>> >>  {
>> >> +    qemu_mutex_lock_iothread();
>> >>      int r = sclp_service_call(env, r1, r2);
>> >>      if (r < 0) {
>> >>          program_interrupt(env, -r, 4);
>> >> -        return 0;
>> >> +        r = 0;
>> >>      }
>> >> +    qemu_mutex_unlock_iothread();
>> >>      return r;
>> >>  }
>> >>
>> >> diff --git a/translate-all.c b/translate-all.c
>> >> index 8a861cb583..f810259c41 100644
>> >> --- a/translate-all.c
>> >> +++ b/translate-all.c
>> >> @@ -55,6 +55,7 @@
>> >>  #include "translate-all.h"
>> >>  #include "qemu/bitmap.h"
>> >>  #include "qemu/timer.h"
>> >> +#include "qemu/main-loop.h"
>> >>  #include "exec/log.h"
>> >>
>> >>  /* #define DEBUG_TB_INVALIDATE */
>> >> @@ -1523,7 +1524,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
>> >>  #ifdef CONFIG_SOFTMMU
>> >>  /* len must be <= 8 and start must be a multiple of len.
>> >>   * Called via softmmu_template.h when code areas are written to with
>> >> - * tb_lock held.
>> >> + * iothread mutex not held.
>> >>   */
>> >>  void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len)
>> >>  {
>> >> @@ -1725,7 +1726,10 @@ void tb_check_watchpoint(CPUState *cpu)
>> >>
>> >>  #ifndef CONFIG_USER_ONLY
>> >>  /* in deterministic execution mode, instructions doing device I/Os
>> >> -   must be at the end of the TB */
>> >> + * must be at the end of the TB.
>> >> + *
>> >> + * Called by softmmu_template.h, with iothread mutex not held.
>> >> + */
>> >>  void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr)
>> >>  {
>> >>  #if defined(TARGET_MIPS) || defined(TARGET_SH4)
>> >> @@ -1937,6 +1941,7 @@ void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf)
>> >>
>> >>  void cpu_interrupt(CPUState *cpu, int mask)
>> >>  {
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >>      cpu->interrupt_request |= mask;
>> >>      cpu->tcg_exit_req = 1;
>> >>  }
>> >> diff --git a/translate-common.c b/translate-common.c
>> >> index 5e989cdf70..d504dd0d33 100644
>> >> --- a/translate-common.c
>> >> +++ b/translate-common.c
>> >> @@ -21,6 +21,7 @@
>> >>  #include "qemu-common.h"
>> >>  #include "qom/cpu.h"
>> >>  #include "sysemu/cpus.h"
>> >> +#include "qemu/main-loop.h"
>> >>
>> >>  uintptr_t qemu_real_host_page_size;
>> >>  intptr_t qemu_real_host_page_mask;
>> >> @@ -30,6 +31,7 @@ intptr_t qemu_real_host_page_mask;
>> >>  static void tcg_handle_interrupt(CPUState *cpu, int mask)
>> >>  {
>> >>      int old_mask;
>> >> +    g_assert(qemu_mutex_iothread_locked());
>> >>
>> >>      old_mask = cpu->interrupt_request;
>> >>      cpu->interrupt_request |= mask;
>> >> @@ -40,17 +42,16 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
>> >>       */
>> >>      if (!qemu_cpu_is_self(cpu)) {
>> >>          qemu_cpu_kick(cpu);
>> >> -        return;
>> >> -    }
>> >> -
>> >> -    if (use_icount) {
>> >> -        cpu->icount_decr.u16.high = 0xffff;
>> >> -        if (!cpu->can_do_io
>> >> -            && (mask & ~old_mask) != 0) {
>> >> -            cpu_abort(cpu, "Raised interrupt while not in I/O function");
>> >> -        }
>> >>      } else {
>> >> -        cpu->tcg_exit_req = 1;
>> >> +        if (use_icount) {
>> >> +            cpu->icount_decr.u16.high = 0xffff;
>> >> +            if (!cpu->can_do_io
>> >> +                && (mask & ~old_mask) != 0) {
>> >> +                cpu_abort(cpu, "Raised interrupt while not in I/O function");
>> >> +            }
>> >> +        } else {
>> >> +            cpu->tcg_exit_req = 1;
>> >> +        }
>> >>      }
>> >>  }
>> >>
>> >> --
>> >> 2.11.0
>> >>
>> >>
>>
>>
>> --
>> Alex Bennée
>>


--
Alex Bennée

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-02-24 11:21 ` [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete Alex Bennée
@ 2017-09-17 13:07   ` Dmitry Osipenko
  2017-09-17 13:22     ` Alex Bennée
  0 siblings, 1 reply; 18+ messages in thread
From: Dmitry Osipenko @ 2017-09-17 13:07 UTC (permalink / raw)
  To: Alex Bennée, peter.maydell; +Cc: open list:ARM, qemu-devel

On 24.02.2017 14:21, Alex Bennée wrote:
> Previously flushes on other vCPUs would only get serviced when they
> exited their TranslationBlocks. While this isn't overly problematic it
> violates the semantics of TLB flush from the point of view of source
> vCPU.
> 
> To solve this we call the cputlb *_all_cpus_synced() functions to do
> the flushes which ensures all flushes are completed by the time the
> vCPU next schedules its own work. As the TLB instructions are modelled
> as CP writes the TB ends at this point meaning cpu->exit_request will
> be checked before the next instruction is executed.
> 
> Deferring the work until the architectural sync point is a possible
> future optimisation.
> 
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Reviewed-by: Richard Henderson <rth@twiddle.net>
> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>  1 file changed, 69 insertions(+), 96 deletions(-)
> 

Hello,

I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
single-threaded TCG is affected. Git bisection lead to this patch, any ideas?

Example:

qemu-system-arm -M vexpress-a9 -smp cpus=2 -accel accel=tcg,thread=single
-kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -serial
stdio -net nic,model=lan9118 -net user -d in_asm,out_asm -D /tmp/qemulog

Last TB from the log:
----------------
IN:
0xc011a450:  ee080f73      mcr	15, 0, r0, cr8, cr3, {3}

OUT: [size=68]
0x7f32d8b93f80:  mov    -0x18(%r14),%ebp
0x7f32d8b93f84:  test   %ebp,%ebp
0x7f32d8b93f86:  jne    0x7f32d8b93fb8
0x7f32d8b93f8c:  mov    %r14,%rdi
0x7f32d8b93f8f:  mov    $0x5620f2aea5d0,%rsi
0x7f32d8b93f99:  mov    (%r14),%edx
0x7f32d8b93f9c:  mov    $0x5620f18107ca,%r10
0x7f32d8b93fa6:  callq  *%r10
0x7f32d8b93fa9:  movl   $0xc011a454,0x3c(%r14)
0x7f32d8b93fb1:  xor    %eax,%eax
0x7f32d8b93fb3:  jmpq   0x7f32d7a4e016
0x7f32d8b93fb8:  lea    -0x14aa07c(%rip),%rax        # 0x7f32d76e9f43
0x7f32d8b93fbf:  jmpq   0x7f32d7a4e016


> diff --git a/target/arm/helper.c b/target/arm/helper.c
> index b41d0494d1..bcedb4a808 100644
> --- a/target/arm/helper.c
> +++ b/target/arm/helper.c
> @@ -536,41 +536,33 @@ static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush(other_cs);
> -    }
> +    tlb_flush_all_cpus_synced(cs);
>  }
>  
>  static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush(other_cs);
> -    }
> +    tlb_flush_all_cpus_synced(cs);
>  }
>  
>  static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page(other_cs, value & TARGET_PAGE_MASK);
> -    }
> +    tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
>  }
>  
>  static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                               uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page(other_cs, value & TARGET_PAGE_MASK);
> -    }
> +    tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
>  }
>  
>  static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -587,14 +579,12 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                    uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_by_mmuidx(other_cs,
> -                            (1 << ARMMMUIdx_S12NSE1) |
> -                            (1 << ARMMMUIdx_S12NSE0) |
> -                            (1 << ARMMMUIdx_S2NS));
> -    }
> +    tlb_flush_by_mmuidx_all_cpus_synced(cs,
> +                                        (1 << ARMMMUIdx_S12NSE1) |
> +                                        (1 << ARMMMUIdx_S12NSE0) |
> +                                        (1 << ARMMMUIdx_S2NS));
>  }
>  
>  static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -621,7 +611,7 @@ static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                 uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>      uint64_t pageaddr;
>  
>      if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) {
> @@ -630,9 +620,8 @@ static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  
>      pageaddr = sextract64(value << 12, 0, 40);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
> -    }
> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                             (1 << ARMMMUIdx_S2NS));
>  }
>  
>  static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -646,11 +635,9 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                   uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
> -    }
> +    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2));
>  }
>  
>  static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -665,12 +652,11 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                   uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>      uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
> -    }
> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                             (1 << ARMMMUIdx_S1E2));
>  }
>  
>  static const ARMCPRegInfo cp_reginfo[] = {
> @@ -2904,8 +2890,7 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env,
>  static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                      uint64_t value)
>  {
> -    ARMCPU *cpu = arm_env_get_cpu(env);
> -    CPUState *cs = CPU(cpu);
> +    CPUState *cs = ENV_GET_CPU(env);
>  
>      if (arm_is_secure_below_el3(env)) {
>          tlb_flush_by_mmuidx(cs,
> @@ -2921,19 +2906,17 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                        uint64_t value)
>  {
> +    CPUState *cs = ENV_GET_CPU(env);
>      bool sec = arm_is_secure_below_el3(env);
> -    CPUState *other_cs;
>  
> -    CPU_FOREACH(other_cs) {
> -        if (sec) {
> -            tlb_flush_by_mmuidx(other_cs,
> -                                (1 << ARMMMUIdx_S1SE1) |
> -                                (1 << ARMMMUIdx_S1SE0));
> -        } else {
> -            tlb_flush_by_mmuidx(other_cs,
> -                                (1 << ARMMMUIdx_S12NSE1) |
> -                                (1 << ARMMMUIdx_S12NSE0));
> -        }
> +    if (sec) {
> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
> +                                            (1 << ARMMMUIdx_S1SE1) |
> +                                            (1 << ARMMMUIdx_S1SE0));
> +    } else {
> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
> +                                            (1 << ARMMMUIdx_S12NSE1) |
> +                                            (1 << ARMMMUIdx_S12NSE0));
>      }
>  }
>  
> @@ -2990,46 +2973,40 @@ static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>       * stage 2 translations, whereas most other scopes only invalidate
>       * stage 1 translations.
>       */
> +    CPUState *cs = ENV_GET_CPU(env);
>      bool sec = arm_is_secure_below_el3(env);
>      bool has_el2 = arm_feature(env, ARM_FEATURE_EL2);
> -    CPUState *other_cs;
> -
> -    CPU_FOREACH(other_cs) {
> -        if (sec) {
> -            tlb_flush_by_mmuidx(other_cs,
> -                                (1 << ARMMMUIdx_S1SE1) |
> -                                (1 << ARMMMUIdx_S1SE0));
> -        } else if (has_el2) {
> -            tlb_flush_by_mmuidx(other_cs,
> -                                (1 << ARMMMUIdx_S12NSE1) |
> -                                (1 << ARMMMUIdx_S12NSE0) |
> -                                (1 << ARMMMUIdx_S2NS));
> -        } else {
> -            tlb_flush_by_mmuidx(other_cs,
> -                                (1 << ARMMMUIdx_S12NSE1) |
> -                                (1 << ARMMMUIdx_S12NSE0));
> -        }
> +
> +    if (sec) {
> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
> +                                            (1 << ARMMMUIdx_S1SE1) |
> +                                            (1 << ARMMMUIdx_S1SE0));
> +    } else if (has_el2) {
> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
> +                                            (1 << ARMMMUIdx_S12NSE1) |
> +                                            (1 << ARMMMUIdx_S12NSE0) |
> +                                            (1 << ARMMMUIdx_S2NS));
> +    } else {
> +          tlb_flush_by_mmuidx_all_cpus_synced(cs,
> +                                              (1 << ARMMMUIdx_S12NSE1) |
> +                                              (1 << ARMMMUIdx_S12NSE0));
>      }
>  }
>  
>  static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                      uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
> -    }
> +    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2));
>  }
>  
>  static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                      uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E3));
> -    }
> +    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E3));
>  }
>  
>  static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -3086,43 +3063,40 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                     uint64_t value)
>  {
> +    ARMCPU *cpu = arm_env_get_cpu(env);
> +    CPUState *cs = CPU(cpu);
>      bool sec = arm_is_secure_below_el3(env);
> -    CPUState *other_cs;
>      uint64_t pageaddr = sextract64(value << 12, 0, 56);
>  
> -    CPU_FOREACH(other_cs) {
> -        if (sec) {
> -            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
> -                                     (1 << ARMMMUIdx_S1SE1) |
> -                                     (1 << ARMMMUIdx_S1SE0));
> -        } else {
> -            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
> -                                     (1 << ARMMMUIdx_S12NSE1) |
> -                                     (1 << ARMMMUIdx_S12NSE0));
> -        }
> +    if (sec) {
> +        tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                                 (1 << ARMMMUIdx_S1SE1) |
> +                                                 (1 << ARMMMUIdx_S1SE0));
> +    } else {
> +        tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                                 (1 << ARMMMUIdx_S12NSE1) |
> +                                                 (1 << ARMMMUIdx_S12NSE0));
>      }
>  }
>  
>  static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                     uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>      uint64_t pageaddr = sextract64(value << 12, 0, 56);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
> -    }
> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                             (1 << ARMMMUIdx_S1E2));
>  }
>  
>  static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                     uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>      uint64_t pageaddr = sextract64(value << 12, 0, 56);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E3));
> -    }
> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                             (1 << ARMMMUIdx_S1E3));
>  }
>  
>  static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
> @@ -3150,7 +3124,7 @@ static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>                                        uint64_t value)
>  {
> -    CPUState *other_cs;
> +    CPUState *cs = ENV_GET_CPU(env);
>      uint64_t pageaddr;
>  
>      if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) {
> @@ -3159,9 +3133,8 @@ static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>  
>      pageaddr = sextract64(value << 12, 0, 48);
>  
> -    CPU_FOREACH(other_cs) {
> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
> -    }
> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
> +                                             (1 << ARMMMUIdx_S2NS));
>  }
>  
>  static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri,
> 


-- 
Dmitry

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-09-17 13:07   ` Dmitry Osipenko
@ 2017-09-17 13:22     ` Alex Bennée
  2017-09-17 13:46       ` Dmitry Osipenko
  0 siblings, 1 reply; 18+ messages in thread
From: Alex Bennée @ 2017-09-17 13:22 UTC (permalink / raw)
  To: Dmitry Osipenko; +Cc: peter.maydell, open list:ARM, qemu-devel


Dmitry Osipenko <digetx@gmail.com> writes:

> On 24.02.2017 14:21, Alex Bennée wrote:
>> Previously flushes on other vCPUs would only get serviced when they
>> exited their TranslationBlocks. While this isn't overly problematic it
>> violates the semantics of TLB flush from the point of view of source
>> vCPU.
>>
>> To solve this we call the cputlb *_all_cpus_synced() functions to do
>> the flushes which ensures all flushes are completed by the time the
>> vCPU next schedules its own work. As the TLB instructions are modelled
>> as CP writes the TB ends at this point meaning cpu->exit_request will
>> be checked before the next instruction is executed.
>>
>> Deferring the work until the architectural sync point is a possible
>> future optimisation.
>>
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>> ---
>>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>>  1 file changed, 69 insertions(+), 96 deletions(-)
>>
>
> Hello,
>
> I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
> checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
> should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
> 32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
> single-threaded TCG is affected. Git bisection lead to this patch, any
> ideas?

It shouldn't cause a problem but can you obtain a backtrace of the
system when hung?

>
> Example:
>
> qemu-system-arm -M vexpress-a9 -smp cpus=2 -accel accel=tcg,thread=single
> -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -serial
> stdio -net nic,model=lan9118 -net user -d in_asm,out_asm -D /tmp/qemulog
>
> Last TB from the log:
> ----------------
> IN:
> 0xc011a450:  ee080f73      mcr	15, 0, r0, cr8, cr3, {3}
>
> OUT: [size=68]
> 0x7f32d8b93f80:  mov    -0x18(%r14),%ebp
> 0x7f32d8b93f84:  test   %ebp,%ebp
> 0x7f32d8b93f86:  jne    0x7f32d8b93fb8
> 0x7f32d8b93f8c:  mov    %r14,%rdi
> 0x7f32d8b93f8f:  mov    $0x5620f2aea5d0,%rsi
> 0x7f32d8b93f99:  mov    (%r14),%edx
> 0x7f32d8b93f9c:  mov    $0x5620f18107ca,%r10
> 0x7f32d8b93fa6:  callq  *%r10
> 0x7f32d8b93fa9:  movl   $0xc011a454,0x3c(%r14)
> 0x7f32d8b93fb1:  xor    %eax,%eax
> 0x7f32d8b93fb3:  jmpq   0x7f32d7a4e016
> 0x7f32d8b93fb8:  lea    -0x14aa07c(%rip),%rax        # 0x7f32d76e9f43
> 0x7f32d8b93fbf:  jmpq   0x7f32d7a4e016
>
>
>> diff --git a/target/arm/helper.c b/target/arm/helper.c
>> index b41d0494d1..bcedb4a808 100644
>> --- a/target/arm/helper.c
>> +++ b/target/arm/helper.c
>> @@ -536,41 +536,33 @@ static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                               uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush(other_cs);
>> -    }
>> +    tlb_flush_all_cpus_synced(cs);
>>  }
>>
>>  static void tlbiasid_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                               uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush(other_cs);
>> -    }
>> +    tlb_flush_all_cpus_synced(cs);
>>  }
>>
>>  static void tlbimva_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                               uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page(other_cs, value & TARGET_PAGE_MASK);
>> -    }
>> +    tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
>>  }
>>
>>  static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                               uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page(other_cs, value & TARGET_PAGE_MASK);
>> -    }
>> +    tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK);
>>  }
>>
>>  static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -587,14 +579,12 @@ static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbiall_nsnh_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                    uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_by_mmuidx(other_cs,
>> -                            (1 << ARMMMUIdx_S12NSE1) |
>> -                            (1 << ARMMMUIdx_S12NSE0) |
>> -                            (1 << ARMMMUIdx_S2NS));
>> -    }
>> +    tlb_flush_by_mmuidx_all_cpus_synced(cs,
>> +                                        (1 << ARMMMUIdx_S12NSE1) |
>> +                                        (1 << ARMMMUIdx_S12NSE0) |
>> +                                        (1 << ARMMMUIdx_S2NS));
>>  }
>>
>>  static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -621,7 +611,7 @@ static void tlbiipas2_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                 uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      uint64_t pageaddr;
>>
>>      if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) {
>> @@ -630,9 +620,8 @@ static void tlbiipas2_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>
>>      pageaddr = sextract64(value << 12, 0, 40);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
>> -    }
>> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                             (1 << ARMMMUIdx_S2NS));
>>  }
>>
>>  static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -646,11 +635,9 @@ static void tlbiall_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbiall_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                   uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
>> -    }
>> +    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2));
>>  }
>>
>>  static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -665,12 +652,11 @@ static void tlbimva_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbimva_hyp_is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                   uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      uint64_t pageaddr = value & ~MAKE_64BIT_MASK(0, 12);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
>> -    }
>> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                             (1 << ARMMMUIdx_S1E2));
>>  }
>>
>>  static const ARMCPRegInfo cp_reginfo[] = {
>> @@ -2904,8 +2890,7 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env,
>>  static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                      uint64_t value)
>>  {
>> -    ARMCPU *cpu = arm_env_get_cpu(env);
>> -    CPUState *cs = CPU(cpu);
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>>      if (arm_is_secure_below_el3(env)) {
>>          tlb_flush_by_mmuidx(cs,
>> @@ -2921,19 +2906,17 @@ static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                        uint64_t value)
>>  {
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      bool sec = arm_is_secure_below_el3(env);
>> -    CPUState *other_cs;
>>
>> -    CPU_FOREACH(other_cs) {
>> -        if (sec) {
>> -            tlb_flush_by_mmuidx(other_cs,
>> -                                (1 << ARMMMUIdx_S1SE1) |
>> -                                (1 << ARMMMUIdx_S1SE0));
>> -        } else {
>> -            tlb_flush_by_mmuidx(other_cs,
>> -                                (1 << ARMMMUIdx_S12NSE1) |
>> -                                (1 << ARMMMUIdx_S12NSE0));
>> -        }
>> +    if (sec) {
>> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
>> +                                            (1 << ARMMMUIdx_S1SE1) |
>> +                                            (1 << ARMMMUIdx_S1SE0));
>> +    } else {
>> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
>> +                                            (1 << ARMMMUIdx_S12NSE1) |
>> +                                            (1 << ARMMMUIdx_S12NSE0));
>>      }
>>  }
>>
>> @@ -2990,46 +2973,40 @@ static void tlbi_aa64_alle1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>       * stage 2 translations, whereas most other scopes only invalidate
>>       * stage 1 translations.
>>       */
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      bool sec = arm_is_secure_below_el3(env);
>>      bool has_el2 = arm_feature(env, ARM_FEATURE_EL2);
>> -    CPUState *other_cs;
>> -
>> -    CPU_FOREACH(other_cs) {
>> -        if (sec) {
>> -            tlb_flush_by_mmuidx(other_cs,
>> -                                (1 << ARMMMUIdx_S1SE1) |
>> -                                (1 << ARMMMUIdx_S1SE0));
>> -        } else if (has_el2) {
>> -            tlb_flush_by_mmuidx(other_cs,
>> -                                (1 << ARMMMUIdx_S12NSE1) |
>> -                                (1 << ARMMMUIdx_S12NSE0) |
>> -                                (1 << ARMMMUIdx_S2NS));
>> -        } else {
>> -            tlb_flush_by_mmuidx(other_cs,
>> -                                (1 << ARMMMUIdx_S12NSE1) |
>> -                                (1 << ARMMMUIdx_S12NSE0));
>> -        }
>> +
>> +    if (sec) {
>> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
>> +                                            (1 << ARMMMUIdx_S1SE1) |
>> +                                            (1 << ARMMMUIdx_S1SE0));
>> +    } else if (has_el2) {
>> +        tlb_flush_by_mmuidx_all_cpus_synced(cs,
>> +                                            (1 << ARMMMUIdx_S12NSE1) |
>> +                                            (1 << ARMMMUIdx_S12NSE0) |
>> +                                            (1 << ARMMMUIdx_S2NS));
>> +    } else {
>> +          tlb_flush_by_mmuidx_all_cpus_synced(cs,
>> +                                              (1 << ARMMMUIdx_S12NSE1) |
>> +                                              (1 << ARMMMUIdx_S12NSE0));
>>      }
>>  }
>>
>>  static void tlbi_aa64_alle2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                      uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E2));
>> -    }
>> +    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E2));
>>  }
>>
>>  static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                      uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_by_mmuidx(other_cs, (1 << ARMMMUIdx_S1E3));
>> -    }
>> +    tlb_flush_by_mmuidx_all_cpus_synced(cs, (1 << ARMMMUIdx_S1E3));
>>  }
>>
>>  static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -3086,43 +3063,40 @@ static void tlbi_aa64_vae3_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                     uint64_t value)
>>  {
>> +    ARMCPU *cpu = arm_env_get_cpu(env);
>> +    CPUState *cs = CPU(cpu);
>>      bool sec = arm_is_secure_below_el3(env);
>> -    CPUState *other_cs;
>>      uint64_t pageaddr = sextract64(value << 12, 0, 56);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        if (sec) {
>> -            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
>> -                                     (1 << ARMMMUIdx_S1SE1) |
>> -                                     (1 << ARMMMUIdx_S1SE0));
>> -        } else {
>> -            tlb_flush_page_by_mmuidx(other_cs, pageaddr,
>> -                                     (1 << ARMMMUIdx_S12NSE1) |
>> -                                     (1 << ARMMMUIdx_S12NSE0));
>> -        }
>> +    if (sec) {
>> +        tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                                 (1 << ARMMMUIdx_S1SE1) |
>> +                                                 (1 << ARMMMUIdx_S1SE0));
>> +    } else {
>> +        tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                                 (1 << ARMMMUIdx_S12NSE1) |
>> +                                                 (1 << ARMMMUIdx_S12NSE0));
>>      }
>>  }
>>
>>  static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                     uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      uint64_t pageaddr = sextract64(value << 12, 0, 56);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E2));
>> -    }
>> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                             (1 << ARMMMUIdx_S1E2));
>>  }
>>
>>  static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                     uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      uint64_t pageaddr = sextract64(value << 12, 0, 56);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S1E3));
>> -    }
>> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                             (1 << ARMMMUIdx_S1E3));
>>  }
>>
>>  static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> @@ -3150,7 +3124,7 @@ static void tlbi_aa64_ipas2e1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>  static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>                                        uint64_t value)
>>  {
>> -    CPUState *other_cs;
>> +    CPUState *cs = ENV_GET_CPU(env);
>>      uint64_t pageaddr;
>>
>>      if (!arm_feature(env, ARM_FEATURE_EL2) || !(env->cp15.scr_el3 & SCR_NS)) {
>> @@ -3159,9 +3133,8 @@ static void tlbi_aa64_ipas2e1is_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>
>>      pageaddr = sextract64(value << 12, 0, 48);
>>
>> -    CPU_FOREACH(other_cs) {
>> -        tlb_flush_page_by_mmuidx(other_cs, pageaddr, (1 << ARMMMUIdx_S2NS));
>> -    }
>> +    tlb_flush_page_by_mmuidx_all_cpus_synced(cs, pageaddr,
>> +                                             (1 << ARMMMUIdx_S2NS));
>>  }
>>
>>  static CPAccessResult aa64_zva_access(CPUARMState *env, const ARMCPRegInfo *ri,
>>


--
Alex Bennée

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-09-17 13:22     ` Alex Bennée
@ 2017-09-17 13:46       ` Dmitry Osipenko
  2017-09-18 10:10         ` Alex Bennée
  0 siblings, 1 reply; 18+ messages in thread
From: Dmitry Osipenko @ 2017-09-17 13:46 UTC (permalink / raw)
  To: Alex Bennée; +Cc: peter.maydell, open list:ARM, qemu-devel

On 17.09.2017 16:22, Alex Bennée wrote:
> 
> Dmitry Osipenko <digetx@gmail.com> writes:
> 
>> On 24.02.2017 14:21, Alex Bennée wrote:
>>> Previously flushes on other vCPUs would only get serviced when they
>>> exited their TranslationBlocks. While this isn't overly problematic it
>>> violates the semantics of TLB flush from the point of view of source
>>> vCPU.
>>>
>>> To solve this we call the cputlb *_all_cpus_synced() functions to do
>>> the flushes which ensures all flushes are completed by the time the
>>> vCPU next schedules its own work. As the TLB instructions are modelled
>>> as CP writes the TB ends at this point meaning cpu->exit_request will
>>> be checked before the next instruction is executed.
>>>
>>> Deferring the work until the architectural sync point is a possible
>>> future optimisation.
>>>
>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>>> ---
>>>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>>>  1 file changed, 69 insertions(+), 96 deletions(-)
>>>
>>
>> Hello,
>>
>> I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
>> checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
>> should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
>> 32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
>> single-threaded TCG is affected. Git bisection lead to this patch, any
>> ideas?
> 
> It shouldn't cause a problem but can you obtain a backtrace of the
> system when hung?
> 

Actually, it looks like TCG enters infinite loop. Do you mean backtrace of QEMU
by 'backtrace of the system'? If so, here it is:

Thread 4 (Thread 0x7ffa37f10700 (LWP 20716)):

#0  0x00007ffa601888bd in poll () at ../sysdeps/unix/syscall-template.S:84

#1  0x00007ffa5e3aa561 in poll (__timeout=-1, __nfds=2, __fds=0x7ffa30006dc0) at
/usr/include/bits/poll2.h:46
#2  poll_func (ufds=0x7ffa30006dc0, nfds=2, timeout=-1, userdata=0x557bd603eae0)
at
/var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:69
#3  0x00007ffa5e39bbb1 in pa_mainloop_poll (m=m@entry=0x557bd60401f0) at
/var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:844
#4  0x00007ffa5e39c24e in pa_mainloop_iterate (m=0x557bd60401f0,
block=<optimized out>, retval=0x0) at
/var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:926
#5  0x00007ffa5e39c300 in pa_mainloop_run (m=0x557bd60401f0,
retval=retval@entry=0x0) at
/var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:944

#6  0x00007ffa5e3aa4a9 in thread (userdata=0x557bd60400f0) at
/var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:100

#7  0x00007ffa599eea38 in internal_thread_func (userdata=0x557bd603e090) at
/var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulsecore/thread-posix.c:81

#8  0x00007ffa60453657 in start_thread (arg=0x7ffa37f10700) at
pthread_create.c:456

#9  0x00007ffa60193c5f in clone () at
../sysdeps/unix/sysv/linux/x86_64/clone.S:97





Thread 3 (Thread 0x7ffa4adff700 (LWP 20715)):


#0  0x00007ffa53e51caf in code_gen_buffer ()


#1  0x0000557bd2fa7f17 in cpu_tb_exec (cpu=0x557bd56160a0, itb=0x7ffa53e51b80
<code_gen_buffer+15481686>) at /home/dima/vl/qemu-tests/accel/tcg/cpu-exec.c:166

#2  0x0000557bd2fa8e0f in cpu_loop_exec_tb (cpu=0x557bd56160a0,
tb=0x7ffa53e51b80 <code_gen_buffer+15481686>, last_tb=0x7ffa4adfea68,
tb_exit=0x7ffa4adfea64) at /home/dima/vl/qemu-tests/accel/tcg/cpu-exec.c:613
#3  0x0000557bd2fa90ff in cpu_exec (cpu=0x557bd56160a0) at
/home/dima/vl/qemu-tests/accel/tcg/cpu-exec.c:711

#4  0x0000557bd2f6dcba in tcg_cpu_exec (cpu=0x557bd56160a0) at
/home/dima/vl/qemu-tests/cpus.c:1270

#5  0x0000557bd2f6dee1 in qemu_tcg_rr_cpu_thread_fn (arg=0x557bd5598e20) at
/home/dima/vl/qemu-tests/cpus.c:1365

#6  0x00007ffa60453657 in start_thread (arg=0x7ffa4adff700) at
pthread_create.c:456

#7  0x00007ffa60193c5f in clone () at
../sysdeps/unix/sysv/linux/x86_64/clone.S:97







Thread 2 (Thread 0x7ffa561bf700 (LWP 20714)):



#0  syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38



#1  0x0000557bd34e1eaa in qemu_futex_wait (f=0x557bd4031798
<rcu_call_ready_event>, val=4294967295) at
/home/dima/vl/qemu-tests/include/qemu/futex.h:26


#2  0x0000557bd34e2071 in qemu_event_wait (ev=0x557bd4031798
<rcu_call_ready_event>) at util/qemu-thread-posix.c:442


#3  0x0000557bd34f9b1f in call_rcu_thread (opaque=0x0) at util/rcu.c:249



#4  0x00007ffa60453657 in start_thread (arg=0x7ffa561bf700) at
pthread_create.c:456


#5  0x00007ffa60193c5f in clone () at
../sysdeps/unix/sysv/linux/x86_64/clone.S:97







Thread 1 (Thread 0x7ffa67502600 (LWP 20713)):



#0  0x00007ffa601889ab in __GI_ppoll (fds=0x557bd5bbf160, nfds=11,
timeout=<optimized out>, sigmask=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:39


#1  0x0000557bd34dc460 in qemu_poll_ns (fds=0x557bd5bbf160, nfds=11,
timeout=29841115) at util/qemu-timer.c:334


#2  0x0000557bd34dd488 in os_host_main_loop_wait (timeout=29841115) at
util/main-loop.c:255


#3  0x0000557bd34dd557 in main_loop_wait (nonblocking=0) at util/main-loop.c:515



#4  0x0000557bd3120f0e in main_loop () at vl.c:1999



#5  0x0000557bd3128d4a in main (argc=17, argv=0x7ffe7de2a248,
envp=0x7ffe7de2a2d8) at vl.c:4877

>>
>> Example:
>>
>> qemu-system-arm -M vexpress-a9 -smp cpus=2 -accel accel=tcg,thread=single
>> -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -serial
>> stdio -net nic,model=lan9118 -net user -d in_asm,out_asm -D /tmp/qemulog
>>
>> Last TB from the log:
>> ----------------
>> IN:
>> 0xc011a450:  ee080f73      mcr	15, 0, r0, cr8, cr3, {3}
>>
>> OUT: [size=68]
>> 0x7f32d8b93f80:  mov    -0x18(%r14),%ebp
>> 0x7f32d8b93f84:  test   %ebp,%ebp
>> 0x7f32d8b93f86:  jne    0x7f32d8b93fb8
>> 0x7f32d8b93f8c:  mov    %r14,%rdi
>> 0x7f32d8b93f8f:  mov    $0x5620f2aea5d0,%rsi
>> 0x7f32d8b93f99:  mov    (%r14),%edx
>> 0x7f32d8b93f9c:  mov    $0x5620f18107ca,%r10
>> 0x7f32d8b93fa6:  callq  *%r10
>> 0x7f32d8b93fa9:  movl   $0xc011a454,0x3c(%r14)
>> 0x7f32d8b93fb1:  xor    %eax,%eax
>> 0x7f32d8b93fb3:  jmpq   0x7f32d7a4e016
>> 0x7f32d8b93fb8:  lea    -0x14aa07c(%rip),%rax        # 0x7f32d76e9f43
>> 0x7f32d8b93fbf:  jmpq   0x7f32d7a4e016
-- 
Dmitry

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-09-17 13:46       ` Dmitry Osipenko
@ 2017-09-18 10:10         ` Alex Bennée
  2017-09-18 12:23           ` Dmitry Osipenko
  0 siblings, 1 reply; 18+ messages in thread
From: Alex Bennée @ 2017-09-18 10:10 UTC (permalink / raw)
  To: Dmitry Osipenko; +Cc: peter.maydell, open list:ARM, qemu-devel


Dmitry Osipenko <digetx@gmail.com> writes:

> On 17.09.2017 16:22, Alex Bennée wrote:
>>
>> Dmitry Osipenko <digetx@gmail.com> writes:
>>
>>> On 24.02.2017 14:21, Alex Bennée wrote:
>>>> Previously flushes on other vCPUs would only get serviced when they
>>>> exited their TranslationBlocks. While this isn't overly problematic it
>>>> violates the semantics of TLB flush from the point of view of source
>>>> vCPU.
>>>>
>>>> To solve this we call the cputlb *_all_cpus_synced() functions to do
>>>> the flushes which ensures all flushes are completed by the time the
>>>> vCPU next schedules its own work. As the TLB instructions are modelled
>>>> as CP writes the TB ends at this point meaning cpu->exit_request will
>>>> be checked before the next instruction is executed.
>>>>
>>>> Deferring the work until the architectural sync point is a possible
>>>> future optimisation.
>>>>
>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>>>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>>>> ---
>>>>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>>>>  1 file changed, 69 insertions(+), 96 deletions(-)
>>>>
>>>
>>> Hello,
>>>
>>> I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
>>> checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
>>> should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
>>> 32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
>>> single-threaded TCG is affected. Git bisection lead to this patch, any
>>> ideas?
>>
>> It shouldn't cause a problem but can you obtain a backtrace of the
>> system when hung?
>>
>
> Actually, it looks like TCG enters infinite loop. Do you mean backtrace of QEMU
> by 'backtrace of the system'? If so, here it is:
>
> Thread 4 (Thread 0x7ffa37f10700 (LWP 20716)):
>
> #0  0x00007ffa601888bd in poll () at ../sysdeps/unix/syscall-template.S:84
>
> #1  0x00007ffa5e3aa561 in poll (__timeout=-1, __nfds=2, __fds=0x7ffa30006dc0) at
> /usr/include/bits/poll2.h:46
> #2  poll_func (ufds=0x7ffa30006dc0, nfds=2, timeout=-1, userdata=0x557bd603eae0)
> at
> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:69
> #3  0x00007ffa5e39bbb1 in pa_mainloop_poll (m=m@entry=0x557bd60401f0) at
> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:844
> #4  0x00007ffa5e39c24e in pa_mainloop_iterate (m=0x557bd60401f0,
> block=<optimized out>, retval=0x0) at
> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:926
> #5  0x00007ffa5e39c300 in pa_mainloop_run (m=0x557bd60401f0,
> retval=retval@entry=0x0) at
> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:944
>
> #6  0x00007ffa5e3aa4a9 in thread (userdata=0x557bd60400f0) at
> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:100
>
> #7  0x00007ffa599eea38 in internal_thread_func (userdata=0x557bd603e090) at
> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulsecore/thread-posix.c:81
>
> #8  0x00007ffa60453657 in start_thread (arg=0x7ffa37f10700) at
> pthread_create.c:456
>
> #9  0x00007ffa60193c5f in clone () at
> ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
>
>
>
>
>
> Thread 3 (Thread 0x7ffa4adff700 (LWP 20715)):
>
>
> #0  0x00007ffa53e51caf in code_gen_buffer ()
>

Well it's not locked up in servicing any flush tasks as it's executing
code. Maybe the guest code is spinning on something?

In the monitor:

  info registers

Will show you where things are, see if the ip is moving each time. Also
you can do a disassemble dump from there to see what code it is stuck
on.

>
> #1  0x0000557bd2fa7f17 in cpu_tb_exec (cpu=0x557bd56160a0, itb=0x7ffa53e51b80
> <code_gen_buffer+15481686>) at /home/dima/vl/qemu-tests/accel/tcg/cpu-exec.c:166
>
> #2  0x0000557bd2fa8e0f in cpu_loop_exec_tb (cpu=0x557bd56160a0,
> tb=0x7ffa53e51b80 <code_gen_buffer+15481686>, last_tb=0x7ffa4adfea68,
> tb_exit=0x7ffa4adfea64) at /home/dima/vl/qemu-tests/accel/tcg/cpu-exec.c:613
> #3  0x0000557bd2fa90ff in cpu_exec (cpu=0x557bd56160a0) at
> /home/dima/vl/qemu-tests/accel/tcg/cpu-exec.c:711
>
> #4  0x0000557bd2f6dcba in tcg_cpu_exec (cpu=0x557bd56160a0) at
> /home/dima/vl/qemu-tests/cpus.c:1270
>
> #5  0x0000557bd2f6dee1 in qemu_tcg_rr_cpu_thread_fn (arg=0x557bd5598e20) at
> /home/dima/vl/qemu-tests/cpus.c:1365
>
> #6  0x00007ffa60453657 in start_thread (arg=0x7ffa4adff700) at
> pthread_create.c:456
>
> #7  0x00007ffa60193c5f in clone () at
> ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
>
>
>
>
>
>
>
> Thread 2 (Thread 0x7ffa561bf700 (LWP 20714)):
>
>
>
> #0  syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
>
>
>
> #1  0x0000557bd34e1eaa in qemu_futex_wait (f=0x557bd4031798
> <rcu_call_ready_event>, val=4294967295) at
> /home/dima/vl/qemu-tests/include/qemu/futex.h:26
>
>
> #2  0x0000557bd34e2071 in qemu_event_wait (ev=0x557bd4031798
> <rcu_call_ready_event>) at util/qemu-thread-posix.c:442
>
>
> #3  0x0000557bd34f9b1f in call_rcu_thread (opaque=0x0) at util/rcu.c:249
>
>
>
> #4  0x00007ffa60453657 in start_thread (arg=0x7ffa561bf700) at
> pthread_create.c:456
>
>
> #5  0x00007ffa60193c5f in clone () at
> ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
>
>
>
>
>
>
>
> Thread 1 (Thread 0x7ffa67502600 (LWP 20713)):
>
>
>
> #0  0x00007ffa601889ab in __GI_ppoll (fds=0x557bd5bbf160, nfds=11,
> timeout=<optimized out>, sigmask=0x0) at ../sysdeps/unix/sysv/linux/ppoll.c:39
>
>
> #1  0x0000557bd34dc460 in qemu_poll_ns (fds=0x557bd5bbf160, nfds=11,
> timeout=29841115) at util/qemu-timer.c:334
>
>
> #2  0x0000557bd34dd488 in os_host_main_loop_wait (timeout=29841115) at
> util/main-loop.c:255
>
>
> #3  0x0000557bd34dd557 in main_loop_wait (nonblocking=0) at util/main-loop.c:515
>
>
>
> #4  0x0000557bd3120f0e in main_loop () at vl.c:1999
>
>
>
> #5  0x0000557bd3128d4a in main (argc=17, argv=0x7ffe7de2a248,
> envp=0x7ffe7de2a2d8) at vl.c:4877
>
>>>
>>> Example:
>>>
>>> qemu-system-arm -M vexpress-a9 -smp cpus=2 -accel accel=tcg,thread=single
>>> -kernel arch/arm/boot/zImage -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -serial
>>> stdio -net nic,model=lan9118 -net user -d in_asm,out_asm -D /tmp/qemulog
>>>
>>> Last TB from the log:
>>> ----------------
>>> IN:
>>> 0xc011a450:  ee080f73      mcr	15, 0, r0, cr8, cr3, {3}
>>>
>>> OUT: [size=68]
>>> 0x7f32d8b93f80:  mov    -0x18(%r14),%ebp
>>> 0x7f32d8b93f84:  test   %ebp,%ebp
>>> 0x7f32d8b93f86:  jne    0x7f32d8b93fb8
>>> 0x7f32d8b93f8c:  mov    %r14,%rdi
>>> 0x7f32d8b93f8f:  mov    $0x5620f2aea5d0,%rsi
>>> 0x7f32d8b93f99:  mov    (%r14),%edx
>>> 0x7f32d8b93f9c:  mov    $0x5620f18107ca,%r10
>>> 0x7f32d8b93fa6:  callq  *%r10
>>> 0x7f32d8b93fa9:  movl   $0xc011a454,0x3c(%r14)
>>> 0x7f32d8b93fb1:  xor    %eax,%eax
>>> 0x7f32d8b93fb3:  jmpq   0x7f32d7a4e016
>>> 0x7f32d8b93fb8:  lea    -0x14aa07c(%rip),%rax        # 0x7f32d76e9f43
>>> 0x7f32d8b93fbf:  jmpq   0x7f32d7a4e016


--
Alex Bennée

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-09-18 10:10         ` Alex Bennée
@ 2017-09-18 12:23           ` Dmitry Osipenko
  2017-09-18 14:00             ` Alex Bennée
  0 siblings, 1 reply; 18+ messages in thread
From: Dmitry Osipenko @ 2017-09-18 12:23 UTC (permalink / raw)
  To: Alex Bennée; +Cc: peter.maydell, open list:ARM, qemu-devel

On 18.09.2017 13:10, Alex Bennée wrote:
> 
> Dmitry Osipenko <digetx@gmail.com> writes:
> 
>> On 17.09.2017 16:22, Alex Bennée wrote:
>>>
>>> Dmitry Osipenko <digetx@gmail.com> writes:
>>>
>>>> On 24.02.2017 14:21, Alex Bennée wrote:
>>>>> Previously flushes on other vCPUs would only get serviced when they
>>>>> exited their TranslationBlocks. While this isn't overly problematic it
>>>>> violates the semantics of TLB flush from the point of view of source
>>>>> vCPU.
>>>>>
>>>>> To solve this we call the cputlb *_all_cpus_synced() functions to do
>>>>> the flushes which ensures all flushes are completed by the time the
>>>>> vCPU next schedules its own work. As the TLB instructions are modelled
>>>>> as CP writes the TB ends at this point meaning cpu->exit_request will
>>>>> be checked before the next instruction is executed.
>>>>>
>>>>> Deferring the work until the architectural sync point is a possible
>>>>> future optimisation.
>>>>>
>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>>>>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>>>>> ---
>>>>>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>>>>>  1 file changed, 69 insertions(+), 96 deletions(-)
>>>>>
>>>>
>>>> Hello,
>>>>
>>>> I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
>>>> checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
>>>> should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
>>>> 32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
>>>> single-threaded TCG is affected. Git bisection lead to this patch, any
>>>> ideas?
>>>
>>> It shouldn't cause a problem but can you obtain a backtrace of the
>>> system when hung?
>>>
>>
>> Actually, it looks like TCG enters infinite loop. Do you mean backtrace of QEMU
>> by 'backtrace of the system'? If so, here it is:
>>
>> Thread 4 (Thread 0x7ffa37f10700 (LWP 20716)):
>>
>> #0  0x00007ffa601888bd in poll () at ../sysdeps/unix/syscall-template.S:84
>>
>> #1  0x00007ffa5e3aa561 in poll (__timeout=-1, __nfds=2, __fds=0x7ffa30006dc0) at
>> /usr/include/bits/poll2.h:46
>> #2  poll_func (ufds=0x7ffa30006dc0, nfds=2, timeout=-1, userdata=0x557bd603eae0)
>> at
>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:69
>> #3  0x00007ffa5e39bbb1 in pa_mainloop_poll (m=m@entry=0x557bd60401f0) at
>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:844
>> #4  0x00007ffa5e39c24e in pa_mainloop_iterate (m=0x557bd60401f0,
>> block=<optimized out>, retval=0x0) at
>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:926
>> #5  0x00007ffa5e39c300 in pa_mainloop_run (m=0x557bd60401f0,
>> retval=retval@entry=0x0) at
>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:944
>>
>> #6  0x00007ffa5e3aa4a9 in thread (userdata=0x557bd60400f0) at
>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:100
>>
>> #7  0x00007ffa599eea38 in internal_thread_func (userdata=0x557bd603e090) at
>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulsecore/thread-posix.c:81
>>
>> #8  0x00007ffa60453657 in start_thread (arg=0x7ffa37f10700) at
>> pthread_create.c:456
>>
>> #9  0x00007ffa60193c5f in clone () at
>> ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
>>
>>
>>
>>
>>
>> Thread 3 (Thread 0x7ffa4adff700 (LWP 20715)):
>>
>>
>> #0  0x00007ffa53e51caf in code_gen_buffer ()
>>
> 
> Well it's not locked up in servicing any flush tasks as it's executing
> code. Maybe the guest code is spinning on something?
> 

Indeed, I should have used 'exec' instead of 'in_asm'.

> In the monitor:
> 
>   info registers
> 
> Will show you where things are, see if the ip is moving each time. Also
> you can do a disassemble dump from there to see what code it is stuck
> on.
> 

I've attached with GDB to QEMU to see where it got stuck. Turned out it is
caused by CONFIG_STRICT_KERNEL_RWX=y of the Linux kernel. Upon boot completion
kernel changes memory permissions and that changing is executed on a dedicated
CPU, while other CPUs are 'stopped' in a busy loop.

This patch just introduced a noticeable performance regression for a
single-threaded TCG, which is probably fine since MTTCG is the default now.
Thank you very much for the suggestions and all your work on MTTCG!

-- 
Dmitry

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-09-18 12:23           ` Dmitry Osipenko
@ 2017-09-18 14:00             ` Alex Bennée
  2017-09-18 15:32               ` Dmitry Osipenko
  0 siblings, 1 reply; 18+ messages in thread
From: Alex Bennée @ 2017-09-18 14:00 UTC (permalink / raw)
  To: Dmitry Osipenko; +Cc: peter.maydell, open list:ARM, qemu-devel


Dmitry Osipenko <digetx@gmail.com> writes:

> On 18.09.2017 13:10, Alex Bennée wrote:
>>
>> Dmitry Osipenko <digetx@gmail.com> writes:
>>
>>> On 17.09.2017 16:22, Alex Bennée wrote:
>>>>
>>>> Dmitry Osipenko <digetx@gmail.com> writes:
>>>>
>>>>> On 24.02.2017 14:21, Alex Bennée wrote:
>>>>>> Previously flushes on other vCPUs would only get serviced when they
>>>>>> exited their TranslationBlocks. While this isn't overly problematic it
>>>>>> violates the semantics of TLB flush from the point of view of source
>>>>>> vCPU.
>>>>>>
>>>>>> To solve this we call the cputlb *_all_cpus_synced() functions to do
>>>>>> the flushes which ensures all flushes are completed by the time the
>>>>>> vCPU next schedules its own work. As the TLB instructions are modelled
>>>>>> as CP writes the TB ends at this point meaning cpu->exit_request will
>>>>>> be checked before the next instruction is executed.
>>>>>>
>>>>>> Deferring the work until the architectural sync point is a possible
>>>>>> future optimisation.
>>>>>>
>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>>>>>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>>>>>> ---
>>>>>>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>>>>>>  1 file changed, 69 insertions(+), 96 deletions(-)
>>>>>>
>>>>>
>>>>> Hello,
>>>>>
>>>>> I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
>>>>> checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
>>>>> should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
>>>>> 32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
>>>>> single-threaded TCG is affected. Git bisection lead to this patch, any
>>>>> ideas?
>>>>
>>>> It shouldn't cause a problem but can you obtain a backtrace of the
>>>> system when hung?
>>>>
>>>
>>> Actually, it looks like TCG enters infinite loop. Do you mean backtrace of QEMU
>>> by 'backtrace of the system'? If so, here it is:
>>>
>>> Thread 4 (Thread 0x7ffa37f10700 (LWP 20716)):
>>>
>>> #0  0x00007ffa601888bd in poll () at ../sysdeps/unix/syscall-template.S:84
>>>
>>> #1  0x00007ffa5e3aa561 in poll (__timeout=-1, __nfds=2, __fds=0x7ffa30006dc0) at
>>> /usr/include/bits/poll2.h:46
>>> #2  poll_func (ufds=0x7ffa30006dc0, nfds=2, timeout=-1, userdata=0x557bd603eae0)
>>> at
>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:69
>>> #3  0x00007ffa5e39bbb1 in pa_mainloop_poll (m=m@entry=0x557bd60401f0) at
>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:844
>>> #4  0x00007ffa5e39c24e in pa_mainloop_iterate (m=0x557bd60401f0,
>>> block=<optimized out>, retval=0x0) at
>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:926
>>> #5  0x00007ffa5e39c300 in pa_mainloop_run (m=0x557bd60401f0,
>>> retval=retval@entry=0x0) at
>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:944
>>>
>>> #6  0x00007ffa5e3aa4a9 in thread (userdata=0x557bd60400f0) at
>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:100
>>>
>>> #7  0x00007ffa599eea38 in internal_thread_func (userdata=0x557bd603e090) at
>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulsecore/thread-posix.c:81
>>>
>>> #8  0x00007ffa60453657 in start_thread (arg=0x7ffa37f10700) at
>>> pthread_create.c:456
>>>
>>> #9  0x00007ffa60193c5f in clone () at
>>> ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
>>>
>>>
>>>
>>>
>>>
>>> Thread 3 (Thread 0x7ffa4adff700 (LWP 20715)):
>>>
>>>
>>> #0  0x00007ffa53e51caf in code_gen_buffer ()
>>>
>>
>> Well it's not locked up in servicing any flush tasks as it's executing
>> code. Maybe the guest code is spinning on something?
>>
>
> Indeed, I should have used 'exec' instead of 'in_asm'.
>
>> In the monitor:
>>
>>   info registers
>>
>> Will show you where things are, see if the ip is moving each time. Also
>> you can do a disassemble dump from there to see what code it is stuck
>> on.
>>
>
> I've attached with GDB to QEMU to see where it got stuck. Turned out it is
> caused by CONFIG_STRICT_KERNEL_RWX=y of the Linux kernel. Upon boot completion
> kernel changes memory permissions and that changing is executed on a dedicated
> CPU, while other CPUs are 'stopped' in a busy loop.
>
> This patch just introduced a noticeable performance regression for a
> single-threaded TCG, which is probably fine since MTTCG is the default now.
> Thank you very much for the suggestions and all your work on MTTCG!

Hmm well it would be nice to know the exact mechanism for that failure.
If we just end up with a very long list of tasks in
cpu->queued_work_first then I guess that explains it but it would be
nice to quantify the problem.

I had trouble seeing where this loop is in the kernel code, got a pointer?

--
Alex Bennée

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete
  2017-09-18 14:00             ` Alex Bennée
@ 2017-09-18 15:32               ` Dmitry Osipenko
  0 siblings, 0 replies; 18+ messages in thread
From: Dmitry Osipenko @ 2017-09-18 15:32 UTC (permalink / raw)
  To: Alex Bennée; +Cc: peter.maydell, open list:ARM, qemu-devel

On 18.09.2017 17:00, Alex Bennée wrote:
> 
> Dmitry Osipenko <digetx@gmail.com> writes:
> 
>> On 18.09.2017 13:10, Alex Bennée wrote:
>>>
>>> Dmitry Osipenko <digetx@gmail.com> writes:
>>>
>>>> On 17.09.2017 16:22, Alex Bennée wrote:
>>>>>
>>>>> Dmitry Osipenko <digetx@gmail.com> writes:
>>>>>
>>>>>> On 24.02.2017 14:21, Alex Bennée wrote:
>>>>>>> Previously flushes on other vCPUs would only get serviced when they
>>>>>>> exited their TranslationBlocks. While this isn't overly problematic it
>>>>>>> violates the semantics of TLB flush from the point of view of source
>>>>>>> vCPU.
>>>>>>>
>>>>>>> To solve this we call the cputlb *_all_cpus_synced() functions to do
>>>>>>> the flushes which ensures all flushes are completed by the time the
>>>>>>> vCPU next schedules its own work. As the TLB instructions are modelled
>>>>>>> as CP writes the TB ends at this point meaning cpu->exit_request will
>>>>>>> be checked before the next instruction is executed.
>>>>>>>
>>>>>>> Deferring the work until the architectural sync point is a possible
>>>>>>> future optimisation.
>>>>>>>
>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>> Reviewed-by: Richard Henderson <rth@twiddle.net>
>>>>>>> Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
>>>>>>> ---
>>>>>>>  target/arm/helper.c | 165 ++++++++++++++++++++++------------------------------
>>>>>>>  1 file changed, 69 insertions(+), 96 deletions(-)
>>>>>>>
>>>>>>
>>>>>> Hello,
>>>>>>
>>>>>> I have an issue with Linux kernel stopping to boot on a SMP 32bit ARM (haven't
>>>>>> checked 64bit) in a single-threaded TCG mode. Kernel reaches point where it
>>>>>> should mount rootfs over NFS and vCPUs stop. This issue is reproducible with any
>>>>>> 32bit ARM machine type. Kernel boots fine with a MTTCG accel, only
>>>>>> single-threaded TCG is affected. Git bisection lead to this patch, any
>>>>>> ideas?
>>>>>
>>>>> It shouldn't cause a problem but can you obtain a backtrace of the
>>>>> system when hung?
>>>>>
>>>>
>>>> Actually, it looks like TCG enters infinite loop. Do you mean backtrace of QEMU
>>>> by 'backtrace of the system'? If so, here it is:
>>>>
>>>> Thread 4 (Thread 0x7ffa37f10700 (LWP 20716)):
>>>>
>>>> #0  0x00007ffa601888bd in poll () at ../sysdeps/unix/syscall-template.S:84
>>>>
>>>> #1  0x00007ffa5e3aa561 in poll (__timeout=-1, __nfds=2, __fds=0x7ffa30006dc0) at
>>>> /usr/include/bits/poll2.h:46
>>>> #2  poll_func (ufds=0x7ffa30006dc0, nfds=2, timeout=-1, userdata=0x557bd603eae0)
>>>> at
>>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:69
>>>> #3  0x00007ffa5e39bbb1 in pa_mainloop_poll (m=m@entry=0x557bd60401f0) at
>>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:844
>>>> #4  0x00007ffa5e39c24e in pa_mainloop_iterate (m=0x557bd60401f0,
>>>> block=<optimized out>, retval=0x0) at
>>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:926
>>>> #5  0x00007ffa5e39c300 in pa_mainloop_run (m=0x557bd60401f0,
>>>> retval=retval@entry=0x0) at
>>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/mainloop.c:944
>>>>
>>>> #6  0x00007ffa5e3aa4a9 in thread (userdata=0x557bd60400f0) at
>>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulse/thread-mainloop.c:100
>>>>
>>>> #7  0x00007ffa599eea38 in internal_thread_func (userdata=0x557bd603e090) at
>>>> /var/tmp/portage/media-sound/pulseaudio-10.0/work/pulseaudio-10.0/src/pulsecore/thread-posix.c:81
>>>>
>>>> #8  0x00007ffa60453657 in start_thread (arg=0x7ffa37f10700) at
>>>> pthread_create.c:456
>>>>
>>>> #9  0x00007ffa60193c5f in clone () at
>>>> ../sysdeps/unix/sysv/linux/x86_64/clone.S:97
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> Thread 3 (Thread 0x7ffa4adff700 (LWP 20715)):
>>>>
>>>>
>>>> #0  0x00007ffa53e51caf in code_gen_buffer ()
>>>>
>>>
>>> Well it's not locked up in servicing any flush tasks as it's executing
>>> code. Maybe the guest code is spinning on something?
>>>
>>
>> Indeed, I should have used 'exec' instead of 'in_asm'.
>>
>>> In the monitor:
>>>
>>>   info registers
>>>
>>> Will show you where things are, see if the ip is moving each time. Also
>>> you can do a disassemble dump from there to see what code it is stuck
>>> on.
>>>
>>
>> I've attached with GDB to QEMU to see where it got stuck. Turned out it is
>> caused by CONFIG_STRICT_KERNEL_RWX=y of the Linux kernel. Upon boot completion
>> kernel changes memory permissions and that changing is executed on a dedicated
>> CPU, while other CPUs are 'stopped' in a busy loop.
>>
>> This patch just introduced a noticeable performance regression for a
>> single-threaded TCG, which is probably fine since MTTCG is the default now.
>> Thank you very much for the suggestions and all your work on MTTCG!
> 
> Hmm well it would be nice to know the exact mechanism for that failure.
> If we just end up with a very long list of tasks in
> cpu->queued_work_first then I guess that explains it but it would be
> nice to quantify the problem.
> 
> I had trouble seeing where this loop is in the kernel code, got a pointer?
> 
The memory permissions changing starts here:

http://elixir.free-electrons.com/linux/v4.14-rc1/source/arch/arm/mm/init.c#L739

The busy loop is here:

http://elixir.free-electrons.com/linux/v4.14-rc1/source/kernel/stop_machine.c#L195

Interestingly, I tried to attach to a 'hanged' QEMU another time and got into
other code. That code has the same pattern, one CPU flushes cache a lot in
shmem_rename2()->need_update()->memchr_inv() and the other is executing something.

So seems busy loop isn't the problem, it's just the TLB flushing is very-very
expensive in TCG. On the other hand I don't see such a problem with MTTCG, so
not sure what's going on with a single-threaded TCG.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2017-09-18 15:32 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20170224112109.3147-1-alex.bennee@linaro.org>
2017-02-24 11:20 ` [PULL 08/24] tcg: drop global lock during TCG code execution Alex Bennée
2017-02-27 12:48   ` [Qemu-devel] " Laurent Desnogues
2017-02-27 14:39     ` Alex Bennée
2017-03-03 20:59       ` Aaron Lindsay
2017-03-03 21:08         ` Alex Bennée
2017-02-24 11:21 ` [PULL 16/24] cputlb and arm/sparc targets: convert mmuidx flushes from varg to bitmap Alex Bennée
2017-02-24 11:21 ` [PULL 20/24] target-arm/powerctl: defer cpu reset work to CPU context Alex Bennée
2017-02-24 11:21 ` [PULL 21/24] target-arm: don't generate WFE/YIELD calls for MTTCG Alex Bennée
2017-02-24 11:21 ` [PULL 22/24] target-arm: ensure all cross vCPUs TLB flushes complete Alex Bennée
2017-09-17 13:07   ` Dmitry Osipenko
2017-09-17 13:22     ` Alex Bennée
2017-09-17 13:46       ` Dmitry Osipenko
2017-09-18 10:10         ` Alex Bennée
2017-09-18 12:23           ` Dmitry Osipenko
2017-09-18 14:00             ` Alex Bennée
2017-09-18 15:32               ` Dmitry Osipenko
2017-02-24 11:21 ` [PULL 23/24] hw/misc/imx6_src: defer clearing of SRC_SCR reset bits Alex Bennée
2017-02-24 11:21 ` [PULL 24/24] tcg: enable MTTCG by default for ARM on x86 hosts Alex Bennée

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).