On 2026/7/10 下午4:09, Tao Cui wrote:
在 2026/7/10 10:58, Bibo Mao 写道:
On 2026/7/10 上午8:50, Tao Cui wrote:
From: Tao Cui <[email protected]>
kvm_set_pv_features() programs the KVM_FEATURE cpucfg attribute, which is a
per-vCPU setting. It was called from kvm_arch_put_registers() under a
function-local static guard, so it ran only once for the whole VM: only the
first vCPU got its pv features pushed to KVM, and on SMP guests the others
never saw KVM_FEATURE_IPI / KVM_FEATURE_STEAL_TIME.
Drop the static guard and push pv features per vCPU under
KVM_PUT_FULL_STATE, the same gate kvm_set_stealtime() already uses. Host
feature detection stays in kvm_arch_init_vcpu(); the per-vCPU state write
belongs in kvm_arch_put_registers().
Signed-off-by: Tao Cui <[email protected]>
---
target/loongarch/kvm/kvm.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index d6539c12ac..c557ee3c3d 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -816,7 +816,6 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp)
int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp)
{
int ret;
- static int once;
ret = kvm_loongarch_put_regs_core(cs);
if (ret) {
@@ -843,19 +842,17 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState
level, Error **errp)
return ret;
}
- if (!once) {
+ if (level >= KVM_PUT_FULL_STATE) {
+ /*
+ * pv_features and steal time are per-vCPU state. Push them on
+ * full-state sync so every vCPU gets its own settings; the kernel
+ * clears the steal-time guest_addr on KVM_PUT_RESET_STATE.
+ */
ret = kvm_set_pv_features(cs);
pv feature is a little different from steal-time. steal-time guest_addr is
created from guest OS, pv feature is created from VMM at beginning. steal-time
guest_addr can be set for many times, and there is bit KVM_STEAL_PHYS_VALID
checking with steal-time guest_addr, however pv feature can be set only once
with existing method.
Hi Bibo,
Thanks for catching this — I hadn't fully considered the double-call path.
KVM_PUT_FULL_STATE fires both at realize (cpu_synchronize_post_init) and
on incoming migration load (cpu_synchronize_all_post_init), so
kvm_set_pv_features() runs twice on the destination.
Although I do not understand flow of VM migration, with KVM_PUT_FULL_STATE
state changing, there are at least two places where this state is set, one is
from cpu_common_realizefn() which calls cpu_synchronize_post_init(), the other
is qemu_loadvm_state()/qemu_loadvm_state_main() which calls
cpu_synchronize_all_post_init().
It seems that VM will fail to migrate since kvm_set_pv_features is called twice
at least here. Do you test VM migration with this patch?
I did test migration (virt-11.2 -> virt-11.2, virt-11.1 -> virt-11.1) and
it passed, but that was on a single host — source and destination computed
the same pv_features. The kernel (kvm_loongarch_cpucfg_set_attr) only
rejects a re-set when the value differs:
if ((kvm->arch.pv_features & LOONGARCH_PV_FEAT_UPDATED) &&
((kvm->arch.pv_features & valid) != val))
return -EINVAL;
So a cross-host migration where the two sides compute different pv_features
would indeed fail on the second set.
I'll add a per-vCPU guard so the push happens exactly once (at the first
FULL_STATE sync); subsequent syncs are skipped and the destination keeps
advertising the features its own host supports. Does that sound like the
right direction?
I do not consider pv feature migration supports before. There are two
points from my side when VM migrates from hostA to hostB, maybe there
are other requirements out of my knowledge :(
1. the pv feature value got from running vCPUs on hostA should be
supported on hostB.
2. if VM is rebooted or one vCPU is hot added, the pv feature value
got from new added vCPU on hostB should be the same with boot CPU0,
which is got from hostA.
Regards
Bibo Mao
Thanks,
Tao
Regards
Bibo Mao
if (ret) {
return ret;
}
- once = 1;
- }
- if (level >= KVM_PUT_FULL_STATE) {
- /*
- * only KVM_PUT_FULL_STATE is required, kvm kernel will clear
- * guest_addr for KVM_PUT_RESET_STATE
- */
ret = kvm_set_stealtime(cs);
if (ret) {
return ret;