diff mbox series

[2/2] accel/kvm: introduce begin/commit listener callbacks

Message ID 20221022154823.1823193-3-eesposit@redhat.com
State New
Headers show
Series KVM: stop all vcpus before modifying memslots | expand

Commit Message

Emanuele Giuseppe Esposito Oct. 22, 2022, 3:48 p.m. UTC
These callback make sure that all vcpus are blocked before
performing memslot updates, and resumed once we are finished.

They rely on kvm support for KVM_KICK_ALL_RUNNING_VCPUS and
KVM_RESUME_ALL_KICKED_VCPUS ioctls to respectively pause and
resume all vcpus that are in KVM_RUN state.

Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
---
 accel/kvm/kvm-all.c | 50 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
diff mbox series

Patch

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 645f0a249a..bd0dfa8613 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -178,6 +178,8 @@  bool kvm_has_guest_debug;
 int kvm_sstep_flags;
 static bool kvm_immediate_exit;
 static hwaddr kvm_max_slot_size = ~0;
+static QemuEvent mem_transaction_proceed;
+
 
 static const KVMCapabilityInfo kvm_required_capabilites[] = {
     KVM_CAP_INFO(USER_MEMORY),
@@ -1523,6 +1525,38 @@  static void kvm_region_del(MemoryListener *listener,
     memory_region_unref(section->mr);
 }
 
+static void kvm_begin(MemoryListener *listener)
+{
+    KVMState *s = kvm_state;
+
+    /*
+     * Make sure BQL is taken so cpus in kvm_cpu_exec that just exited from
+     * KVM_RUN do not continue, since many run->exit_reason take it anyways.
+     */
+    assert(qemu_mutex_iothread_locked());
+
+    /*
+     * Stop incoming cpus that want to execute KVM_RUN from running.
+     * Makes cpus calling qemu_event_wait() in kvm_cpu_exec() block.
+     */
+    qemu_event_reset(&mem_transaction_proceed);
+
+    /* Ask KVM to stop all vcpus that are currently running KVM_RUN */
+    kvm_vm_ioctl(s, KVM_KICK_ALL_RUNNING_VCPUS);
+}
+
+static void kvm_commit(MemoryListener *listener)
+{
+    KVMState *s = kvm_state;
+    assert(qemu_mutex_iothread_locked());
+
+    /* Ask KVM to resume all vcpus that are currently blocked in KVM_RUN */
+    kvm_vm_ioctl(s, KVM_RESUME_ALL_KICKED_VCPUS);
+
+    /* Resume cpus waiting in qemu_event_wait() in kvm_cpu_exec() */
+    qemu_event_set(&mem_transaction_proceed);
+}
+
 static void kvm_log_sync(MemoryListener *listener,
                          MemoryRegionSection *section)
 {
@@ -1668,6 +1702,8 @@  void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
     kml->listener.region_del = kvm_region_del;
     kml->listener.log_start = kvm_log_start;
     kml->listener.log_stop = kvm_log_stop;
+    kml->listener.begin = kvm_begin;
+    kml->listener.commit = kvm_commit;
     kml->listener.priority = 10;
     kml->listener.name = name;
 
@@ -2611,6 +2647,7 @@  static int kvm_init(MachineState *ms)
     }
 
     kvm_state = s;
+    qemu_event_init(&mem_transaction_proceed, false);
 
     ret = kvm_arch_init(ms, s);
     if (ret < 0) {
@@ -2875,6 +2912,19 @@  int kvm_cpu_exec(CPUState *cpu)
     }
 
     qemu_mutex_unlock_iothread();
+
+    /*
+     * Wait that a running memory transaction (memslot update) is concluded.
+     *
+     * If the event state is EV_SET, it means kvm_commit() has already finished
+     * and called qemu_event_set(), therefore cpu can execute.
+     *
+     * If it's EV_FREE, it means kvm_begin() has already called
+     * qemu_event_reset(), therefore a memory transaction is happening and the
+     * cpu must wait.
+     */
+    qemu_event_wait(&mem_transaction_proceed);
+
     cpu_exec_start(cpu);
 
     do {