* [RFC PATCH] target/arm: hold bql when SEV sets the event_register
@ 2026-10-12 13:48 Alex Bennée
2026-10-12 14:52 ` Alex Bennée
2026-10-13 11:41 ` Peter Maydell
0 siblings, 2 replies; 6+ messages in thread
From: Alex Bennée @ 2026-10-12 13:48 UTC (permalink / raw)
To: qemu-devel
Cc: Alex Bennée, Paolo Bonzini, Richard Henderson,
Philippe Mathieu-Daudé, Peter Maydell,
open list:ARM TCG CPUs
There is a race in qemu_process_cpu_events and sleeping on halt_cond
where we can miss the setting of the event_register which would cause
arm_cpu_has_work to report we shouldn't sleep. As by definition the
bql has to be held when we go to sleep against halt_cond guard the
kicking of the cpus by the SEV helper with the BQL to ensure things
are properly serialised.
The other places that set event_register are fine as they are either
in the same vCPU context or are already serialised by the BQL.
Also use qemu_cond_wait_bql like the other halt_cond users.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4660
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
system/cpus.c | 2 +-
target/arm/tcg/op_helper.c | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/system/cpus.c b/system/cpus.c
index e11a5aab6a6..985732217fb 100644
--- a/system/cpus.c
+++ b/system/cpus.c
@@ -412,7 +412,7 @@ void qemu_process_cpu_events(CPUState *cpu)
slept = true;
qemu_plugin_vcpu_idle_cb(cpu);
}
- qemu_cond_wait(cpu->halt_cond, &bql);
+ qemu_cond_wait_bql(cpu->halt_cond);
}
if (slept) {
qemu_plugin_vcpu_resume_cb(cpu);
diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
index af56eff9f18..7c1fdab79d7 100644
--- a/target/arm/tcg/op_helper.c
+++ b/target/arm/tcg/op_helper.c
@@ -471,6 +471,9 @@ void HELPER(wfit)(CPUARMState *env, uint32_t rd)
void HELPER(sev)(CPUARMState *env)
{
CPUState *cs = env_cpu(env);
+
+ BQL_LOCK_GUARD();
+
CPU_FOREACH(cs) {
ARMCPU *target_cpu = ARM_CPU(cs);
target_cpu->env.event_register = true;
--
2.47.3
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
2026-10-12 13:48 [RFC PATCH] target/arm: hold bql when SEV sets the event_register Alex Bennée
@ 2026-10-12 14:52 ` Alex Bennée
2026-10-12 16:27 ` Paolo Bonzini
2026-10-13 11:41 ` Peter Maydell
1 sibling, 1 reply; 6+ messages in thread
From: Alex Bennée @ 2026-10-12 14:52 UTC (permalink / raw)
To: qemu-devel
Cc: Paolo Bonzini, Richard Henderson, Philippe Mathieu-Daudé,
Peter Maydell, open list:ARM TCG CPUs
Alex Bennée <alex.bennee@linaro.org> writes:
> There is a race in qemu_process_cpu_events and sleeping on halt_cond
> where we can miss the setting of the event_register which would cause
> arm_cpu_has_work to report we shouldn't sleep. As by definition the
> bql has to be held when we go to sleep against halt_cond guard the
> kicking of the cpus by the SEV helper with the BQL to ensure things
> are properly serialised.
>
> The other places that set event_register are fine as they are either
> in the same vCPU context or are already serialised by the BQL.
>
> Also use qemu_cond_wait_bql like the other halt_cond users.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4660
This also fixes 4670
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
> system/cpus.c | 2 +-
> target/arm/tcg/op_helper.c | 3 +++
> 2 files changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/system/cpus.c b/system/cpus.c
> index e11a5aab6a6..985732217fb 100644
> --- a/system/cpus.c
> +++ b/system/cpus.c
> @@ -412,7 +412,7 @@ void qemu_process_cpu_events(CPUState *cpu)
> slept = true;
> qemu_plugin_vcpu_idle_cb(cpu);
> }
> - qemu_cond_wait(cpu->halt_cond, &bql);
> + qemu_cond_wait_bql(cpu->halt_cond);
> }
> if (slept) {
> qemu_plugin_vcpu_resume_cb(cpu);
> diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
> index af56eff9f18..7c1fdab79d7 100644
> --- a/target/arm/tcg/op_helper.c
> +++ b/target/arm/tcg/op_helper.c
> @@ -471,6 +471,9 @@ void HELPER(wfit)(CPUARMState *env, uint32_t rd)
> void HELPER(sev)(CPUARMState *env)
> {
> CPUState *cs = env_cpu(env);
> +
> + BQL_LOCK_GUARD();
> +
> CPU_FOREACH(cs) {
> ARMCPU *target_cpu = ARM_CPU(cs);
> target_cpu->env.event_register = true;
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
2026-10-12 14:52 ` Alex Bennée
@ 2026-10-12 16:27 ` Paolo Bonzini
2026-10-12 18:47 ` Alex Bennée
0 siblings, 1 reply; 6+ messages in thread
From: Paolo Bonzini @ 2026-10-12 16:27 UTC (permalink / raw)
To: Alex Bennée, qemu-devel
Cc: Richard Henderson, Philippe Mathieu-Daudé, Peter Maydell,
open list:ARM TCG CPUs
On 10/12/26 16:52, Alex Bennée wrote:
>> diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
>> index af56eff9f18..7c1fdab79d7 100644
>> --- a/target/arm/tcg/op_helper.c
>> +++ b/target/arm/tcg/op_helper.c
>> @@ -471,6 +471,9 @@ void HELPER(wfit)(CPUARMState *env, uint32_t rd)
>> void HELPER(sev)(CPUARMState *env)
>> {
>> CPUState *cs = env_cpu(env);
>> +
>> + BQL_LOCK_GUARD();
>> +
>> CPU_FOREACH(cs) {
>> ARMCPU *target_cpu = ARM_CPU(cs);
>> target_cpu->env.event_register = true;
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
The use of cmpxchg in wfxt_timer_cb is fishy though.
Clearing HALT_WFE should be the task of the target CPU, not the timer.
The timer_mod() should be *after* halt_reason(*), and then you can just
do a qatomic_read() instead of a cmpxchg(**) in wfxt_timer_cb().
However, I don't understand at all why wfxt_timer_cb needs to set
event_register to true. It seems to me that a wfet + wfe sequence,
where the first wfet times out, *should* halt again on the second wfe;
but as the code is written it doesn't.
I think wfxt_timer_cb only needs to call cpu_interrupt().
Thanks,
Paolo
(*) always, even if the halt reason is not HALT_WFE, if only for consistency
(**) you don't need a lock or acquire/release here, because the timer
callback always sees everything that happened before timer_mod(). In a
sense, timer_mod() *is* the release operation and starting the callback
is the acquire operation.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
2026-10-12 16:27 ` Paolo Bonzini
@ 2026-10-12 18:47 ` Alex Bennée
2026-10-13 5:13 ` Paolo Bonzini
0 siblings, 1 reply; 6+ messages in thread
From: Alex Bennée @ 2026-10-12 18:47 UTC (permalink / raw)
To: Paolo Bonzini
Cc: qemu-devel, Richard Henderson, Philippe Mathieu-Daudé,
Peter Maydell, open list:ARM TCG CPUs
Paolo Bonzini <pbonzini@redhat.com> writes:
> On 10/12/26 16:52, Alex Bennée wrote:
>>> diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c
>>> index af56eff9f18..7c1fdab79d7 100644
>>> --- a/target/arm/tcg/op_helper.c
>>> +++ b/target/arm/tcg/op_helper.c
>>> @@ -471,6 +471,9 @@ void HELPER(wfit)(CPUARMState *env, uint32_t rd)
>>> void HELPER(sev)(CPUARMState *env)
>>> {
>>> CPUState *cs = env_cpu(env);
>>> +
>>> + BQL_LOCK_GUARD();
>>> +
>>> CPU_FOREACH(cs) {
>>> ARMCPU *target_cpu = ARM_CPU(cs);
>>> target_cpu->env.event_register = true;
> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
>
> The use of cmpxchg in wfxt_timer_cb is fishy though.
>
> Clearing HALT_WFE should be the task of the target CPU, not the timer.
> The timer_mod() should be *after* halt_reason(*), and then you can
> just do a qatomic_read() instead of a cmpxchg(**) in wfxt_timer_cb().
>
> However, I don't understand at all why wfxt_timer_cb needs to set
> event_register to true. It seems to me that a wfet + wfe sequence,
> where the first wfet times out, *should* halt again on the second wfe;
> but as the code is written it doesn't.
>
> I think wfxt_timer_cb only needs to call cpu_interrupt().
Something like:
--8<---------------cut here---------------start------------->8---
modified target/arm/cpu.c
@@ -877,25 +877,22 @@ 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);
+
+ qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
+
if (cpu->wfxt_timer) {
timer_del(cpu->wfxt_timer);
}
- /* clear the halt reason */
- qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
}
return leave_halt;
}
#endif
/*
- * Unlike almost everything else that messes with the halt_reason and
- * event_register details the timer callbacks are not in the vCPU
- * context.
- *
- * To prevent races we atomically consume a HALT_WFE and set the event
- * register. Either way we trigger the an exit event.
+ * Unlike almost everything else that messes with the halt_reason the
+ * timer callbacks are not in the vCPU context so we must use atomics
+ * to check it.
*/
static void arm_wfxt_timer_cb(void *opaque)
{
@@ -903,17 +900,15 @@ static void arm_wfxt_timer_cb(void *opaque)
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
- if (qatomic_cmpxchg(&env->halt_reason, HALT_WFE, NOT_HALTED)) {
- qatomic_set(&env->event_register, true);
+ if (qatomic_read(&env->halt_reason) == HALT_WFE) {
+ /*
+ * We expect the CPU to be halted; this will cause arm_cpu_is_work()
+ * to return true (so we will come out of halt even with no other
+ * pending interrupt), and the TCG accelerator's cpu_exec_interrupt()
+ * function auto-clears the CPU_INTERRUPT_EXITTB flag for us.
+ */
+ cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
}
-
- /*
- * We expect the CPU to be halted; this will cause arm_cpu_is_work()
- * to return true (so we will come out of halt even with no other
- * pending interrupt), and the TCG accelerator's cpu_exec_interrupt()
- * function auto-clears the CPU_INTERRUPT_EXITTB flag for us.
- */
- cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
}
#endif
--8<---------------cut here---------------end--------------->8---
?
>
> Thanks,
>
> Paolo
>
> (*) always, even if the halt reason is not HALT_WFE, if only for consistency
>
> (**) you don't need a lock or acquire/release here, because the timer
> callback always sees everything that happened before timer_mod(). In
> a sense, timer_mod() *is* the release operation and starting the
> callback is the acquire operation.
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
2026-10-12 18:47 ` Alex Bennée
@ 2026-10-13 5:13 ` Paolo Bonzini
0 siblings, 0 replies; 6+ messages in thread
From: Paolo Bonzini @ 2026-10-13 5:13 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, Richard Henderson, Philippe Mathieu-Daudé,
Peter Maydell, open list:ARM TCG CPUs
[-- Attachment #1: Type: text/plain, Size: 3081 bytes --]
Il lun 12 ott 2026, 20:47 Alex Bennée <alex.bennee@linaro.org> ha scritto:
> Paolo Bonzini <pbonzini@redhat.com> writes:
>
>
> > I think wfxt_timer_cb only needs to call cpu_interrupt().
>
> Something like:
>
No, the cpu_interrupt must be unconditional due to the WFIT instruction.
Your patch puts it under qatomic_read(&env->halt_reason) == HALT_WFE.
>
> +
> + qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
> +
> if (cpu->wfxt_timer) {
> timer_del(cpu->wfxt_timer);
> }
> - /* clear the halt reason */
> - qatomic_set(&cpu->env.halt_reason, NOT_HALTED);
>
I was thinking about the ordering before timer_mod, in the
wfe/wfi/wfet/wfit helpers. However it is not relevant if the timer callback
does not check halt_reason.
Paolo
}
> return leave_halt;
> }
> #endif
>
> /*
> - * Unlike almost everything else that messes with the halt_reason and
> - * event_register details the timer callbacks are not in the vCPU
> - * context.
> - *
> - * To prevent races we atomically consume a HALT_WFE and set the event
> - * register. Either way we trigger the an exit event.
> + * Unlike almost everything else that messes with the halt_reason the
> + * timer callbacks are not in the vCPU context so we must use atomics
> + * to check it.
> */
> static void arm_wfxt_timer_cb(void *opaque)
> {
> @@ -903,17 +900,15 @@ static void arm_wfxt_timer_cb(void *opaque)
> CPUState *cs = CPU(cpu);
> CPUARMState *env = &cpu->env;
>
> - if (qatomic_cmpxchg(&env->halt_reason, HALT_WFE, NOT_HALTED)) {
> - qatomic_set(&env->event_register, true);
> + if (qatomic_read(&env->halt_reason) == HALT_WFE) {
> + /*
> + * We expect the CPU to be halted; this will cause
> arm_cpu_is_work()
> + * to return true (so we will come out of halt even with no other
> + * pending interrupt), and the TCG accelerator's
> cpu_exec_interrupt()
> + * function auto-clears the CPU_INTERRUPT_EXITTB flag for us.
> + */
> + cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
> }
> -
> - /*
> - * We expect the CPU to be halted; this will cause arm_cpu_is_work()
> - * to return true (so we will come out of halt even with no other
> - * pending interrupt), and the TCG accelerator's cpu_exec_interrupt()
> - * function auto-clears the CPU_INTERRUPT_EXITTB flag for us.
> - */
> - cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
> }
> #endif
> --8<---------------cut here---------------end--------------->8---
>
> ?
>
> >
> > Thanks,
> >
> > Paolo
> >
> > (*) always, even if the halt reason is not HALT_WFE, if only for
> consistency
> >
> > (**) you don't need a lock or acquire/release here, because the timer
> > callback always sees everything that happened before timer_mod(). In
> > a sense, timer_mod() *is* the release operation and starting the
> > callback is the acquire operation.
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
>
>
[-- Attachment #2: Type: text/html, Size: 4419 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [RFC PATCH] target/arm: hold bql when SEV sets the event_register
2026-10-12 13:48 [RFC PATCH] target/arm: hold bql when SEV sets the event_register Alex Bennée
2026-10-12 14:52 ` Alex Bennée
@ 2026-10-13 11:41 ` Peter Maydell
1 sibling, 0 replies; 6+ messages in thread
From: Peter Maydell @ 2026-10-13 11:41 UTC (permalink / raw)
To: Alex Bennée
Cc: qemu-devel, Paolo Bonzini, Richard Henderson,
Philippe Mathieu-Daudé, open list:ARM TCG CPUs
On Mon, 12 Oct 2026 at 14:48, Alex Bennée <alex.bennee@linaro.org> wrote:
>
> There is a race in qemu_process_cpu_events and sleeping on halt_cond
> where we can miss the setting of the event_register which would cause
> arm_cpu_has_work to report we shouldn't sleep. As by definition the
> bql has to be held when we go to sleep against halt_cond guard the
> kicking of the cpus by the SEV helper with the BQL to ensure things
> are properly serialised.
>
> The other places that set event_register are fine as they are either
> in the same vCPU context or are already serialised by the BQL.
>
> Also use qemu_cond_wait_bql like the other halt_cond users.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4660
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> ---
> system/cpus.c | 2 +-
> target/arm/tcg/op_helper.c | 3 +++
> 2 files changed, 4 insertions(+), 1 deletion(-)
This should be
Cc: qemu-stable@nongnu.org
Fixes: d238858bff6 ("target/arm: Implement WFE, SEV and SEVONPEND for Cortex-M")
right?
thanks
-- PMM
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-13 11:42 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 13:48 [RFC PATCH] target/arm: hold bql when SEV sets the event_register Alex Bennée
2026-10-12 14:52 ` Alex Bennée
2026-10-12 16:27 ` Paolo Bonzini
2026-10-12 18:47 ` Alex Bennée
2026-10-13 5:13 ` Paolo Bonzini
2026-10-13 11:41 ` Peter Maydell
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.