* [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change
2026-08-16 5:33 [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Dongli Zhang
@ 2026-08-16 5:33 ` Dongli Zhang
2026-08-16 5:55 ` sashiko-bot
2026-08-16 5:33 ` [PATCH 2/4] KVM: arm64: " Dongli Zhang
` (4 subsequent siblings)
5 siblings, 1 reply; 16+ messages in thread
From: Dongli Zhang @ 2026-08-16 5:33 UTC (permalink / raw)
To: kvm, kvmarm, linux-kselftest
Cc: seanjc, pbonzini, maz, oupton, fuad.tabba, joey.gouly, seiden,
suzuki.poulose, yuzenghui, dwmw2, joe.jin
KVM does not support vCPU hotplug. When a vCPU is removed, its
corresponding data structures are not freed by KVM. Instead, QEMU destroys
only the userspace state and the vCPU thread, while the KVM vCPU fd remains
open and parked in QEMU.
As a result, vcpu->arch.st.last_steal is not reset.
If the same vCPU is later re-created by QEMU, last_steal retains its old
value, while current->sched_info.run_delay starts from zero since a new
vCPU thread is created. This causes
current->sched_info.run_delay - vcpu->arch.st.last_steal to produce a
large, bogus value.
Fix this by resetting vcpu->arch.st.last_steal unconditionally to
current->sched_info.run_delay when KVM vCPU PID is changed.
Assisted-by: Codex:GPT-5.5
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
arch/x86/kvm/Kconfig | 1 +
arch/x86/kvm/x86.c | 7 +++++++
2 files changed, 8 insertions(+)
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index 801bf9e520db..b7cb2ceda6d9 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -40,6 +40,7 @@ config KVM_X86
select HAVE_KVM_MSI
select HAVE_KVM_CPU_RELAX_INTERCEPT
select HAVE_KVM_NO_POLL
+ select HAVE_KVM_VCPU_RUN_PID_CHANGE
select VIRT_XFER_TO_GUEST_WORK
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select KVM_VFIO
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 47cb9eba113b..33be45eec32b 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3717,6 +3717,13 @@ void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu)
}
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_service_local_tlb_flush_requests);
+int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.st.last_steal = current->sched_info.run_delay;
+
+ return 0;
+}
+
static void record_steal_time(struct kvm_vcpu *vcpu)
{
struct gfn_to_hva_cache *ghc = &vcpu->arch.st.cache;
--
2.43.5
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change
2026-08-16 5:33 ` [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change Dongli Zhang
@ 2026-08-16 5:55 ` sashiko-bot
2026-08-17 20:21 ` Dongli Zhang
0 siblings, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-08-16 5:55 UTC (permalink / raw)
To: Dongli Zhang; +Cc: Oliver Upton, Marc Zyngier, kvm, kvmarm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] The patch addresses the steal time corruption on vCPU PID change for x86 KVM PV but completely misses the equivalent KVM Xen steal time (`vcpu->arch.xen.last_steal`), leaving it exposed to the identical integer underflow.
- [High] The identical vCPU thread migration bug exists in ARM64, RISC-V, and LoongArch steal time accounting, but the patch only implements the fix for x86.
--
--- Patch [1]: [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change ---
commit d7367ba78784ba11d9b540abce98359a8e902828
Author: Dongli Zhang <dongli.zhang@oracle.com>
KVM: x86: Reset last_steal on vCPU pid change
KVM does not reset last_steal on vCPU re-creation/hotplug, causing a bogus
large steal time due to run_delay starting from zero. This patch fixes it by
resetting vcpu->arch.st.last_steal in kvm_arch_vcpu_run_pid_change on x86.
> diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
> index 801bf9e520db3..b7cb2ceda6d9a 100644
> --- a/arch/x86/kvm/Kconfig
> +++ b/arch/x86/kvm/Kconfig
> @@ -40,6 +40,7 @@ config KVM_X86
> select HAVE_KVM_MSI
> select HAVE_KVM_CPU_RELAX_INTERCEPT
> select HAVE_KVM_NO_POLL
> + select HAVE_KVM_VCPU_RUN_PID_CHANGE
[Severity: High]
This is a pre-existing issue, but does this same integer underflow bug exist
on other architectures?
Looking at ARM64, RISC-V, and LoongArch, they all track steal time using
current->sched_info.run_delay and subtract the old last_steal value, which
appears identically vulnerable to underflow on vCPU thread recreation.
For example, on ARM64:
arch/arm64/kvm/pvtime.c:kvm_update_stolen_time() {
...
steal += vcpu->arch.steal.last_steal - last_steal;
...
}
(Note that arm64's kvm_arch_vcpu_run_pid_change() returns early if
vcpu_has_run_once(vcpu) is true, skipping the opportunity to update last_steal.)
Are there plans to address this on the other architectures?
> select VIRT_XFER_TO_GUEST_WORK
> select KVM_GENERIC_DIRTYLOG_READ_PROTECT
> select KVM_VFIO
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 47cb9eba113b1..33be45eec32bc 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
[ ... ]
> @@ -3717,6 +3717,13 @@ void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu)
> }
> EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_service_local_tlb_flush_requests);
>
> +int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
> +{
> + vcpu->arch.st.last_steal = current->sched_info.run_delay;
> +
[Severity: High]
This isn't a bug introduced by this patch, but should this function also
reset vcpu->arch.xen.last_steal?
Looking at kvm_xen_update_runstate() in arch/x86/kvm/xen.c, the stolen time
for Xen runstate is calculated similarly:
arch/x86/kvm/xen.c:kvm_xen_update_runstate() {
...
u64 steal_ns = run_delay - vx->last_steal;
delta_ns -= steal_ns;
vx->runstate_times[RUNSTATE_runnable] += steal_ns;
...
vx->last_steal = run_delay;
}
If the thread changes and run_delay resets to 0, wouldn't vx->last_steal
also cause an identical integer underflow?
> + return 0;
> +}
> +
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816053630.527528-1-dongli.zhang@oracle.com?part=1
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change
2026-08-16 5:55 ` sashiko-bot
@ 2026-08-17 20:21 ` Dongli Zhang
0 siblings, 0 replies; 16+ messages in thread
From: Dongli Zhang @ 2026-08-17 20:21 UTC (permalink / raw)
To: sashiko-reviews; +Cc: Oliver Upton, Marc Zyngier, kvm, kvmarm
On Sat, Aug 15, 2026 10:55:25PM -0700, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to
> consider: Pre-existing issues: - [High] The patch addresses the steal time
> corruption on vCPU PID change for x86 KVM PV but completely misses the
> equivalent KVM
>
>
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The patch addresses the steal time corruption on vCPU PID change for x86 KVM PV but completely misses the equivalent KVM Xen steal time (`vcpu->arch.xen.last_steal`), leaving it exposed to the identical integer underflow.
> - [High] The identical vCPU thread migration bug exists in ARM64, RISC-V, and LoongArch steal time accounting, but the patch only implements the fix for x86.
> --
>
> --- Patch [1]: [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change ---
> commit d7367ba78784ba11d9b540abce98359a8e902828
> Author: Dongli Zhang <dongli.zhang@oracle.com>
>
> KVM: x86: Reset last_steal on vCPU pid change
>
> KVM does not reset last_steal on vCPU re-creation/hotplug, causing a bogus
> large steal time due to run_delay starting from zero. This patch fixes it by
> resetting vcpu->arch.st.last_steal in kvm_arch_vcpu_run_pid_change on x86.
>
>> diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
>> index 801bf9e520db3..b7cb2ceda6d9a 100644
>> --- a/arch/x86/kvm/Kconfig
>> +++ b/arch/x86/kvm/Kconfig
>> @@ -40,6 +40,7 @@ config KVM_X86
>> select HAVE_KVM_MSI
>> select HAVE_KVM_CPU_RELAX_INTERCEPT
>> select HAVE_KVM_NO_POLL
>> + select HAVE_KVM_VCPU_RUN_PID_CHANGE
>
> [Severity: High]
> This is a pre-existing issue, but does this same integer underflow bug exist
> on other architectures?
>
> Looking at ARM64, RISC-V, and LoongArch, they all track steal time using
> current->sched_info.run_delay and subtract the old last_steal value, which
> appears identically vulnerable to underflow on vCPU thread recreation.
>
> For example, on ARM64:
>
> arch/arm64/kvm/pvtime.c:kvm_update_stolen_time() {
> ...
> steal += vcpu->arch.steal.last_steal - last_steal;
> ...
> }
>
> (Note that arm64's kvm_arch_vcpu_run_pid_change() returns early if
> vcpu_has_run_once(vcpu) is true, skipping the opportunity to update last_steal.)
>
> Are there plans to address this on the other architectures?
Patch 2 addresses the arm64 case.
risc-v and loongarch may need similar handling, but I didn't include them in
this series because I only have x86 and arm64 KVM test environments available to
validate the fix. I don't currently have a risc-v or loongarch setup for KVM
testing.
>
>> select VIRT_XFER_TO_GUEST_WORK
>> select KVM_GENERIC_DIRTYLOG_READ_PROTECT
>> select KVM_VFIO
>> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
>> index 47cb9eba113b1..33be45eec32bc 100644
>> --- a/arch/x86/kvm/x86.c
>> +++ b/arch/x86/kvm/x86.c
> [ ... ]
>> @@ -3717,6 +3717,13 @@ void kvm_service_local_tlb_flush_requests(struct kvm_vcpu *vcpu)
>> }
>> EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_service_local_tlb_flush_requests);
>>
>> +int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
>> +{
>> + vcpu->arch.st.last_steal = current->sched_info.run_delay;
>> +
>
> [Severity: High]
> This isn't a bug introduced by this patch, but should this function also
> reset vcpu->arch.xen.last_steal?
>
> Looking at kvm_xen_update_runstate() in arch/x86/kvm/xen.c, the stolen time
> for Xen runstate is calculated similarly:
>
> arch/x86/kvm/xen.c:kvm_xen_update_runstate() {
> ...
> u64 steal_ns = run_delay - vx->last_steal;
> delta_ns -= steal_ns;
> vx->runstate_times[RUNSTATE_runnable] += steal_ns;
> ...
> vx->last_steal = run_delay;
> }
>
> If the thread changes and run_delay resets to 0, wouldn't vx->last_steal
> also cause an identical integer underflow?
>
As explained in the cover letter, "Although David also suggested doing the same
for Xen-on-KVM vCPUs, this series does not reset last_steal for Xen vCPUs. That
change itself would not be difficult, but Xen uses a different mechanism to
account downtime, including runnable time and offline time when a vCPU is not
running. It may therefore need no additional ioctl, or a smaller ioctl
extension, to account run_delay left over from the previous PID. For now, this
series changes only regular x86 steal time and arm64 PV time."
Dongli Zhang
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 2/4] KVM: arm64: Reset last_steal on vCPU pid change
2026-08-16 5:33 [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Dongli Zhang
2026-08-16 5:33 ` [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change Dongli Zhang
@ 2026-08-16 5:33 ` Dongli Zhang
2026-08-17 8:42 ` Marc Zyngier
2026-08-16 5:33 ` [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86 Dongli Zhang
` (3 subsequent siblings)
5 siblings, 1 reply; 16+ messages in thread
From: Dongli Zhang @ 2026-08-16 5:33 UTC (permalink / raw)
To: kvm, kvmarm, linux-kselftest
Cc: seanjc, pbonzini, maz, oupton, fuad.tabba, joey.gouly, seiden,
suzuki.poulose, yuzenghui, dwmw2, joe.jin
The previous commit resets x86 steal time accounting when the vCPU PID is
changed. Do the same for arm64.
KVM keeps a vCPU fd alive when userspace hot-unplugs a vCPU. If the fd is
later reused from a new vCPU thread, vcpu->arch.steal.last_steal still
reflects the old task's run_delay, while current->sched_info.run_delay
belongs to the new task.
Reset vcpu->arch.steal.last_steal from kvm_arch_vcpu_run_pid_change()
unconditionally so the next stolen time update computes its delta against
the new task's run_delay.
Assisted-by: Codex:GPT-5.5
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
arch/arm64/include/asm/kvm_host.h | 1 +
arch/arm64/kvm/arm.c | 2 ++
arch/arm64/kvm/pvtime.c | 5 +++++
3 files changed, 8 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bae2c4f92ef5..4607f956e787 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -1339,6 +1339,7 @@ static inline bool kvm_arch_pmi_in_guest(struct kvm_vcpu *vcpu)
long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu);
void kvm_update_stolen_time(struct kvm_vcpu *vcpu);
+void kvm_reset_stolen_time(struct kvm_vcpu *vcpu);
bool kvm_arm_pvtime_supported(void);
int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 9a6c72a18672..0f6e63eace21 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -929,6 +929,8 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
if (!kvm_arm_vcpu_is_finalized(vcpu))
return -EPERM;
+ kvm_reset_stolen_time(vcpu);
+
if (likely(vcpu_has_run_once(vcpu)))
return 0;
diff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c
index 4ceabaa4c30b..000bf49cc0fd 100644
--- a/arch/arm64/kvm/pvtime.c
+++ b/arch/arm64/kvm/pvtime.c
@@ -32,6 +32,11 @@ void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
srcu_read_unlock(&kvm->srcu, idx);
}
+void kvm_reset_stolen_time(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.steal.last_steal = current->sched_info.run_delay;
+}
+
long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
{
u32 feature = smccc_get_arg1(vcpu);
--
2.43.5
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 2/4] KVM: arm64: Reset last_steal on vCPU pid change
2026-08-16 5:33 ` [PATCH 2/4] KVM: arm64: " Dongli Zhang
@ 2026-08-17 8:42 ` Marc Zyngier
2026-08-17 21:29 ` Dongli Zhang
0 siblings, 1 reply; 16+ messages in thread
From: Marc Zyngier @ 2026-08-17 8:42 UTC (permalink / raw)
To: Dongli Zhang
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui, dwmw2,
joe.jin
On Sun, 16 Aug 2026 06:33:03 +0100,
Dongli Zhang <dongli.zhang@oracle.com> wrote:
>
> The previous commit resets x86 steal time accounting when the vCPU PID is
> changed. Do the same for arm64.
Drop this statement, it really doesn't provide any information.
>
> KVM keeps a vCPU fd alive when userspace hot-unplugs a vCPU. If the fd is
> later reused from a new vCPU thread, vcpu->arch.steal.last_steal still
> reflects the old task's run_delay, while current->sched_info.run_delay
> belongs to the new task.
>
> Reset vcpu->arch.steal.last_steal from kvm_arch_vcpu_run_pid_change()
> unconditionally so the next stolen time update computes its delta against
> the new task's run_delay.
>
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
> ---
> arch/arm64/include/asm/kvm_host.h | 1 +
> arch/arm64/kvm/arm.c | 2 ++
> arch/arm64/kvm/pvtime.c | 5 +++++
> 3 files changed, 8 insertions(+)
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index bae2c4f92ef5..4607f956e787 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -1339,6 +1339,7 @@ static inline bool kvm_arch_pmi_in_guest(struct kvm_vcpu *vcpu)
> long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
> gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu);
> void kvm_update_stolen_time(struct kvm_vcpu *vcpu);
> +void kvm_reset_stolen_time(struct kvm_vcpu *vcpu);
>
> bool kvm_arm_pvtime_supported(void);
> int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 9a6c72a18672..0f6e63eace21 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -929,6 +929,8 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
> if (!kvm_arm_vcpu_is_finalized(vcpu))
> return -EPERM;
>
> + kvm_reset_stolen_time(vcpu);
> +
> if (likely(vcpu_has_run_once(vcpu)))
> return 0;
>
> diff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c
> index 4ceabaa4c30b..000bf49cc0fd 100644
> --- a/arch/arm64/kvm/pvtime.c
> +++ b/arch/arm64/kvm/pvtime.c
> @@ -32,6 +32,11 @@ void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
> srcu_read_unlock(&kvm->srcu, idx);
> }
>
> +void kvm_reset_stolen_time(struct kvm_vcpu *vcpu)
> +{
> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
> +}
> +
> long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
> {
> u32 feature = smccc_get_arg1(vcpu);
Why isn't this common code? I really don't see the point in making
this arch-specific code. I'd expect something like this (untested):
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index ace5801a592f..3fb77360af01 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -950,6 +950,8 @@ struct kvm_vcpu_arch {
pid_t pid;
};
+#define kvm_arch_vcpu_last_steal(v) (v)->arch.steal.last_steal
+
/*
* Each 'flag' is composed of a comma-separated triplet:
*
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 45e784462ec6..117aeb49231a 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4459,6 +4459,9 @@ static long kvm_vcpu_ioctl(struct file *filp,
if (r)
break;
+ if (IS_ENABLED(CONFIG_HAVE_PV_STEAL_CLOCK_GEN))
+ kvm_arch_vcpu_last_steal(vcpu) = current->sched_info.run_delay;
+
newpid = get_task_pid(current, PIDTYPE_PID);
write_lock(&vcpu->pid_lock);
vcpu->pid = newpid;
where each architecture that implements steal time provides an
accessor, and the core code is in charge of the adjustment.
It also makes sure that we don't leave any architecture behind.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 2/4] KVM: arm64: Reset last_steal on vCPU pid change
2026-08-17 8:42 ` Marc Zyngier
@ 2026-08-17 21:29 ` Dongli Zhang
0 siblings, 0 replies; 16+ messages in thread
From: Dongli Zhang @ 2026-08-17 21:29 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui, dwmw2,
joe.jin
On Mon, Aug 17, 2026 1:42:20AM -0700, Marc Zyngier wrote:
> On Sun, 16 Aug 2026 06:33:03 +0100,
> Dongli Zhang <dongli.zhang@oracle.com> wrote:
>>
>> The previous commit resets x86 steal time accounting when the vCPU PID is
>> changed. Do the same for arm64.
>
> Drop this statement, it really doesn't provide any information.
Sure.
>
>>
>> KVM keeps a vCPU fd alive when userspace hot-unplugs a vCPU. If the fd is
>> later reused from a new vCPU thread, vcpu->arch.steal.last_steal still
>> reflects the old task's run_delay, while current->sched_info.run_delay
>> belongs to the new task.
>>
>> Reset vcpu->arch.steal.last_steal from kvm_arch_vcpu_run_pid_change()
>> unconditionally so the next stolen time update computes its delta against
>> the new task's run_delay.
>>
>> Assisted-by: Codex:GPT-5.5
>> Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
>> ---
>> arch/arm64/include/asm/kvm_host.h | 1 +
>> arch/arm64/kvm/arm.c | 2 ++
>> arch/arm64/kvm/pvtime.c | 5 +++++
>> 3 files changed, 8 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
>> index bae2c4f92ef5..4607f956e787 100644
>> --- a/arch/arm64/include/asm/kvm_host.h
>> +++ b/arch/arm64/include/asm/kvm_host.h
>> @@ -1339,6 +1339,7 @@ static inline bool kvm_arch_pmi_in_guest(struct kvm_vcpu *vcpu)
>> long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu);
>> gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu);
>> void kvm_update_stolen_time(struct kvm_vcpu *vcpu);
>> +void kvm_reset_stolen_time(struct kvm_vcpu *vcpu);
>>
>> bool kvm_arm_pvtime_supported(void);
>> int kvm_arm_pvtime_set_attr(struct kvm_vcpu *vcpu,
>> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
>> index 9a6c72a18672..0f6e63eace21 100644
>> --- a/arch/arm64/kvm/arm.c
>> +++ b/arch/arm64/kvm/arm.c
>> @@ -929,6 +929,8 @@ int kvm_arch_vcpu_run_pid_change(struct kvm_vcpu *vcpu)
>> if (!kvm_arm_vcpu_is_finalized(vcpu))
>> return -EPERM;
>>
>> + kvm_reset_stolen_time(vcpu);
>> +
>> if (likely(vcpu_has_run_once(vcpu)))
>> return 0;
>>
>> diff --git a/arch/arm64/kvm/pvtime.c b/arch/arm64/kvm/pvtime.c
>> index 4ceabaa4c30b..000bf49cc0fd 100644
>> --- a/arch/arm64/kvm/pvtime.c
>> +++ b/arch/arm64/kvm/pvtime.c
>> @@ -32,6 +32,11 @@ void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
>> srcu_read_unlock(&kvm->srcu, idx);
>> }
>>
>> +void kvm_reset_stolen_time(struct kvm_vcpu *vcpu)
>> +{
>> + vcpu->arch.steal.last_steal = current->sched_info.run_delay;
>> +}
>> +
>> long kvm_hypercall_pv_features(struct kvm_vcpu *vcpu)
>> {
>> u32 feature = smccc_get_arg1(vcpu);
>
> Why isn't this common code? I really don't see the point in making
> this arch-specific code. I'd expect something like this (untested):
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index ace5801a592f..3fb77360af01 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -950,6 +950,8 @@ struct kvm_vcpu_arch {
> pid_t pid;
> };
>
> +#define kvm_arch_vcpu_last_steal(v) (v)->arch.steal.last_steal
> +
> /*
> * Each 'flag' is composed of a comma-separated triplet:
> *
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index 45e784462ec6..117aeb49231a 100644
> --- a/virt/kvm/kvm_main.c
> +++ b/virt/kvm/kvm_main.c
> @@ -4459,6 +4459,9 @@ static long kvm_vcpu_ioctl(struct file *filp,
> if (r)
> break;
>
> + if (IS_ENABLED(CONFIG_HAVE_PV_STEAL_CLOCK_GEN))
> + kvm_arch_vcpu_last_steal(vcpu) = current->sched_info.run_delay;
> +
> newpid = get_task_pid(current, PIDTYPE_PID);
> write_lock(&vcpu->pid_lock);
> vcpu->pid = newpid;
>
> where each architecture that implements steal time provides an
> accessor, and the core code is in charge of the adjustment.
>
> It also makes sure that we don't leave any architecture behind.
>
Or how about making it something like below?
if (IS_ENABLED(CONFIG_HAVE_PV_STEAL_CLOCK_GEN))
kvm_arch_vcpu_reset_last_steal(vcpu);
That would still move the policy to common KVM code, while leaving the exact
arch state to the architecture implementation.
For x86, the hook can reset vcpu->arch.st.last_steal for regular KVM steal time.
If we also decide to cover Xen runstate in this series, the same x86 hook can
additionally reset vcpu->arch.xen.last_steal.
Thank you very much!
Dongli Zhang
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86
2026-08-16 5:33 [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Dongli Zhang
2026-08-16 5:33 ` [PATCH 1/4] KVM: x86: Reset last_steal on vCPU pid change Dongli Zhang
2026-08-16 5:33 ` [PATCH 2/4] KVM: arm64: " Dongli Zhang
@ 2026-08-16 5:33 ` Dongli Zhang
2026-08-16 5:55 ` sashiko-bot
2026-08-16 5:33 ` [PATCH 4/4] KVM: selftests: Add arm64 coverage for steal time pid changes Dongli Zhang
` (2 subsequent siblings)
5 siblings, 1 reply; 16+ messages in thread
From: Dongli Zhang @ 2026-08-16 5:33 UTC (permalink / raw)
To: kvm, kvmarm, linux-kselftest
Cc: seanjc, pbonzini, maz, oupton, fuad.tabba, joey.gouly, seiden,
suzuki.poulose, yuzenghui, dwmw2, joe.jin
Add a selftest for the case where the same vCPU fd is run from a new host
thread after steal time has already been enabled and updated.
Pin the vCPU thread and a busy-loop thread to CPU 0, force host-side
run_delay to accumulate, and run the vCPU again to observe guest steal
time. Then run the same vCPU fd from a newly created host thread and verify
that the next steal time value observed on the new thread remains monotonic
and sane relative to the value observed on the old thread.
This indirectly validates that vcpu->arch.st.last_steal is reset when the
vCPU run PID changes.
Assisted-by: Codex:GPT-5.5
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/steal_time_change_pid.c | 162 ++++++++++++++++++
2 files changed, 163 insertions(+)
create mode 100644 tools/testing/selftests/kvm/steal_time_change_pid.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 6fc34e9bf8e1..c8ce17851561 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -159,6 +159,7 @@ TEST_GEN_PROGS_x86 += hardware_disable_test
TEST_GEN_PROGS_x86 += mmu_stress_test
TEST_GEN_PROGS_x86 += rseq_test
TEST_GEN_PROGS_x86 += steal_time
+TEST_GEN_PROGS_x86 += steal_time_change_pid
TEST_GEN_PROGS_x86 += system_counter_offset_test
TEST_GEN_PROGS_x86 += pre_fault_memory_test
diff --git a/tools/testing/selftests/kvm/steal_time_change_pid.c b/tools/testing/selftests/kvm/steal_time_change_pid.c
new file mode 100644
index 000000000000..3c39594db398
--- /dev/null
+++ b/tools/testing/selftests/kvm/steal_time_change_pid.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Verify that KVM resets steal-time accounting when a vCPU fd is run from
+ * a different host PID.
+ */
+
+#include <pthread.h>
+#include <asm/kvm_para.h>
+#include "kvm_util.h"
+#include "processor.h"
+
+#define ST_GPA_BASE (1 << 30)
+#define ST_SANE_DELTA_NS (1ULL << 63)
+
+static void *st_gva;
+static u64 guest_stolen_time;
+static u64 main_steal;
+static u64 thread_steal;
+
+#if defined(__x86_64__)
+
+#define STEAL_TIME_SIZE ((sizeof(struct kvm_steal_time) + 63) & ~63)
+
+static void guest_code(void)
+{
+ struct kvm_steal_time *st = st_gva;
+
+ WRITE_ONCE(guest_stolen_time, READ_ONCE(st->steal));
+ GUEST_SYNC(0);
+
+ WRITE_ONCE(guest_stolen_time, READ_ONCE(st->steal));
+ GUEST_SYNC(1);
+
+ WRITE_ONCE(guest_stolen_time, READ_ONCE(st->steal));
+ GUEST_DONE();
+}
+
+static bool steal_time_supported(struct kvm_vcpu *vcpu)
+{
+ return kvm_cpu_has(X86_FEATURE_KVM_STEAL_TIME);
+}
+
+static void steal_time_enable(struct kvm_vcpu *vcpu)
+{
+ vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME,
+ (ulong)st_gva | KVM_MSR_ENABLED);
+}
+
+#else
+#error "steal_time_change_pid is not implemented on this architecture"
+#endif
+
+static void run_vcpu(struct kvm_vcpu *vcpu)
+{
+ struct ucall uc;
+
+ vcpu_run(vcpu);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_SYNC:
+ case UCALL_DONE:
+ break;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ default:
+ TEST_ASSERT(false, "Unexpected exit: %s",
+ exit_reason_str(vcpu->run->exit_reason));
+ }
+}
+
+static void *do_steal_time(void *arg)
+{
+ struct timespec ts, stop;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ stop = timespec_add_ns(ts, MIN_RUN_DELAY_NS);
+
+ while (timespec_to_ns(timespec_sub(ts, stop)) < 0)
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return NULL;
+}
+
+static void *vcpu_thread(void *arg)
+{
+ struct kvm_vcpu *vcpu = arg;
+
+ run_vcpu(vcpu);
+ sync_global_from_guest(vcpu->vm, guest_stolen_time);
+ thread_steal = guest_stolen_time;
+
+ return NULL;
+}
+
+int main(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ pthread_attr_t attr;
+ pthread_t thread;
+ cpu_set_t cpuset;
+ long run_delay;
+ long run_delay_delta;
+
+ ksft_print_header();
+ ksft_set_plan(1);
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(0, &cpuset);
+ pthread_attr_init(&attr);
+ pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
+ pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ ST_GPA_BASE, 1, 1, 0);
+ virt_map(vm, ST_GPA_BASE, ST_GPA_BASE, 1);
+
+ st_gva = (void *)ST_GPA_BASE;
+ sync_global_to_guest(vm, st_gva);
+ memset(addr_gva2hva(vm, ST_GPA_BASE), 0, STEAL_TIME_SIZE);
+
+ TEST_REQUIRE(steal_time_supported(vcpu));
+
+ steal_time_enable(vcpu);
+ run_vcpu(vcpu);
+
+ run_delay = get_run_delay();
+ pthread_create(&thread, &attr, do_steal_time, NULL);
+
+ while (get_run_delay() - run_delay < MIN_RUN_DELAY_NS)
+ sched_yield();
+
+ pthread_join(thread, NULL);
+ run_delay_delta = get_run_delay() - run_delay;
+ TEST_ASSERT(run_delay_delta >= MIN_RUN_DELAY_NS,
+ "Expected run_delay >= %ld, got %ld",
+ MIN_RUN_DELAY_NS, run_delay_delta);
+
+ run_vcpu(vcpu);
+ sync_global_from_guest(vm, guest_stolen_time);
+ main_steal = guest_stolen_time;
+
+ TEST_ASSERT(main_steal >= MIN_RUN_DELAY_NS,
+ "Expected steal time >= %ld, got %"PRIu64,
+ MIN_RUN_DELAY_NS, main_steal);
+
+ pthread_create(&thread, NULL, vcpu_thread, vcpu);
+ pthread_join(thread, NULL);
+
+ TEST_ASSERT(thread_steal >= main_steal &&
+ thread_steal - main_steal < ST_SANE_DELTA_NS,
+ "Expected sane steal after vCPU pid change: "
+ "old=%"PRIu64", new=%"PRIu64,
+ main_steal, thread_steal);
+
+ ksft_test_result_pass("steal time remains sane across vCPU pid change\n");
+
+ pthread_attr_destroy(&attr);
+ kvm_vm_free(vm);
+ ksft_finished();
+}
--
2.43.5
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86
2026-08-16 5:33 ` [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86 Dongli Zhang
@ 2026-08-16 5:55 ` sashiko-bot
2026-08-17 20:28 ` Dongli Zhang
0 siblings, 1 reply; 16+ messages in thread
From: sashiko-bot @ 2026-08-16 5:55 UTC (permalink / raw)
To: Dongli Zhang; +Cc: kvmarm, kvm, Oliver Upton, Marc Zyngier
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The test races and hangs infinitely if the main thread is scheduled during the helper thread's 200µs wall-clock window.
- [Medium] Unhandled `pthread_create()` and `pthread_setaffinity_np()` errors lead to an infinite test hang and undefined behavior.
--
--- Patch [3]: [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86 ---
commit dbc0e15e73158fcb584e1322da57a03214c88dd3
Author: Dongli Zhang <dongli.zhang@oracle.com>
KVM: selftests: Test steal time across vCPU pid changes on x86
Add a selftest for the case where the same vCPU fd is run from a new host
thread after steal time has already been enabled and updated.
> diff --git a/tools/testing/selftests/kvm/steal_time_change_pid.c b/tools/testing/selftests/kvm/steal_time_change_pid.c
> new file mode 100644
> index 0000000000000..3c39594db398e
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/steal_time_change_pid.c
[ ... ]
> +int main(void)
> +{
> + struct kvm_vcpu *vcpu;
> + struct kvm_vm *vm;
> + pthread_attr_t attr;
> + pthread_t thread;
> + cpu_set_t cpuset;
> + long run_delay;
> + long run_delay_delta;
> +
> + ksft_print_header();
> + ksft_set_plan(1);
> +
> + CPU_ZERO(&cpuset);
> + CPU_SET(0, &cpuset);
> + pthread_attr_init(&attr);
> + pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
> + pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
[Severity: Medium]
Is an error check needed for pthread_setaffinity_np()?
If CPU 0 is offline or excluded by the process taskset mask, this will fail
silently. The threads would then run on different CPUs and avoid runqueue
contention, preventing the main thread's wait time from increasing and
potentially causing an infinite hang in the later sched_yield() loop.
[ ... ]
> + steal_time_enable(vcpu);
> + run_vcpu(vcpu);
> +
> + run_delay = get_run_delay();
> + pthread_create(&thread, &attr, do_steal_time, NULL);
[Severity: Medium]
What happens if pthread_create() fails here?
If thread creation fails, do_steal_time() won't execute to generate
contention, and the subsequent loop waiting for run_delay to increase could
spin infinitely.
> +
> + while (get_run_delay() - run_delay < MIN_RUN_DELAY_NS)
> + sched_yield();
[Severity: Medium]
Can this loop hang infinitely if the main thread is scheduled during the
helper thread's wall-clock window?
The helper thread do_steal_time() bounds its execution to strictly
MIN_RUN_DELAY_NS of wall-clock time:
static void *do_steal_time(void *arg)
{
struct timespec ts, stop;
clock_gettime(CLOCK_MONOTONIC, &ts);
stop = timespec_add_ns(ts, MIN_RUN_DELAY_NS);
while (timespec_to_ns(timespec_sub(ts, stop)) < 0)
clock_gettime(CLOCK_MONOTONIC, &ts);
return NULL;
}
If a scheduler tick occurs during this window, the main thread may preempt
the helper thread to evaluate this loop condition.
While the main thread is running, its runqueue wait time (run_delay) does
not increase, but the helper thread's wall-clock timer still advances. As a
result, the helper thread could exit having been on the runqueue/CPU for
exactly the requested wall-clock time, but the main thread will have
accumulated strictly less than MIN_RUN_DELAY_NS of wait time.
Once the helper thread exits, there are no competing threads on the pinned
CPU, so the main thread's wait time stops increasing permanently, causing
this loop to spin infinitely.
[ ... ]
> + run_vcpu(vcpu);
> + sync_global_from_guest(vm, guest_stolen_time);
> + main_steal = guest_stolen_time;
> +
> + TEST_ASSERT(main_steal >= MIN_RUN_DELAY_NS,
> + "Expected steal time >= %ld, got %"PRIu64,
> + MIN_RUN_DELAY_NS, main_steal);
> +
> + pthread_create(&thread, NULL, vcpu_thread, vcpu);
[Severity: Medium]
Should we check the return value of pthread_create() here?
If it fails, the thread variable remains uninitialized, which would trigger
undefined behavior when passed to pthread_join() on the next line.
> + pthread_join(thread, NULL);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260816053630.527528-1-dongli.zhang@oracle.com?part=3
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86
2026-08-16 5:55 ` sashiko-bot
@ 2026-08-17 20:28 ` Dongli Zhang
0 siblings, 0 replies; 16+ messages in thread
From: Dongli Zhang @ 2026-08-17 20:28 UTC (permalink / raw)
To: sashiko-reviews; +Cc: kvmarm, kvm, Oliver Upton, Marc Zyngier
On Sat, Aug 15, 2026 10:55:19PM -0700, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Medium] The test races and hangs infinitely if the main thread is scheduled during the helper thread's 200µs wall-clock window.
> - [Medium] Unhandled `pthread_create()` and `pthread_setaffinity_np()` errors lead to an infinite test hang and undefined behavior.
> --
>
> --- Patch [3]: [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86 ---
> commit dbc0e15e73158fcb584e1322da57a03214c88dd3
> Author: Dongli Zhang <dongli.zhang@oracle.com>
>
> KVM: selftests: Test steal time across vCPU pid changes on x86
>
> Add a selftest for the case where the same vCPU fd is run from a new host
> thread after steal time has already been enabled and updated.
>
>> diff --git a/tools/testing/selftests/kvm/steal_time_change_pid.c b/tools/testing/selftests/kvm/steal_time_change_pid.c
>> new file mode 100644
>> index 0000000000000..3c39594db398e
>> --- /dev/null
>> +++ b/tools/testing/selftests/kvm/steal_time_change_pid.c
>
> [ ... ]
>
>> +int main(void)
>> +{
>> + struct kvm_vcpu *vcpu;
>> + struct kvm_vm *vm;
>> + pthread_attr_t attr;
>> + pthread_t thread;
>> + cpu_set_t cpuset;
>> + long run_delay;
>> + long run_delay_delta;
>> +
>> + ksft_print_header();
>> + ksft_set_plan(1);
>> +
>> + CPU_ZERO(&cpuset);
>> + CPU_SET(0, &cpuset);
>> + pthread_attr_init(&attr);
>> + pthread_attr_setaffinity_np(&attr, sizeof(cpuset), &cpuset);
>> + pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
>
> [Severity: Medium]
> Is an error check needed for pthread_setaffinity_np()?
>
> If CPU 0 is offline or excluded by the process taskset mask, this will fail
> silently. The threads would then run on different CPUs and avoid runqueue
> contention, preventing the main thread's wait time from increasing and
> potentially causing an infinite hang in the later sched_yield() loop.
This test intentionally follows the existing steal_time.c pattern, which uses
the same CPU0 affinity setup, unchecked pthread calls, and run_delay wait loop.
>
> [ ... ]
>
>> + steal_time_enable(vcpu);
>> + run_vcpu(vcpu);
>> +
>> + run_delay = get_run_delay();
>> + pthread_create(&thread, &attr, do_steal_time, NULL);
>
> [Severity: Medium]
> What happens if pthread_create() fails here?
>
> If thread creation fails, do_steal_time() won't execute to generate
> contention, and the subsequent loop waiting for run_delay to increase could
> spin infinitely.
>
>> +
>> + while (get_run_delay() - run_delay < MIN_RUN_DELAY_NS)
>> + sched_yield();
This test intentionally follows the existing steal_time.c pattern, which uses
the same CPU0 affinity setup, unchecked pthread calls, and run_delay wait loop.
>
> [Severity: Medium]
> Can this loop hang infinitely if the main thread is scheduled during the
> helper thread's wall-clock window?
>
> The helper thread do_steal_time() bounds its execution to strictly
> MIN_RUN_DELAY_NS of wall-clock time:
This test intentionally follows the existing steal_time.c pattern, which uses
the same CPU0 affinity setup, unchecked pthread calls, and run_delay wait loop.
>
> static void *do_steal_time(void *arg)
> {
> struct timespec ts, stop;
> clock_gettime(CLOCK_MONOTONIC, &ts);
> stop = timespec_add_ns(ts, MIN_RUN_DELAY_NS);
> while (timespec_to_ns(timespec_sub(ts, stop)) < 0)
> clock_gettime(CLOCK_MONOTONIC, &ts);
> return NULL;
> }
>
> If a scheduler tick occurs during this window, the main thread may preempt
> the helper thread to evaluate this loop condition.
>
> While the main thread is running, its runqueue wait time (run_delay) does
> not increase, but the helper thread's wall-clock timer still advances. As a
> result, the helper thread could exit having been on the runqueue/CPU for
> exactly the requested wall-clock time, but the main thread will have
> accumulated strictly less than MIN_RUN_DELAY_NS of wait time.
>
> Once the helper thread exits, there are no competing threads on the pinned
> CPU, so the main thread's wait time stops increasing permanently, causing
> this loop to spin infinitely.
>
> [ ... ]
>
>> + run_vcpu(vcpu);
>> + sync_global_from_guest(vm, guest_stolen_time);
>> + main_steal = guest_stolen_time;
>> +
>> + TEST_ASSERT(main_steal >= MIN_RUN_DELAY_NS,
>> + "Expected steal time >= %ld, got %"PRIu64,
>> + MIN_RUN_DELAY_NS, main_steal);
>> +
>> + pthread_create(&thread, NULL, vcpu_thread, vcpu);
>
> [Severity: Medium]
> Should we check the return value of pthread_create() here?
>
> If it fails, the thread variable remains uninitialized, which would trigger
> undefined behavior when passed to pthread_join() on the next line.
>
>> + pthread_join(thread, NULL);
>
This test intentionally follows the existing steal_time.c pattern, which uses
the same CPU0 affinity setup, unchecked pthread calls, and run_delay wait loop.
Thank you very much!
Dongli Zhang
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 4/4] KVM: selftests: Add arm64 coverage for steal time pid changes
2026-08-16 5:33 [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Dongli Zhang
` (2 preceding siblings ...)
2026-08-16 5:33 ` [PATCH 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86 Dongli Zhang
@ 2026-08-16 5:33 ` Dongli Zhang
2026-08-17 8:15 ` [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Marc Zyngier
2026-08-17 11:51 ` David Woodhouse
5 siblings, 0 replies; 16+ messages in thread
From: Dongli Zhang @ 2026-08-16 5:33 UTC (permalink / raw)
To: kvm, kvmarm, linux-kselftest
Cc: seanjc, pbonzini, maz, oupton, fuad.tabba, joey.gouly, seiden,
suzuki.poulose, yuzenghui, dwmw2, joe.jin
The previous patch added the common steal_time_change_pid test with x86
support. Add the arm64 support so the same test also covers arm64.
Use KVM_ARM_VCPU_PVTIME_IPA to enable the steal time shared page from
userspace, and use the PV_TIME_ST SMCCC call in the guest to retrieve and
read that page.
Assisted-by: Codex:GPT-5.5
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/steal_time_change_pid.c | 54 +++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index c8ce17851561..b01a3403d602 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -199,6 +199,7 @@ TEST_GEN_PROGS_arm64 += guest_memfd_test
TEST_GEN_PROGS_arm64 += mmu_stress_test
TEST_GEN_PROGS_arm64 += rseq_test
TEST_GEN_PROGS_arm64 += steal_time
+TEST_GEN_PROGS_arm64 += steal_time_change_pid
TEST_GEN_PROGS_s390 = $(TEST_GEN_PROGS_COMMON)
TEST_GEN_PROGS_s390 += s390/memop
diff --git a/tools/testing/selftests/kvm/steal_time_change_pid.c b/tools/testing/selftests/kvm/steal_time_change_pid.c
index 3c39594db398..12d4927d1c01 100644
--- a/tools/testing/selftests/kvm/steal_time_change_pid.c
+++ b/tools/testing/selftests/kvm/steal_time_change_pid.c
@@ -46,6 +46,60 @@ static void steal_time_enable(struct kvm_vcpu *vcpu)
(ulong)st_gva | KVM_MSR_ENABLED);
}
+#elif defined(__aarch64__)
+
+#define STEAL_TIME_SIZE ((sizeof(struct st_time) + 63) & ~63)
+
+#define PV_TIME_ST 0xc5000021
+
+struct st_time {
+ u32 rev;
+ u32 attr;
+ u64 st_time;
+};
+
+static void guest_code(void)
+{
+ struct arm_smccc_res res;
+ struct st_time *st;
+
+ do_smccc(PV_TIME_ST, 0, 0, 0, 0, 0, 0, 0, &res);
+ GUEST_ASSERT_NE(res.a0, -1);
+ GUEST_ASSERT_EQ(res.a0, (ulong)st_gva);
+
+ st = (struct st_time *)res.a0;
+ WRITE_ONCE(guest_stolen_time, READ_ONCE(st->st_time));
+ GUEST_SYNC(0);
+
+ WRITE_ONCE(guest_stolen_time, READ_ONCE(st->st_time));
+ GUEST_SYNC(1);
+
+ WRITE_ONCE(guest_stolen_time, READ_ONCE(st->st_time));
+ GUEST_DONE();
+}
+
+static bool steal_time_supported(struct kvm_vcpu *vcpu)
+{
+ struct kvm_device_attr dev = {
+ .group = KVM_ARM_VCPU_PVTIME_CTRL,
+ .attr = KVM_ARM_VCPU_PVTIME_IPA,
+ };
+
+ return !__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev);
+}
+
+static void steal_time_enable(struct kvm_vcpu *vcpu)
+{
+ u64 st_ipa = (ulong)st_gva;
+ struct kvm_device_attr dev = {
+ .group = KVM_ARM_VCPU_PVTIME_CTRL,
+ .attr = KVM_ARM_VCPU_PVTIME_IPA,
+ .addr = (u64)&st_ipa,
+ };
+
+ vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev);
+}
+
#else
#error "steal_time_change_pid is not implemented on this architecture"
#endif
--
2.43.5
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64)
2026-08-16 5:33 [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Dongli Zhang
` (3 preceding siblings ...)
2026-08-16 5:33 ` [PATCH 4/4] KVM: selftests: Add arm64 coverage for steal time pid changes Dongli Zhang
@ 2026-08-17 8:15 ` Marc Zyngier
2026-08-17 21:05 ` Dongli Zhang
2026-08-17 11:51 ` David Woodhouse
5 siblings, 1 reply; 16+ messages in thread
From: Marc Zyngier @ 2026-08-17 8:15 UTC (permalink / raw)
To: Dongli Zhang
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui, dwmw2,
joe.jin
On Sun, 16 Aug 2026 06:33:01 +0100,
Dongli Zhang <dongli.zhang@oracle.com> wrote:
[...]
> 4. Guest kernel changes are not included. I may send it separately to keep
> this series limited to the KVM hypervisor.
Hold on. Do you mean you are changing the guest visible behaviour of a
PV interface? That's an ABI. It *cannot* change unilaterally.
>
> [PATCH 1/5] x86/kvm: Reset prev_steal_time and prev_steal_time_rq when enabling steal time
> https://lore.kernel.org/all/20260505003044.78693-2-dongli.zhang@oracle.com
>
> 5. There is one remaining corner case: this series resets last_steal when
> the vCPU run PID changes, but not when steal time is enabled. If additional
> host run_delay is accumulated after the PID changes but before the guest
> enables steal time, that delta could be unexpectedly accounted to guest
> vCPU steal time. In practice, this should not happen for Linux guests.
Why is Linux immune to this? Also, KVM does not cater for Linux guests
only.
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64)
2026-08-17 8:15 ` [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Marc Zyngier
@ 2026-08-17 21:05 ` Dongli Zhang
0 siblings, 0 replies; 16+ messages in thread
From: Dongli Zhang @ 2026-08-17 21:05 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui, dwmw2,
joe.jin
On Mon, Aug 17, 2026 1:15:02AM -0700, Marc Zyngier wrote:
> On Sun, 16 Aug 2026 06:33:01 +0100,
> Dongli Zhang <dongli.zhang@oracle.com> wrote:
>
> [...]
>
>> 4. Guest kernel changes are not included. I may send it separately to keep
>> this series limited to the KVM hypervisor.
>
> Hold on. Do you mean you are changing the guest visible behaviour of a
> PV interface? That's an ABI. It *cannot* change unilaterally.
No, this does not change the ABI.
Taking x86 as an example, the Linux guest currently does not reset the stealtime
accounting metadata, i.e. rq->prev_steal_time and rq->prev_steal_time_rq, when a
vCPU is brought online. I meant to reset that metadata before enabling KVM x86
steal time via MSR_KVM_STEAL_TIME, so that the guest is not affected by a stale
steal-time value from before the vCPU was offlined.
For example, in the code below, the guest resets rq->prev_steal_time and
rq->prev_steal_time_rq after enabling KVM steal time via MSR_KVM_STEAL_TIME.
[PATCH 1/5] x86/kvm: Reset prev_steal_time and prev_steal_time_rq when enabling
steal time
https://lore.kernel.org/all/20260505003044.78693-2-dongli.zhang@oracle.com
+void sched_steal_time_cpu_init(int cpu, u64 steal)
+{
+ struct rq *rq = cpu_rq(cpu);
+
+ rq->prev_steal_time = steal;
+#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
+ rq->prev_steal_time_rq = steal;
+#endif
+}
+
#ifdef CONFIG_HAVE_PV_STEAL_CLOCK_GEN
static u64 native_steal_clock(int cpu)
{
@@ -337,6 +354,12 @@ static void kvm_register_steal_time(void)
return;
wrmsrq(MSR_KVM_STEAL_TIME, (slow_virt_to_phys(st) | KVM_MSR_ENABLED));
+
+ /*
+ * This CPU is not ready to be scheduled yet.
+ */
+ sched_steal_time_cpu_init(cpu, kvm_steal_clock(cpu));
+
pr_debug("stealtime: cpu %d, msr %llx\n", cpu,
(unsigned long long) slow_virt_to_phys(st));
}
>
>>
>> [PATCH 1/5] x86/kvm: Reset prev_steal_time and prev_steal_time_rq when enabling steal time
>> https://urldefense.com/v3/__https://lore.kernel.org/all/20260505003044.78693-2-dongli.zhang@oracle.com__;!!ACWV5N9M2RV99hQ!Kt5J7E63dvSlZsI1Ovt02JMZuh0BwYZZupi0BMQXDxnYkQ6iPNwB7tuWgd7wSVOEVHCF40thrD8Apg$
>>
>> 5. There is one remaining corner case: this series resets last_steal when
>> the vCPU run PID changes, but not when steal time is enabled. If additional
>> host run_delay is accumulated after the PID changes but before the guest
>> enables steal time, that delta could be unexpectedly accounted to guest
>> vCPU steal time. In practice, this should not happen for Linux guests.
>
> Why is Linux immune to this? Also, KVM does not cater for Linux guests
> only.
I should have explained this more clearly.
I did not mean that Linux is immune in the architectural sense, or that KVM
should rely on Linux guest behavior. The corner case I had in mind is:
1. The vCPU is run by a new host PID, so KVM resets last_steal to the new PID's
current run_delay.
2. The guest has not enabled steal time yet.
3. The new host task accumulates additional run_delay before the guest enables
steal time.
4. When steal time is later enabled, that pre-enable run_delay can be included
in the first stealtime update.
For the Linux guest on QEMU/KVM x86 path, this window should normally be very
small. For example, on x86, a Linux guest enables KVM steal time immediately
from the CPU online path. Also, QEMU does not enter KVM_RUN for an offline
vCPU, so the normal QEMU/KVM hotplug path leaves little opportunity to
accumulate run_delay before steal time is enabled.
Thank you very much!
Dongli Zhang
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64)
2026-08-16 5:33 [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Dongli Zhang
` (4 preceding siblings ...)
2026-08-17 8:15 ` [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64) Marc Zyngier
@ 2026-08-17 11:51 ` David Woodhouse
2026-08-17 22:04 ` Dongli Zhang
5 siblings, 1 reply; 16+ messages in thread
From: David Woodhouse @ 2026-08-17 11:51 UTC (permalink / raw)
To: dongli.zhang
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, maz, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui,
joe.jin
[-- Attachment #1: Type: text/plain, Size: 1497 bytes --]
On Sat, 2026-08-15 at 22:33 -0700, Dongli Zhang wrote:
> 3. Although David also suggested doing the same for Xen-on-KVM vCPUs, this
> series does not reset last_steal for Xen vCPUs.
Hm, the more I look at this, the more I like the Xen-on-KVM approach of
just letting userspace save/restore the values.
Migration Just Works™, as the runstate times are migrated along with
the 'current' runstate being set to RUNSTATE_runnable, and the next
catch-up attributes all the intervening time to steal time, correctly
tracking the migration downtime.
For your vCPU hotplug case, a VMM hosting Xen guests can use the same
API to set all the times to zero and the current runstate to
RUNSTATE_offline.
Shouldn't we just add the same save/restore facility for the native KVM
steal time too? It'd make live migration fully transparent, and a VMM
could handle your vCPU hotplug case just the same way by setting it to
zero (the ioctl would recapture last_steal on the current pid just like
KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA does).
On the *guest* side, is anything really needed once we fix the host not
to expose nonsense values? You've given Marc the impression that we
need guest-side changes to match the host-side changes in this thread,
and that wasn't my understanding.
We *should* still fix the guests, of course, but I still prefer my
approach to that, as discussed in
https://lore.kernel.org/all/f0535c47ea81a311efd5cade70543cdf7b25b15c.camel@infradead.org/
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 6179 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64)
2026-08-17 11:51 ` David Woodhouse
@ 2026-08-17 22:04 ` Dongli Zhang
2026-08-17 22:23 ` David Woodhouse
0 siblings, 1 reply; 16+ messages in thread
From: Dongli Zhang @ 2026-08-17 22:04 UTC (permalink / raw)
To: David Woodhouse
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, maz, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui,
joe.jin
On Mon, Aug 17, 2026 4:51:38AM -0700, David Woodhouse wrote:
> On Sat, 2026-08-15 at 22:33 -0700, Dongli Zhang wrote:
>> 3. Although David also suggested doing the same for Xen-on-KVM vCPUs, this
>> series does not reset last_steal for Xen vCPUs.
>
> Hm, the more I look at this, the more I like the Xen-on-KVM approach of
> just letting userspace save/restore the values.
>
> Migration Just Works™, as the runstate times are migrated along with
> the 'current' runstate being set to RUNSTATE_runnable, and the next
> catch-up attributes all the intervening time to steal time, correctly
> tracking the migration downtime.
>
> For your vCPU hotplug case, a VMM hosting Xen guests can use the same
> API to set all the times to zero and the current runstate to
> RUNSTATE_offline.
>
> Shouldn't we just add the same save/restore facility for the native KVM
> steal time too? It'd make live migration fully transparent, and a VMM
> could handle your vCPU hotplug case just the same way by setting it to
> zero (the ioctl would recapture last_steal on the current pid just like
> KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA does).
+1
I fully agree with this.
I agree that a save/restore for native KVM stealtime is the best approach so
far. Instead of only getting/setting last_steal, perhaps it should also include
the current vCPU kvm-clock value when the vCPU is preempted. In addition to live
migration and vCPU hotplug, I have recently identified more scenarios where
steal time can be problematic, which I can share in a separate thread.
That said, KVM steal time may need to be rearchitected.
However, I still think the current patchset is useful. It is small and can be
backported to stable kernels, where adding support for a new ioctl would not be
appropriate.
>
>
> On the *guest* side, is anything really needed once we fix the host not
> to expose nonsense values? You've given Marc the impression that we
> need guest-side changes to match the host-side changes in this thread,
> and that wasn't my understanding.
>
> We *should* still fix the guests, of course, but I still prefer my
> approach to that, as discussed in
> https://lore.kernel.org/all/f0535c47ea81a311efd5cade70543cdf7b25b15c.camel@infradead.org/
Regarding the guest side: yes, your approach avoids accounting an excessive
amount of steal time from bogus host values. But I still wonder whether it also
makes sense for the guest to reset its own baseline whenever it starts using the
feature.
For example, KVM paravirtualization initializes "kvm_sched_clock_offset" when
kvm-clock starts being used, and Xen paravirtualization uses
"xen_sched_clock_offset" for a similar purpose. So perhaps it is not a bad idea
to reset the baseline when enabling a paravirtual feature.
For x86 KVM steal time, that would mean resetting "rq->prev_steal_time" and
"rq->prev_steal_time_rq" before enabling MSR_KVM_STEAL_TIME.
Thank you very much!
Dongli Zhang
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/4] KVM: Reset steal time accounting on vCPU pid change (x86 and arm64)
2026-08-17 22:04 ` Dongli Zhang
@ 2026-08-17 22:23 ` David Woodhouse
0 siblings, 0 replies; 16+ messages in thread
From: David Woodhouse @ 2026-08-17 22:23 UTC (permalink / raw)
To: Dongli Zhang
Cc: kvm, kvmarm, linux-kselftest, seanjc, pbonzini, maz, oupton,
fuad.tabba, joey.gouly, seiden, suzuki.poulose, yuzenghui,
joe.jin
[-- Attachment #1: Type: text/plain, Size: 3835 bytes --]
On Mon, 2026-08-17 at 15:04 -0700, Dongli Zhang wrote:
>
>
> On Mon, Aug 17, 2026 4:51:38AM -0700, David Woodhouse wrote:
> > On Sat, 2026-08-15 at 22:33 -0700, Dongli Zhang wrote:
> > > 3. Although David also suggested doing the same for Xen-on-KVM vCPUs, this
> > > series does not reset last_steal for Xen vCPUs.
> >
> > Hm, the more I look at this, the more I like the Xen-on-KVM approach of
> > just letting userspace save/restore the values.
> >
> > Migration Just Works™, as the runstate times are migrated along with
> > the 'current' runstate being set to RUNSTATE_runnable, and the next
> > catch-up attributes all the intervening time to steal time, correctly
> > tracking the migration downtime.
> >
> > For your vCPU hotplug case, a VMM hosting Xen guests can use the same
> > API to set all the times to zero and the current runstate to
> > RUNSTATE_offline.
> >
> > Shouldn't we just add the same save/restore facility for the native KVM
> > steal time too? It'd make live migration fully transparent, and a VMM
> > could handle your vCPU hotplug case just the same way by setting it to
> > zero (the ioctl would recapture last_steal on the current pid just like
> > KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA does).
>
> +1
>
> I fully agree with this.
>
> I agree that a save/restore for native KVM stealtime is the best approach so
> far. Instead of only getting/setting last_steal, perhaps it should also include
> the current vCPU kvm-clock value when the vCPU is preempted.
There's no "perhaps" about it. You NEVER add clock APIs which just take
one clock value in isolation with nothing to relate it to. Yes, it
*absolutely* has to be a tuple of { steal, kvmclock } at the same
moment, much like the Xen runstates are.
I guess the 'set' API might also want a flag to indicate whether the
delta (the time from the kvmclock in the tuple, to the current
kvmclock) should be accounted as stolen or not. Or would it *always* be
stolen?
> In addition to live
> migration and vCPU hotplug, I have recently identified more scenarios where
> steal time can be problematic, which I can share in a separate thread.
>
> That said, KVM steal time may need to be rearchitected.
>
> However, I still think the current patchset is useful. It is small and can be
> backported to stable kernels, where adding support for a new ioctl would not be
> appropriate.
>
> >
> >
> > On the *guest* side, is anything really needed once we fix the host not
> > to expose nonsense values? You've given Marc the impression that we
> > need guest-side changes to match the host-side changes in this thread,
> > and that wasn't my understanding.
> >
> > We *should* still fix the guests, of course, but I still prefer my
> > approach to that, as discussed in
> > https://lore.kernel.org/all/f0535c47ea81a311efd5cade70543cdf7b25b15c.camel@infradead.org/
>
>
> Regarding the guest side: yes, your approach avoids accounting an excessive
> amount of steal time from bogus host values. But I still wonder whether it also
> makes sense for the guest to reset its own baseline whenever it starts using the
> feature.
>
> For example, KVM paravirtualization initializes "kvm_sched_clock_offset" when
> kvm-clock starts being used, and Xen paravirtualization uses
> "xen_sched_clock_offset" for a similar purpose. So perhaps it is not a bad idea
> to reset the baseline when enabling a paravirtual feature.
>
> For x86 KVM steal time, that would mean resetting "rq->prev_steal_time" and
> "rq->prev_steal_time_rq" before enabling MSR_KVM_STEAL_TIME.
Yeah, maybe. Although when you said above that it might need to be
rearchitected, I was kind of hoping you were planning for KVM to
remember the actual value and not just increment it in guest memory.
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 6179 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread