Fix two migration issues for virtual machines in KVM mode:
1.It saves and restores the vCPU's privilege mode to ensure that the
vCPU's privilege mode is correct after migration.
2.It saves and restores the vCPU's mp_state (runnable or stopped) and
includes this state in the migration sequence, upgrading the vmstate
version to ensure that the vCPU's mp_state is correct after migration.
KVM_PUT_RUNTIME_STATE only synchronizes the vCPU’s runtime-modified
state (such as registers), whereas mp_state is related to system boot,
multi-core initialization, and is not modified during normal operation.
Therefore, mp_state is only synchronized to KVM during KVM_PUT_RESET_STATE
and KVM_PUT_FULL_STATE.
Signed-off-by: Xie Bo <xb@ultrarisc.com>
---
target/riscv/kvm/kvm-cpu.c | 60 ++++++++++++++++++++++++++++--------
target/riscv/kvm/kvm_riscv.h | 3 +-
target/riscv/machine.c | 5 +--
3 files changed, 52 insertions(+), 16 deletions(-)
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index 0f4997a918..1434dd1c51 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -576,6 +576,12 @@ static int kvm_riscv_get_regs_core(CPUState *cs)
}
env->pc = reg;
+ ret = kvm_get_one_reg(cs, RISCV_CORE_REG(env, mode), ®);
+ if (ret) {
+ return ret;
+ }
+ env->priv = reg;
+
for (i = 1; i < 32; i++) {
uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i);
ret = kvm_get_one_reg(cs, id, ®);
@@ -601,6 +607,12 @@ static int kvm_riscv_put_regs_core(CPUState *cs)
return ret;
}
+ reg = env->priv;
+ ret = kvm_set_one_reg(cs, RISCV_CORE_REG(env, mode), ®);
+ if (ret) {
+ return ret;
+ }
+
for (i = 1; i < 32; i++) {
uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i);
reg = env->gpr[i];
@@ -1244,25 +1256,52 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
return ret;
}
+ ret = kvm_riscv_sync_mpstate_to_qemu(cs);
+ if (ret) {
+ return ret;
+ }
+
return ret;
}
-int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state)
+int kvm_riscv_sync_mpstate_to_kvm(CPUState *cs)
{
+ int ret = 0;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
if (cap_has_mp_state) {
struct kvm_mp_state mp_state = {
- .mp_state = state
+ .mp_state = env->mp_state
};
- int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
+ ret = kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state);
if (ret) {
- fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n",
+ fprintf(stderr, "%s: failed to sync MP_STATE to KVM %d/%s\n",
__func__, ret, strerror(-ret));
- return -1;
}
}
- return 0;
+ return ret;
+}
+
+int kvm_riscv_sync_mpstate_to_qemu(CPUState *cs)
+{
+ int ret = 0;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (cap_has_mp_state) {
+ struct kvm_mp_state mp_state;
+
+ ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state);
+ if (ret) {
+ fprintf(stderr, "%s: failed to sync MP_STATE to QEMU %d/%s\n",
+ __func__, ret, strerror(-ret));
+ return ret;
+ }
+ env->mp_state = mp_state.mp_state;
+ }
+
+ return ret;
}
int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
@@ -1289,13 +1328,8 @@ int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
return ret;
}
- if (KVM_PUT_RESET_STATE == level) {
- RISCVCPU *cpu = RISCV_CPU(cs);
- if (cs->cpu_index == 0) {
- ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE);
- } else {
- ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED);
- }
+ if (level >= KVM_PUT_RESET_STATE) {
+ ret = kvm_riscv_sync_mpstate_to_kvm(cs);
if (ret) {
return ret;
}
diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h
index b2bcd1041f..953db94160 100644
--- a/target/riscv/kvm/kvm_riscv.h
+++ b/target/riscv/kvm/kvm_riscv.h
@@ -28,7 +28,8 @@ 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);
+int kvm_riscv_sync_mpstate_to_kvm(CPUState *cs);
+int kvm_riscv_sync_mpstate_to_qemu(CPUState *cs);
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 889e2b6570..8562a0a1d6 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -401,8 +401,8 @@ static const VMStateDescription vmstate_ssp = {
const VMStateDescription vmstate_riscv_cpu = {
.name = "cpu",
- .version_id = 10,
- .minimum_version_id = 10,
+ .version_id = 11,
+ .minimum_version_id = 11,
.post_load = riscv_cpu_post_load,
.fields = (const VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32),
@@ -422,6 +422,7 @@ const VMStateDescription vmstate_riscv_cpu = {
VMSTATE_UNUSED(4),
VMSTATE_UINT32(env.misa_ext_mask, RISCVCPU),
VMSTATE_UINTTL(env.priv, RISCVCPU),
+ VMSTATE_UINT32(env.mp_state, RISCVCPU),
VMSTATE_BOOL(env.virt_enabled, RISCVCPU),
VMSTATE_UINT64(env.resetvec, RISCVCPU),
VMSTATE_UINTTL(env.mhartid, RISCVCPU),
--
2.43.0
On Wed, Sep 10, 2025 at 05:35:28PM +0800, Xie Bo wrote:
> Fix two migration issues for virtual machines in KVM mode:
> 1.It saves and restores the vCPU's privilege mode to ensure that the
> vCPU's privilege mode is correct after migration.
> 2.It saves and restores the vCPU's mp_state (runnable or stopped) and
> includes this state in the migration sequence, upgrading the vmstate
> version to ensure that the vCPU's mp_state is correct after migration.
>
> KVM_PUT_RUNTIME_STATE only synchronizes the vCPU’s runtime-modified
> state (such as registers), whereas mp_state is related to system boot,
> multi-core initialization, and is not modified during normal operation.
> Therefore, mp_state is only synchronized to KVM during KVM_PUT_RESET_STATE
> and KVM_PUT_FULL_STATE.
>
> Signed-off-by: Xie Bo <xb@ultrarisc.com>
> ---
> target/riscv/kvm/kvm-cpu.c | 60 ++++++++++++++++++++++++++++--------
> target/riscv/kvm/kvm_riscv.h | 3 +-
> target/riscv/machine.c | 5 +--
> 3 files changed, 52 insertions(+), 16 deletions(-)
>
> diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
> index 0f4997a918..1434dd1c51 100644
> --- a/target/riscv/kvm/kvm-cpu.c
> +++ b/target/riscv/kvm/kvm-cpu.c
> @@ -576,6 +576,12 @@ static int kvm_riscv_get_regs_core(CPUState *cs)
> }
> env->pc = reg;
>
> + ret = kvm_get_one_reg(cs, RISCV_CORE_REG(env, mode), ®);
> + if (ret) {
> + return ret;
> + }
> + env->priv = reg;
> +
> for (i = 1; i < 32; i++) {
> uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i);
> ret = kvm_get_one_reg(cs, id, ®);
> @@ -601,6 +607,12 @@ static int kvm_riscv_put_regs_core(CPUState *cs)
> return ret;
> }
>
> + reg = env->priv;
> + ret = kvm_set_one_reg(cs, RISCV_CORE_REG(env, mode), ®);
> + if (ret) {
> + return ret;
> + }
> +
> for (i = 1; i < 32; i++) {
> uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i);
> reg = env->gpr[i];
> @@ -1244,25 +1256,52 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
> return ret;
> }
>
> + ret = kvm_riscv_sync_mpstate_to_qemu(cs);
> + if (ret) {
> + return ret;
> + }
> +
> return ret;
> }
>
> -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state)
> +int kvm_riscv_sync_mpstate_to_kvm(CPUState *cs)
> {
> + int ret = 0;
> + CPURISCVState *env = &RISCV_CPU(cs)->env;
> +
> if (cap_has_mp_state) {
> struct kvm_mp_state mp_state = {
> - .mp_state = state
> + .mp_state = env->mp_state
> };
>
> - int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
> + ret = kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state);
> if (ret) {
> - fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n",
> + fprintf(stderr, "%s: failed to sync MP_STATE to KVM %d/%s\n",
> __func__, ret, strerror(-ret));
> - return -1;
> }
> }
>
> - return 0;
> + return ret;
> +}
> +
> +int kvm_riscv_sync_mpstate_to_qemu(CPUState *cs)
> +{
> + int ret = 0;
> + CPURISCVState *env = &RISCV_CPU(cs)->env;
> +
> + if (cap_has_mp_state) {
> + struct kvm_mp_state mp_state;
> +
> + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state);
> + if (ret) {
> + fprintf(stderr, "%s: failed to sync MP_STATE to QEMU %d/%s\n",
> + __func__, ret, strerror(-ret));
> + return ret;
> + }
> + env->mp_state = mp_state.mp_state;
> + }
> +
> + return ret;
> }
>
> int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
> @@ -1289,13 +1328,8 @@ int kvm_arch_put_registers(CPUState *cs, int level, Error **errp)
> return ret;
> }
>
> - if (KVM_PUT_RESET_STATE == level) {
> - RISCVCPU *cpu = RISCV_CPU(cs);
> - if (cs->cpu_index == 0) {
> - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_RUNNABLE);
> - } else {
> - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, KVM_MP_STATE_STOPPED);
> - }
> + if (level >= KVM_PUT_RESET_STATE) {
> + ret = kvm_riscv_sync_mpstate_to_kvm(cs);
> if (ret) {
> return ret;
> }
> diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h
> index b2bcd1041f..953db94160 100644
> --- a/target/riscv/kvm/kvm_riscv.h
> +++ b/target/riscv/kvm/kvm_riscv.h
> @@ -28,7 +28,8 @@ 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);
> +int kvm_riscv_sync_mpstate_to_kvm(CPUState *cs);
> +int kvm_riscv_sync_mpstate_to_qemu(CPUState *cs);
> 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 889e2b6570..8562a0a1d6 100644
> --- a/target/riscv/machine.c
> +++ b/target/riscv/machine.c
> @@ -401,8 +401,8 @@ static const VMStateDescription vmstate_ssp = {
>
> const VMStateDescription vmstate_riscv_cpu = {
> .name = "cpu",
> - .version_id = 10,
> - .minimum_version_id = 10,
> + .version_id = 11,
> + .minimum_version_id = 11,
> .post_load = riscv_cpu_post_load,
> .fields = (const VMStateField[]) {
> VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32),
> @@ -422,6 +422,7 @@ const VMStateDescription vmstate_riscv_cpu = {
> VMSTATE_UNUSED(4),
> VMSTATE_UINT32(env.misa_ext_mask, RISCVCPU),
> VMSTATE_UINTTL(env.priv, RISCVCPU),
> + VMSTATE_UINT32(env.mp_state, RISCVCPU),
> VMSTATE_BOOL(env.virt_enabled, RISCVCPU),
> VMSTATE_UINT64(env.resetvec, RISCVCPU),
> VMSTATE_UINTTL(env.mhartid, RISCVCPU),
> --
> 2.43.0
>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
© 2016 - 2026 Red Hat, Inc.