From: Michael Roth <michael.roth@amd.com>
To: <qemu-devel@nongnu.org>
Cc: <pbonzini@redhat.com>, <berrange@redhat.com>,
<eduardo@habkost.net>, <armbru@redhat.com>,
<thomas.lendacky@amd.com>, <huibo.wang@amd.com>
Subject: [PATCH v3 1/3] hw/cpu: Add async_run_on_cpu_after_accel()
Date: Mon, 26 Oct 2026 12:21:47 -0500 [thread overview]
Message-ID: <20261026172300.2061829-2-michael.roth@amd.com> (raw)
In-Reply-To: <20261026172300.2061829-1-michael.roth@amd.com>
For some operations, hypervisors/accelerators like KVM might requiring
calling back into the accelerator code prior to userspace being able to
complete handling of certains operations. One common example is I/O
emulation where the accelerator call may be necessary to complete
pending work on the accelerator side and confirming completion to
userspace via another exit, which (for this particular case) already has
handling in KVM-specific code.
In some cases however, userspace may need to perform additional work on
behalf of the vCPU after the accelerator exists, requiring some kind of
per-vCPU callback mechanism. async_run_on_cpu() already provides
something very similar, but it forces an exit from the inner loop, and
is processed at the start of the outer loop, so those callbacks might
be issued before the accelerator has a chance to get called again.
Introduce a new variant, async_run_on_cpu_after_accel(), that is
designed to queue up work to be processed after the next accelerator
exit, and without unnecessarily forcing an exit from the inner loop. The
handling is via process_queued_cpu_accel_work(), which is left to
accelerator/arch-specific code to implement in their inner loops where
appropriate.
KVM will use this general per-vCPU infrastructure to handle callbacks
for these cases. Other accelerators maybe have other uses in the future.
Signed-off-by: Michael Roth <michael.roth@amd.com>
---
cpu-common.c | 66 +++++++++++++++++++++++++++++++++++++++++++
hw/core/cpu-common.c | 2 ++
include/hw/core/cpu.h | 33 ++++++++++++++++++++++
3 files changed, 101 insertions(+)
diff --git a/cpu-common.c b/cpu-common.c
index adb76b3a78..0e735da673 100644
--- a/cpu-common.c
+++ b/cpu-common.c
@@ -141,6 +141,23 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
cpu_exit(cpu);
}
+/*
+ * Queue work on a CPU to run after the next accelerator exit.
+ *
+ * This helper intentionally leaves the inner loop running rather than forcing
+ * an exit via cpu_exit(), since the intention is to immediately call back into
+ * the accelerator to process pending events, and then upon exiting issue
+ * callbacks/notifications to any CPUs that need to complete any work that is
+ * pending on that accelerator call/exit.
+ */
+static void queue_accel_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi)
+{
+ assert(wi->free);
+ qemu_mutex_lock(&cpu->work_mutex);
+ QSIMPLEQ_INSERT_TAIL(&cpu->accel_work_list, wi, node);
+ qemu_mutex_unlock(&cpu->work_mutex);
+}
+
void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data,
QemuMutex *mutex)
{
@@ -178,6 +195,24 @@ void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data)
queue_work_on_cpu(cpu, wi);
}
+void async_run_on_cpu_after_accel(CPUState *cpu, run_on_cpu_func func,
+ run_on_cpu_data data)
+{
+ struct qemu_work_item *wi;
+
+ wi = g_new0(struct qemu_work_item, 1);
+
+ /*
+ * Only asynchronous accelerator work items are allowed. I.e. wi->free
+ * must be true. Subsequent code will assert this.
+ */
+ wi->func = func;
+ wi->data = data;
+ wi->free = true;
+
+ queue_accel_work_on_cpu(cpu, wi);
+}
+
/* Wait for pending exclusive operations to complete. The CPU list lock
must be held. */
static inline void exclusive_idle(void)
@@ -349,6 +384,37 @@ void free_queued_cpu_work(CPUState *cpu)
}
}
+void free_queued_cpu_accel_work(CPUState *cpu)
+{
+ while (!QSIMPLEQ_EMPTY(&cpu->accel_work_list)) {
+ struct qemu_work_item *wi = QSIMPLEQ_FIRST(&cpu->accel_work_list);
+ QSIMPLEQ_REMOVE_HEAD(&cpu->accel_work_list, node);
+ assert(wi->free);
+ g_free(wi);
+ }
+}
+
+void process_queued_cpu_accel_work(CPUState *cpu)
+{
+ struct qemu_work_item *wi;
+
+ qemu_mutex_lock(&cpu->work_mutex);
+
+ while (!QSIMPLEQ_EMPTY(&cpu->accel_work_list)) {
+ wi = QSIMPLEQ_FIRST(&cpu->accel_work_list);
+ QSIMPLEQ_REMOVE_HEAD(&cpu->accel_work_list, node);
+
+ qemu_mutex_unlock(&cpu->work_mutex);
+ wi->func(cpu, wi->data);
+ qemu_mutex_lock(&cpu->work_mutex);
+
+ assert(wi->free);
+ g_free(wi);
+ }
+
+ qemu_mutex_unlock(&cpu->work_mutex);
+}
+
void process_queued_cpu_work(CPUState *cpu)
{
struct qemu_work_item *wi;
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 4f4c87f33a..599963a85d 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -304,6 +304,7 @@ static void cpu_common_initfn(Object *obj)
qemu_mutex_init(&cpu->work_mutex);
qemu_lockcnt_init(&cpu->in_ioctl_lock);
QSIMPLEQ_INIT(&cpu->work_list);
+ QSIMPLEQ_INIT(&cpu->accel_work_list);
QTAILQ_INIT(&cpu->breakpoints);
QTAILQ_INIT(&cpu->watchpoints);
@@ -332,6 +333,7 @@ static void cpu_common_finalize(Object *obj)
}
#endif
free_queued_cpu_work(cpu);
+ free_queued_cpu_accel_work(cpu);
/* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */
if (cpu->gdb_regs) {
g_array_free(cpu->gdb_regs, TRUE);
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 5b7c330047..8c55269074 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -523,6 +523,7 @@ struct CPUState {
QemuMutex work_mutex;
QSIMPLEQ_HEAD(, qemu_work_item) work_list;
+ QSIMPLEQ_HEAD(, qemu_work_item) accel_work_list;
struct CPUAddressSpace *cpu_ases;
AddressSpace *as;
@@ -968,6 +969,24 @@ void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data);
*/
void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data);
+/**
+ * async_run_on_cpu_after_accel:
+ * @cpu: The vCPU to run on.
+ * @func: The function to be executed.
+ * @data: Data to pass to the function.
+ *
+ * Schedules the function @func for execution on the vCPU @cpu asynchronously,
+ * to be handled only after the next invocation of the configured accelerator.
+ * Unlike async_run_on_cpu(), @func will not be run with the BQL held and is
+ * instead left to @func itself to take if needed.
+ *
+ * This does not guarantee any sort of expedited exiting from the accelerator
+ * or the inner loop; this aspect is instead left up to
+ * accelerator/arch-specific code.
+ */
+void async_run_on_cpu_after_accel(CPUState *cpu, run_on_cpu_func func,
+ run_on_cpu_data data);
+
/**
* async_safe_run_on_cpu:
* @cpu: The vCPU to run on.
@@ -1115,12 +1134,26 @@ void cpu_remove_sync(CPUState *cpu);
*/
void free_queued_cpu_work(CPUState *cpu);
+/**
+ * free_queued_cpu_accel_work() - free all items on CPU accel work queue
+ * @cpu: The CPU which accel work queue to free.
+ */
+void free_queued_cpu_accel_work(CPUState *cpu);
+
/**
* process_queued_cpu_work() - process all items on CPU work queue
* @cpu: The CPU which work queue to process.
*/
void process_queued_cpu_work(CPUState *cpu);
+/**
+ * process_queued_cpu_accel_work() - process all items on CPU accel work queue
+ * @cpu: The CPU which work queue to process.
+ *
+ * Must be called without the BQL held.
+ */
+void process_queued_cpu_accel_work(CPUState *cpu);
+
/**
* cpu_exec_start:
* @cpu: The CPU for the current thread.
--
2.43.0
next prev parent reply other threads:[~2026-10-26 17:24 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 17:21 [PATCH v3 0/3] SEV-SNP: Add support for SNP certificate fetching Michael Roth
2026-10-26 17:21 ` Michael Roth [this message]
2026-10-26 17:21 ` [PATCH v3 2/3] accel/kvm: Add kvm_immediate_exit callback infrastructure Michael Roth
2026-10-26 17:21 ` [PATCH v3 3/3] i386/sev: Add KVM_EXIT_SNP_REQ_CERTS support for certificate-fetching Michael Roth
2026-10-27 12:38 ` Markus Armbruster
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261026172300.2061829-2-michael.roth@amd.com \
--to=michael.roth@amd.com \
--cc=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=eduardo@habkost.net \
--cc=huibo.wang@amd.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=thomas.lendacky@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.