@samitouri / QOSamiQemu / commits / ddac7cfb00

target/riscv/kvm: Preserve MP state across migration

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 streams where the subsection is absent retain the destination reset behavior. Bump the RISC-V CPU VMState version and minimum version to 12 for the new pre_load hook and KVM MP-state subsection. Keep the subsection out of KVM migration streams when the host does not support the MP-state capability. Signed-off-by: Xie Bo <xb@ultrarisc.com> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com> Message-ID: <20260808125157.1220511-3-xb@ultrarisc.com> Signed-off-by: Alistair Francis <alistair.francis@wdc.com>

Xie Bo committed Aug 8, 2026 at 20:51 UTC ddac7cfb00ad771f6ef3450fff2d09e749fdbaf9
4 files changed +86 -19
target/riscv/cpu.h
+4
@@ -538,6 +538,10 @@ struct CPUArchState {
538 uint64_t kvm_timer_compare;
539 uint64_t kvm_timer_state;
540 uint64_t kvm_timer_frequency;
541 +
542 + /* KVM multiprocessor state */
543 + uint32_t kvm_mp_state;
544 + bool kvm_mp_state_loaded;
545 #endif /* CONFIG_KVM */
546 };
547
target/riscv/kvm/kvm-cpu.c
+34 -16
@@ -1374,25 +1374,35 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
1374 return ret;
1375 }
1376
1377 + if (cap_has_mp_state) {
1378 + struct kvm_mp_state mp_state;
1379 +
1380 + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state);
1381 + if (ret) {
1382 + return ret;
1383 + }
1384 + RISCV_CPU(cs)->env.kvm_mp_state = mp_state.mp_state;
1385 + }
1386 +
1387 return ret;
1388 }
1389
1380 -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state)
1390 +bool kvm_riscv_has_mp_state(void)
1391 {
1382 - if (cap_has_mp_state) {
1383 - struct kvm_mp_state mp_state = {
1384 - .mp_state = state
1385 - };
1392 + return cap_has_mp_state;
1393 +}
1394
1387 - int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state);
1388 - if (ret) {
1389 - fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n",
1390 - __func__, ret, strerror(-ret));
1391 - return -1;
1392 - }
1395 +static int kvm_riscv_put_mp_state(CPUState *cs)
1396 +{
1397 + struct kvm_mp_state mp_state = {
1398 + .mp_state = RISCV_CPU(cs)->env.kvm_mp_state,
1399 + };
1400 +
1401 + if (!cap_has_mp_state) {
1402 + return 0;
1403 }
1404
1395 - return 0;
1405 + return kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state);
1406 }
1407
1408 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)
1441 }
1442
1443 if (KVM_PUT_RESET_STATE == level) {
1434 - RISCVCPU *cpu = RISCV_CPU(cs);
1435 - int state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE
1436 - : KVM_MP_STATE_STOPPED;
1437 - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, state);
1444 + CPURISCVState *env = &RISCV_CPU(cs)->env;
1445 +
1446 + env->kvm_mp_state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE
1447 + : KVM_MP_STATE_STOPPED;
1448 + env->kvm_mp_state_loaded = false;
1449 + ret = kvm_riscv_put_mp_state(cs);
1450 + if (ret) {
1451 + return ret;
1452 + }
1453 + } else if (KVM_PUT_FULL_STATE == level &&
1454 + RISCV_CPU(cs)->env.kvm_mp_state_loaded) {
1455 + ret = kvm_riscv_put_mp_state(cs);
1456 if (ret) {
1457 return ret;
1458 }
target/riscv/kvm/kvm_riscv.h
+1 -1
@@ -28,7 +28,7 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift,
28 uint64_t aplic_base, uint64_t imsic_base,
29 uint64_t guest_num);
30 void riscv_kvm_aplic_request(void *opaque, int irq, int level);
31 -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state);
31 +bool kvm_riscv_has_mp_state(void);
32 void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp);
33 uint64_t kvm_riscv_get_timebase_frequency(RISCVCPU *cpu);
34
target/riscv/machine.c
+47 -2
@@ -25,6 +25,9 @@
25 #include "exec/icount.h"
26 #include "target/riscv/tcg/debug.h"
27 #include "hw/riscv/machines-qom.h"
28 +#ifdef CONFIG_KVM
29 +#include "kvm/kvm_riscv.h"
30 +#endif
31
32 static bool pmp_needed(void *opaque)
33 {
@@ -222,6 +225,44 @@ static const VMStateDescription vmstate_kvmtimer = {
225 VMSTATE_END_OF_LIST()
226 }
227 };
228 +
229 +static int riscv_cpu_kvm_pre_load(void *opaque)
230 +{
231 + RISCVCPU *cpu = opaque;
232 +
233 + cpu->env.kvm_mp_state_loaded = false;
234 + return 0;
235 +}
236 +
237 +static bool kvm_mp_state_needed(void *opaque)
238 +{
239 + return kvm_enabled() && kvm_riscv_has_mp_state();
240 +}
241 +
242 +static int kvm_mp_state_post_load(void *opaque, int version_id)
243 +{
244 + RISCVCPU *cpu = opaque;
245 + CPURISCVState *env = &cpu->env;
246 +
247 + if (!kvm_enabled() || !kvm_riscv_has_mp_state()) {
248 + return -ENOTSUP;
249 + }
250 +
251 + env->kvm_mp_state_loaded = true;
252 + return 0;
253 +}
254 +
255 +static const VMStateDescription vmstate_kvm_mp_state = {
256 + .name = "cpu/kvm-mp-state",
257 + .version_id = 1,
258 + .minimum_version_id = 1,
259 + .needed = kvm_mp_state_needed,
260 + .post_load = kvm_mp_state_post_load,
261 + .fields = (const VMStateField[]) {
262 + VMSTATE_UINT32(env.kvm_mp_state, RISCVCPU),
263 + VMSTATE_END_OF_LIST()
264 + }
265 +};
266 #endif
267
268 static bool debug_needed(void *opaque)
@@ -457,8 +498,11 @@ static const VMStateDescription vmstate_mseccfg = {
498
499 const VMStateDescription vmstate_riscv_cpu = {
500 .name = "cpu",
460 - .version_id = 11,
461 - .minimum_version_id = 11,
501 + .version_id = 12,
502 + .minimum_version_id = 12,
503 +#ifdef CONFIG_KVM
504 + .pre_load = riscv_cpu_kvm_pre_load,
505 +#endif
506 .post_load = riscv_cpu_post_load,
507 .fields = (const VMStateField[]) {
508 VMSTATE_UINT64_ARRAY(env.gpr, RISCVCPU, 32),
@@ -522,6 +566,7 @@ const VMStateDescription vmstate_riscv_cpu = {
566 &vmstate_rv128,
567 #ifdef CONFIG_KVM
568 &vmstate_kvmtimer,
569 + &vmstate_kvm_mp_state,
570 #endif
571 &vmstate_envcfg,
572 &vmstate_debug,