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,
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.