* [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt
@ 2026-09-23 17:10 Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 01/21] system: Document has_work() synchronization requirements Philippe Mathieu-Daudé
` (20 more replies)
0 siblings, 21 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:10 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Missing review: 4, 7, 9-12, 17
Since v2:
- Make all ARM halt_reason accesses atomic, including WFI/WFE helpers
and halt-exit handling, and read event_register atomically.
- Keep the RISC-V changes focused on MIP: skip empty updates, add a
shared accessor, and convert existing MIP reads to use it.
Since v1:
- Collected Richard R-b tags (sometimes commit description updated).
- Documented the BQL and synchronization requirements of has_work().
- Renamed the callbacks to poll_during_halt() and leaving_halt().
- Addressed Peter's comments by removing racy halt-work assertions,
documenting the BQL contract, and explaining why x86 #DB delivery
through do_interrupt_all() is safe without the BQL.
- Addressed Paolo's comments by making ARM PSCI state updates atomic,
kicking remote vCPUs after power-state changes, and ensuring x86
APIC polling runs before cpu_has_work() with the BQL held only
around APIC access.
- Sanitized s390_cpu_get_state() for atomic accesses without BQL.
This series was inspired by a previous thread on the list [*].
Refactor the CPU halt-to-execution transition logic in TCG as
something more explicit and composable.
Core problem: TCGCPUOps::cpu_exec_halt callback mixed concerns,
it checked for work, processed async events, and handled state
transitions all in one place.
Solution: introduces two dedicated callbacks:
* poll_during_halt(): Process target-specific async events
before checking for work. Called early in cpu_exec().
* leaving_halt(): Perform target-specific state updates
when transitioning from halt to execution.
This separation allows the generic cpu_exec() code to orchestrate
the flow cleanly (process events, check for work, transition state).
Only 2 targets need to be migrated (x86 and ARM) then we can remove
the redundant cpu_exec_halt() hook.
The changes are expected to be purely refactoring with no functional
impact.
[*] https://lore.kernel.org/qemu-devel/CABgObfaDAhrpnVqQaKgG6uxPQe1YDu77YOsUEx9nqrN=3M2cGw@mail.gmail.com/
Philippe Mathieu-Daudé (21):
system: Document has_work() synchronization requirements
target/s390x: Use s390_cpu_get_state() consistently
target/s390x: Make s390_cpu_set_state() return void
target/s390x: Use S390CpuState for CPU state APIs
target/s390x: Access S390CpuState atomically
target/arm: Un-inline arm_set_cpu_power_state()
target/arm: Kick vCPU after updating PSCI power state
target/arm: Access PSCI state atomically
target/arm: Access halt state atomically
target/riscv: Skip empty MIP updates
target/riscv: Add riscv_cpu_get_mip()
target/riscv: Access MIP atomically
accel/tcg: Refactor poll-while-halted flow in cpu_exec()
accel/tcg: Introduce poll_during_halt() and leaving_halt()
target/arm: Split leaving-halt handling from arm_cpu_exec_halt()
target/arm: Convert cpu_exec_halt() to leaving_halt()
target/i386: Use an acquire load for interrupt_request
target/i386: Split APIC polling from x86_cpu_exec_halt()
target/i386: Split leaving-halt handling from x86_cpu_exec_halt()
target/i386: Use generic halt transition handlers
accel/tcg: Remove the redundant cpu_exec_halt() hook
include/accel/tcg/cpu-ops.h | 36 ++++++++++++++++-------------
include/hw/core/sysemu-cpu-ops.h | 6 +++--
target/arm/internals.h | 11 +--------
target/i386/tcg/helper-tcg.h | 3 ++-
target/loongarch/internals.h | 1 -
target/riscv/cpu.h | 1 +
target/riscv/internals.h | 5 ----
target/s390x/cpu.h | 9 ++++----
target/s390x/kvm/kvm_s390x.h | 2 +-
target/s390x/s390x-internal.h | 1 -
accel/tcg/cpu-exec.c | 20 ++++++++++------
hw/intc/riscv_aclint.c | 2 +-
hw/intc/s390_flic.c | 7 ++++--
target/alpha/cpu.c | 1 -
target/arm/arm-powerctl.c | 13 +++++++++++
target/arm/cpu.c | 31 +++++++++++--------------
target/arm/tcg/cpu-v7m.c | 1 -
target/arm/tcg/op_helper.c | 8 +++----
target/avr/cpu.c | 1 -
target/hexagon/cpu.c | 1 -
target/hppa/cpu.c | 1 -
target/i386/cpu.c | 5 +++-
target/i386/tcg/system/seg_helper.c | 17 +++++++++-----
target/i386/tcg/tcg-cpu.c | 3 ++-
target/loongarch/cpu.c | 2 +-
target/loongarch/tcg/tcg_cpu.c | 1 -
target/m68k/cpu.c | 1 -
target/microblaze/cpu.c | 1 -
target/mips/cpu.c | 1 -
target/or1k/cpu.c | 1 -
target/ppc/cpu_init.c | 1 -
target/riscv/cpu.c | 5 ++--
target/riscv/tcg/cpu_helper.c | 20 ++++++++++++----
target/riscv/tcg/csr.c | 6 +++--
target/riscv/tcg/tcg-cpu.c | 1 -
target/rx/cpu.c | 1 -
target/s390x/cpu-system.c | 26 ++++++++++++---------
target/s390x/cpu.c | 3 +--
target/s390x/kvm/kvm.c | 4 ++--
target/s390x/kvm/stubs.c | 2 +-
target/s390x/machine.c | 2 +-
target/s390x/sigp.c | 9 ++++++--
target/sh4/cpu.c | 1 -
target/sparc/cpu.c | 1 -
target/tricore/cpu.c | 1 -
target/xtensa/cpu.c | 1 -
46 files changed, 150 insertions(+), 127 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v3 01/21] system: Document has_work() synchronization requirements
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
@ 2026-09-23 17:10 ` Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 02/21] target/s390x: Use s390_cpu_get_state() consistently Philippe Mathieu-Daudé
` (19 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:10 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Document that has_work() may be called with or without the BQL.
Require implementations to remain idempotent and avoid consuming
work, and to synchronize state shared with other threads without
acquiring the BQL unconditionally.
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
include/hw/core/sysemu-cpu-ops.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h
index e56eea18b78..d125540262d 100644
--- a/include/hw/core/sysemu-cpu-ops.h
+++ b/include/hw/core/sysemu-cpu-ops.h
@@ -19,8 +19,10 @@ typedef struct SysemuCPUOps {
/**
* @has_work: Callback for checking if there is work to do.
*
- * This function should be idempotent (i.e. not change state) as
- * it will likely be queried multiple times before a CPU resumes.
+ * This callback may be called with or without the BQL. It must be
+ * idempotent, must not consume work, and must not assume that the BQL
+ * is held or acquire it unconditionally. State shared with other
+ * threads must use appropriate synchronization.
*/
bool (*has_work)(CPUState *cpu); /* MANDATORY NON-NULL */
/**
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 02/21] target/s390x: Use s390_cpu_get_state() consistently
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 01/21] system: Document has_work() synchronization requirements Philippe Mathieu-Daudé
@ 2026-09-23 17:10 ` Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 03/21] target/s390x: Make s390_cpu_set_state() return void Philippe Mathieu-Daudé
` (18 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:10 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Eric Farman, Cornelia Huck, Matthew Rosato, Ilya Leoshkevich,
David Hildenbrand, Halil Pasic, Christian Borntraeger
Replace direct reads of env::cpu_state with s390_cpu_get_state().
This keeps CPU state reads consistent across CPU transitions.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Eric Farman <farman@linux.ibm.com>
---
target/s390x/cpu-system.c | 2 +-
target/s390x/cpu.c | 2 +-
target/s390x/kvm/kvm.c | 2 +-
target/s390x/machine.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c
index cc9685221ae..c938c77d0bd 100644
--- a/target/s390x/cpu-system.c
+++ b/target/s390x/cpu-system.c
@@ -263,7 +263,7 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
cpu_state);
exit(1);
}
- if (kvm_enabled() && cpu->env.cpu_state != cpu_state) {
+ if (kvm_enabled() && s390_cpu_get_state(cpu) != cpu_state) {
kvm_s390_set_cpu_state(cpu, cpu_state);
}
cpu->env.cpu_state = cpu_state;
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
index 7c725b8a4a4..85afc5dec9a 100644
--- a/target/s390x/cpu.c
+++ b/target/s390x/cpu.c
@@ -130,7 +130,7 @@ static void s390_query_cpu_fast(CPUState *cpu, CpuInfoFast *value)
{
S390CPU *s390_cpu = S390_CPU(cpu);
- value->u.s390x.cpu_state = s390_cpu->env.cpu_state;
+ value->u.s390x.cpu_state = s390_cpu_get_state(s390_cpu);
#if !defined(CONFIG_USER_ONLY)
if (s390_has_topology()) {
value->u.s390x.has_dedicated = true;
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index b34a9127141..6622886032e 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -407,7 +407,7 @@ int kvm_arch_init_vcpu(CPUState *cs)
{
unsigned int max_cpus = MACHINE(qdev_get_machine())->smp.max_cpus;
S390CPU *cpu = S390_CPU(cs);
- kvm_s390_set_cpu_state(cpu, cpu->env.cpu_state);
+ kvm_s390_set_cpu_state(cpu, s390_cpu_get_state(cpu));
cpu->irqstate = g_malloc0(VCPU_IRQ_BUF_SIZE(max_cpus));
return 0;
}
diff --git a/target/s390x/machine.c b/target/s390x/machine.c
index f714834a98a..b44fba43f14 100644
--- a/target/s390x/machine.c
+++ b/target/s390x/machine.c
@@ -32,7 +32,7 @@ static int cpu_post_load(void *opaque, int version_id)
* than via cpu_synchronize_state, we need update kvm here.
*/
if (kvm_enabled()) {
- kvm_s390_set_cpu_state(cpu, cpu->env.cpu_state);
+ kvm_s390_set_cpu_state(cpu, s390_cpu_get_state(cpu));
return kvm_s390_vcpu_interrupt_post_load(cpu);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 03/21] target/s390x: Make s390_cpu_set_state() return void
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 01/21] system: Document has_work() synchronization requirements Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 02/21] target/s390x: Use s390_cpu_get_state() consistently Philippe Mathieu-Daudé
@ 2026-09-23 17:10 ` Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 04/21] target/s390x: Use S390CpuState for CPU state APIs Philippe Mathieu-Daudé
` (17 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:10 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Philippe Mathieu-Daudé, Eric Farman, Ilya Leoshkevich,
David Hildenbrand, Cornelia Huck, Matthew Rosato, Halil Pasic,
Christian Borntraeger
From: Philippe Mathieu-Daudé <philmd@linaro.org>
The return value of s390_cpu_set_state() is only used by the STOP
interrupt handler. Make the setter return void and count running
CPUs directly at that call site.
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Eric Farman <farman@linux.ibm.com>
---
target/s390x/cpu.h | 5 ++---
target/s390x/cpu-system.c | 4 +---
target/s390x/sigp.c | 3 ++-
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 998bbb0d7ff..2d1dcc7045c 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -847,11 +847,10 @@ void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg);
int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id,
int vq, bool assign);
#ifndef CONFIG_USER_ONLY
-unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu);
+void s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu);
#else
-static inline unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
+static inline void s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
{
- return 0;
}
#endif /* CONFIG_USER_ONLY */
static inline uint8_t s390_cpu_get_state(const S390CPU *cpu)
diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c
index c938c77d0bd..52561fffa3a 100644
--- a/target/s390x/cpu-system.c
+++ b/target/s390x/cpu-system.c
@@ -236,7 +236,7 @@ void s390_cpu_unhalt(S390CPU *cpu)
}
}
-unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
+void s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
{
trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state);
@@ -267,8 +267,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
kvm_s390_set_cpu_state(cpu, cpu_state);
}
cpu->env.cpu_state = cpu_state;
-
- return s390_count_running_cpus();
}
void s390_cmma_reset(void)
diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c
index b6d44c5c31b..1801b8caa6e 100644
--- a/target/s390x/sigp.c
+++ b/target/s390x/sigp.c
@@ -617,7 +617,8 @@ void do_stop_interrupt(CPUS390XState *env)
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
}
env->sigp_order = 0;
- if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
+ s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
+ if (s390_count_running_cpus() == 0) {
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
}
env->pending_int &= ~INTERRUPT_STOP;
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 04/21] target/s390x: Use S390CpuState for CPU state APIs
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (2 preceding siblings ...)
2026-09-23 17:10 ` [PATCH v3 03/21] target/s390x: Make s390_cpu_set_state() return void Philippe Mathieu-Daudé
@ 2026-09-23 17:10 ` Philippe Mathieu-Daudé
2026-09-23 17:32 ` Eric Farman
2026-09-23 17:10 ` [PATCH v3 05/21] target/s390x: Access S390CpuState atomically Philippe Mathieu-Daudé
` (16 subsequent siblings)
20 siblings, 1 reply; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:10 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Halil Pasic, Christian Borntraeger, David Hildenbrand,
Jason Herne, Cornelia Huck, Eric Farman, Matthew Rosato,
Ilya Leoshkevich
Use the QAPI S390CpuState enum for CPU state accessors instead
of uint8_t. Handle state checks with switch statements.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/s390x/cpu.h | 6 +++---
target/s390x/kvm/kvm_s390x.h | 2 +-
hw/intc/s390_flic.c | 7 +++++--
target/s390x/cpu-system.c | 18 ++++++++++++------
target/s390x/kvm/kvm.c | 2 +-
target/s390x/kvm/stubs.c | 2 +-
target/s390x/sigp.c | 6 +++++-
7 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 2d1dcc7045c..bf9f6ed1de2 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -847,13 +847,13 @@ void s390_do_cpu_set_diag318(CPUState *cs, run_on_cpu_data arg);
int s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch_id,
int vq, bool assign);
#ifndef CONFIG_USER_ONLY
-void s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu);
+void s390_cpu_set_state(S390CpuState cpu_state, S390CPU *cpu);
#else
-static inline void s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
+static inline void s390_cpu_set_state(S390CpuState cpu_state, S390CPU *cpu)
{
}
#endif /* CONFIG_USER_ONLY */
-static inline uint8_t s390_cpu_get_state(const S390CPU *cpu)
+static inline S390CpuState s390_cpu_get_state(const S390CPU *cpu)
{
return cpu->env.cpu_state;
}
diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h
index 3c4fa0489cf..b1ff66f04e3 100644
--- a/target/s390x/kvm/kvm_s390x.h
+++ b/target/s390x/kvm/kvm_s390x.h
@@ -22,7 +22,7 @@ int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf,
int kvm_s390_mem_op_pv(S390CPU *cpu, vaddr addr, void *hostbuf, int len,
bool is_write);
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code);
-int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
+int kvm_s390_set_cpu_state(S390CPU *cpu, S390CpuState cpu_state);
void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
int kvm_s390_get_hpage(void);
diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c
index 57fd4b2b81c..b5256b3ba88 100644
--- a/hw/intc/s390_flic.c
+++ b/hw/intc/s390_flic.c
@@ -193,8 +193,11 @@ static void qemu_s390_flic_notify(uint32_t type)
cpu_set_interrupt(cs, CPU_INTERRUPT_HARD);
/* ignore CPUs that are not sleeping */
- if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING &&
- s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD) {
+ switch (s390_cpu_get_state(cpu)) {
+ case S390_CPU_STATE_LOAD:
+ case S390_CPU_STATE_OPERATING:
+ break;
+ default:
continue;
}
diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c
index 52561fffa3a..fffac09f241 100644
--- a/target/s390x/cpu-system.c
+++ b/target/s390x/cpu-system.c
@@ -44,8 +44,11 @@ bool s390_cpu_has_work(CPUState *cs)
S390CPU *cpu = S390_CPU(cs);
/* STOPPED cpus can never wake up */
- if (s390_cpu_get_state(cpu) != S390_CPU_STATE_LOAD &&
- s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
+ switch (s390_cpu_get_state(cpu)) {
+ case S390_CPU_STATE_LOAD:
+ case S390_CPU_STATE_OPERATING:
+ break;
+ default:
return false;
}
@@ -202,12 +205,15 @@ unsigned s390_count_running_cpus(void)
int nr_running = 0;
CPU_FOREACH(cpu) {
- uint8_t state = S390_CPU(cpu)->env.cpu_state;
- if (state == S390_CPU_STATE_OPERATING ||
- state == S390_CPU_STATE_LOAD) {
+ switch (s390_cpu_get_state(S390_CPU(cpu))) {
+ case S390_CPU_STATE_LOAD:
+ case S390_CPU_STATE_OPERATING:
if (!disabled_wait(cpu)) {
nr_running++;
}
+ break;
+ default:
+ break;
}
}
@@ -236,7 +242,7 @@ void s390_cpu_unhalt(S390CPU *cpu)
}
}
-void s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
+void s390_cpu_set_state(S390CpuState cpu_state, S390CPU *cpu)
{
trace_cpu_set_state(CPU(cpu)->cpu_index, cpu_state);
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 6622886032e..dc67e848864 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -2007,7 +2007,7 @@ int kvm_s390_get_ri(void)
return cap_ri;
}
-int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
+int kvm_s390_set_cpu_state(S390CPU *cpu, S390CpuState cpu_state)
{
struct kvm_mp_state mp_state = {};
int ret;
diff --git a/target/s390x/kvm/stubs.c b/target/s390x/kvm/stubs.c
index ebf3c83994d..c5ec7d3f1d3 100644
--- a/target/s390x/kvm/stubs.c
+++ b/target/s390x/kvm/stubs.c
@@ -128,7 +128,7 @@ int kvm_s390_mem_op_pv(S390CPU *cpu, vaddr addr, void *hostbuf, int len,
g_assert_not_reached();
}
-int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state)
+int kvm_s390_set_cpu_state(S390CPU *cpu, S390CpuState cpu_state)
{
g_assert_not_reached();
}
diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c
index 1801b8caa6e..09004e98a21 100644
--- a/target/s390x/sigp.c
+++ b/target/s390x/sigp.c
@@ -39,7 +39,7 @@ static void set_sigp_status(SigpInfo *si, uint64_t status)
static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
{
- uint8_t state = s390_cpu_get_state(dst_cpu);
+ S390CpuState state = s390_cpu_get_state(dst_cpu);
bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL;
uint64_t status = 0;
@@ -221,6 +221,8 @@ static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
cpu_synchronize_state(cs);
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
break;
+ default:
+ break;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
@@ -362,6 +364,8 @@ static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
case S390_CPU_STATE_OPERATING:
cpu_inject_restart(cpu);
break;
+ default:
+ break;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 05/21] target/s390x: Access S390CpuState atomically
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (3 preceding siblings ...)
2026-09-23 17:10 ` [PATCH v3 04/21] target/s390x: Use S390CpuState for CPU state APIs Philippe Mathieu-Daudé
@ 2026-09-23 17:10 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 06/21] target/arm: Un-inline arm_set_cpu_power_state() Philippe Mathieu-Daudé
` (15 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:10 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Eric Farman, Ilya Leoshkevich, David Hildenbrand, Cornelia Huck,
Matthew Rosato
Use qatomic_read/set() in the S390 CPU state accessors.
This avoids non-atomic reads in s390_cpu_has_work().
Reported-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Eric Farman <farman@linux.ibm.com>
---
target/s390x/cpu.h | 2 +-
target/s390x/cpu-system.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index bf9f6ed1de2..ef73b15218e 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -855,7 +855,7 @@ static inline void s390_cpu_set_state(S390CpuState cpu_state, S390CPU *cpu)
#endif /* CONFIG_USER_ONLY */
static inline S390CpuState s390_cpu_get_state(const S390CPU *cpu)
{
- return cpu->env.cpu_state;
+ return qatomic_read(&cpu->env.cpu_state);
}
diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c
index fffac09f241..d38de4fea2b 100644
--- a/target/s390x/cpu-system.c
+++ b/target/s390x/cpu-system.c
@@ -272,7 +272,7 @@ void s390_cpu_set_state(S390CpuState cpu_state, S390CPU *cpu)
if (kvm_enabled() && s390_cpu_get_state(cpu) != cpu_state) {
kvm_s390_set_cpu_state(cpu, cpu_state);
}
- cpu->env.cpu_state = cpu_state;
+ qatomic_set(&cpu->env.cpu_state, cpu_state);
}
void s390_cmma_reset(void)
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 06/21] target/arm: Un-inline arm_set_cpu_power_state()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (4 preceding siblings ...)
2026-09-23 17:10 ` [PATCH v3 05/21] target/s390x: Access S390CpuState atomically Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 07/21] target/arm: Kick vCPU after updating PSCI power state Philippe Mathieu-Daudé
` (14 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Move arm_set_cpu_power_state() from the header to arm-powerctl.c,
keeping the power-state helper with the other power-control code.
Restrict the call in arm_cpu_reset_hold() to system emulation.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/arm/internals.h | 8 +-------
target/arm/arm-powerctl.c | 7 +++++++
target/arm/cpu.c | 4 ++--
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 1775835ad50..1efda543270 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -2082,12 +2082,6 @@ bool arm_cpu_match_cpreg_mig_tolerance(ARMCPU *cpu, uint64_t kvmidx,
/**
* arm_set_cpu_power_state() - set power state synced with halt_reason
*/
-static inline void arm_set_cpu_power_state(ARMCPU *cpu, ARMPSCIState state)
-{
- CPUARMState *env = &cpu->env;
-
- cpu->power_state = state;
- env->halt_reason = state == PSCI_OFF ? HALT_PSCI : NOT_HALTED;
-}
+void arm_set_cpu_power_state(ARMCPU *cpu, ARMPSCIState state);
#endif
diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index a06be5cc997..213e7ae056c 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -45,6 +45,13 @@ struct CpuOnInfo {
bool target_aa64;
};
+void arm_set_cpu_power_state(ARMCPU *cpu, ARMPSCIState state)
+{
+ CPUARMState *env = &cpu->env;
+
+ cpu->power_state = state;
+ env->halt_reason = state == PSCI_OFF ? HALT_PSCI : NOT_HALTED;
+}
static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
run_on_cpu_data data)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index f58a1db843a..76aa47ac503 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -351,8 +351,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type)
env->vfp.xregs[ARM_VFP_MVFR1] = cpu->isar.mvfr1;
env->vfp.xregs[ARM_VFP_MVFR2] = cpu->isar.mvfr2;
- arm_set_cpu_power_state(cpu, cs->start_powered_off ? PSCI_OFF : PSCI_ON);
-
if (arm_feature(env, ARM_FEATURE_AARCH64)) {
/* 64 bit CPUs always start in 64 bit mode */
env->aarch64 = true;
@@ -671,6 +669,8 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type)
arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH_F16]);
#ifndef CONFIG_USER_ONLY
+ arm_set_cpu_power_state(cpu, cs->start_powered_off ? PSCI_OFF : PSCI_ON);
+
if (kvm_enabled()) {
kvm_arm_reset_vcpu(cpu);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 07/21] target/arm: Kick vCPU after updating PSCI power state
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (5 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 06/21] target/arm: Un-inline arm_set_cpu_power_state() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 08/21] target/arm: Access PSCI state atomically Philippe Mathieu-Daudé
` (13 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
PSCI can update the power state of a vCPU other than the caller.
Kick that vCPU after changing its state so it observes the update
promptly.
Reported-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/arm/arm-powerctl.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index 213e7ae056c..1bd8a549cc9 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -48,9 +48,14 @@ struct CpuOnInfo {
void arm_set_cpu_power_state(ARMCPU *cpu, ARMPSCIState state)
{
CPUARMState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
cpu->power_state = state;
env->halt_reason = state == PSCI_OFF ? HALT_PSCI : NOT_HALTED;
+
+ if (!qemu_cpu_is_self(cs)) {
+ qemu_cpu_kick(cs);
+ }
}
static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 08/21] target/arm: Access PSCI state atomically
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (6 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 07/21] target/arm: Kick vCPU after updating PSCI power state Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 09/21] target/arm: Access halt " Philippe Mathieu-Daudé
` (12 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Use qatomic_set() when updating ARMCPU::power_state and
CPUARMState::halt_reason. Read ARMCPU::power_state atomically in
arm_cpu_has_work(), which can run without the BQL held.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/arm/arm-powerctl.c | 5 +++--
target/arm/cpu.c | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c
index 1bd8a549cc9..fcac26d9190 100644
--- a/target/arm/arm-powerctl.c
+++ b/target/arm/arm-powerctl.c
@@ -50,8 +50,9 @@ void arm_set_cpu_power_state(ARMCPU *cpu, ARMPSCIState state)
CPUARMState *env = &cpu->env;
CPUState *cs = CPU(cpu);
- cpu->power_state = state;
- env->halt_reason = state == PSCI_OFF ? HALT_PSCI : NOT_HALTED;
+ qatomic_set(&cpu->power_state, state);
+ qatomic_set(&env->halt_reason, state == PSCI_OFF ? HALT_PSCI
+ : NOT_HALTED);
if (!qemu_cpu_is_self(cs)) {
qemu_cpu_kick(cs);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 76aa47ac503..5cfd3bcfc8e 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -149,7 +149,7 @@ static bool arm_cpu_has_work(CPUState *cs)
* Only another PSCI call can wake the CPU up in which case the
* power_state would be set by arm_set_cpu_on_and_reset_async_work()
*/
- if (cpu->power_state == PSCI_OFF) {
+ if (qatomic_read(&cpu->power_state) == PSCI_OFF) {
g_assert(cpu->env.halt_reason == HALT_PSCI);
return false;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 09/21] target/arm: Access halt state atomically
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (7 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 08/21] target/arm: Access PSCI state atomically Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:29 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 10/21] target/riscv: Skip empty MIP updates Philippe Mathieu-Daudé
` (11 subsequent siblings)
20 siblings, 1 reply; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
arm_cpu_has_work() runs without the BQL held and can inspect
halt_reason and event_register while other CPU contexts update
them. The WFxT timer may also consume HALT_WFE asynchronously.
Use atomic accesses for the halt state, including the WFI/WFE
and halt-exit stores. This keeps the halt/wakeup protocol
race-free and matches the atomic state transitions used by the
asynchronous wake-up paths.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
v3: Dropped rth's R-b because of HELPER()s call also updated
---
target/arm/cpu.c | 7 ++++---
target/arm/tcg/op_helper.c | 8 ++++----
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 5cfd3bcfc8e..090470da40a 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -144,20 +144,21 @@ int arm_cpu_mmu_index(CPUState *cs, bool ifetch)
static bool arm_cpu_has_work(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
+ ARMHaltReason halt_reason = qatomic_read(&cpu->env.halt_reason);
/*
* Only another PSCI call can wake the CPU up in which case the
* power_state would be set by arm_set_cpu_on_and_reset_async_work()
*/
if (qatomic_read(&cpu->power_state) == PSCI_OFF) {
- g_assert(cpu->env.halt_reason == HALT_PSCI);
+ g_assert(halt_reason == HALT_PSCI);
return false;
}
/*
* A wake-up event should only wake us if we are halted on a WFE
*/
- if (cpu->env.halt_reason == HALT_WFE && cpu->env.event_register) {
+ if (halt_reason == HALT_WFE && qatomic_read(&cpu->env.event_register)) {
return true;
}
@@ -882,7 +883,7 @@ bool arm_cpu_exec_halt(CPUState *cs)
timer_del(cpu->wfxt_timer);
}
/* clear the halt reason */
- cpu->env.halt_reason = NOT_HALTED;
+ qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
}
return leave_halt;
}
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index c2b09176cb1..643b1482523 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -398,7 +398,7 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len)
target_el);
}
- env->halt_reason = HALT_WFI;
+ qatomic_set(&env->halt_reason, HALT_WFI);
cs->exception_index = EXCP_HLT;
cs->halted = 1;
cpu_loop_exit(cs);
@@ -460,7 +460,7 @@ void HELPER(wfit)(CPUARMState *env, uint32_t rd)
} else {
timer_mod(cpu->wfxt_timer, nexttick);
}
- env->halt_reason = HALT_WFI;
+ qatomic_set(&env->halt_reason, HALT_WFI);
cs->exception_index = EXCP_HLT;
cs->halted = 1;
cpu_loop_exit(cs);
@@ -629,7 +629,7 @@ void HELPER(wfe)(CPUARMState *env, uint32_t insn_len)
}
}
- env->halt_reason = HALT_WFE;
+ qatomic_set(&env->halt_reason, HALT_WFE);
cs->exception_index = EXCP_HLT;
cs->halted = 1;
cpu_loop_exit(cs);
@@ -723,7 +723,7 @@ void HELPER(wfet)(CPUARMState *env, uint32_t rd)
}
}
- env->halt_reason = HALT_WFE;
+ qatomic_set(&env->halt_reason, HALT_WFE);
cs->exception_index = EXCP_HLT;
cs->halted = 1;
cpu_loop_exit(cs);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 10/21] target/riscv: Skip empty MIP updates
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (8 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 09/21] target/arm: Access halt " Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 19:02 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 11/21] target/riscv: Add riscv_cpu_get_mip() Philippe Mathieu-Daudé
` (10 subsequent siblings)
20 siblings, 1 reply; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu
Return early when the effective mask is zero. This avoids taking
the BQL and processing interrupts when riscv_cpu_update_mip()
cannot change env->mip.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/riscv/tcg/cpu_helper.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/target/riscv/tcg/cpu_helper.c b/target/riscv/tcg/cpu_helper.c
index 07d92226527..be64f9ee8f2 100644
--- a/target/riscv/tcg/cpu_helper.c
+++ b/target/riscv/tcg/cpu_helper.c
@@ -580,6 +580,9 @@ uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value)
/* No need to update mip for VSTIP */
mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
+ if (!mask) {
+ return old;
+ }
BQL_LOCK_GUARD();
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 11/21] target/riscv: Add riscv_cpu_get_mip()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (9 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 10/21] target/riscv: Skip empty MIP updates Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 19:04 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 12/21] target/riscv: Access MIP atomically Philippe Mathieu-Daudé
` (9 subsequent siblings)
20 siblings, 1 reply; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu
Introduce riscv_cpu_get_mip() and use it for MIP reads in interrupt,
CSR, and ACLINT code. Centralize reads of env->mip before changing
the accessor implementation.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/riscv/cpu.h | 1 +
hw/intc/riscv_aclint.c | 2 +-
target/riscv/cpu.c | 3 ++-
target/riscv/tcg/cpu_helper.c | 17 ++++++++++++-----
target/riscv/tcg/csr.c | 6 ++++--
5 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 8c8ffe94cd8..be8c937bf5d 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -684,6 +684,7 @@ bool riscv_cpu_translate_for_debug(CPUState *cs, vaddr addr,
bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env);
int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts);
+uint64_t riscv_cpu_get_mip(const CPURISCVState *env);
uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask,
uint64_t value);
void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level);
diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c
index 2d133d0dcf9..4bd359c3380 100644
--- a/hw/intc/riscv_aclint.c
+++ b/hw/intc/riscv_aclint.c
@@ -438,7 +438,7 @@ static uint64_t riscv_aclint_swi_read(void *opaque, hwaddr addr,
qemu_log_mask(LOG_GUEST_ERROR,
"aclint-swi: invalid hartid: %zu", hartid);
} else if ((addr & 0x3) == 0) {
- return (swi->sswi) ? 0 : ((env->mip & MIP_MSIP) > 0);
+ return (swi->sswi) ? 0 : ((riscv_cpu_get_mip(env) & MIP_MSIP) > 0);
}
}
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index da4c1a090b9..62ac4507ed5 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -916,8 +916,9 @@ uint64_t riscv_cpu_all_pending(CPURISCVState *env)
uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN);
uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
uint64_t vstip = (env->vstime_irq) ? MIP_VSTIP : 0;
+ uint64_t mip = riscv_cpu_get_mip(env);
- return (env->mip | vsgein | vstip) & env->mie;
+ return (mip | vsgein | vstip) & env->mie;
}
int riscv_cpu_mirq_pending(CPURISCVState *env)
diff --git a/target/riscv/tcg/cpu_helper.c b/target/riscv/tcg/cpu_helper.c
index be64f9ee8f2..e524798d312 100644
--- a/target/riscv/tcg/cpu_helper.c
+++ b/target/riscv/tcg/cpu_helper.c
@@ -526,6 +526,11 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
}
}
+uint64_t riscv_cpu_get_mip(const CPURISCVState *env)
+{
+ return env->mip;
+}
+
void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level)
{
CPURISCVState *env = &cpu->env;
@@ -554,6 +559,7 @@ void riscv_cpu_interrupt(CPURISCVState *env)
{
uint64_t gein, vsgein = 0, vstip = 0, irqf = 0;
CPUState *cs = env_cpu(env);
+ uint64_t mip = riscv_cpu_get_mip(env);
BQL_LOCK_GUARD();
@@ -567,7 +573,7 @@ void riscv_cpu_interrupt(CPURISCVState *env)
vstip = env->vstime_irq ? MIP_VSTIP : 0;
- if (env->mip | vsgein | vstip | irqf) {
+ if (mip | vsgein | vstip | irqf) {
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
@@ -576,7 +582,7 @@ void riscv_cpu_interrupt(CPURISCVState *env)
uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value)
{
- uint64_t old = env->mip;
+ uint64_t old = riscv_cpu_get_mip(env);
/* No need to update mip for VSTIP */
mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
@@ -586,7 +592,7 @@ uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value)
BQL_LOCK_GUARD();
- env->mip = (env->mip & ~mask) | (value & mask);
+ env->mip = (old & ~mask) | (value & mask);
riscv_cpu_interrupt(env);
@@ -2064,10 +2070,11 @@ void riscv_cpu_do_interrupt(CPUState *cs)
bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG);
target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK;
uint64_t deleg = async ? env->mideleg : env->medeleg;
+ uint64_t mip = riscv_cpu_get_mip(env);
bool s_injected = env->mvip & (1ULL << cause) & env->mvien &&
- !(env->mip & (1ULL << cause));
+ !(mip & (1ULL << cause));
bool vs_injected = env->hvip & (1ULL << cause) & env->hvien &&
- !(env->mip & (1ULL << cause));
+ !(mip & (1ULL << cause));
bool smode_double_trap = false;
uint64_t hdeleg = async ? env->hideleg : env->hedeleg;
const bool prev_virt = env->virt_enabled;
diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c
index 061bc9db77b..a0e32f14276 100644
--- a/target/riscv/tcg/csr.c
+++ b/target/riscv/tcg/csr.c
@@ -3816,7 +3816,7 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno,
if (mask) {
old_mip = riscv_cpu_update_mip(env, mask, (new_val & mask));
} else {
- old_mip = env->mip;
+ old_mip = riscv_cpu_get_mip(env);
}
if (csrno != CSR_HVIP) {
@@ -4549,8 +4549,10 @@ static RISCVException read_vstopi(CPURISCVState *env, int csrno,
hviprio = get_field(env->hvictl, HVICTL_IPRIO);
if (gein) {
+ uint64_t mip = riscv_cpu_get_mip(env);
+
vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0;
- vseip = env->mie & (env->mip | vsgein) & MIP_VSEIP;
+ vseip = env->mie & (mip | vsgein) & MIP_VSEIP;
if (gein <= env->geilen && vseip) {
siid[scount] = IRQ_S_EXT;
siprio[scount] = IPRIO_MMAXIPRIO + 1;
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 12/21] target/riscv: Access MIP atomically
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (10 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 11/21] target/riscv: Add riscv_cpu_get_mip() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 19:05 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 13/21] accel/tcg: Refactor poll-while-halted flow in cpu_exec() Philippe Mathieu-Daudé
` (8 subsequent siblings)
20 siblings, 1 reply; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu
Use qatomic_read() in riscv_cpu_get_mip() and qatomic_set()
when updating env->mip. This makes MIP accesses safe for
riscv_cpu_has_work(), which may run without the BQL.
Reported-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/riscv/tcg/cpu_helper.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/riscv/tcg/cpu_helper.c b/target/riscv/tcg/cpu_helper.c
index e524798d312..2081875a206 100644
--- a/target/riscv/tcg/cpu_helper.c
+++ b/target/riscv/tcg/cpu_helper.c
@@ -528,7 +528,7 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env)
uint64_t riscv_cpu_get_mip(const CPURISCVState *env)
{
- return env->mip;
+ return qatomic_read(&env->mip);
}
void riscv_cpu_set_rnmi(RISCVCPU *cpu, uint32_t irq, bool level)
@@ -592,7 +592,7 @@ uint64_t riscv_cpu_update_mip(CPURISCVState *env, uint64_t mask, uint64_t value)
BQL_LOCK_GUARD();
- env->mip = (old & ~mask) | (value & mask);
+ qatomic_set(&env->mip, (old & ~mask) | (value & mask));
riscv_cpu_interrupt(env);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 13/21] accel/tcg: Refactor poll-while-halted flow in cpu_exec()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (11 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 12/21] target/riscv: Access MIP atomically Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 14/21] accel/tcg: Introduce poll_during_halt() and leaving_halt() Philippe Mathieu-Daudé
` (7 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Invert cpu_handle_halt()'s logic to read in cpu_exec()
as "if halted, poll for async events and find work; if
no work, return". Rename as cpu_poll_while_halted().
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
accel/tcg/cpu-exec.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 46b723cb734..76182803592 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -654,18 +654,18 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
}
#ifndef CONFIG_USER_ONLY
-static inline bool cpu_handle_halt(CPUState *cpu)
+static bool cpu_poll_while_halted(CPUState *cpu)
{
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
bool leave_halt = tcg_ops->cpu_exec_halt(cpu);
if (!leave_halt) {
- return true;
+ return false;
}
cpu->halted = 0; /* allow execution */
- return false;
+ return true;
}
#endif /* !CONFIG_USER_ONLY */
@@ -1027,7 +1027,7 @@ int cpu_exec(CPUState *cpu)
#ifndef CONFIG_USER_ONLY
if (cpu->halted) {
- if (cpu_handle_halt(cpu)) {
+ if (!cpu_poll_while_halted(cpu)) {
return EXCP_HALTED;
}
assert(!cpu->halted);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 14/21] accel/tcg: Introduce poll_during_halt() and leaving_halt()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (12 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 13/21] accel/tcg: Refactor poll-while-halted flow in cpu_exec() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 15/21] target/arm: Split leaving-halt handling from arm_cpu_exec_halt() Philippe Mathieu-Daudé
` (6 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Split target-specific halt handling into an optional
poll_during_halt() callback before cpu_has_work() and a
leaving_halt() callback after work is found.
Keep cpu_exec_halt() as a legacy alternative for targets
that have not migrated. Require each target to provide either
cpu_exec_halt() or leaving_halt(), but not both.
Inspired-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
include/accel/tcg/cpu-ops.h | 24 +++++++++++++++++++++++-
accel/tcg/cpu-exec.c | 21 +++++++++++++++++----
2 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h
index 3ff6e6810e0..1ee5d3d8bdb 100644
--- a/include/accel/tcg/cpu-ops.h
+++ b/include/accel/tcg/cpu-ops.h
@@ -169,6 +169,27 @@ struct TCGCPUOps {
*/
vaddr (*untagged_addr)(CPUState *cs, vaddr addr);
#else
+ /**
+ * @poll_during_halt: Poll target-specific events while the CPU is halted.
+ * @cpu: vCPU context
+ *
+ * Called when the CPU is halted to handle target-specific asynchronous
+ * event processing before instruction execution begins.
+ * The caller does not hold the BQL.
+ */
+ void (*poll_during_halt)(CPUState *cpu);
+ /**
+ * @leaving_halt: Perform target-specific cleanup before resuming
+ * execution.
+ * @cpu: vCPU context
+ *
+ * Called after a halted CPU has detected pending work and is about to
+ * resume execution from the halted state. This callback performs any
+ * necessary target-specific state transitions or synchronization before
+ * instruction execution resumes. The caller does not hold the BQL.
+ * Either this callback or @cpu_exec_halt must be provided (but not both).
+ */
+ void (*leaving_halt)(CPUState *cpu);
/** @do_interrupt: Callback for interrupt handling. */
void (*do_interrupt)(CPUState *cpu);
/** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */
@@ -185,7 +206,8 @@ struct TCGCPUOps {
* if it should remain in the halted state. (This should generally
* be the same value that cpu_has_work() would return.)
*
- * This method must be provided. If the target does not need to
+ * Either @leaving_halt or this method must be provided, but not both.
+ * If the target does not need to
* do anything special for halt, the same function used for its
* SysemuCPUOps::has_work method can be used here, as they have the
* same function signature.
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 76182803592..edb719b0d10 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -48,6 +48,7 @@
#include "internal-common.h"
#if !defined(CONFIG_USER_ONLY)
#include "accel/tcg/iommu.h"
+#include "hw/core/sysemu-cpu-ops.h"
#endif
/* -icount align implementation. */
@@ -657,10 +658,22 @@ static inline void tb_add_jump(TranslationBlock *tb, int n,
static bool cpu_poll_while_halted(CPUState *cpu)
{
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
- bool leave_halt = tcg_ops->cpu_exec_halt(cpu);
- if (!leave_halt) {
- return false;
+ if (tcg_ops->leaving_halt) {
+ assert(!tcg_ops->cpu_exec_halt);
+ if (tcg_ops->poll_during_halt) {
+ tcg_ops->poll_during_halt(cpu);
+ }
+ if (!cpu_has_work(cpu)) {
+ return false;
+ }
+ tcg_ops->leaving_halt(cpu);
+ } else {
+ assert(!tcg_ops->poll_during_halt);
+ assert(cpu->cc->sysemu_ops->has_work == tcg_ops->cpu_exec_halt);
+ if (!tcg_ops->cpu_exec_halt(cpu)) {
+ return false;
+ }
}
cpu->halted = 0; /* allow execution */
@@ -1059,7 +1072,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp)
/* Check mandatory TCGCPUOps handlers */
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
#ifndef CONFIG_USER_ONLY
- assert(tcg_ops->cpu_exec_halt);
+ assert(tcg_ops->cpu_exec_halt || tcg_ops->leaving_halt);
assert(tcg_ops->cpu_exec_interrupt);
assert(tcg_ops->cpu_exec_reset);
assert(tcg_ops->pointer_wrap);
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 15/21] target/arm: Split leaving-halt handling from arm_cpu_exec_halt()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (13 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 14/21] accel/tcg: Introduce poll_during_halt() and leaving_halt() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 16/21] target/arm: Convert cpu_exec_halt() to leaving_halt() Philippe Mathieu-Daudé
` (5 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Move WFxT timer cleanup and halt-reason reset into
arm_cpu_leaving_halt(). Keep arm_cpu_exec_halt()
focused on deciding whether the CPU can leave the
halted state.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/arm/cpu.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 090470da40a..0885a8eb03e 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -872,18 +872,24 @@ static bool arm_cpu_internal_is_big_endian(CPUState *cs)
}
#ifdef CONFIG_TCG
+static void arm_cpu_leaving_halt(CPUState *cs)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+
+ /* We're about to come out of WFI/WFE: disable the WFxT timer */
+ if (cpu->wfxt_timer) {
+ timer_del(cpu->wfxt_timer);
+ }
+ /* clear the halt reason */
+ qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
+}
+
bool arm_cpu_exec_halt(CPUState *cs)
{
bool leave_halt = cpu_has_work(cs);
if (leave_halt) {
- /* We're about to come out of WFI/WFE: disable the WFxT timer */
- ARMCPU *cpu = ARM_CPU(cs);
- if (cpu->wfxt_timer) {
- timer_del(cpu->wfxt_timer);
- }
- /* clear the halt reason */
- qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
+ arm_cpu_leaving_halt(cs);
}
return leave_halt;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 16/21] target/arm: Convert cpu_exec_halt() to leaving_halt()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (14 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 15/21] target/arm: Split leaving-halt handling from arm_cpu_exec_halt() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request Philippe Mathieu-Daudé
` (4 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
arm_cpu_exec_halt() now merely checks cpu_has_work() and
conditionally call arm_cpu_leaving_halt(), which is now
the generic flow in cpu_has_work_after_processing_async_events().
Register the leaving_halt handler to use the generic
target-agnostic flow and remove the arm_cpu_exec_halt() wrapper.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/arm/internals.h | 3 ---
target/arm/cpu.c | 12 +-----------
target/arm/tcg/cpu-v7m.c | 1 -
3 files changed, 1 insertion(+), 15 deletions(-)
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 1efda543270..cfe932d0e5d 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -431,9 +431,6 @@ void arm_restore_state_to_opc(CPUState *cs,
#ifdef CONFIG_TCG
TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs);
void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb);
-
-/* Our implementation of TCGCPUOps::cpu_exec_halt */
-bool arm_cpu_exec_halt(CPUState *cs);
int arm_cpu_mmu_index(CPUState *cs, bool ifetch);
#endif /* CONFIG_TCG */
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 0885a8eb03e..81f1f8b5052 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -883,16 +883,6 @@ static void arm_cpu_leaving_halt(CPUState *cs)
/* clear the halt reason */
qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
}
-
-bool arm_cpu_exec_halt(CPUState *cs)
-{
- bool leave_halt = cpu_has_work(cs);
-
- if (leave_halt) {
- arm_cpu_leaving_halt(cs);
- }
- return leave_halt;
-}
#endif
/*
@@ -2617,7 +2607,7 @@ static const TCGCPUOps arm_tcg_ops = {
.tlb_fill_align = arm_cpu_tlb_fill_align,
.pointer_wrap = aprofile_pointer_wrap,
.cpu_exec_interrupt = arm_cpu_exec_interrupt,
- .cpu_exec_halt = arm_cpu_exec_halt,
+ .leaving_halt = arm_cpu_leaving_halt,
.cpu_exec_reset = cpu_reset,
.do_interrupt = arm_cpu_do_interrupt,
.do_transaction_failed = arm_cpu_do_transaction_failed,
diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c
index 48b31d5eac8..1093c6cbf63 100644
--- a/target/arm/tcg/cpu-v7m.c
+++ b/target/arm/tcg/cpu-v7m.c
@@ -302,7 +302,6 @@ static const TCGCPUOps arm_v7m_tcg_ops = {
.tlb_fill_align = arm_cpu_tlb_fill_align,
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt,
- .cpu_exec_halt = arm_cpu_exec_halt,
.cpu_exec_reset = cpu_reset,
.do_interrupt = arm_v7m_cpu_do_interrupt,
.do_transaction_failed = arm_cpu_do_transaction_failed,
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (15 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 16/21] target/arm: Convert cpu_exec_halt() to leaving_halt() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 19:18 ` Richard Henderson
2026-09-28 6:26 ` Zhao Liu
2026-09-23 17:11 ` [PATCH v3 18/21] target/i386: Split APIC polling from x86_cpu_exec_halt() Philippe Mathieu-Daudé
` (3 subsequent siblings)
20 siblings, 2 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Zhao Liu
x86_cpu_has_work() may run without the BQL, but currently reads
CPUState::interrupt_request directly. Other threads update this
field with qatomic_or() in cpu_set_interrupt(), so the direct
read races with an atomic access.
Load the interrupt request with qatomic_load_acquire(), matching
cpu_test_interrupt(). This provides the acquire ordering required
before x86_cpu_pending_interrupt() inspects the CPU state.
Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
target/i386/cpu.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index b97f144aea5..ee1aa3a5a70 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -10640,7 +10640,10 @@ int x86_cpu_pending_interrupt(const CPUState *cs, int interrupt_request)
static bool x86_cpu_has_work(CPUState *cs)
{
- return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0;
+ uint32_t pending_interrupts;
+
+ pending_interrupts = qatomic_load_acquire(&cs->interrupt_request);
+ return x86_cpu_pending_interrupt(cs, pending_interrupts) != 0;
}
#endif /* !CONFIG_USER_ONLY */
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 18/21] target/i386: Split APIC polling from x86_cpu_exec_halt()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (16 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 19/21] target/i386: Split leaving-halt handling " Philippe Mathieu-Daudé
` (2 subsequent siblings)
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Move APIC polling into x86_cpu_poll_during_halt() while
keeping the remaining halt handling in x86_cpu_exec_halt().
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/i386/tcg/helper-tcg.h | 1 +
target/i386/tcg/system/seg_helper.c | 11 +++++++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h
index f4b2ff740d5..58e8ca0bc1b 100644
--- a/target/i386/tcg/helper-tcg.h
+++ b/target/i386/tcg/helper-tcg.h
@@ -37,6 +37,7 @@
*/
void x86_cpu_do_interrupt(CPUState *cpu);
#ifndef CONFIG_USER_ONLY
+void x86_cpu_poll_during_halt(CPUState *cpu);
bool x86_cpu_exec_halt(CPUState *cpu);
bool x86_need_replay_interrupt(int interrupt_request);
bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c
index 8c7856be81e..9ce608a0bb7 100644
--- a/target/i386/tcg/system/seg_helper.c
+++ b/target/i386/tcg/system/seg_helper.c
@@ -128,10 +128,9 @@ void x86_cpu_do_interrupt(CPUState *cs)
}
}
-bool x86_cpu_exec_halt(CPUState *cpu)
+void x86_cpu_poll_during_halt(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
- CPUX86State *env = &x86_cpu->env;
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) {
bql_lock();
@@ -139,6 +138,14 @@ bool x86_cpu_exec_halt(CPUState *cpu)
cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL);
bql_unlock();
}
+}
+
+bool x86_cpu_exec_halt(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ x86_cpu_poll_during_halt(cpu);
if (!cpu_has_work(cpu)) {
return false;
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 19/21] target/i386: Split leaving-halt handling from x86_cpu_exec_halt()
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (17 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 18/21] target/i386: Split APIC polling from x86_cpu_exec_halt() Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 20/21] target/i386: Use generic halt transition handlers Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 21/21] accel/tcg: Remove the redundant cpu_exec_halt() hook Philippe Mathieu-Daudé
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Move debug exception handling performed when leaving HLT into
x86_cpu_leaving_halt(). Keep x86_cpu_exec_halt() responsible
for checking whether the CPU can resume.
Document that #DB IRQ delivery through do_interrupt_all() runs
in the vCPU thread and does not require BQL protection.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/i386/tcg/helper-tcg.h | 1 +
target/i386/tcg/system/seg_helper.c | 25 +++++++++++++++++--------
2 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h
index 58e8ca0bc1b..96accb30a80 100644
--- a/target/i386/tcg/helper-tcg.h
+++ b/target/i386/tcg/helper-tcg.h
@@ -39,6 +39,7 @@ void x86_cpu_do_interrupt(CPUState *cpu);
#ifndef CONFIG_USER_ONLY
void x86_cpu_poll_during_halt(CPUState *cpu);
bool x86_cpu_exec_halt(CPUState *cpu);
+void x86_cpu_leaving_halt(CPUState *cpu);
bool x86_need_replay_interrupt(int interrupt_request);
bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
#endif
diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c
index 9ce608a0bb7..566b3d4d80c 100644
--- a/target/i386/tcg/system/seg_helper.c
+++ b/target/i386/tcg/system/seg_helper.c
@@ -140,22 +140,31 @@ void x86_cpu_poll_during_halt(CPUState *cpu)
}
}
-bool x86_cpu_exec_halt(CPUState *cpu)
+void x86_cpu_leaving_halt(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
- CPUX86State *env = &x86_cpu->env;
-
- x86_cpu_poll_during_halt(cpu);
-
- if (!cpu_has_work(cpu)) {
- return false;
- }
+ CPUX86State *env = cpu_env(cpu);
/* Complete HLT instruction. */
if (env->eflags & TF_MASK) {
env->dr[6] |= DR6_BS;
+ /*
+ * do_interrupt_all() is the normal TCG exception-delivery path
+ * and runs in the same vCPU thread context, so #DB delivery does
+ * not require BQL protection here.
+ */
do_interrupt_all(x86_cpu, EXCP01_DB, 0, 0, env->eip, 0);
}
+}
+
+bool x86_cpu_exec_halt(CPUState *cpu)
+{
+ if (!cpu_has_work(cpu)) {
+ return false;
+ }
+
+ x86_cpu_leaving_halt(cpu);
+
return true;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 20/21] target/i386: Use generic halt transition handlers
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (18 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 19/21] target/i386: Split leaving-halt handling " Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 21/21] accel/tcg: Remove the redundant cpu_exec_halt() hook Philippe Mathieu-Daudé
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé
Register x86_cpu_poll_during_halt() and x86_cpu_leaving_halt()
with the generic TCG halt flow. Remove the target-specific
x86_cpu_exec_halt() wrapper.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
target/i386/tcg/helper-tcg.h | 1 -
target/i386/tcg/system/seg_helper.c | 11 -----------
target/i386/tcg/tcg-cpu.c | 3 ++-
3 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h
index 96accb30a80..4d543556a9f 100644
--- a/target/i386/tcg/helper-tcg.h
+++ b/target/i386/tcg/helper-tcg.h
@@ -38,7 +38,6 @@
void x86_cpu_do_interrupt(CPUState *cpu);
#ifndef CONFIG_USER_ONLY
void x86_cpu_poll_during_halt(CPUState *cpu);
-bool x86_cpu_exec_halt(CPUState *cpu);
void x86_cpu_leaving_halt(CPUState *cpu);
bool x86_need_replay_interrupt(int interrupt_request);
bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req);
diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c
index 566b3d4d80c..e24d01000ad 100644
--- a/target/i386/tcg/system/seg_helper.c
+++ b/target/i386/tcg/system/seg_helper.c
@@ -157,17 +157,6 @@ void x86_cpu_leaving_halt(CPUState *cpu)
}
}
-bool x86_cpu_exec_halt(CPUState *cpu)
-{
- if (!cpu_has_work(cpu)) {
- return false;
- }
-
- x86_cpu_leaving_halt(cpu);
-
- return true;
-}
-
bool x86_need_replay_interrupt(int interrupt_request)
{
/*
diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c
index 6f5dc06b3b9..2fde412394f 100644
--- a/target/i386/tcg/tcg-cpu.c
+++ b/target/i386/tcg/tcg-cpu.c
@@ -180,7 +180,8 @@ const TCGCPUOps x86_tcg_ops = {
.tlb_fill = x86_cpu_tlb_fill,
.pointer_wrap = x86_pointer_wrap,
.do_interrupt = x86_cpu_do_interrupt,
- .cpu_exec_halt = x86_cpu_exec_halt,
+ .poll_during_halt = x86_cpu_poll_during_halt,
+ .leaving_halt = x86_cpu_leaving_halt,
.cpu_exec_interrupt = x86_cpu_exec_interrupt,
.cpu_exec_reset = x86_cpu_exec_reset,
.do_unaligned_access = x86_cpu_do_unaligned_access,
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [PATCH v3 21/21] accel/tcg: Remove the redundant cpu_exec_halt() hook
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
` (19 preceding siblings ...)
2026-09-23 17:11 ` [PATCH v3 20/21] target/i386: Use generic halt transition handlers Philippe Mathieu-Daudé
@ 2026-09-23 17:11 ` Philippe Mathieu-Daudé
20 siblings, 0 replies; 29+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-23 17:11 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Philippe Mathieu-Daudé,
Michael Rolnik, Brian Cain, Pierrick Bouvier, Helge Deller,
Song Gao, Bibo Mao, Xianglai Li, Laurent Vivier,
Edgar E. Iglesias, Aurelien Jarno, Jiaxun Yang, Aleksandar Rikalo,
Stafford Horne, Chinmay Rath, Nicholas Piggin, Glenn Miles,
Harsh Prateek Bora, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu, Yoshinori Sato,
Cornelia Huck, Eric Farman, Matthew Rosato, Ilya Leoshkevich,
David Hildenbrand, Mark Cave-Ayland, Artyom Tarasenko,
Max Filippov
All remaining users of cpu_exec_halt simply alias cpu_has_work().
Call cpu_has_work() directly from the generic halt flow and remove
the redundant hook registrations and interface definition.
Make affected cpu_has_work() helpers private where possible.
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
---
include/accel/tcg/cpu-ops.h | 18 ------------------
target/loongarch/internals.h | 1 -
target/riscv/internals.h | 5 -----
target/s390x/s390x-internal.h | 1 -
accel/tcg/cpu-exec.c | 23 ++++++++---------------
target/alpha/cpu.c | 1 -
target/avr/cpu.c | 1 -
target/hexagon/cpu.c | 1 -
target/hppa/cpu.c | 1 -
target/loongarch/cpu.c | 2 +-
target/loongarch/tcg/tcg_cpu.c | 1 -
target/m68k/cpu.c | 1 -
target/microblaze/cpu.c | 1 -
target/mips/cpu.c | 1 -
target/or1k/cpu.c | 1 -
target/ppc/cpu_init.c | 1 -
target/riscv/cpu.c | 2 +-
target/riscv/tcg/tcg-cpu.c | 1 -
target/rx/cpu.c | 1 -
target/s390x/cpu-system.c | 2 +-
target/s390x/cpu.c | 1 -
target/sh4/cpu.c | 1 -
target/sparc/cpu.c | 1 -
target/tricore/cpu.c | 1 -
target/xtensa/cpu.c | 1 -
25 files changed, 11 insertions(+), 60 deletions(-)
diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h
index 1ee5d3d8bdb..4195a29e1e4 100644
--- a/include/accel/tcg/cpu-ops.h
+++ b/include/accel/tcg/cpu-ops.h
@@ -187,7 +187,6 @@ struct TCGCPUOps {
* resume execution from the halted state. This callback performs any
* necessary target-specific state transitions or synchronization before
* instruction execution resumes. The caller does not hold the BQL.
- * Either this callback or @cpu_exec_halt must be provided (but not both).
*/
void (*leaving_halt)(CPUState *cpu);
/** @do_interrupt: Callback for interrupt handling. */
@@ -196,23 +195,6 @@ struct TCGCPUOps {
bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request);
/** @cpu_exec_reset: Callback for reset in cpu_exec. */
void (*cpu_exec_reset)(CPUState *cpu);
- /**
- * @cpu_exec_halt: Callback for handling halt in cpu_exec.
- *
- * The target CPU should do any special processing here that it needs
- * to do when the CPU is in the halted state.
- *
- * Return true to indicate that the CPU should now leave halt, false
- * if it should remain in the halted state. (This should generally
- * be the same value that cpu_has_work() would return.)
- *
- * Either @leaving_halt or this method must be provided, but not both.
- * If the target does not need to
- * do anything special for halt, the same function used for its
- * SysemuCPUOps::has_work method can be used here, as they have the
- * same function signature.
- */
- bool (*cpu_exec_halt)(CPUState *cpu);
/**
* @tlb_fill_align: Handle a softmmu tlb miss
* @cpu: cpu context
diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h
index f9a0680fe0f..2e31442453e 100644
--- a/target/loongarch/internals.h
+++ b/target/loongarch/internals.h
@@ -38,7 +38,6 @@ void cpu_loongarch_timer_cb(void *opaque);
uint64_t cpu_loongarch_get_timer_counter(CPUTimerState *timer);
uint64_t cpu_loongarch_get_timer_ticks(CPUTimerState *timer);
void cpu_loongarch_set_timer_config(CPUTimerState *timer, uint64_t value);
-bool loongarch_cpu_has_work(CPUState *cs);
bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env);
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/riscv/internals.h b/target/riscv/internals.h
index 5d84e4de960..fb2d8b3e17a 100644
--- a/target/riscv/internals.h
+++ b/target/riscv/internals.h
@@ -201,11 +201,6 @@ static inline target_ulong get_xepc_mask(CPURISCVState *env)
}
}
-#ifndef CONFIG_USER_ONLY
-/* Our implementation of SysemuCPUOps::has_work */
-bool riscv_cpu_has_work(CPUState *cs);
-#endif
-
/* Zjpm addr masking routine */
static inline target_ulong adjust_addr_body(CPURISCVState *env,
target_ulong addr,
diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h
index da60b2b1fd4..df1297b5c67 100644
--- a/target/s390x/s390x-internal.h
+++ b/target/s390x/s390x-internal.h
@@ -248,7 +248,6 @@ bool s390_cpu_system_realize(DeviceState *dev, Error **errp);
void s390_cpu_finalize(Object *obj);
void s390_cpu_system_class_init(CPUClass *cc);
void s390_cpu_machine_reset_cb(void *opaque);
-bool s390_cpu_has_work(CPUState *cs);
#endif /* CONFIG_USER_ONLY */
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index edb719b0d10..30a9a8aed42 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -48,7 +48,6 @@
#include "internal-common.h"
#if !defined(CONFIG_USER_ONLY)
#include "accel/tcg/iommu.h"
-#include "hw/core/sysemu-cpu-ops.h"
#endif
/* -icount align implementation. */
@@ -659,21 +658,16 @@ static bool cpu_poll_while_halted(CPUState *cpu)
{
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
+ if (tcg_ops->poll_during_halt) {
+ tcg_ops->poll_during_halt(cpu);
+ }
+
+ if (!cpu_has_work(cpu)) {
+ return false;
+ }
+
if (tcg_ops->leaving_halt) {
- assert(!tcg_ops->cpu_exec_halt);
- if (tcg_ops->poll_during_halt) {
- tcg_ops->poll_during_halt(cpu);
- }
- if (!cpu_has_work(cpu)) {
- return false;
- }
tcg_ops->leaving_halt(cpu);
- } else {
- assert(!tcg_ops->poll_during_halt);
- assert(cpu->cc->sysemu_ops->has_work == tcg_ops->cpu_exec_halt);
- if (!tcg_ops->cpu_exec_halt(cpu)) {
- return false;
- }
}
cpu->halted = 0; /* allow execution */
@@ -1072,7 +1066,6 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp)
/* Check mandatory TCGCPUOps handlers */
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
#ifndef CONFIG_USER_ONLY
- assert(tcg_ops->cpu_exec_halt || tcg_ops->leaving_halt);
assert(tcg_ops->cpu_exec_interrupt);
assert(tcg_ops->cpu_exec_reset);
assert(tcg_ops->pointer_wrap);
diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c
index 12e86021663..7d1d036e1d3 100644
--- a/target/alpha/cpu.c
+++ b/target/alpha/cpu.c
@@ -265,7 +265,6 @@ static const TCGCPUOps alpha_tcg_ops = {
.tlb_fill = alpha_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_notreached,
.cpu_exec_interrupt = alpha_cpu_exec_interrupt,
- .cpu_exec_halt = alpha_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = alpha_cpu_do_interrupt,
.do_transaction_failed = alpha_cpu_do_transaction_failed,
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
index f8409f32ab9..a01e6fa4751 100644
--- a/target/avr/cpu.c
+++ b/target/avr/cpu.c
@@ -246,7 +246,6 @@ static const TCGCPUOps avr_tcg_ops = {
.restore_state_to_opc = avr_restore_state_to_opc,
.mmu_index = avr_cpu_mmu_index,
.cpu_exec_interrupt = avr_cpu_exec_interrupt,
- .cpu_exec_halt = avr_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.tlb_fill = avr_cpu_tlb_fill,
.do_interrupt = avr_cpu_do_interrupt,
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index fad526ccfb5..e7defcca8a7 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -783,7 +783,6 @@ static const TCGCPUOps hexagon_tcg_ops = {
.cpu_exec_reset = cpu_reset,
.tlb_fill = hexagon_tlb_fill,
.do_unaligned_access = hexagon_cpu_do_unaligned_access,
- .cpu_exec_halt = hexagon_cpu_has_work,
.do_interrupt = hexagon_cpu_do_interrupt,
#endif /* !CONFIG_USER_ONLY */
};
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 07b49e51326..7b1309a7c38 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -270,7 +270,6 @@ static const TCGCPUOps hppa_tcg_ops = {
.tlb_fill_align = hppa_cpu_tlb_fill_align,
.pointer_wrap = cpu_pointer_wrap_notreached,
.cpu_exec_interrupt = hppa_cpu_exec_interrupt,
- .cpu_exec_halt = hppa_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = hppa_cpu_do_interrupt,
.do_unaligned_access = hppa_cpu_do_unaligned_access,
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index f22f64efb80..6b765797393 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -123,7 +123,7 @@ bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
#endif
#ifndef CONFIG_USER_ONLY
-bool loongarch_cpu_has_work(CPUState *cs)
+static bool loongarch_cpu_has_work(CPUState *cs)
{
bool has_work = false;
diff --git a/target/loongarch/tcg/tcg_cpu.c b/target/loongarch/tcg/tcg_cpu.c
index 4b1d44a1644..7ea2a1eb933 100644
--- a/target/loongarch/tcg/tcg_cpu.c
+++ b/target/loongarch/tcg/tcg_cpu.c
@@ -327,7 +327,6 @@ const TCGCPUOps loongarch_tcg_ops = {
.tlb_fill = loongarch_cpu_tlb_fill,
.pointer_wrap = loongarch_pointer_wrap,
.cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
- .cpu_exec_halt = loongarch_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = loongarch_cpu_do_interrupt,
.do_transaction_failed = loongarch_cpu_do_transaction_failed,
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index 9b52ad5fc23..c7771cbf643 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -710,7 +710,6 @@ static const TCGCPUOps m68k_tcg_ops = {
.tlb_fill = m68k_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = m68k_cpu_exec_interrupt,
- .cpu_exec_halt = m68k_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = m68k_cpu_do_interrupt,
.do_transaction_failed = m68k_cpu_transaction_failed,
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c
index 389a5124b12..6013a8def8e 100644
--- a/target/microblaze/cpu.c
+++ b/target/microblaze/cpu.c
@@ -448,7 +448,6 @@ static const TCGCPUOps mb_tcg_ops = {
.tlb_fill = mb_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = mb_cpu_exec_interrupt,
- .cpu_exec_halt = mb_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = mb_cpu_do_interrupt,
.do_transaction_failed = mb_cpu_transaction_failed,
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index 0fead20d651..c069aa1c0b7 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -716,7 +716,6 @@ static const TCGCPUOps mips_tcg_ops = {
.tlb_fill = mips_cpu_tlb_fill,
.pointer_wrap = mips_pointer_wrap,
.cpu_exec_interrupt = mips_cpu_exec_interrupt,
- .cpu_exec_halt = mips_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = mips_cpu_do_interrupt,
.do_transaction_failed = mips_cpu_do_transaction_failed,
diff --git a/target/or1k/cpu.c b/target/or1k/cpu.c
index 66c00c0930c..42d9351b6d7 100644
--- a/target/or1k/cpu.c
+++ b/target/or1k/cpu.c
@@ -266,7 +266,6 @@ static const TCGCPUOps openrisc_tcg_ops = {
.tlb_fill = openrisc_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = openrisc_cpu_exec_interrupt,
- .cpu_exec_halt = openrisc_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = openrisc_cpu_do_interrupt,
#endif /* !CONFIG_USER_ONLY */
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 6c626843c93..ef59c56f416 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -7496,7 +7496,6 @@ static const TCGCPUOps ppc_tcg_ops = {
.tlb_fill = ppc_cpu_tlb_fill,
.pointer_wrap = ppc_pointer_wrap,
.cpu_exec_interrupt = ppc_cpu_exec_interrupt,
- .cpu_exec_halt = ppc_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = ppc_cpu_do_interrupt,
.cpu_exec_enter = ppc_cpu_exec_enter,
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 62ac4507ed5..c0ce7eceb2d 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -954,7 +954,7 @@ int riscv_cpu_vsirq_pending(CPURISCVState *env)
(irqs | irqs_f_vs), env->hviprio);
}
-bool riscv_cpu_has_work(CPUState *cs)
+static bool riscv_cpu_has_work(CPUState *cs)
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index b68160af830..260ac6d89e7 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -286,7 +286,6 @@ const TCGCPUOps riscv_tcg_ops = {
.tlb_fill = riscv_cpu_tlb_fill,
.pointer_wrap = riscv_pointer_wrap,
.cpu_exec_interrupt = riscv_cpu_exec_interrupt,
- .cpu_exec_halt = riscv_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = riscv_cpu_do_interrupt,
.do_transaction_failed = riscv_cpu_do_transaction_failed,
diff --git a/target/rx/cpu.c b/target/rx/cpu.c
index 9b8473d71cf..69946928995 100644
--- a/target/rx/cpu.c
+++ b/target/rx/cpu.c
@@ -227,7 +227,6 @@ static const TCGCPUOps rx_tcg_ops = {
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = rx_cpu_exec_interrupt,
- .cpu_exec_halt = rx_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = rx_cpu_do_interrupt,
};
diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c
index d38de4fea2b..82c89080a17 100644
--- a/target/s390x/cpu-system.c
+++ b/target/s390x/cpu-system.c
@@ -39,7 +39,7 @@
#include "system/tcg.h"
#include "hw/core/sysemu-cpu-ops.h"
-bool s390_cpu_has_work(CPUState *cs)
+static bool s390_cpu_has_work(CPUState *cs)
{
S390CPU *cpu = S390_CPU(cs);
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
index 85afc5dec9a..e7f81fabff6 100644
--- a/target/s390x/cpu.c
+++ b/target/s390x/cpu.c
@@ -384,7 +384,6 @@ static const TCGCPUOps s390_tcg_ops = {
.tlb_fill = s390_cpu_tlb_fill,
.pointer_wrap = s390_pointer_wrap,
.cpu_exec_interrupt = s390_cpu_exec_interrupt,
- .cpu_exec_halt = s390_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = s390_cpu_do_interrupt,
.debug_excp_handler = s390x_cpu_debug_excp_handler,
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
index 3bbdee301d5..55c73a1da35 100644
--- a/target/sh4/cpu.c
+++ b/target/sh4/cpu.c
@@ -312,7 +312,6 @@ static const TCGCPUOps superh_tcg_ops = {
.tlb_fill = superh_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_notreached,
.cpu_exec_interrupt = superh_cpu_exec_interrupt,
- .cpu_exec_halt = superh_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = superh_cpu_do_interrupt,
.do_unaligned_access = superh_cpu_do_unaligned_access,
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index ae9bdca9df8..1529f9570da 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -1074,7 +1074,6 @@ static const TCGCPUOps sparc_tcg_ops = {
.tlb_fill = sparc_cpu_tlb_fill,
.pointer_wrap = sparc_pointer_wrap,
.cpu_exec_interrupt = sparc_cpu_exec_interrupt,
- .cpu_exec_halt = sparc_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = sparc_cpu_do_interrupt,
.do_transaction_failed = sparc_cpu_do_transaction_failed,
diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c
index 84a05e3d363..82b56c3cd56 100644
--- a/target/tricore/cpu.c
+++ b/target/tricore/cpu.c
@@ -223,7 +223,6 @@ static const TCGCPUOps tricore_tcg_ops = {
.tlb_fill = tricore_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = tricore_cpu_exec_interrupt,
- .cpu_exec_halt = tricore_cpu_has_work,
.cpu_exec_reset = cpu_reset,
};
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
index 7c25b9ab707..99c8bd4b5fb 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -325,7 +325,6 @@ static const TCGCPUOps xtensa_tcg_ops = {
.tlb_fill = xtensa_cpu_tlb_fill,
.pointer_wrap = cpu_pointer_wrap_uint32,
.cpu_exec_interrupt = xtensa_cpu_exec_interrupt,
- .cpu_exec_halt = xtensa_cpu_has_work,
.cpu_exec_reset = cpu_reset,
.do_interrupt = xtensa_cpu_do_interrupt,
.do_transaction_failed = xtensa_cpu_do_transaction_failed,
--
2.53.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [PATCH v3 09/21] target/arm: Access halt state atomically
2026-09-23 17:11 ` [PATCH v3 09/21] target/arm: Access halt " Philippe Mathieu-Daudé
@ 2026-09-23 17:29 ` Richard Henderson
0 siblings, 0 replies; 29+ messages in thread
From: Richard Henderson @ 2026-09-23 17:29 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Peter Maydell
On 9/23/26 10:11, Philippe Mathieu-Daudé wrote:
> arm_cpu_has_work() runs without the BQL held and can inspect
> halt_reason and event_register while other CPU contexts update
> them. The WFxT timer may also consume HALT_WFE asynchronously.
>
> Use atomic accesses for the halt state, including the WFI/WFE
> and halt-exit stores. This keeps the halt/wakeup protocol
> race-free and matches the atomic state transitions used by the
> asynchronous wake-up paths.
>
> Signed-off-by: Philippe Mathieu-Daudé<philmd@oss.qualcomm.com>
> ---
> v3: Dropped rth's R-b because of HELPER()s call also updated
> ---
> target/arm/cpu.c | 7 ++++---
> target/arm/tcg/op_helper.c | 8 ++++----
> 2 files changed, 8 insertions(+), 7 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v3 04/21] target/s390x: Use S390CpuState for CPU state APIs
2026-09-23 17:10 ` [PATCH v3 04/21] target/s390x: Use S390CpuState for CPU state APIs Philippe Mathieu-Daudé
@ 2026-09-23 17:32 ` Eric Farman
0 siblings, 0 replies; 29+ messages in thread
From: Eric Farman @ 2026-09-23 17:32 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Richard Henderson, Peter Maydell, Halil Pasic,
Christian Borntraeger, David Hildenbrand, Jason Herne,
Cornelia Huck, Matthew Rosato, Ilya Leoshkevich
On 9/23/26 1:10 PM, Philippe Mathieu-Daudé wrote:
> Use the QAPI S390CpuState enum for CPU state accessors instead
> of uint8_t. Handle state checks with switch statements.
>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> ---
> target/s390x/cpu.h | 6 +++---
> target/s390x/kvm/kvm_s390x.h | 2 +-
> hw/intc/s390_flic.c | 7 +++++--
> target/s390x/cpu-system.c | 18 ++++++++++++------
> target/s390x/kvm/kvm.c | 2 +-
> target/s390x/kvm/stubs.c | 2 +-
> target/s390x/sigp.c | 6 +++++-
> 7 files changed, 28 insertions(+), 15 deletions(-)
Reviewed-by: Eric Farman <farman@linux.ibm.com>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v3 10/21] target/riscv: Skip empty MIP updates
2026-09-23 17:11 ` [PATCH v3 10/21] target/riscv: Skip empty MIP updates Philippe Mathieu-Daudé
@ 2026-09-23 19:02 ` Richard Henderson
0 siblings, 0 replies; 29+ messages in thread
From: Richard Henderson @ 2026-09-23 19:02 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Peter Maydell, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu
On 9/23/26 10:11, Philippe Mathieu-Daudé wrote:
> Return early when the effective mask is zero. This avoids taking
> the BQL and processing interrupts when riscv_cpu_update_mip()
> cannot change env->mip.
>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
This breaks the usage in riscv_cpu_set_mode. One could argue that
should be calling riscv_cpu_interrupt directly, but you'd want to
examine locking as well.
> /* No need to update mip for VSTIP */
> mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
> + if (!mask) {
> + return old;
> + }
If this is kept in some way, this should be written as
if (!mask || (mask == MIP_VSTIP && env->vstime_irq)) {
return env->mip;
}
Note that there is an existing bug in which the returned 'old' is
fetched before the bql lock is taken. Note as well that the existing
mip update statement doesn't use 'old', but could if it were fetched at
the right time.
r~
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v3 11/21] target/riscv: Add riscv_cpu_get_mip()
2026-09-23 17:11 ` [PATCH v3 11/21] target/riscv: Add riscv_cpu_get_mip() Philippe Mathieu-Daudé
@ 2026-09-23 19:04 ` Richard Henderson
0 siblings, 0 replies; 29+ messages in thread
From: Richard Henderson @ 2026-09-23 19:04 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Peter Maydell, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu
On 9/23/26 10:11, Philippe Mathieu-Daudé wrote:
> @@ -554,6 +559,7 @@ void riscv_cpu_interrupt(CPURISCVState *env)
> {
> uint64_t gein, vsgein = 0, vstip = 0, irqf = 0;
> CPUState *cs = env_cpu(env);
> + uint64_t mip = riscv_cpu_get_mip(env);
>
> BQL_LOCK_GUARD();
>
You don't want to move any reads outside the lock.
r~
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v3 12/21] target/riscv: Access MIP atomically
2026-09-23 17:11 ` [PATCH v3 12/21] target/riscv: Access MIP atomically Philippe Mathieu-Daudé
@ 2026-09-23 19:05 ` Richard Henderson
0 siblings, 0 replies; 29+ messages in thread
From: Richard Henderson @ 2026-09-23 19:05 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Peter Maydell, Palmer Dabbelt, Alistair Francis, Weiwei Li,
Daniel Henrique Barboza, Liu Zhiwei, Chao Liu
On 9/23/26 10:11, Philippe Mathieu-Daudé wrote:
> Use qatomic_read() in riscv_cpu_get_mip() and qatomic_set()
> when updating env->mip. This makes MIP accesses safe for
> riscv_cpu_has_work(), which may run without the BQL.
>
> Reported-by: Paolo Bonzini<pbonzini@redhat.com>
> Signed-off-by: Philippe Mathieu-Daudé<philmd@oss.qualcomm.com>
> ---
> target/riscv/tcg/cpu_helper.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request
2026-09-23 17:11 ` [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request Philippe Mathieu-Daudé
@ 2026-09-23 19:18 ` Richard Henderson
2026-09-28 6:26 ` Zhao Liu
1 sibling, 0 replies; 29+ messages in thread
From: Richard Henderson @ 2026-09-23 19:18 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, qemu-devel
Cc: qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini, qemu-riscv,
Peter Maydell, Zhao Liu
On 9/23/26 10:11, Philippe Mathieu-Daudé wrote:
> x86_cpu_has_work() may run without the BQL, but currently reads
> CPUState::interrupt_request directly. Other threads update this
> field with qatomic_or() in cpu_set_interrupt(), so the direct
> read races with an atomic access.
>
> Load the interrupt request with qatomic_load_acquire(), matching
> cpu_test_interrupt(). This provides the acquire ordering required
> before x86_cpu_pending_interrupt() inspects the CPU state.
>
> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> ---
> target/i386/cpu.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/target/i386/cpu.c b/target/i386/cpu.c
> index b97f144aea5..ee1aa3a5a70 100644
> --- a/target/i386/cpu.c
> +++ b/target/i386/cpu.c
> @@ -10640,7 +10640,10 @@ int x86_cpu_pending_interrupt(const CPUState *cs, int interrupt_request)
>
> static bool x86_cpu_has_work(CPUState *cs)
> {
> - return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0;
> + uint32_t pending_interrupts;
> +
> + pending_interrupts = qatomic_load_acquire(&cs->interrupt_request);
> + return x86_cpu_pending_interrupt(cs, pending_interrupts) != 0;
> }
> #endif /* !CONFIG_USER_ONLY */
>
For the change overall,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Because of -ftrivial-auto-var-init=zero, I am now in general against
uninitialized local variables, if at all possible, everywhere.
I know you've added the local variable due to wrapping, and split the
initialization for the same reason. In this case, perhaps best to just
use a shorter variable name:
uint32_t ir = qatomic_load_acquire(&cs->interrupt_request);
return x86_cpu_pending_interrupt(cs, ir) != 0;
but in others, just wrap the initialization:
uint32_t pending_interrupts
= qatomic_load_acquire(&cs->interrupt_request);
r~
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request
2026-09-23 17:11 ` [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request Philippe Mathieu-Daudé
2026-09-23 19:18 ` Richard Henderson
@ 2026-09-28 6:26 ` Zhao Liu
1 sibling, 0 replies; 29+ messages in thread
From: Zhao Liu @ 2026-09-28 6:26 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: qemu-devel, qemu-s390x, qemu-ppc, qemu-arm, Paolo Bonzini,
qemu-riscv, Richard Henderson, Peter Maydell
On Wed, Sep 23, 2026 at 07:11:11PM +0200, Philippe Mathieu-Daudé wrote:
> Date: Wed, 23 Sep 2026 19:11:11 +0200
> From: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> Subject: [PATCH v3 17/21] target/i386: Use an acquire load for
> interrupt_request
> X-Mailer: git-send-email 2.53.0
>
> x86_cpu_has_work() may run without the BQL, but currently reads
> CPUState::interrupt_request directly. Other threads update this
> field with qatomic_or() in cpu_set_interrupt(), so the direct
> read races with an atomic access.
>
> Load the interrupt request with qatomic_load_acquire(), matching
> cpu_test_interrupt(). This provides the acquire ordering required
> before x86_cpu_pending_interrupt() inspects the CPU state.
>
> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
> ---
> target/i386/cpu.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
(and I agree with Richard's comment :-))
Regards,
Zhao
^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2026-09-28 6:28 UTC | newest]
Thread overview: 29+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-23 17:10 [PATCH v3 00/21] accel/tcg: Make halt-to-exec transition explicit and remove cpu_exec_halt Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 01/21] system: Document has_work() synchronization requirements Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 02/21] target/s390x: Use s390_cpu_get_state() consistently Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 03/21] target/s390x: Make s390_cpu_set_state() return void Philippe Mathieu-Daudé
2026-09-23 17:10 ` [PATCH v3 04/21] target/s390x: Use S390CpuState for CPU state APIs Philippe Mathieu-Daudé
2026-09-23 17:32 ` Eric Farman
2026-09-23 17:10 ` [PATCH v3 05/21] target/s390x: Access S390CpuState atomically Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 06/21] target/arm: Un-inline arm_set_cpu_power_state() Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 07/21] target/arm: Kick vCPU after updating PSCI power state Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 08/21] target/arm: Access PSCI state atomically Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 09/21] target/arm: Access halt " Philippe Mathieu-Daudé
2026-09-23 17:29 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 10/21] target/riscv: Skip empty MIP updates Philippe Mathieu-Daudé
2026-09-23 19:02 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 11/21] target/riscv: Add riscv_cpu_get_mip() Philippe Mathieu-Daudé
2026-09-23 19:04 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 12/21] target/riscv: Access MIP atomically Philippe Mathieu-Daudé
2026-09-23 19:05 ` Richard Henderson
2026-09-23 17:11 ` [PATCH v3 13/21] accel/tcg: Refactor poll-while-halted flow in cpu_exec() Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 14/21] accel/tcg: Introduce poll_during_halt() and leaving_halt() Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 15/21] target/arm: Split leaving-halt handling from arm_cpu_exec_halt() Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 16/21] target/arm: Convert cpu_exec_halt() to leaving_halt() Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 17/21] target/i386: Use an acquire load for interrupt_request Philippe Mathieu-Daudé
2026-09-23 19:18 ` Richard Henderson
2026-09-28 6:26 ` Zhao Liu
2026-09-23 17:11 ` [PATCH v3 18/21] target/i386: Split APIC polling from x86_cpu_exec_halt() Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 19/21] target/i386: Split leaving-halt handling " Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 20/21] target/i386: Use generic halt transition handlers Philippe Mathieu-Daudé
2026-09-23 17:11 ` [PATCH v3 21/21] accel/tcg: Remove the redundant cpu_exec_halt() hook Philippe Mathieu-Daudé
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.