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@xxxxxxxxxx> --- accel/kvm/kvm-all.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) 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 { -- 2.31.1