From nobody Mon Aug 24 21:40:51 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=none dis=none) header.from=infradead.org ARC-Seal: i=1; a=rsa-sha256; t=1783113866; cv=none; d=zohomail.com; s=zohoarc; b=NG23NWrZWNCjCn9T+THGjO8By02ZDYqZma0ia272YHcVMQqvrxt4cNuI1p/F77SVNcXZbwURDZYWtf9esffwez8caOT6rHIZ6ee887WRd7GpoOs6h5e+CT5Z7QvHi5LsCstKPR+C5H8nkkyS9UGd5nC1V25tXiCg8ssmb/vPPVw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783113866; h=Content-Type:Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To:Cc; bh=wRdzvKD2bSU5nsIEUajIFrEJsqOT3VIHViFgVZVhuqk=; b=R+owTb6Df80W/gvX4cRrzvRfVMkYBmDGX5SsFaXrkrlCB5/lY7F1DbY2JC4ltA5mv7TsqP6/YnFSZdl81Xe9RBpGASgjtJiu76nd3IWjQzPexPG0mgU+nX5tvRt0gK+NZAZcCeGZwEcjoytPS+gljj0FAc4PYC+O5Lpb/J92j8Y= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1783113866400241.41556134000416; Fri, 3 Jul 2026 14:24:26 -0700 (PDT) Received: from list by lists.xenproject.org with outflank-mailman.1353908.1609705 (Exim 4.92) (envelope-from ) id 1wflMd-0008E9-3e; Fri, 03 Jul 2026 21:23:59 +0000 Received: by outflank-mailman (output) from mailman id 1353908.1609705; Fri, 03 Jul 2026 21:23:59 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wflMc-0008A0-EG; Fri, 03 Jul 2026 21:23:58 +0000 Received: by outflank-mailman (input) for mailman id 1353908; Fri, 03 Jul 2026 21:23:54 +0000 Received: from mx.expurgate.net ([195.190.135.10]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1wflMY-0007C5-A7 for xen-devel@lists.xenproject.org; Fri, 03 Jul 2026 21:23:54 +0000 Received: from mx.expurgate.net (helo=localhost) by mx.expurgate.net with esmtp id 1wflMX-001XO6-N0; Fri, 03 Jul 2026 23:23:53 +0200 Received: from [10.42.69.5] (helo=localhost) by localhost with ESMTP (eXpurgate MTA 0.9.1) (envelope-from ) id 6a4827d9-5cb7-0a2a0a5109dd-0a2a4505dcf2-44 for ; Fri, 03 Jul 2026 23:23:53 +0200 Received: from [90.155.92.199] (helo=desiato.infradead.org) by tlsNG-c201ff.mxtls.expurgate.net with ESMTPS (eXpurgate 4.57.1) (envelope-from ) id 6a482869-3cb2-0a2a45050019-5a9b5cc79154-3 for ; Fri, 03 Jul 2026 23:23:53 +0200 Received: from [2001:8b0:10b:1::425] (helo=i7.infradead.org) by desiato.infradead.org with esmtpsa (Exim 4.99.2 #2 (Red Hat Linux)) id 1wflKc-000000059OA-40uX; Fri, 03 Jul 2026 21:23:38 +0000 Received: from dwoodhou by i7.infradead.org with local (Exim 4.99.2 #2 (Red Hat Linux)) id 1wflKX-00000001RPL-0RPj; Fri, 03 Jul 2026 22:21:49 +0100 X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Authentication-Results: eu.smtp.expurgate.cloud; dkim=pass header.s=desiato.20200630 header.d=infradead.org header.i="@infradead.org" header.h="Sender:Content-Transfer-Encoding:Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:To:From" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding: Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:To: From:Reply-To:Cc:Content-ID:Content-Description; bh=wRdzvKD2bSU5nsIEUajIFrEJsqOT3VIHViFgVZVhuqk=; b=i2HbQgc8AAYdlD8WujdrvoPn1U qoodV8ewcMdiGBQg69o486bMpQqj3pTV6LFQ+p4lqOS+REgBO5BemjRGoJIYKDskYoX3dzxU/dlWs fn2vzHq+FScArRGbLA7uB4jy7vpg8ka9zk3nmtu49+oDQijwv+s28SVc4L6+1aNJSTs0xpcGVGk+4 PUaalf6n5Bs4BQ+P+PIB8knTn4QDDa19PFHuBjY+s0Xdh5h6LrgJk/Hs3e1pM9yixhVu0mhnBsWjJ q2hW1MEceXMx6MN7p1chCFiOsbQ0DLRYilZs1FhSzRg3dTm8+BBZ+L5AdoSzqbp/un2xzu5DlNmAh h7pBrsWw==; From: David Woodhouse To: Paolo Bonzini , Jonathan Corbet , Shuah Khan , Sean Christopherson , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Vitaly Kuznetsov , Juergen Gross , Boris Ostrovsky , David Woodhouse , Paul Durrant , Jonathan Cameron , Sascha Bischoff , Marc Zyngier , Joey Gouly , Jack Allister , Dongli Zhang , joe.jin@oracle.com, kvm@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, xen-devel@lists.xenproject.org, linux-kselftest@vger.kernel.org Subject: [PATCH v6 23/36] KVM: selftests: Add master clock offset test Date: Fri, 3 Jul 2026 22:18:02 +0100 Message-ID: <20260703212145.343527-24-dwmw2@infradead.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260703212145.343527-1-dwmw2@infradead.org> References: <20260703212145.343527-1-dwmw2@infradead.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: David Woodhouse X-SRS-Rewrite: SMTP reverse-path rewritten from by desiato.infradead.org. See http://www.infradead.org/rpr.html X-purgate-ID: tlsNG-c201ff/1783113833-151042B8-9021AFCA/0/0 X-purgate-type: clean X-purgate-size: 8060 X-ZohoMail-DKIM: pass (identity @infradead.org) X-ZM-MESSAGEID: 1783113867064158500 From: David Woodhouse Verify that KVM master clock mode remains active when vCPUs have different TSC offsets but the same frequency. Creates three vCPUs, sets one to a different TSC value, and confirms: - KVM_CLOCK_HOST_TSC is set (master clock active) - KVM_CLOCK_TSC_STABLE IS set (master clock =E2=87=92 stable flag) - PVCLOCK_TSC_STABLE_BIT is NOT set (offsets differ) Signed-off-by: David Woodhouse Assisted-by: Kiro (claude-opus-4.6-1m) --- tools/testing/selftests/kvm/Makefile.kvm | 1 + .../kvm/x86/masterclock_offset_test.c | 180 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/masterclock_offset_test= .c diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selft= ests/kvm/Makefile.kvm index 90568ab631d7..7ecaaf82056e 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -106,6 +106,7 @@ TEST_GEN_PROGS_x86 +=3D x86/pmu_event_filter_test TEST_GEN_PROGS_x86 +=3D x86/private_mem_conversions_test TEST_GEN_PROGS_x86 +=3D x86/private_mem_kvm_exits_test TEST_GEN_PROGS_x86 +=3D x86/pvclock_test +TEST_GEN_PROGS_x86 +=3D x86/masterclock_offset_test TEST_GEN_PROGS_x86 +=3D x86/pvclock_migration_test TEST_GEN_PROGS_x86 +=3D x86/set_boot_cpu_id TEST_GEN_PROGS_x86 +=3D x86/set_sregs_test diff --git a/tools/testing/selftests/kvm/x86/masterclock_offset_test.c b/to= ols/testing/selftests/kvm/x86/masterclock_offset_test.c new file mode 100644 index 000000000000..88e2bd2edab5 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/masterclock_offset_test.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test that KVM master clock mode works with different TSC offsets + * as long as all vCPUs have the same TSC frequency. + */ +#include +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" + +#include + +#define KVMCLOCK_GPA 0xc0000000ull +#define TSC_OFFSET (1000000000ULL) + +static uint64_t pvclock_calc(struct pvclock_vcpu_time_info *pvti, uint64_t= guest_tsc) +{ + uint64_t delta =3D guest_tsc - pvti->tsc_timestamp; + + if (pvti->tsc_shift >=3D 0) + delta <<=3D pvti->tsc_shift; + else + delta >>=3D -(int)pvti->tsc_shift; + + return pvti->system_time + ((__uint128_t)delta * pvti->tsc_to_system_mul = >> 32); +} + +static void guest_code(void) +{ + wrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | KVM_MSR_ENABLED); + for (;;) + GUEST_SYNC(0); +} + +int main(void) +{ + struct kvm_vcpu *vcpus[3]; + struct kvm_clock_data clock; + struct pvclock_vcpu_time_info pvti[3]; + struct kvm_vm *vm; + uint64_t offset0, host_tsc, clk0, clk2; + int i; + + TEST_REQUIRE(sys_clocksource_is_based_on_tsc()); + + vm =3D vm_create_with_vcpus(3, guest_code, vcpus); + + TEST_REQUIRE(!__vcpu_has_device_attr(vcpus[0], KVM_VCPU_TSC_CTRL, + KVM_VCPU_TSC_OFFSET)); + + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, + KVMCLOCK_GPA, 1, + vm_calc_num_guest_pages(VM_MODE_DEFAULT, + getpagesize()), 0); + virt_map(vm, KVMCLOCK_GPA, KVMCLOCK_GPA, + vm_calc_num_guest_pages(VM_MODE_DEFAULT, getpagesize())); + + /* Get vCPU 0's default offset and set vCPU 2's offset higher */ + vcpu_device_attr_get(vcpus[0], KVM_VCPU_TSC_CTRL, + KVM_VCPU_TSC_OFFSET, &offset0); + uint64_t offset2 =3D offset0 + TSC_OFFSET; + vcpu_device_attr_set(vcpus[2], KVM_VCPU_TSC_CTRL, + KVM_VCPU_TSC_OFFSET, &offset2); + + /* Run each vCPU to enable kvmclock (with offset already set) */ + for (i =3D 0; i < 3; i++) { + vcpu_run(vcpus[i]); + TEST_ASSERT_KVM_EXIT_REASON(vcpus[i], KVM_EXIT_IO); + } + + /* Check master clock is active */ + memset(&clock, 0, sizeof(clock)); + vm_ioctl(vm, KVM_GET_CLOCK, &clock); + pr_info("KVM_GET_CLOCK flags: 0x%x\n", clock.flags); + TEST_ASSERT(clock.flags & KVM_CLOCK_HOST_TSC, + "Master clock should be active, flags=3D0x%x", clock.flags); + TEST_ASSERT(clock.flags & KVM_CLOCK_TSC_STABLE, + "KVM_CLOCK_TSC_STABLE should be set, flags=3D0x%x", clock.flags); + + /* Get per-vCPU pvclock in order 0, 2, 1 */ + int order[] =3D {0, 2, 1}; + for (i =3D 0; i < 3; i++) { + int idx =3D order[i]; + __vcpu_ioctl(vcpus[idx], KVM_GET_CLOCK_GUEST, &pvti[idx]); + pr_info("vCPU %d: tsc_timestamp=3D%lu system_time=3D%lu " + "mul=3D%u shift=3D%d flags=3D0x%x\n", + idx, (unsigned long)pvti[idx].tsc_timestamp, + (unsigned long)pvti[idx].system_time, + pvti[idx].tsc_to_system_mul, pvti[idx].tsc_shift, + pvti[idx].flags); + } + + /* Read guest TSCs: should see (0+OFF) < 2 < (1+OFF) */ + uint64_t gtsc0 =3D vcpu_get_msr(vcpus[0], MSR_IA32_TSC); + uint64_t gtsc2 =3D vcpu_get_msr(vcpus[2], MSR_IA32_TSC); + uint64_t gtsc1 =3D vcpu_get_msr(vcpus[1], MSR_IA32_TSC); + pr_info("Guest TSCs: vcpu0=3D%lu vcpu2=3D%lu vcpu1=3D%lu\n", + (unsigned long)gtsc0, (unsigned long)gtsc2, (unsigned long)gtsc1); + pr_info("vcpu0+OFF=3D%lu vcpu1+OFF=3D%lu\n", + (unsigned long)(gtsc0 + TSC_OFFSET), + (unsigned long)(gtsc1 + TSC_OFFSET)); + TEST_ASSERT(gtsc0 + TSC_OFFSET < gtsc2 && gtsc2 < gtsc1 + TSC_OFFSET, + "Expected (vcpu0+OFF) < vcpu2 < (vcpu1+OFF)"); + + /* PVCLOCK_TSC_STABLE_BIT should NOT be set (offsets differ) */ + TEST_ASSERT(!(pvti[2].flags & PVCLOCK_TSC_STABLE_BIT), + "PVCLOCK_TSC_STABLE_BIT should NOT be set, flags=3D0x%x", + pvti[2].flags); + + /* Same mul/shift */ + TEST_ASSERT(pvti[0].tsc_to_system_mul =3D=3D pvti[2].tsc_to_system_mul && + pvti[0].tsc_shift =3D=3D pvti[2].tsc_shift, + "All vCPUs should have same mul/shift"); + + /* + * Read host TSC once. At this instant: + * vCPU 0 guest TSC =3D host_tsc + offset0 + * vCPU 2 guest TSC =3D host_tsc + offset0 + TSC_OFFSET + * Feed each through its pvclock. Expect the same kvmclock. + */ + host_tsc =3D rdtsc(); + clk0 =3D pvclock_calc(&pvti[0], host_tsc + offset0); + clk2 =3D pvclock_calc(&pvti[2], host_tsc + offset0 + TSC_OFFSET); + + pr_info("kvmclock via vCPU 0: %lu ns\n", (unsigned long)clk0); + pr_info("kvmclock via vCPU 2: %lu ns\n", (unsigned long)clk2); + TEST_ASSERT(clk0 =3D=3D clk2, + "kvmclock from offset vCPUs should match exactly, " + "diff=3D%ld ns", (long)(clk2 - clk0)); + + pr_info("PASSED: pvclock consistent across offset vCPUs\n"); + + /* + * Now add an hour to the VM kvmclock via KVM_SET_CLOCK, run each + * vCPU to pick up the update, and check they're still in sync. + */ + { +#define ONE_HOUR_NS (3600ULL * NSEC_PER_SEC) + struct kvm_clock_data setclk =3D { .clock =3D clock.clock + ONE_HOUR_NS = }; + + vm_ioctl(vm, KVM_SET_CLOCK, &setclk); + } + + /* Guest code does GUEST_SYNC then exits =E2=80=94 run each to see update= */ + for (i =3D 0; i < 3; i++) { + vcpu_run(vcpus[order[i]]); + TEST_ASSERT_KVM_EXIT_REASON(vcpus[order[i]], KVM_EXIT_IO); + } + + /* Re-read pvclocks */ + for (i =3D 0; i < 3; i++) + __vcpu_ioctl(vcpus[order[i]], KVM_GET_CLOCK_GUEST, &pvti[order[i]]); + + pr_info("After +1h: vCPU 0 system_time=3D%lu, vCPU 2 system_time=3D%lu\n", + (unsigned long)pvti[0].system_time, + (unsigned long)pvti[2].system_time); + TEST_ASSERT(pvti[0].system_time =3D=3D pvti[2].system_time, + "system_time should still match after KVM_SET_CLOCK"); + + host_tsc =3D rdtsc(); + clk0 =3D pvclock_calc(&pvti[0], host_tsc + offset0); + clk2 =3D pvclock_calc(&pvti[2], host_tsc + offset0 + TSC_OFFSET); + + pr_info("After +1h: kvmclock via vCPU 0: %lu ns\n", (unsigned long)clk0); + pr_info("After +1h: kvmclock via vCPU 2: %lu ns\n", (unsigned long)clk2); + TEST_ASSERT(clk0 =3D=3D clk2, + "After +1h: kvmclock should still match, diff=3D%ld ns", + (long)(clk2 - clk0)); + + /* Verify the clock actually moved by ~1 hour */ + TEST_ASSERT(clk0 > ONE_HOUR_NS, + "Clock should be > 1 hour after set, got %lu ns", + (unsigned long)clk0); + + pr_info("PASSED: pvclock still consistent after KVM_SET_CLOCK +1h\n"); + kvm_vm_free(vm); + return 0; +} --=20 2.54.0