[PATCH v10 2/2] target/riscv/kvm: Preserve MP state across migration
Xie Bo <[email protected]> Thu, 30 Jul 2026 13:19:26 +0800
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
RISC-V KVM initializes secondary vCPUs in KVM_MP_STATE_STOPPED, but QEMU does not save their runtime MP state. A destination therefore retains reset MP state after migration and cannot reliably resume all vCPUs. Save KVM_GET_MP_STATE in a capability-gated KVM VMState subsection and restore it on KVM_PUT_FULL_STATE. Keep the existing reset initialization path unchanged. Track whether the subsection was loaded so migration streams from older QEMU versions retain their previous destination reset behavior. Using a subsection avoids changing the generic RISC-V CPU VMState version and keeps the state out of TCG migration streams. Signed-off-by: Xie Bo <[email protected]> --- target/riscv/cpu.h | 4 +++ target/riscv/kvm/kvm-cpu.c | 50 ++++++++++++++++++++++++------------ target/riscv/kvm/kvm_riscv.h | 2 +- target/riscv/machine.c | 45 ++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 17 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c9dfa7daff..e980b5964d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -538,6 +538,10 @@ struct CPUArchState { uint64_t kvm_timer_compare; uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; + + /* KVM multiprocessor state */ + uint32_t kvm_mp_state; + bool kvm_mp_state_loaded; #endif /* CONFIG_KVM */ }; diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 8218832fbe..cc5655d46d 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1374,25 +1374,35 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) return ret; } + if (cap_has_mp_state) { + struct kvm_mp_state mp_state; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); + if (ret) { + return ret; + } + RISCV_CPU(cs)->env.kvm_mp_state = mp_state.mp_state; + } + return ret; } -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state) +bool kvm_riscv_has_mp_state(void) { - if (cap_has_mp_state) { - struct kvm_mp_state mp_state = { - .mp_state = state - }; + return cap_has_mp_state; +} - int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); - if (ret) { - fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n", - __func__, ret, strerror(-ret)); - return -1; - } +static int kvm_riscv_put_mp_state(CPUState *cs) +{ + struct kvm_mp_state mp_state = { + .mp_state = RISCV_CPU(cs)->env.kvm_mp_state, + }; + + if (!cap_has_mp_state) { + return 0; } - return 0; + return kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); } int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) @@ -1431,10 +1441,18 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) } if (KVM_PUT_RESET_STATE == level) { - RISCVCPU *cpu = RISCV_CPU(cs); - int state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE - : KVM_MP_STATE_STOPPED; - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, state); + CPURISCVState *env = &RISCV_CPU(cs)->env; + + env->kvm_mp_state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE + : KVM_MP_STATE_STOPPED; + env->kvm_mp_state_loaded = false; + ret = kvm_riscv_put_mp_state(cs); + if (ret) { + return ret; + } + } else if (KVM_PUT_FULL_STATE == level && + RISCV_CPU(cs)->env.kvm_mp_state_loaded) { + ret = kvm_riscv_put_mp_state(cs); if (ret) { return ret; } diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h index b2bcd1041f..61eaa12443 100644 --- a/target/riscv/kvm/kvm_riscv.h +++ b/target/riscv/kvm/kvm_riscv.h @@ -28,7 +28,7 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, uint64_t aplic_base, uint64_t imsic_base, uint64_t guest_num); void riscv_kvm_aplic_request(void *opaque, int irq, int level); -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state); +bool kvm_riscv_has_mp_state(void); void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp); uint64_t kvm_riscv_get_timebase_frequency(RISCVCPU *cpu); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 0ab613a298..26ecee2a48 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -25,6 +25,9 @@ #include "exec/icount.h" #include "target/riscv/tcg/debug.h" #include "hw/riscv/machines-qom.h" +#ifdef CONFIG_KVM +#include "kvm/kvm_riscv.h" +#endif static bool pmp_needed(void *opaque) { @@ -222,6 +225,44 @@ static const VMStateDescription vmstate_kvmtimer = { VMSTATE_END_OF_LIST() } }; + +static int riscv_cpu_kvm_pre_load(void *opaque) +{ + RISCVCPU *cpu = opaque; + + cpu->env.kvm_mp_state_loaded = false; + return 0; +} + +static bool kvm_mp_state_needed(void *opaque) +{ + return kvm_enabled() && kvm_riscv_has_mp_state(); +} + +static int kvm_mp_state_post_load(void *opaque, int version_id) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + if (!kvm_enabled() || !kvm_riscv_has_mp_state()) { + return -ENOTSUP; + } + + env->kvm_mp_state_loaded = true; + return 0; +} + +static const VMStateDescription vmstate_kvm_mp_state = { + .name = "cpu/kvm-mp-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = kvm_mp_state_needed, + .post_load = kvm_mp_state_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(env.kvm_mp_state, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; #endif static bool debug_needed(void *opaque) @@ -459,6 +500,9 @@ const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", .version_id = 11, .minimum_version_id = 11, +#ifdef CONFIG_KVM + .pre_load = riscv_cpu_kvm_pre_load, +#endif .post_load = riscv_cpu_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.gpr, RISCVCPU, 32), @@ -522,6 +566,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_rv128, #ifdef CONFIG_KVM &vmstate_kvmtimer, + &vmstate_kvm_mp_state, #endif &vmstate_envcfg, &vmstate_debug, -- 2.17.1