diff mbox series

[v2,4/7] target: Push BQL on ->do_interrupt down into per-arch implementation

Message ID 20200819182856.4893-5-robert.foley@linaro.org (mailing list archive)
State New, archived
Headers show
Series accel/tcg: remove implied BQL from cpu_handle_interrupt/exception path | expand

Commit Message

Robert Foley Aug. 19, 2020, 6:28 p.m. UTC
As part of pushing the BQL down into the per-arch implementation,
the first change is to remove the holding of BQL from
cpu_handle_exception.  Next, we made changes per-arch to
re-add a new *_do_interrupt function, which gets the BQL and then
calls to *_do_interrupt_locked.  We also pointed the per-arch
->do_interrupt at the new *_do_interrupt.

This patch is part of a series of transitions to move the
BQL down into the do_interrupt per arch functions.  This set of
transitions is needed to maintain bisectability.

It is worth mentioning that arm and cris are slightly different.
In a prior patch for these arches, we added a new CPUClass method
 ->do_interrupt_locked.  The only difference for arm and cris
is that their new *_do_interrupt functions will be able to
utilize this new ->do_interrupt_locked method.

avr is another exception.  avr, arm and cris all had a similar
case where their *_cpu_exec_interrupt was calling to
the CPUClass ->do_interrupt.  This causes an issue when we push
the lock down since ->do_interrupt will try to acquire the BQL, but
the calling context already has it.  To solve this for arm and
cris, we added a new CPUCLass method as explained above.  Moreover,
it was actually required for these arches since they have more than
one possible value of ->do_interrupt.  In the case of avr,
there is only one possible value of ->do_interrupt, so for that reason
we changed the avr_cpu_exec_interrupt to call directly to
avr_cpu_do_interrupt_locked rather than call cc->do_interrupt.

The purpose of this set of changes is to set the groundwork
so that an arch could move towards removing
the BQL from the cpu_handle_interrupt/exception paths.

This approach was suggested by Paolo Bonzini.
For reference, here are key posts in the discussion, explaining
the reasoning/benefits of this approach.

https://lists.gnu.org/archive/html/qemu-devel/2020-08/msg01517.html
https://lists.gnu.org/archive/html/qemu-devel/2020-08/msg00784.html
https://lists.gnu.org/archive/html/qemu-devel/2020-07/msg08731.html
https://lists.gnu.org/archive/html/qemu-devel/2020-08/msg00044.html

Signed-off-by: Robert Foley <robert.foley@linaro.org>
---
 accel/tcg/cpu-exec.c            |  2 --
 target/alpha/cpu.c              |  2 +-
 target/alpha/cpu.h              |  2 +-
 target/alpha/helper.c           |  9 ++++++++-
 target/arm/cpu.c                |  2 +-
 target/arm/cpu.h                |  2 ++
 target/arm/cpu_tcg.c            |  2 +-
 target/arm/helper.c             |  8 ++++++++
 target/avr/cpu.c                |  2 +-
 target/avr/cpu.h                |  2 +-
 target/avr/helper.c             | 16 ++++++++++++----
 target/cris/cpu.c               |  7 +------
 target/cris/cpu.h               |  1 +
 target/cris/helper.c            |  8 ++++++++
 target/hppa/cpu.c               |  2 +-
 target/hppa/cpu.h               |  2 +-
 target/hppa/int_helper.c        |  9 ++++++++-
 target/i386/cpu.c               |  2 +-
 target/i386/cpu.h               |  2 +-
 target/i386/seg_helper.c        |  9 ++++++++-
 target/lm32/cpu.c               |  2 +-
 target/lm32/cpu.h               |  2 +-
 target/lm32/helper.c            |  9 ++++++++-
 target/m68k/cpu.c               |  2 +-
 target/m68k/cpu.h               |  2 +-
 target/m68k/op_helper.c         | 11 +++++++++--
 target/microblaze/cpu.c         |  2 +-
 target/microblaze/cpu.h         |  2 +-
 target/microblaze/helper.c      | 11 +++++++++--
 target/mips/cpu.c               |  2 +-
 target/mips/helper.c            |  9 ++++++++-
 target/mips/internal.h          |  2 +-
 target/nios2/cpu.c              |  2 +-
 target/nios2/cpu.h              |  1 +
 target/nios2/helper.c           |  9 +++++++++
 target/openrisc/cpu.c           |  2 +-
 target/openrisc/cpu.h           |  2 +-
 target/openrisc/interrupt.c     |  9 ++++++++-
 target/ppc/cpu.h                |  1 +
 target/ppc/excp_helper.c        |  7 +++++++
 target/ppc/translate_init.inc.c |  2 +-
 target/riscv/cpu.c              |  2 +-
 target/riscv/cpu.h              |  2 +-
 target/riscv/cpu_helper.c       | 11 ++++++++++-
 target/rx/cpu.c                 |  2 +-
 target/rx/cpu.h                 |  1 +
 target/rx/helper.c              |  7 +++++++
 target/s390x/cpu.c              |  2 +-
 target/s390x/excp_helper.c      |  7 +++++++
 target/s390x/internal.h         |  1 +
 target/sh4/cpu.c                |  2 +-
 target/sh4/cpu.h                |  2 +-
 target/sh4/helper.c             | 11 +++++++++--
 target/sparc/cpu.c              |  2 +-
 target/sparc/cpu.h              |  1 +
 target/sparc/int32_helper.c     |  7 +++++++
 target/sparc/int64_helper.c     |  7 +++++++
 target/tilegx/cpu.c             |  9 ++++++++-
 target/unicore32/cpu.c          |  2 +-
 target/unicore32/cpu.h          |  1 +
 target/unicore32/softmmu.c      |  7 +++++++
 target/xtensa/cpu.c             |  2 +-
 target/xtensa/cpu.h             |  2 +-
 target/xtensa/exc_helper.c      | 11 +++++++++--
 64 files changed, 223 insertions(+), 60 deletions(-)

Comments

Richard Henderson Aug. 31, 2020, 9:37 p.m. UTC | #1
On 8/19/20 11:28 AM, Robert Foley wrote:
> avr is another exception.  avr, arm and cris all had a similar
> case where their *_cpu_exec_interrupt was calling to
> the CPUClass ->do_interrupt.  This causes an issue when we push
> the lock down since ->do_interrupt will try to acquire the BQL, but
> the calling context already has it.

Alpha is in this lest as well, correct?

> diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h
> index 4c6753df34..be29bdd530 100644
> --- a/target/alpha/cpu.h
> +++ b/target/alpha/cpu.h
> @@ -276,7 +276,7 @@ struct AlphaCPU {
>  extern const VMStateDescription vmstate_alpha_cpu;
>  #endif
>  
> -void alpha_cpu_do_interrupt_locked(CPUState *cpu);
> +void alpha_cpu_do_interrupt(CPUState *cpu);
>  bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req);
>  void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags);
>  hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
> diff --git a/target/alpha/helper.c b/target/alpha/helper.c
> index ff9a2a7765..e497dd269e 100644
> --- a/target/alpha/helper.c
> +++ b/target/alpha/helper.c
> @@ -295,7 +295,7 @@ bool alpha_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
>  }
>  #endif /* USER_ONLY */
>  
> -void alpha_cpu_do_interrupt_locked(CPUState *cs)
> +static void alpha_cpu_do_interrupt_locked(CPUState *cs)
>  {
>      AlphaCPU *cpu = ALPHA_CPU(cs);
>      CPUAlphaState *env = &cpu->env;
> @@ -407,6 +407,13 @@ void alpha_cpu_do_interrupt_locked(CPUState *cs)
>  #endif /* !USER_ONLY */
>  }
>  
> +void alpha_cpu_do_interrupt(CPUState *cs)
> +{
> +    qemu_mutex_lock_iothread();
> +    alpha_cpu_do_interrupt_locked(cs);
> +    qemu_mutex_unlock_iothread();
> +}
> +
>  bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
>  {
>      AlphaCPU *cpu = ALPHA_CPU(cs);

This rename should have been done in patch 1, as with all others.
Moreover, this leaves a bug in alpha_cpu_exec_interrupt in that it should be
calling alpha_cpu_do_interrupt_locked.

That seems to be the only instance of this mistake.


r~
diff mbox series

Patch

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 80d0e649b2..e661635f06 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -517,9 +517,7 @@  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;
 
             if (unlikely(cpu->singlestep_enabled)) {
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index cb1074e0f9..09677c6c44 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -217,7 +217,7 @@  static void alpha_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = alpha_cpu_class_by_name;
     cc->has_work = alpha_cpu_has_work;
-    cc->do_interrupt = alpha_cpu_do_interrupt_locked;
+    cc->do_interrupt = alpha_cpu_do_interrupt;
     cc->cpu_exec_interrupt = alpha_cpu_exec_interrupt;
     cc->dump_state = alpha_cpu_dump_state;
     cc->set_pc = alpha_cpu_set_pc;
diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h
index 4c6753df34..be29bdd530 100644
--- a/target/alpha/cpu.h
+++ b/target/alpha/cpu.h
@@ -276,7 +276,7 @@  struct AlphaCPU {
 extern const VMStateDescription vmstate_alpha_cpu;
 #endif
 
-void alpha_cpu_do_interrupt_locked(CPUState *cpu);
+void alpha_cpu_do_interrupt(CPUState *cpu);
 bool alpha_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags);
 hwaddr alpha_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/alpha/helper.c b/target/alpha/helper.c
index ff9a2a7765..e497dd269e 100644
--- a/target/alpha/helper.c
+++ b/target/alpha/helper.c
@@ -295,7 +295,7 @@  bool alpha_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
 }
 #endif /* USER_ONLY */
 
-void alpha_cpu_do_interrupt_locked(CPUState *cs)
+static void alpha_cpu_do_interrupt_locked(CPUState *cs)
 {
     AlphaCPU *cpu = ALPHA_CPU(cs);
     CPUAlphaState *env = &cpu->env;
@@ -407,6 +407,13 @@  void alpha_cpu_do_interrupt_locked(CPUState *cs)
 #endif /* !USER_ONLY */
 }
 
+void alpha_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    alpha_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     AlphaCPU *cpu = ALPHA_CPU(cs);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index d15b459399..12eda7611f 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2224,7 +2224,7 @@  static void arm_cpu_class_init(ObjectClass *oc, void *data)
     cc->gdb_read_register = arm_cpu_gdb_read_register;
     cc->gdb_write_register = arm_cpu_gdb_write_register;
 #ifndef CONFIG_USER_ONLY
-    cc->do_interrupt = arm_cpu_do_interrupt_locked;
+    cc->do_interrupt = arm_cpu_do_interrupt;
     acc->do_interrupt_locked = arm_cpu_do_interrupt_locked;
     cc->get_phys_page_attrs_debug = arm_cpu_get_phys_page_attrs_debug;
     cc->asidx_from_attrs = arm_asidx_from_attrs;
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 1f522964b5..57b8904dec 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -991,7 +991,9 @@  uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz);
 extern const VMStateDescription vmstate_arm_cpu;
 #endif
 
+void arm_cpu_do_interrupt(CPUState *cpu);
 void arm_cpu_do_interrupt_locked(CPUState *cpu);
+void arm_v7m_cpu_do_interrupt(CPUState *cpu);
 void arm_v7m_cpu_do_interrupt_locked(CPUState *cpu);
 bool arm_cpu_exec_interrupt(CPUState *cpu, int int_req);
 
diff --git a/target/arm/cpu_tcg.c b/target/arm/cpu_tcg.c
index caf0d54c2c..df36cfda76 100644
--- a/target/arm/cpu_tcg.c
+++ b/target/arm/cpu_tcg.c
@@ -601,7 +601,7 @@  static void arm_v7m_class_init(ObjectClass *oc, void *data)
 
     acc->info = data;
 #ifndef CONFIG_USER_ONLY
-    cc->do_interrupt = arm_v7m_cpu_do_interrupt_locked;
+    cc->do_interrupt = arm_cpu_do_interrupt;
     acc->do_interrupt_locked = arm_v7m_cpu_do_interrupt_locked;
 #endif
 
diff --git a/target/arm/helper.c b/target/arm/helper.c
index e07924daf5..f0ab750088 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -9840,6 +9840,14 @@  static void handle_semihosting(CPUState *cs)
 }
 #endif
 
+void arm_cpu_do_interrupt(CPUState *cs)
+{
+    ARMCPUClass *acc = ARM_CPU_GET_CLASS(cs);
+    qemu_mutex_lock_iothread();
+    acc->do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 /* Handle a CPU exception for A and R profile CPUs.
  * Do any appropriate logging, handle PSCI calls, and then hand off
  * to the AArch64-entry or AArch32-entry function depending on the
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
index d856069230..5d9c4ad5bf 100644
--- a/target/avr/cpu.c
+++ b/target/avr/cpu.c
@@ -197,7 +197,7 @@  static void avr_cpu_class_init(ObjectClass *oc, void *data)
     cc->class_by_name = avr_cpu_class_by_name;
 
     cc->has_work = avr_cpu_has_work;
-    cc->do_interrupt = avr_cpu_do_interrupt_locked;
+    cc->do_interrupt = avr_cpu_do_interrupt;
     cc->cpu_exec_interrupt = avr_cpu_exec_interrupt;
     cc->dump_state = avr_cpu_dump_state;
     cc->set_pc = avr_cpu_set_pc;
diff --git a/target/avr/cpu.h b/target/avr/cpu.h
index 66a26f08ef..d148e8c75a 100644
--- a/target/avr/cpu.h
+++ b/target/avr/cpu.h
@@ -156,7 +156,7 @@  typedef struct AVRCPU {
 
 extern const struct VMStateDescription vms_avr_cpu;
 
-void avr_cpu_do_interrupt_locked(CPUState *cpu);
+void avr_cpu_do_interrupt(CPUState *cpu);
 bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
 hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
 int avr_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
diff --git a/target/avr/helper.c b/target/avr/helper.c
index 096bc35945..2c8c3af580 100644
--- a/target/avr/helper.c
+++ b/target/avr/helper.c
@@ -24,17 +24,18 @@ 
 #include "exec/address-spaces.h"
 #include "exec/helper-proto.h"
 
+static void avr_cpu_do_interrupt_locked(CPUState *cs);
+
 bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     bool ret = false;
-    CPUClass *cc = CPU_GET_CLASS(cs);
     AVRCPU *cpu = AVR_CPU(cs);
     CPUAVRState *env = &cpu->env;
 
     if (interrupt_request & CPU_INTERRUPT_RESET) {
         if (cpu_interrupts_enabled(env)) {
             cs->exception_index = EXCP_RESET;
-            cc->do_interrupt(cs);
+            avr_cpu_do_interrupt_locked(cs);
 
             cs->interrupt_request &= ~CPU_INTERRUPT_RESET;
 
@@ -45,7 +46,7 @@  bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
         if (cpu_interrupts_enabled(env) && env->intsrc != 0) {
             int index = ctz32(env->intsrc);
             cs->exception_index = EXCP_INT(index);
-            cc->do_interrupt(cs);
+            avr_cpu_do_interrupt_locked(cs);
 
             env->intsrc &= env->intsrc - 1; /* clear the interrupt */
             cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
@@ -56,7 +57,7 @@  bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
     return ret;
 }
 
-void avr_cpu_do_interrupt_locked(CPUState *cs)
+static void avr_cpu_do_interrupt_locked(CPUState *cs)
 {
     AVRCPU *cpu = AVR_CPU(cs);
     CPUAVRState *env = &cpu->env;
@@ -89,6 +90,13 @@  void avr_cpu_do_interrupt_locked(CPUState *cs)
     cs->exception_index = -1;
 }
 
+void avr_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    avr_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf,
                             int len, bool is_write)
 {
diff --git a/target/cris/cpu.c b/target/cris/cpu.c
index 948eeb6260..c3d77c31e8 100644
--- a/target/cris/cpu.c
+++ b/target/cris/cpu.c
@@ -199,7 +199,6 @@  static void crisv8_cpu_class_init(ObjectClass *oc, void *data)
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 8;
-    cc->do_interrupt = crisv10_cpu_do_interrupt_locked;
     ccc->do_interrupt_locked = crisv10_cpu_do_interrupt_locked;
     cc->gdb_read_register = crisv10_cpu_gdb_read_register;
     cc->tcg_initialize = cris_initialize_crisv10_tcg;
@@ -211,7 +210,6 @@  static void crisv9_cpu_class_init(ObjectClass *oc, void *data)
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 9;
-    cc->do_interrupt = crisv10_cpu_do_interrupt_locked;
     ccc->do_interrupt_locked = crisv10_cpu_do_interrupt_locked;
     cc->gdb_read_register = crisv10_cpu_gdb_read_register;
     cc->tcg_initialize = cris_initialize_crisv10_tcg;
@@ -223,7 +221,6 @@  static void crisv10_cpu_class_init(ObjectClass *oc, void *data)
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 10;
-    cc->do_interrupt = crisv10_cpu_do_interrupt_locked;
     ccc->do_interrupt_locked = crisv10_cpu_do_interrupt_locked;
     cc->gdb_read_register = crisv10_cpu_gdb_read_register;
     cc->tcg_initialize = cris_initialize_crisv10_tcg;
@@ -235,7 +232,6 @@  static void crisv11_cpu_class_init(ObjectClass *oc, void *data)
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 11;
-    cc->do_interrupt = crisv10_cpu_do_interrupt_locked;
     ccc->do_interrupt_locked = crisv10_cpu_do_interrupt_locked;
     cc->gdb_read_register = crisv10_cpu_gdb_read_register;
     cc->tcg_initialize = cris_initialize_crisv10_tcg;
@@ -247,7 +243,6 @@  static void crisv17_cpu_class_init(ObjectClass *oc, void *data)
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 17;
-    cc->do_interrupt = crisv10_cpu_do_interrupt_locked;
     ccc->do_interrupt_locked = crisv10_cpu_do_interrupt_locked;
     cc->gdb_read_register = crisv10_cpu_gdb_read_register;
     cc->tcg_initialize = cris_initialize_crisv10_tcg;
@@ -273,7 +268,7 @@  static void cris_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = cris_cpu_class_by_name;
     cc->has_work = cris_cpu_has_work;
-    cc->do_interrupt = cris_cpu_do_interrupt_locked;
+    cc->do_interrupt = cris_cpu_do_interrupt;
     ccc->do_interrupt_locked = cris_cpu_do_interrupt_locked;
     cc->cpu_exec_interrupt = cris_cpu_exec_interrupt;
     cc->dump_state = cris_cpu_dump_state;
diff --git a/target/cris/cpu.h b/target/cris/cpu.h
index 597ccd6451..d8ee6b9400 100644
--- a/target/cris/cpu.h
+++ b/target/cris/cpu.h
@@ -187,6 +187,7 @@  struct CRISCPU {
 extern const VMStateDescription vmstate_cris_cpu;
 #endif
 
+void cris_cpu_do_interrupt(CPUState *cpu);
 void cris_cpu_do_interrupt_locked(CPUState *cpu);
 void crisv10_cpu_do_interrupt_locked(CPUState *cpu);
 bool cris_cpu_exec_interrupt(CPUState *cpu, int int_req);
diff --git a/target/cris/helper.c b/target/cris/helper.c
index 3b7ee74813..0e053782ab 100644
--- a/target/cris/helper.c
+++ b/target/cris/helper.c
@@ -38,6 +38,14 @@ 
 #define D_LOG(...) do { } while (0)
 #endif
 
+void cris_cpu_do_interrupt(CPUState *cs)
+{
+    CRISCPUClass *acc = CRIS_CPU_GET_CLASS(cs);
+    qemu_mutex_lock_iothread();
+    acc->do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 #if defined(CONFIG_USER_ONLY)
 
 void cris_cpu_do_interrupt_locked(CPUState *cs)
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 7241ffbd7f..287055f96e 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -139,7 +139,7 @@  static void hppa_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = hppa_cpu_class_by_name;
     cc->has_work = hppa_cpu_has_work;
-    cc->do_interrupt = hppa_cpu_do_interrupt_locked;
+    cc->do_interrupt = hppa_cpu_do_interrupt;
     cc->cpu_exec_interrupt = hppa_cpu_exec_interrupt;
     cc->dump_state = hppa_cpu_dump_state;
     cc->set_pc = hppa_cpu_set_pc;
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index 7fc7682ca8..801a4fb1ba 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -323,7 +323,7 @@  int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc);
 hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr);
 int hppa_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
 int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
-void hppa_cpu_do_interrupt_locked(CPUState *cpu);
+void hppa_cpu_do_interrupt(CPUState *cpu);
 bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void hppa_cpu_dump_state(CPUState *cs, FILE *f, int);
 bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c
index 31fce959d6..03cb521a96 100644
--- a/target/hppa/int_helper.c
+++ b/target/hppa/int_helper.c
@@ -90,7 +90,7 @@  void HELPER(write_eiem)(CPUHPPAState *env, target_ureg val)
 }
 #endif /* !CONFIG_USER_ONLY */
 
-void hppa_cpu_do_interrupt_locked(CPUState *cs)
+static void hppa_cpu_do_interrupt_locked(CPUState *cs)
 {
     HPPACPU *cpu = HPPA_CPU(cs);
     CPUHPPAState *env = &cpu->env;
@@ -246,6 +246,13 @@  void hppa_cpu_do_interrupt_locked(CPUState *cs)
     cs->exception_index = -1;
 }
 
+void hppa_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    hppa_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
 #ifndef CONFIG_USER_ONLY
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index fdb8ae11b6..592aa0baf7 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -7297,7 +7297,7 @@  static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
     cc->parse_features = x86_cpu_parse_featurestr;
     cc->has_work = x86_cpu_has_work;
 #ifdef CONFIG_TCG
-    cc->do_interrupt = x86_cpu_do_interrupt_locked;
+    cc->do_interrupt = x86_cpu_do_interrupt;
     cc->cpu_exec_interrupt = x86_cpu_exec_interrupt;
 #endif
     cc->dump_state = x86_cpu_dump_state;
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 8d4dac129b..d784eeaf29 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1770,7 +1770,7 @@  extern VMStateDescription vmstate_x86_cpu;
  * x86_cpu_do_interrupt:
  * @cpu: vCPU the interrupt is to be handled by.
  */
-void x86_cpu_do_interrupt_locked(CPUState *cpu);
+void x86_cpu_do_interrupt(CPUState *cpu);
 bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
 int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request);
 
diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c
index 0d8464abec..74484eb175 100644
--- a/target/i386/seg_helper.c
+++ b/target/i386/seg_helper.c
@@ -1280,7 +1280,7 @@  static void do_interrupt_all(X86CPU *cpu, int intno, int is_int,
 #endif
 }
 
-void x86_cpu_do_interrupt_locked(CPUState *cs)
+static void x86_cpu_do_interrupt_locked(CPUState *cs)
 {
     X86CPU *cpu = X86_CPU(cs);
     CPUX86State *env = &cpu->env;
@@ -1310,6 +1310,13 @@  void x86_cpu_do_interrupt_locked(CPUState *cs)
 #endif
 }
 
+void x86_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    x86_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw)
 {
     do_interrupt_all(env_archcpu(env), intno, 0, 0, 0, is_hw);
diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c
index 93da742520..9e7d8ca929 100644
--- a/target/lm32/cpu.c
+++ b/target/lm32/cpu.c
@@ -222,7 +222,7 @@  static void lm32_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = lm32_cpu_class_by_name;
     cc->has_work = lm32_cpu_has_work;
-    cc->do_interrupt = lm32_cpu_do_interrupt_locked;
+    cc->do_interrupt = lm32_cpu_do_interrupt;
     cc->cpu_exec_interrupt = lm32_cpu_exec_interrupt;
     cc->dump_state = lm32_cpu_dump_state;
     cc->set_pc = lm32_cpu_set_pc;
diff --git a/target/lm32/cpu.h b/target/lm32/cpu.h
index cd96a2905e..01d408eb55 100644
--- a/target/lm32/cpu.h
+++ b/target/lm32/cpu.h
@@ -198,7 +198,7 @@  struct LM32CPU {
 extern const VMStateDescription vmstate_lm32_cpu;
 #endif
 
-void lm32_cpu_do_interrupt_locked(CPUState *cpu);
+void lm32_cpu_do_interrupt(CPUState *cpu);
 bool lm32_cpu_exec_interrupt(CPUState *cs, int int_req);
 void lm32_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr lm32_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/lm32/helper.c b/target/lm32/helper.c
index 8599a59df2..6184e212cf 100644
--- a/target/lm32/helper.c
+++ b/target/lm32/helper.c
@@ -148,7 +148,7 @@  void lm32_debug_excp_handler(CPUState *cs)
     }
 }
 
-void lm32_cpu_do_interrupt_locked(CPUState *cs)
+static void lm32_cpu_do_interrupt_locked(CPUState *cs)
 {
     LM32CPU *cpu = LM32_CPU(cs);
     CPULM32State *env = &cpu->env;
@@ -198,6 +198,13 @@  void lm32_cpu_do_interrupt_locked(CPUState *cs)
     }
 }
 
+void lm32_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    lm32_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool lm32_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     LM32CPU *cpu = LM32_CPU(cs);
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index 6ac0dd87a6..f2585154f5 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -277,7 +277,7 @@  static void m68k_cpu_class_init(ObjectClass *c, void *data)
 
     cc->class_by_name = m68k_cpu_class_by_name;
     cc->has_work = m68k_cpu_has_work;
-    cc->do_interrupt = m68k_cpu_do_interrupt_locked;
+    cc->do_interrupt = m68k_cpu_do_interrupt;
     cc->cpu_exec_interrupt = m68k_cpu_exec_interrupt;
     cc->dump_state = m68k_cpu_dump_state;
     cc->set_pc = m68k_cpu_set_pc;
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 1afbe94570..521ac67cdd 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -164,7 +164,7 @@  struct M68kCPU {
 };
 
 
-void m68k_cpu_do_interrupt_locked(CPUState *cpu);
+void m68k_cpu_do_interrupt(CPUState *cpu);
 bool m68k_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void m68k_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr m68k_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c
index 8fd6481883..3f0c99124a 100644
--- a/target/m68k/op_helper.c
+++ b/target/m68k/op_helper.c
@@ -25,7 +25,7 @@ 
 
 #if defined(CONFIG_USER_ONLY)
 
-void m68k_cpu_do_interrupt_locked(CPUState *cs)
+static void m68k_cpu_do_interrupt_locked(CPUState *cs)
 {
     cs->exception_index = -1;
 }
@@ -443,7 +443,7 @@  static void do_interrupt_all(CPUM68KState *env, int is_hw)
     cf_interrupt_all(env, is_hw);
 }
 
-void m68k_cpu_do_interrupt_locked(CPUState *cs)
+static void m68k_cpu_do_interrupt_locked(CPUState *cs)
 {
     M68kCPU *cpu = M68K_CPU(cs);
     CPUM68KState *env = &cpu->env;
@@ -504,6 +504,13 @@  void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
 }
 #endif
 
+void m68k_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    m68k_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     M68kCPU *cpu = M68K_CPU(cs);
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
index b19e386bcf..ce70f7d281 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -316,7 +316,7 @@  static void mb_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = mb_cpu_class_by_name;
     cc->has_work = mb_cpu_has_work;
-    cc->do_interrupt = mb_cpu_do_interrupt_locked;
+    cc->do_interrupt = mb_cpu_do_interrupt;
     cc->cpu_exec_interrupt = mb_cpu_exec_interrupt;
     cc->dump_state = mb_cpu_dump_state;
     cc->set_pc = mb_cpu_set_pc;
diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h
index 7617565a0c..a31134b65c 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -315,7 +315,7 @@  struct MicroBlazeCPU {
 };
 
 
-void mb_cpu_do_interrupt_locked(CPUState *cs);
+void mb_cpu_do_interrupt(CPUState *cs);
 bool mb_cpu_exec_interrupt(CPUState *cs, int int_req);
 void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c
index 263cdf59be..f241587b40 100644
--- a/target/microblaze/helper.c
+++ b/target/microblaze/helper.c
@@ -28,7 +28,7 @@ 
 
 #if defined(CONFIG_USER_ONLY)
 
-void mb_cpu_do_interrupt_locked(CPUState *cs)
+static void mb_cpu_do_interrupt_locked(CPUState *cs)
 {
     MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
     CPUMBState *env = &cpu->env;
@@ -108,7 +108,7 @@  bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     cpu_loop_exit_restore(cs, retaddr);
 }
 
-void mb_cpu_do_interrupt_locked(CPUState *cs)
+static void mb_cpu_do_interrupt_locked(CPUState *cs)
 {
     MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
     CPUMBState *env = &cpu->env;
@@ -287,6 +287,13 @@  hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 }
 #endif
 
+void mb_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    mb_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index c616a0ef5a..ec9dde5100 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -196,7 +196,7 @@  static void mips_cpu_class_init(ObjectClass *c, void *data)
 
     cc->class_by_name = mips_cpu_class_by_name;
     cc->has_work_with_iothread_lock = mips_cpu_has_work;
-    cc->do_interrupt = mips_cpu_do_interrupt_locked;
+    cc->do_interrupt = mips_cpu_do_interrupt;
     cc->cpu_exec_interrupt = mips_cpu_exec_interrupt;
     cc->dump_state = mips_cpu_dump_state;
     cc->set_pc = mips_cpu_set_pc;
diff --git a/target/mips/helper.c b/target/mips/helper.c
index a85c4057d0..3941392b95 100644
--- a/target/mips/helper.c
+++ b/target/mips/helper.c
@@ -1083,7 +1083,7 @@  static inline void set_badinstr_registers(CPUMIPSState *env)
 }
 #endif
 
-void mips_cpu_do_interrupt_locked(CPUState *cs)
+static void mips_cpu_do_interrupt_locked(CPUState *cs)
 {
 #if !defined(CONFIG_USER_ONLY)
     MIPSCPU *cpu = MIPS_CPU(cs);
@@ -1398,6 +1398,13 @@  void mips_cpu_do_interrupt_locked(CPUState *cs)
     cs->exception_index = EXCP_NONE;
 }
 
+void mips_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    mips_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     if (interrupt_request & CPU_INTERRUPT_HARD) {
diff --git a/target/mips/internal.h b/target/mips/internal.h
index fb0181c095..7f159a9230 100644
--- a/target/mips/internal.h
+++ b/target/mips/internal.h
@@ -80,7 +80,7 @@  enum CPUMIPSMSADataFormat {
     DF_DOUBLE
 };
 
-void mips_cpu_do_interrupt_locked(CPUState *cpu);
+void mips_cpu_do_interrupt(CPUState *cpu);
 bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void mips_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c
index cc813181a4..7116b3f230 100644
--- a/target/nios2/cpu.c
+++ b/target/nios2/cpu.c
@@ -192,7 +192,7 @@  static void nios2_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = nios2_cpu_class_by_name;
     cc->has_work = nios2_cpu_has_work;
-    cc->do_interrupt = nios2_cpu_do_interrupt_locked;
+    cc->do_interrupt = nios2_cpu_do_interrupt;
     cc->cpu_exec_interrupt = nios2_cpu_exec_interrupt;
     cc->dump_state = nios2_cpu_dump_state;
     cc->set_pc = nios2_cpu_set_pc;
diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
index dcf1715092..6fa60b1399 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -195,6 +195,7 @@  typedef struct Nios2CPU {
 
 
 void nios2_tcg_init(void);
+void nios2_cpu_do_interrupt(CPUState *cs);
 void nios2_cpu_do_interrupt_locked(CPUState *cs);
 int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
 void dump_mmu(CPUNios2State *env);
diff --git a/target/nios2/helper.c b/target/nios2/helper.c
index 25c6c6d4d8..c81aa073f4 100644
--- a/target/nios2/helper.c
+++ b/target/nios2/helper.c
@@ -312,3 +312,12 @@  bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     cpu_loop_exit_restore(cs, retaddr);
 }
 #endif /* !CONFIG_USER_ONLY */
+
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    nios2_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
+
diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c
index e428946dc2..fd2da39124 100644
--- a/target/openrisc/cpu.c
+++ b/target/openrisc/cpu.c
@@ -154,7 +154,7 @@  static void openrisc_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = openrisc_cpu_class_by_name;
     cc->has_work = openrisc_cpu_has_work;
-    cc->do_interrupt = openrisc_cpu_do_interrupt_locked;
+    cc->do_interrupt = openrisc_cpu_do_interrupt;
     cc->cpu_exec_interrupt = openrisc_cpu_exec_interrupt;
     cc->dump_state = openrisc_cpu_dump_state;
     cc->set_pc = openrisc_cpu_set_pc;
diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h
index e77f075cd9..f37a52e153 100644
--- a/target/openrisc/cpu.h
+++ b/target/openrisc/cpu.h
@@ -316,7 +316,7 @@  typedef struct OpenRISCCPU {
 
 
 void cpu_openrisc_list(void);
-void openrisc_cpu_do_interrupt_locked(CPUState *cpu);
+void openrisc_cpu_do_interrupt(CPUState *cpu);
 bool openrisc_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void openrisc_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c
index f95289444f..a5ca3ece78 100644
--- a/target/openrisc/interrupt.c
+++ b/target/openrisc/interrupt.c
@@ -26,7 +26,7 @@ 
 #include "hw/loader.h"
 #endif
 
-void openrisc_cpu_do_interrupt_locked(CPUState *cs)
+static void openrisc_cpu_do_interrupt_locked(CPUState *cs)
 {
 #ifndef CONFIG_USER_ONLY
     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
@@ -101,6 +101,13 @@  void openrisc_cpu_do_interrupt_locked(CPUState *cs)
     cs->exception_index = -1;
 }
 
+void openrisc_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    openrisc_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool openrisc_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index ed297acdb4..2569d43e59 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1231,6 +1231,7 @@  struct PPCVirtualHypervisorClass {
                      TYPE_PPC_VIRTUAL_HYPERVISOR)
 #endif /* CONFIG_USER_ONLY */
 
+void ppc_cpu_do_interrupt(CPUState *cpu);
 void ppc_cpu_do_interrupt_locked(CPUState *cpu);
 bool ppc_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void ppc_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index fe9b122fd0..2cb1aaa296 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -1428,3 +1428,10 @@  void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr,
     env->error_code = insn & 0x03FF0000;
     cpu_loop_exit(cs);
 }
+
+void ppc_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    ppc_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index 653b04aef6..27ae7fa195 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -10885,7 +10885,7 @@  static void ppc_cpu_class_init(ObjectClass *oc, void *data)
     pcc->parent_parse_features = cc->parse_features;
     cc->parse_features = ppc_cpu_parse_featurestr;
     cc->has_work_with_iothread_lock = ppc_cpu_has_work;
-    cc->do_interrupt = ppc_cpu_do_interrupt_locked;
+    cc->do_interrupt = ppc_cpu_do_interrupt;
     cc->cpu_exec_interrupt = ppc_cpu_exec_interrupt;
     cc->dump_state = ppc_cpu_dump_state;
     cc->dump_statistics = ppc_cpu_dump_statistics;
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 833e6d4f1e..832171c360 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -537,7 +537,7 @@  static void riscv_cpu_class_init(ObjectClass *c, void *data)
 
     cc->class_by_name = riscv_cpu_class_by_name;
     cc->has_work_with_iothread_lock = riscv_cpu_has_work;
-    cc->do_interrupt = riscv_cpu_do_interrupt_locked;
+    cc->do_interrupt = riscv_cpu_do_interrupt;
     cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
     cc->dump_state = riscv_cpu_dump_state;
     cc->set_pc = riscv_cpu_set_pc;
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 372005b79c..a804a5d0ba 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -312,7 +312,7 @@  extern const char * const riscv_fpr_regnames[];
 extern const char * const riscv_excp_names[];
 extern const char * const riscv_intr_names[];
 
-void riscv_cpu_do_interrupt_locked(CPUState *cpu);
+void riscv_cpu_do_interrupt(CPUState *cpu);
 int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
 int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
 bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index 477cf66b66..2c8ee8ca2b 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -25,6 +25,8 @@ 
 #include "tcg/tcg-op.h"
 #include "trace.h"
 
+static void riscv_cpu_do_interrupt_locked(CPUState *cs);
+
 int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
 {
 #ifdef CONFIG_USER_ONLY
@@ -814,13 +816,20 @@  bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
 #endif
 }
 
+void riscv_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    riscv_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 /*
  * Handle Traps
  *
  * Adapted from Spike's processor_t::take_trap.
  *
  */
-void riscv_cpu_do_interrupt_locked(CPUState *cs)
+static void riscv_cpu_do_interrupt_locked(CPUState *cs)
 {
 #if !defined(CONFIG_USER_ONLY)
 
diff --git a/target/rx/cpu.c b/target/rx/cpu.c
index 51e10da7cc..219e05397b 100644
--- a/target/rx/cpu.c
+++ b/target/rx/cpu.c
@@ -185,7 +185,7 @@  static void rx_cpu_class_init(ObjectClass *klass, void *data)
 
     cc->class_by_name = rx_cpu_class_by_name;
     cc->has_work = rx_cpu_has_work;
-    cc->do_interrupt = rx_cpu_do_interrupt_locked;
+    cc->do_interrupt = rx_cpu_do_interrupt;
     cc->cpu_exec_interrupt = rx_cpu_exec_interrupt;
     cc->dump_state = rx_cpu_dump_state;
     cc->set_pc = rx_cpu_set_pc;
diff --git a/target/rx/cpu.h b/target/rx/cpu.h
index d188e7d43f..ec085d0386 100644
--- a/target/rx/cpu.h
+++ b/target/rx/cpu.h
@@ -125,6 +125,7 @@  typedef RXCPU ArchCPU;
 #define CPU_RESOLVING_TYPE TYPE_RX_CPU
 
 const char *rx_crname(uint8_t cr);
+void rx_cpu_do_interrupt(CPUState *cpu);
 void rx_cpu_do_interrupt_locked(CPUState *cpu);
 bool rx_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void rx_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
diff --git a/target/rx/helper.c b/target/rx/helper.c
index 332f89435a..8c0512b62a 100644
--- a/target/rx/helper.c
+++ b/target/rx/helper.c
@@ -119,6 +119,13 @@  void rx_cpu_do_interrupt_locked(CPUState *cs)
     env->regs[0] = env->isp;
 }
 
+void rx_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    rx_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool rx_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     RXCPU *cpu = RXCPU(cs);
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
index eb23d64f36..4d0d323cf9 100644
--- a/target/s390x/cpu.c
+++ b/target/s390x/cpu.c
@@ -493,7 +493,7 @@  static void s390_cpu_class_init(ObjectClass *oc, void *data)
     cc->class_by_name = s390_cpu_class_by_name,
     cc->has_work_with_iothread_lock = s390_cpu_has_work;
 #ifdef CONFIG_TCG
-    cc->do_interrupt = s390_cpu_do_interrupt_locked;
+    cc->do_interrupt = s390_cpu_do_interrupt;
 #endif
     cc->dump_state = s390_cpu_dump_state;
     cc->set_pc = s390_cpu_set_pc;
diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c
index a663127f17..a020db1725 100644
--- a/target/s390x/excp_helper.c
+++ b/target/s390x/excp_helper.c
@@ -611,3 +611,10 @@  void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
 }
 
 #endif /* CONFIG_USER_ONLY */
+
+void s390_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    s390_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
diff --git a/target/s390x/internal.h b/target/s390x/internal.h
index 6ab0fb481a..b4660ad255 100644
--- a/target/s390x/internal.h
+++ b/target/s390x/internal.h
@@ -268,6 +268,7 @@  ObjectClass *s390_cpu_class_by_name(const char *name);
 
 /* excp_helper.c */
 void s390x_cpu_debug_excp_handler(CPUState *cs);
+void s390_cpu_do_interrupt(CPUState *cpu);
 void s390_cpu_do_interrupt_locked(CPUState *cpu);
 bool s390_cpu_exec_interrupt(CPUState *cpu, int int_req);
 bool s390_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
index 5e5921a220..18f3448183 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -218,7 +218,7 @@  static void superh_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = superh_cpu_class_by_name;
     cc->has_work = superh_cpu_has_work;
-    cc->do_interrupt = superh_cpu_do_interrupt_locked;
+    cc->do_interrupt = superh_cpu_do_interrupt;
     cc->cpu_exec_interrupt = superh_cpu_exec_interrupt;
     cc->dump_state = superh_cpu_dump_state;
     cc->set_pc = superh_cpu_set_pc;
diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h
index 2ae3dd132b..dbe58c7888 100644
--- a/target/sh4/cpu.h
+++ b/target/sh4/cpu.h
@@ -204,7 +204,7 @@  struct SuperHCPU {
 };
 
 
-void superh_cpu_do_interrupt_locked(CPUState *cpu);
+void superh_cpu_do_interrupt(CPUState *cpu);
 bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void superh_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/sh4/helper.c b/target/sh4/helper.c
index 2d61f65d50..471650b622 100644
--- a/target/sh4/helper.c
+++ b/target/sh4/helper.c
@@ -45,7 +45,7 @@ 
 
 #if defined(CONFIG_USER_ONLY)
 
-void superh_cpu_do_interrupt_locked(CPUState *cs)
+static void superh_cpu_do_interrupt_locked(CPUState *cs)
 {
     cs->exception_index = -1;
 }
@@ -58,7 +58,7 @@  int cpu_sh4_is_cached(CPUSH4State *env, target_ulong addr)
 
 #else /* !CONFIG_USER_ONLY */
 
-void superh_cpu_do_interrupt_locked(CPUState *cs)
+static void superh_cpu_do_interrupt_locked(CPUState *cs)
 {
     SuperHCPU *cpu = SUPERH_CPU(cs);
     CPUSH4State *env = &cpu->env;
@@ -782,6 +782,13 @@  int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
 
 #endif
 
+void superh_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    superh_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     if (interrupt_request & CPU_INTERRUPT_HARD) {
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index 4c8842adcf..1c26bbd59b 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -863,7 +863,7 @@  static void sparc_cpu_class_init(ObjectClass *oc, void *data)
     cc->class_by_name = sparc_cpu_class_by_name;
     cc->parse_features = sparc_cpu_parse_features;
     cc->has_work_with_iothread_lock = sparc_cpu_has_work;
-    cc->do_interrupt = sparc_cpu_do_interrupt_locked;
+    cc->do_interrupt = sparc_cpu_do_interrupt;
     cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt;
     cc->dump_state = sparc_cpu_dump_state;
 #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index 3563e65d73..ec8684ec93 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -568,6 +568,7 @@  struct SPARCCPU {
 extern const VMStateDescription vmstate_sparc_cpu;
 #endif
 
+void sparc_cpu_do_interrupt(CPUState *cpu);
 void sparc_cpu_do_interrupt_locked(CPUState *cpu);
 void sparc_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c
index 90f4aa4a78..7a1f15b06c 100644
--- a/target/sparc/int32_helper.c
+++ b/target/sparc/int32_helper.c
@@ -138,6 +138,13 @@  void sparc_cpu_do_interrupt_locked(CPUState *cs)
 #endif
 }
 
+void sparc_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    sparc_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 #if !defined(CONFIG_USER_ONLY)
 static void leon3_cache_control_int(CPUSPARCState *env)
 {
diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c
index b81b4abaa8..aa3d9aa585 100644
--- a/target/sparc/int64_helper.c
+++ b/target/sparc/int64_helper.c
@@ -198,6 +198,13 @@  void sparc_cpu_do_interrupt_locked(CPUState *cs)
     cs->exception_index = -1;
 }
 
+void sparc_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    sparc_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 trap_state *cpu_tsptr(CPUSPARCState* env)
 {
     return &env->ts[env->tl & MAXTL_MASK];
diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c
index a2ff335977..a450dc63a3 100644
--- a/target/tilegx/cpu.c
+++ b/target/tilegx/cpu.c
@@ -125,6 +125,13 @@  static bool tilegx_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
     cpu_loop_exit_restore(cs, retaddr);
 }
 
+static void tilegx_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    tilegx_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 static bool tilegx_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     if (interrupt_request & CPU_INTERRUPT_HARD) {
@@ -147,7 +154,7 @@  static void tilegx_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = tilegx_cpu_class_by_name;
     cc->has_work = tilegx_cpu_has_work;
-    cc->do_interrupt = tilegx_cpu_do_interrupt_locked;
+    cc->do_interrupt = tilegx_cpu_do_interrupt;
     cc->cpu_exec_interrupt = tilegx_cpu_exec_interrupt;
     cc->dump_state = tilegx_cpu_dump_state;
     cc->set_pc = tilegx_cpu_set_pc;
diff --git a/target/unicore32/cpu.c b/target/unicore32/cpu.c
index a96077d666..06bf4b4b63 100644
--- a/target/unicore32/cpu.c
+++ b/target/unicore32/cpu.c
@@ -131,7 +131,7 @@  static void uc32_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = uc32_cpu_class_by_name;
     cc->has_work = uc32_cpu_has_work;
-    cc->do_interrupt = uc32_cpu_do_interrupt_locked;
+    cc->do_interrupt = uc32_cpu_do_interrupt;
     cc->cpu_exec_interrupt = uc32_cpu_exec_interrupt;
     cc->dump_state = uc32_cpu_dump_state;
     cc->set_pc = uc32_cpu_set_pc;
diff --git a/target/unicore32/cpu.h b/target/unicore32/cpu.h
index d948392ff3..9453b5d7b3 100644
--- a/target/unicore32/cpu.h
+++ b/target/unicore32/cpu.h
@@ -75,6 +75,7 @@  struct UniCore32CPU {
 };
 
 
+void uc32_cpu_do_interrupt(CPUState *cpu);
 void uc32_cpu_do_interrupt_locked(CPUState *cpu);
 bool uc32_cpu_exec_interrupt(CPUState *cpu, int int_req);
 void uc32_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
diff --git a/target/unicore32/softmmu.c b/target/unicore32/softmmu.c
index a12526a8ca..7fb28f3c12 100644
--- a/target/unicore32/softmmu.c
+++ b/target/unicore32/softmmu.c
@@ -120,6 +120,13 @@  void uc32_cpu_do_interrupt_locked(CPUState *cs)
     cpu_interrupt_request_or(cs, CPU_INTERRUPT_EXITTB);
 }
 
+void uc32_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    uc32_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address,
         int access_type, int is_user, uint32_t *phys_ptr, int *prot,
         target_ulong *page_size)
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
index 7962bc66a8..0f96483563 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -190,7 +190,7 @@  static void xtensa_cpu_class_init(ObjectClass *oc, void *data)
 
     cc->class_by_name = xtensa_cpu_class_by_name;
     cc->has_work_with_iothread_lock = xtensa_cpu_has_work;
-    cc->do_interrupt = xtensa_cpu_do_interrupt_locked;
+    cc->do_interrupt = xtensa_cpu_do_interrupt;
     cc->cpu_exec_interrupt = xtensa_cpu_exec_interrupt;
     cc->dump_state = xtensa_cpu_dump_state;
     cc->set_pc = xtensa_cpu_set_pc;
diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h
index c02f531b64..32749378bf 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -563,7 +563,7 @@  struct XtensaCPU {
 bool xtensa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
                          MMUAccessType access_type, int mmu_idx,
                          bool probe, uintptr_t retaddr);
-void xtensa_cpu_do_interrupt_locked(CPUState *cpu);
+void xtensa_cpu_do_interrupt(CPUState *cpu);
 bool xtensa_cpu_exec_interrupt(CPUState *cpu, int interrupt_request);
 void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
                                       unsigned size, MMUAccessType access_type,
diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c
index 10d4762f36..822ed7aad8 100644
--- a/target/xtensa/exc_helper.c
+++ b/target/xtensa/exc_helper.c
@@ -195,7 +195,7 @@  static void handle_interrupt(CPUXtensaState *env)
 }
 
 /* Called from cpu_handle_interrupt with BQL held */
-void xtensa_cpu_do_interrupt_locked(CPUState *cs)
+static void xtensa_cpu_do_interrupt_locked(CPUState *cs)
 {
     XtensaCPU *cpu = XTENSA_CPU(cs);
     CPUXtensaState *env = &cpu->env;
@@ -254,11 +254,18 @@  void xtensa_cpu_do_interrupt_locked(CPUState *cs)
     check_interrupts(env);
 }
 #else
-void xtensa_cpu_do_interrupt_locked(CPUState *cs)
+static void xtensa_cpu_do_interrupt_locked(CPUState *cs)
 {
 }
 #endif
 
+void xtensa_cpu_do_interrupt(CPUState *cs)
+{
+    qemu_mutex_lock_iothread();
+    xtensa_cpu_do_interrupt_locked(cs);
+    qemu_mutex_unlock_iothread();
+}
+
 bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 {
     if (interrupt_request & CPU_INTERRUPT_HARD) {