@@ -169,6 +169,20 @@ struct TCGCPUOps {
*/
vaddr (*untagged_addr)(CPUState *cs, vaddr addr);
#else
+ /**
+ * @do_interrupt: Deliver a pending exception/interrupt to the CPU
+ *
+ * Called when cs->exception_index contains the exception code to deliver.
+ * Updates CPU architectural state (usually before executing a guest
+ * exception handler).
+ *
+ * Called from cpu_handle_exception(). Implementations must acquire the
+ * BQL before modifying CPU state and hold it for the duration of the
+ * handler (this ensures safe access to shared CPU and device state during
+ * exception delivery, which may be cross-vCPU).
+ */
+ void (*do_interrupt)(CPUState *cpu);
+
/**
* @do_interrupt_locked: Deliver a pending exception/interrupt to the CPU
* @cpu: cpu context
@@ -727,9 +727,13 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
if (replay_exception()) {
const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops;
- bql_lock();
- tcg_ops->do_interrupt_locked(cpu);
- bql_unlock();
+ if (tcg_ops->do_interrupt) {
+ tcg_ops->do_interrupt(cpu);
+ } else {
+ bql_lock();
+ tcg_ops->do_interrupt_locked(cpu);
+ bql_unlock();
+ }
cpu->exception_index = -1;
if (unlikely(cpu_single_stepping(cpu))) {
@@ -1059,7 +1063,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp)
assert(tcg_ops->cpu_exec_halt);
assert(tcg_ops->cpu_exec_interrupt);
assert(tcg_ops->cpu_exec_reset);
- assert(tcg_ops->do_interrupt_locked);
+ assert(tcg_ops->do_interrupt || tcg_ops->do_interrupt_locked);
assert(tcg_ops->pointer_wrap);
#endif /* !CONFIG_USER_ONLY */
assert(tcg_ops->translate_code);
Add TCGCPUOps::do_interrupt() as a wrapper that targets can implement to control BQL locking independently. The existing do_interrupt_locked() callback remains for BQL-held contexts. This allows each target to decide when to acquire the BQL. Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> --- include/accel/tcg/cpu-ops.h | 14 ++++++++++++++ accel/tcg/cpu-exec.c | 12 ++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-)