Re: [PATCH v10 2/2] target/riscv/kvm: Preserve MP state across migration
Daniel Henrique Barboza <[email protected]>
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
Hi, On 7/30/2026 2:19 AM, Xie Bo wrote: > 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, Adding a .pre_load and a subsection requires a vmstate version_id bump, i.e. we need to bump version_id and minimum_version_id to 12. With this change: Reviewed-by: Daniel Henrique Barboza <[email protected]> > +#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,