Message ID | 20231122121741.21087-1-m.tyutin@yadro.com |
---|---|
State | New |
Headers | show |
Series | accel/tcg: TCG Plugin callback on a vCpu interrupt | expand |
Mikhail Tyutin <m.tyutin@yadro.com> writes: > TCG Plugin callback to notify plugins when interrupt is triggered for > a vCpu. The plugin can optionally use this notification to see reason > of aborted instruction execution. > > Signed-off-by: Mikhail Tyutin <m.tyutin@yadro.com> > --- > accel/tcg/cpu-exec.c | 5 +++++ > include/qemu/plugin-event.h | 1 + > include/qemu/plugin.h | 4 ++++ > include/qemu/qemu-plugin.h | 12 +++++++++++- > plugins/core.c | 12 ++++++++++++ > plugins/qemu-plugins.symbols | 1 + > 6 files changed, 34 insertions(+), 1 deletion(-) > > diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c > index c938eb96f8..9110f7e290 100644 > --- a/accel/tcg/cpu-exec.c > +++ b/accel/tcg/cpu-exec.c The biggest problem I have with this approach is we are adding to an already overly complex exception/interrupt code in the main loop. > @@ -750,6 +750,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) > if (replay_exception()) { > CPUClass *cc = CPU_GET_CLASS(cpu); > qemu_mutex_lock_iothread(); > + qemu_plugin_vcpu_interrupt_cb(cpu); > cc->tcg_ops->do_interrupt(cpu); This is at least what we think of as a traditional IRQ. Something that interrupts the flow of the processor and after some target specific processing goes somewhere else. > qemu_mutex_unlock_iothread(); > cpu->exception_index = -1; > @@ -829,6 +830,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, > /* Do nothing */ > } else if (interrupt_request & CPU_INTERRUPT_HALT) { > replay_interrupt(); > + qemu_plugin_vcpu_interrupt_cb(cpu); > cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; > cpu->halted = 1; This isn't really an interrupt - its used by a selection of the various architectures to bring the vCPU to a halted state. However it's not the only way to halt the CPU as grepping for "->halted = 1;" will show you. If we successfully halt we'll eventually end up calling: qemu_plugin_vcpu_idle_cb() > cpu->exception_index = EXCP_HLT; > @@ -840,6 +842,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, > X86CPU *x86_cpu = X86_CPU(cpu); > CPUArchState *env = &x86_cpu->env; > replay_interrupt(); > + qemu_plugin_vcpu_interrupt_cb(cpu); > cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); > do_cpu_init(x86_cpu); > cpu->exception_index = EXCP_HALTED; This is some x86 specific hack that we've not managed to find a sensible way to excise out of the common code. > @@ -849,6 +852,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, > #else > else if (interrupt_request & CPU_INTERRUPT_RESET) { > replay_interrupt(); > + qemu_plugin_vcpu_interrupt_cb(cpu); > cpu_reset(cpu); This again is not an IRQ really but another (minority) method for generating a cold reset in the vCPU. There may be an argument to add a plugin hook in cpu_reset() but I suspect most of the calls to it would be artificial artefacts of the way to start and configure vCPUS as we bring objects up. > qemu_mutex_unlock_iothread(); > return true; > @@ -866,6 +870,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, > if (need_replay_interrupt(interrupt_request)) { > replay_interrupt(); > } > + qemu_plugin_vcpu_interrupt_cb(cpu); This may be an interrupt but we also may have restarted the loop via cpu_loop_exit() so missing the rest of the logic. Some of these handlers will also call cc->tcg_ops->do_interrupt(cpu) like above. <snip> > > +void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu) > +{ > + plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INTERRUPT); > +} > + > void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, > qemu_plugin_vcpu_simple_cb_t cb) > { > @@ -411,6 +417,12 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, > plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb); > } > > +void qemu_plugin_register_vcpu_interrupt_cb(qemu_plugin_id_t id, > + qemu_plugin_vcpu_simple_cb_t cb) > +{ > + plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INTERRUPT, cb); > +} > + > void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, > qemu_plugin_simple_cb_t cb) > { > diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols > index 71f6c90549..c8621f9950 100644 > --- a/plugins/qemu-plugins.symbols > +++ b/plugins/qemu-plugins.symbols > @@ -30,6 +30,7 @@ > qemu_plugin_register_vcpu_mem_cb; > qemu_plugin_register_vcpu_mem_inline; > qemu_plugin_register_vcpu_resume_cb; > + qemu_plugin_register_vcpu_interrupt_cb; > qemu_plugin_register_vcpu_syscall_cb; > qemu_plugin_register_vcpu_syscall_ret_cb; > qemu_plugin_register_vcpu_tb_exec_cb; When we add new callbacks we should at least use them in one of the plugins to check they work.
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c938eb96f8..9110f7e290 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -750,6 +750,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) if (replay_exception()) { CPUClass *cc = CPU_GET_CLASS(cpu); qemu_mutex_lock_iothread(); + qemu_plugin_vcpu_interrupt_cb(cpu); cc->tcg_ops->do_interrupt(cpu); qemu_mutex_unlock_iothread(); cpu->exception_index = -1; @@ -829,6 +830,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Do nothing */ } else if (interrupt_request & CPU_INTERRUPT_HALT) { replay_interrupt(); + qemu_plugin_vcpu_interrupt_cb(cpu); cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; cpu->exception_index = EXCP_HLT; @@ -840,6 +842,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, X86CPU *x86_cpu = X86_CPU(cpu); CPUArchState *env = &x86_cpu->env; replay_interrupt(); + qemu_plugin_vcpu_interrupt_cb(cpu); cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); do_cpu_init(x86_cpu); cpu->exception_index = EXCP_HALTED; @@ -849,6 +852,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, #else else if (interrupt_request & CPU_INTERRUPT_RESET) { replay_interrupt(); + qemu_plugin_vcpu_interrupt_cb(cpu); cpu_reset(cpu); qemu_mutex_unlock_iothread(); return true; @@ -866,6 +870,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, if (need_replay_interrupt(interrupt_request)) { replay_interrupt(); } + qemu_plugin_vcpu_interrupt_cb(cpu); /* * After processing the interrupt, ensure an EXCP_DEBUG is * raised when single-stepping so that GDB doesn't miss the diff --git a/include/qemu/plugin-event.h b/include/qemu/plugin-event.h index 7056d8427b..fe054c25dd 100644 --- a/include/qemu/plugin-event.h +++ b/include/qemu/plugin-event.h @@ -16,6 +16,7 @@ enum qemu_plugin_event { QEMU_PLUGIN_EV_VCPU_TB_TRANS, QEMU_PLUGIN_EV_VCPU_IDLE, QEMU_PLUGIN_EV_VCPU_RESUME, + QEMU_PLUGIN_EV_VCPU_INTERRUPT, QEMU_PLUGIN_EV_VCPU_SYSCALL, QEMU_PLUGIN_EV_VCPU_SYSCALL_RET, QEMU_PLUGIN_EV_FLUSH, diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 7fdc3a4849..f942e45f41 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -190,6 +190,7 @@ void qemu_plugin_vcpu_exit_hook(CPUState *cpu); void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb); void qemu_plugin_vcpu_idle_cb(CPUState *cpu); void qemu_plugin_vcpu_resume_cb(CPUState *cpu); +void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu); void qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, @@ -270,6 +271,9 @@ static inline void qemu_plugin_vcpu_idle_cb(CPUState *cpu) static inline void qemu_plugin_vcpu_resume_cb(CPUState *cpu) { } +static inline void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu) +{ } + static inline void qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6, diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 4daab6efd2..4b978f98f4 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -54,7 +54,7 @@ typedef uint64_t qemu_plugin_id_t; extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; -#define QEMU_PLUGIN_VERSION 1 +#define QEMU_PLUGIN_VERSION 2 /** * struct qemu_info_t - system information for plugins @@ -215,6 +215,16 @@ QEMU_PLUGIN_API void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb); +/** + * qemu_plugin_register_vcpu_interrupt_cb() - register a vCPU interrupt callback + * @id: plugin ID + * @cb: callback function + * + * The @cb function is called every time an interrupt is triggered on given vCPU. + */ +void qemu_plugin_register_vcpu_interrupt_cb(qemu_plugin_id_t id, + qemu_plugin_vcpu_simple_cb_t cb); + /** struct qemu_plugin_tb - Opaque handle for a translation block */ struct qemu_plugin_tb; /** struct qemu_plugin_insn - Opaque handle for a translated instruction */ diff --git a/plugins/core.c b/plugins/core.c index 49588285dd..3f9d273613 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -102,6 +102,7 @@ static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev) case QEMU_PLUGIN_EV_VCPU_EXIT: case QEMU_PLUGIN_EV_VCPU_IDLE: case QEMU_PLUGIN_EV_VCPU_RESUME: + case QEMU_PLUGIN_EV_VCPU_INTERRUPT: /* iterate safely; plugins might uninstall themselves at any time */ QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { qemu_plugin_vcpu_simple_cb_t func = cb->f.vcpu_simple; @@ -399,6 +400,11 @@ void qemu_plugin_vcpu_resume_cb(CPUState *cpu) plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME); } +void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu) +{ + plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INTERRUPT); +} + void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id, qemu_plugin_vcpu_simple_cb_t cb) { @@ -411,6 +417,12 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id, plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb); } +void qemu_plugin_register_vcpu_interrupt_cb(qemu_plugin_id_t id, + qemu_plugin_vcpu_simple_cb_t cb) +{ + plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INTERRUPT, cb); +} + void qemu_plugin_register_flush_cb(qemu_plugin_id_t id, qemu_plugin_simple_cb_t cb) { diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 71f6c90549..c8621f9950 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -30,6 +30,7 @@ qemu_plugin_register_vcpu_mem_cb; qemu_plugin_register_vcpu_mem_inline; qemu_plugin_register_vcpu_resume_cb; + qemu_plugin_register_vcpu_interrupt_cb; qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_ret_cb; qemu_plugin_register_vcpu_tb_exec_cb;
TCG Plugin callback to notify plugins when interrupt is triggered for a vCpu. The plugin can optionally use this notification to see reason of aborted instruction execution. Signed-off-by: Mikhail Tyutin <m.tyutin@yadro.com> --- accel/tcg/cpu-exec.c | 5 +++++ include/qemu/plugin-event.h | 1 + include/qemu/plugin.h | 4 ++++ include/qemu/qemu-plugin.h | 12 +++++++++++- plugins/core.c | 12 ++++++++++++ plugins/qemu-plugins.symbols | 1 + 6 files changed, 34 insertions(+), 1 deletion(-)