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 <[email protected]> Signed-off-by: Philippe Mathieu-Daudé <[email protected]> Reviewed-by: Richard Henderson <[email protected]> --- 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
