[PATCH v2 3/4] KVM: selftests: Test steal time across vCPU pid changes on x86
Fuad Tabba
fuad.tabba at linux.dev
Sun Sep 6 10:27:42 PDT 2026
Hi Dongli,
On Fri, 4 Sept 2026 at 18:56, Dongli Zhang <dongli.zhang at oracle.com> wrote:
>
> 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.
Since this is based on steal_time.c anyway, would it make sense to
fold it in rather than add a separate file?
steal_time.c already has the per-arch scaffolding, so the pid-change
case could reuse the existing riscv and loongarch support (which you
said you couldn't add here for lack of hosts) instead of duplicating
the x86/arm64 setup. Or am I missing something?
Cheers,
/fuad
>
> 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->last_steal is reset when the vCPU run
> PID changes.
>
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Dongli Zhang <dongli.zhang at oracle.com>
> ---
> As I have access to only x86 and arm64 KVM hosts, I created and validated
> the selftest on those two architectures only.
>
> 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 96bab7002d39..036c28849f84 100644
> --- a/tools/testing/selftests/kvm/Makefile.kvm
> +++ b/tools/testing/selftests/kvm/Makefile.kvm
> @@ -164,6 +164,7 @@ TEST_GEN_PROGS_x86 += irq_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.7
>
More information about the kvm-riscv
mailing list