| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/03 22:02 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_vcpu_ioctl_get_clock_guest",
"kvm_vcpu_ioctl_set_clock_guest",
"kvm_arch_tsc_get_attr",
"kvm_arch_tsc_set_attr",
"kvm_arch_tsc_has_attr",
"kvm_guest_time_update",
"kvm_synchronize_tsc",
"kvm_update_masterclock"
],
"Reasoning": "The patch introduces new KVM ioctls (KVM_GET_CLOCK_GUEST, KVM_SET_CLOCK_GUEST) and a new device attribute (KVM_VCPU_TSC_SCALE) for managing guest clocks and TSC scaling. It also modifies core KVM timekeeping functions like kvm_guest_time_update and kvm_synchronize_tsc. These are functional changes to reachable core kernel logic that should be fuzzed.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/03 22:02 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit d0600dd63912d02580f09d082e72314faa4a3434\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Jul 3 22:02:36 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst\nindex 52bbbb553ce107..2268b4442df614 100644\n--- a/Documentation/virt/kvm/api.rst\n+++ b/Documentation/virt/kvm/api.rst\n@@ -6553,6 +6553,43 @@ KVM_S390_KEYOP_SSKE\n Sets the storage key for the guest address ``guest_addr`` to the key\n specified in ``key``, returning the previous value in ``key``.\n \n+4.145 KVM_GET_CLOCK_GUEST\n+----------------------------\n+\n+:Capability: none\n+:Architectures: x86_64\n+:Type: vcpu ioctl\n+:Parameters: struct pvclock_vcpu_time_info (out)\n+:Returns: 0 on success, \u003c0 on error\n+\n+Retrieves the current time information structure used for KVM/PV clocks,\n+in precisely the form advertised to the guest vCPU, which gives parameters\n+for a direct conversion from a guest TSC value to nanoseconds.\n+\n+When the KVM clock is not in \"master clock\" mode, for example because the\n+host TSC is unreliable or the guest TSCs are oddly configured, the KVM clock\n+is actually defined by the host CLOCK_MONOTONIC_RAW instead of the guest TSC.\n+In this case, the KVM_GET_CLOCK_GUEST ioctl returns -EINVAL.\n+\n+4.146 KVM_SET_CLOCK_GUEST\n+----------------------------\n+\n+:Capability: none\n+:Architectures: x86_64\n+:Type: vcpu ioctl\n+:Parameters: struct pvclock_vcpu_time_info (in)\n+:Returns: 0 on success, \u003c0 on error\n+\n+Sets the KVM clock (for the whole VM) in terms of the vCPU TSC, using the\n+pvclock structure as returned by KVM_GET_CLOCK_GUEST. This allows the precise\n+arithmetic relationship between guest TSC and KVM clock to be preserved by\n+userspace across migration.\n+\n+When the KVM clock is not in \"master clock\" mode, and the KVM clock is actually\n+defined by the host CLOCK_MONOTONIC_RAW, this ioctl returns -EINVAL. Userspace\n+may choose to set the clock using the less precise KVM_SET_CLOCK ioctl, or may\n+choose to fail, denying migration to a host whose TSC is misbehaving.\n+\n .. _kvm_run:\n \n 5. The kvm_run structure\ndiff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst\nindex 5e38058200105e..de9f58f75eb931 100644\n--- a/Documentation/virt/kvm/devices/vcpu.rst\n+++ b/Documentation/virt/kvm/devices/vcpu.rst\n@@ -229,6 +229,7 @@ including the layout of the stolen time structure.\n :Architectures: x86\n \n 4.1 ATTRIBUTE: KVM_VCPU_TSC_OFFSET\n+----------------------------------\n \n :Parameters: 64-bit unsigned TSC offset\n \n@@ -243,7 +244,10 @@ Returns:\n Specifies the guest's TSC offset relative to the host's TSC. The guest's\n TSC is then derived by the following equation:\n \n- guest_tsc = host_tsc + KVM_VCPU_TSC_OFFSET\n+ guest_tsc = ((host_tsc * tsc_ratio) \u003e\u003e tsc_frac_bits) + KVM_VCPU_TSC_OFFSET\n+\n+The values of tsc_ratio and tsc_frac_bits can be obtained using\n+the KVM_VCPU_TSC_SCALE attribute.\n \n This attribute is useful to adjust the guest's TSC on live migration,\n so that the TSC counts the time during which the VM was paused. The\n@@ -251,44 +255,100 @@ following describes a possible algorithm to use for this purpose.\n \n From the source VMM process:\n \n-1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (tsc_src),\n+1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (host_tsc_src),\n kvmclock nanoseconds (guest_src), and host CLOCK_REALTIME nanoseconds\n- (host_src).\n+ (time_src) at a given moment (Tsrc).\n+\n+2. For each vCPU[i]:\n+\n+ a. Read the KVM_VCPU_TSC_OFFSET attribute to record the guest TSC offset\n+ (ofs_src[i]).\n \n-2. Read the KVM_VCPU_TSC_OFFSET attribute for every vCPU to record the\n- guest TSC offset (ofs_src[i]).\n+ b. Read the KVM_VCPU_TSC_SCALE attribute to record the guest TSC scaling\n+ ratio (ratio_src[i], frac_bits_src[i]).\n \n-3. Invoke the KVM_GET_TSC_KHZ ioctl to record the frequency of the\n- guest's TSC (freq).\n+ c. Use host_tsc_src and the scaling/offset factors to calculate this\n+ vCPU's TSC at time Tsrc:\n+\n+ tsc_src[i] = ((host_tsc_src * ratio_src[i]) \u003e\u003e frac_bits_src[i]) + ofs_src[i]\n+\n+3. Invoke the KVM_GET_CLOCK_GUEST ioctl on the boot vCPU to return the KVM\n+ clock as a function of the guest TSC (pvti_src). (This ioctl may not\n+ succeed if the host and guest TSCs are not consistent and well-behaved.)\n \n From the destination VMM process:\n \n-4. Invoke the KVM_SET_CLOCK ioctl, providing the source nanoseconds from\n- kvmclock (guest_src) and CLOCK_REALTIME (host_src) in their respective\n- fields. Ensure that the KVM_CLOCK_REALTIME flag is set in the provided\n- structure.\n+4. Before creating the vCPUs, invoke the KVM_SET_TSC_KHZ ioctl on the VM, to\n+ set the scaled frequency of the guest's TSC (freq).\n+\n+5. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (host_tsc_dst) and\n+ host CLOCK_REALTIME nanoseconds (time_dst) at a given moment (Tdst).\n+\n+6. Calculate the number of nanoseconds elapsed between Tsrc and Tdst:\n+\n+ ΔT = time_dst - time_src\n+\n+7. As each vCPU[i] is created:\n+\n+ a. Read the KVM_VCPU_TSC_SCALE attribute to record the guest TSC scaling\n+ ratio (ratio_dst[i], frac_bits_dst[i]).\n+\n+ b. Calculate the intended guest TSC value at time Tdst:\n+\n+ tsc_dst[i] = tsc_src[i] + (ΔT * freq[i])\n \n- KVM will advance the VM's kvmclock to account for elapsed time since\n- recording the clock values. Note that this will cause problems in\n- the guest (e.g., timeouts) unless CLOCK_REALTIME is synchronized\n- between the source and destination, and a reasonably short time passes\n- between the source pausing the VMs and the destination executing\n- steps 4-7.\n+ c. Use host_tsc_dst and the scaling factors to calculate this vCPU's\n+ raw scaled TSC at time Tdst without offsetting:\n+\n+ raw_dst[i] = ((host_tsc_dst * ratio_dst[i]) \u003e\u003e frac_bits_dst[i])\n+\n+ d. Calculate ofs_dst[i] = tsc_dst[i] - raw_dst[i] and set the resulting\n+ offset using the KVM_VCPU_TSC_OFFSET attribute.\n+\n+8. If pvti_src was provided, invoke the KVM_SET_CLOCK_GUEST ioctl on the boot\n+ vCPU to restore the KVM clock as a precise function of the guest TSC.\n+\n+9. If KVM_SET_CLOCK_GUEST was not available or failed (e.g. because the\n+ master clock is not active), fall back to the KVM_SET_CLOCK ioctl,\n+ providing the source nanoseconds from kvmclock (guest_src) and\n+ CLOCK_REALTIME (time_src) in their respective fields. Ensure that the\n+ KVM_CLOCK_REALTIME flag is set in the provided structure.\n+\n+ KVM will restore the VM's kvmclock, accounting for elapsed time since\n+ the clock values were recorded. Note that this will cause problems in\n+ the guest (e.g., timeouts) unless CLOCK_REALTIME is synchronized between\n+ the source and destination, and a reasonably short time passes between\n+ the source pausing the VMs and the destination resuming them.\n+ Due to the KVM_[SG]ET_CLOCK API using CLOCK_REALTIME instead of\n+ CLOCK_TAI, leap seconds during the migration may also introduce errors.\n+\n+4.2 ATTRIBUTE: KVM_VCPU_TSC_SCALE\n+---------------------------------\n+\n+:Parameters: struct kvm_vcpu_tsc_scale\n+\n+Returns:\n+\n+\t ======= ======================================\n+\t -EFAULT Error reading the provided parameter\n+\t\t address.\n+\t -ENXIO Attribute not supported (no TSC scaling)\n+\t -EINVAL Invalid request to write the attribute\n+\t ======= ======================================\n \n-5. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (tsc_dest) and\n- kvmclock nanoseconds (guest_dest).\n+This read-only attribute reports the guest's TSC scaling factor, in the form\n+of a fixed-point number represented by the following structure::\n \n-6. Adjust the guest TSC offsets for every vCPU to account for (1) time\n- elapsed since recording state and (2) difference in TSCs between the\n- source and destination machine:\n+ struct kvm_vcpu_tsc_scale {\n+\t__u64 tsc_ratio;\n+\t__u64 tsc_frac_bits;\n+ };\n \n- ofs_dst[i] = ofs_src[i] -\n- (guest_src - guest_dest) * freq +\n- (tsc_src - tsc_dest)\n+The tsc_frac_bits field indicates the location of the fixed point, such that\n+host TSC values are converted to guest TSC using the formula:\n \n- (\"ofs[i] + tsc - guest * freq\" is the guest TSC value corresponding to\n- a time of 0 in kvmclock. The above formula ensures that it is the\n- same on the destination as it was on the source).\n+ guest_tsc = ((host_tsc * tsc_ratio) \u003e\u003e tsc_frac_bits) + offset\n \n-7. Write the KVM_VCPU_TSC_OFFSET attribute for every vCPU with the\n- respective value derived in the previous step.\n+Userspace can use this to precisely calculate the guest TSC from the host\n+TSC at any given moment. This is needed for accurate migration of guests,\n+as described in the documentation for the KVM_VCPU_TSC_OFFSET attribute.\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 905c29fe499642..057ccef8d1fa5c 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -14420,7 +14420,7 @@ S:\tSupported\n T:\tgit git://git.kernel.org/pub/scm/virt/kvm/kvm.git\n F:\tarch/um/include/asm/kvm_para.h\n F:\tarch/x86/include/asm/kvm_para.h\n-F:\tarch/x86/include/asm/pvclock-abi.h\n+F:\tarch/x86/include/uapi/asm/pvclock-abi.h\n F:\tarch/x86/include/uapi/asm/kvm_para.h\n F:\tarch/x86/kernel/kvm.c\n F:\tarch/x86/kernel/kvmclock.c\n@@ -29105,7 +29105,7 @@ R:\tBoris Ostrovsky \u003cboris.ostrovsky@oracle.com\u003e\n L:\txen-devel@lists.xenproject.org (moderated for non-subscribers)\n S:\tSupported\n F:\tarch/x86/configs/xen.config\n-F:\tarch/x86/include/asm/pvclock-abi.h\n+F:\tarch/x86/include/uapi/asm/pvclock-abi.h\n F:\tarch/x86/include/asm/xen/\n F:\tarch/x86/platform/pvh/\n F:\tarch/x86/xen/\ndiff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h\nindex f14009f25a3b6a..da9141ea7d7b3d 100644\n--- a/arch/x86/include/asm/kvm_host.h\n+++ b/arch/x86/include/asm/kvm_host.h\n@@ -950,7 +950,7 @@ struct kvm_vcpu_arch {\n \tgpa_t time;\n \ts8 pvclock_tsc_shift;\n \tu32 pvclock_tsc_mul;\n-\tunsigned int hw_tsc_khz;\n+\tu64 hw_tsc_hz;\n \tstruct gfn_to_pfn_cache pv_time;\n \t/* set guest stopped flag in pvclock flags field */\n \tbool pvclock_set_guest_stopped_request;\n@@ -970,6 +970,7 @@ struct kvm_vcpu_arch {\n \tu64 this_tsc_nsec;\n \tu64 this_tsc_write;\n \tu64 this_tsc_generation;\n+\tu64 this_tsc_freq_generation;\n \tbool tsc_catchup;\n \tbool tsc_always_catchup;\n \ts8 virtual_tsc_shift;\n@@ -1486,14 +1487,16 @@ struct kvm_arch {\n \t * preemption-disabled region, so it must be a raw spinlock.\n \t */\n \traw_spinlock_t tsc_write_lock;\n-\tu64 last_tsc_nsec;\n-\tu64 last_tsc_write;\n-\tu32 last_tsc_khz;\n-\tu64 last_tsc_offset;\n+\tu32 cur_tsc_khz;\n+\tu64 cur_tsc_scaling_ratio;\n \tu64 cur_tsc_nsec;\n \tu64 cur_tsc_write;\n \tu64 cur_tsc_offset;\n \tu64 cur_tsc_generation;\n+\tbool all_vcpus_matched_tsc;\n+\tbool all_vcpus_matched_freq;\n+\tint nr_vcpus_matched_freq;\n+\tu64 cur_tsc_freq_generation;\n \tint nr_vcpus_matched_tsc;\n \n \tu32 default_tsc_khz;\n@@ -1505,6 +1508,9 @@ struct kvm_arch {\n \tu64 master_kernel_ns;\n \tu64 master_cycle_now;\n \tstruct ratelimit_state kvmclock_update_rs;\n+\tu64 master_tsc_scaling_ratio;\n+\ts8 master_tsc_shift;\n+\tu32 master_tsc_mul;\n \n #ifdef CONFIG_KVM_HYPERV\n \tstruct kvm_hv hyperv;\ndiff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h\nindex 5f2b30d0405c87..fb38486ff74231 100644\n--- a/arch/x86/include/uapi/asm/kvm.h\n+++ b/arch/x86/include/uapi/asm/kvm.h\n@@ -12,6 +12,7 @@\n #include \u003clinux/types.h\u003e\n #include \u003clinux/ioctl.h\u003e\n #include \u003clinux/stddef.h\u003e\n+#include \u003casm/pvclock-abi.h\u003e\n \n #define KVM_PIO_PAGE_OFFSET 1\n #define KVM_COALESCED_MMIO_PAGE_OFFSET 2\n@@ -961,6 +962,12 @@ struct kvm_hyperv_eventfd {\n /* for KVM_{GET,SET,HAS}_DEVICE_ATTR */\n #define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */\n #define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */\n+#define KVM_VCPU_TSC_SCALE 1 /* attribute for TSC scaling factor */\n+\n+struct kvm_vcpu_tsc_scale {\n+\t__u64 tsc_ratio;\n+\t__u64 tsc_frac_bits;\n+};\n \n /* x86-specific KVM_EXIT_HYPERCALL flags. */\n #define KVM_EXIT_HYPERCALL_LONG_MODE\t_BITULL(0)\ndiff --git a/arch/x86/include/asm/pvclock-abi.h b/arch/x86/include/uapi/asm/pvclock-abi.h\nsimilarity index 82%\nrename from arch/x86/include/asm/pvclock-abi.h\nrename to arch/x86/include/uapi/asm/pvclock-abi.h\nindex b9fece5fc96d6f..6d70cf64036293 100644\n--- a/arch/x86/include/asm/pvclock-abi.h\n+++ b/arch/x86/include/uapi/asm/pvclock-abi.h\n@@ -1,6 +1,9 @@\n-/* SPDX-License-Identifier: GPL-2.0 */\n+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */\n #ifndef _ASM_X86_PVCLOCK_ABI_H\n #define _ASM_X86_PVCLOCK_ABI_H\n+\n+#include \u003clinux/types.h\u003e\n+\n #ifndef __ASSEMBLER__\n \n /*\n@@ -24,20 +27,20 @@\n */\n \n struct pvclock_vcpu_time_info {\n-\tu32 version;\n-\tu32 pad0;\n-\tu64 tsc_timestamp;\n-\tu64 system_time;\n-\tu32 tsc_to_system_mul;\n-\ts8 tsc_shift;\n-\tu8 flags;\n-\tu8 pad[2];\n+\t__u32 version;\n+\t__u32 pad0;\n+\t__u64 tsc_timestamp;\n+\t__u64 system_time;\n+\t__u32 tsc_to_system_mul;\n+\t__s8 tsc_shift;\n+\t__u8 flags;\n+\t__u8 pad[2];\n } __attribute__((__packed__)); /* 32 bytes */\n \n struct pvclock_wall_clock {\n-\tu32 version;\n-\tu32 sec;\n-\tu32 nsec;\n+\t__u32 version;\n+\t__u32 sec;\n+\t__u32 nsec;\n } __attribute__((__packed__));\n \n #define PVCLOCK_TSC_STABLE_BIT\t(1 \u003c\u003c 0)\ndiff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c\nindex e69156b54cfffc..826637a0b72d6f 100644\n--- a/arch/x86/kvm/cpuid.c\n+++ b/arch/x86/kvm/cpuid.c\n@@ -2117,22 +2117,6 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,\n \t\t} else if (function == 0x80000007) {\n \t\t\tif (kvm_hv_invtsc_suppressed(vcpu))\n \t\t\t\t*edx \u0026= ~feature_bit(CONSTANT_TSC);\n-\t\t} else if (IS_ENABLED(CONFIG_KVM_XEN) \u0026\u0026\n-\t\t\t kvm_xen_is_tsc_leaf(vcpu, function)) {\n-\t\t\t/*\n-\t\t\t * Update guest TSC frequency information if necessary.\n-\t\t\t * Ignore failures, there is no sane value that can be\n-\t\t\t * provided if KVM can't get the TSC frequency.\n-\t\t\t */\n-\t\t\tif (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu))\n-\t\t\t\tkvm_guest_time_update(vcpu);\n-\n-\t\t\tif (index == 1) {\n-\t\t\t\t*ecx = vcpu-\u003earch.pvclock_tsc_mul;\n-\t\t\t\t*edx = vcpu-\u003earch.pvclock_tsc_shift;\n-\t\t\t} else if (index == 2) {\n-\t\t\t\t*eax = vcpu-\u003earch.hw_tsc_khz;\n-\t\t\t}\n \t\t}\n \t} else {\n \t\t*eax = *ebx = *ecx = *edx = 0;\ndiff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c\nindex e02a38da5296e3..c46a34aeb3dfb8 100644\n--- a/arch/x86/kvm/svm/svm.c\n+++ b/arch/x86/kvm/svm/svm.c\n@@ -5557,7 +5557,8 @@ static __init int svm_hardware_setup(void)\n \t\t\t\t XFEATURE_MASK_BNDCSR);\n \n \tif (tsc_scaling) {\n-\t\tif (!boot_cpu_has(X86_FEATURE_TSCRATEMSR)) {\n+\t\tif (!boot_cpu_has(X86_FEATURE_TSCRATEMSR) ||\n+\t\t !boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) {\n \t\t\ttsc_scaling = false;\n \t\t} else {\n \t\t\tpr_info(\"TSC scaling supported\\n\");\ndiff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c\nindex b9103de01428eb..54e92d94155ed9 100644\n--- a/arch/x86/kvm/vmx/vmx.c\n+++ b/arch/x86/kvm/vmx/vmx.c\n@@ -2792,6 +2792,16 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,\n \tif (!cpu_has_sgx())\n \t\t_cpu_based_2nd_exec_control \u0026= ~SECONDARY_EXEC_ENCLS_EXITING;\n \n+\t/*\n+\t * KVM doesn't re-derive the TSC scaling ratio when the host TSC\n+\t * frequency changes, so TSC scaling is only usable with a constant\n+\t * TSC. Clear the control here rather than in vmx_hardware_setup() so\n+\t * that the per-CPU configs recomputed by vmx_check_processor_compat()\n+\t * stay consistent with the golden vmcs_config.\n+\t */\n+\tif (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))\n+\t\t_cpu_based_2nd_exec_control \u0026= ~SECONDARY_EXEC_TSC_SCALING;\n+\n \tif (_cpu_based_exec_control \u0026 CPU_BASED_ACTIVATE_TERTIARY_CONTROLS)\n \t\t_cpu_based_3rd_exec_control =\n \t\t\tadjust_vmx_controls64(KVM_OPTIONAL_VMX_TERTIARY_VM_EXEC_CONTROL,\ndiff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c\nindex 0550359ed798fc..d35f2ac3ef494c 100644\n--- a/arch/x86/kvm/x86.c\n+++ b/arch/x86/kvm/x86.c\n@@ -35,6 +35,7 @@\n #include \"smm.h\"\n \n #include \u003clinux/clocksource.h\u003e\n+#include \u003clinux/timekeeping.h\u003e\n #include \u003clinux/interrupt.h\u003e\n #include \u003clinux/kvm.h\u003e\n #include \u003clinux/fs.h\u003e\n@@ -203,6 +204,10 @@ module_param(mitigate_smt_rsb, bool, 0444);\n * usermode, e.g. SYSCALL MSRs and TSC_AUX, can be deferred until the CPU\n * returns to userspace, i.e. the kernel can run with the guest's value.\n */\n+#ifdef CONFIG_X86_64\n+static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);\n+#endif\n+static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value);\n #define KVM_MAX_NR_USER_RETURN_MSRS 16\n \n struct kvm_user_return_msrs {\n@@ -2342,71 +2347,11 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)\n \treturn kvm_set_msr_ignored_check(vcpu, index, *data, true);\n }\n \n-#ifdef CONFIG_X86_64\n-struct pvclock_clock {\n-\tint vclock_mode;\n-\tu64 cycle_last;\n-\tu64 mask;\n-\tu32 mult;\n-\tu32 shift;\n-\tu64 base_cycles;\n-\tu64 offset;\n-};\n-\n-struct pvclock_gtod_data {\n-\tseqcount_t\tseq;\n-\n-\tstruct pvclock_clock clock; /* extract of a clocksource struct */\n-\tstruct pvclock_clock raw_clock; /* extract of a clocksource struct */\n-\n-\tktime_t\t\toffs_boot;\n-\tu64\t\twall_time_sec;\n-};\n-\n-static struct pvclock_gtod_data pvclock_gtod_data;\n-\n-static void update_pvclock_gtod(struct timekeeper *tk)\n-{\n-\tstruct pvclock_gtod_data *vdata = \u0026pvclock_gtod_data;\n-\n-\twrite_seqcount_begin(\u0026vdata-\u003eseq);\n-\n-\t/* copy pvclock gtod data */\n-\tvdata-\u003eclock.vclock_mode\t= tk-\u003etkr_mono.clock-\u003evdso_clock_mode;\n-\tvdata-\u003eclock.cycle_last\t\t= tk-\u003etkr_mono.cycle_last;\n-\tvdata-\u003eclock.mask\t\t= tk-\u003etkr_mono.mask;\n-\tvdata-\u003eclock.mult\t\t= tk-\u003etkr_mono.mult;\n-\tvdata-\u003eclock.shift\t\t= tk-\u003etkr_mono.shift;\n-\tvdata-\u003eclock.base_cycles\t= tk-\u003etkr_mono.xtime_nsec;\n-\tvdata-\u003eclock.offset\t\t= tk-\u003etkr_mono.base;\n-\n-\tvdata-\u003eraw_clock.vclock_mode\t= tk-\u003etkr_raw.clock-\u003evdso_clock_mode;\n-\tvdata-\u003eraw_clock.cycle_last\t= tk-\u003etkr_raw.cycle_last;\n-\tvdata-\u003eraw_clock.mask\t\t= tk-\u003etkr_raw.mask;\n-\tvdata-\u003eraw_clock.mult\t\t= tk-\u003etkr_raw.mult;\n-\tvdata-\u003eraw_clock.shift\t\t= tk-\u003etkr_raw.shift;\n-\tvdata-\u003eraw_clock.base_cycles\t= tk-\u003etkr_raw.xtime_nsec;\n-\tvdata-\u003eraw_clock.offset\t\t= tk-\u003etkr_raw.base;\n-\n-\tvdata-\u003ewall_time_sec = tk-\u003extime_sec;\n-\n-\tvdata-\u003eoffs_boot\t\t= tk-\u003eoffs_boot;\n-\n-\twrite_seqcount_end(\u0026vdata-\u003eseq);\n-}\n-\n static s64 get_kvmclock_base_ns(void)\n {\n \t/* Count up from boot time, but with the frequency of the raw clock. */\n-\treturn ktime_to_ns(ktime_add(ktime_get_raw(), pvclock_gtod_data.offs_boot));\n+\treturn ktime_to_ns(ktime_mono_to_any(ktime_get_raw(), TK_OFFS_BOOT));\n }\n-#else\n-static s64 get_kvmclock_base_ns(void)\n-{\n-\t/* Master clock not used, so we can just use CLOCK_BOOTTIME. */\n-\treturn ktime_get_boottime_ns();\n-}\n-#endif\n \n static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock, int sec_hi_ofs)\n {\n@@ -2462,13 +2407,13 @@ static void kvm_write_system_time(struct kvm_vcpu *vcpu, gpa_t system_time,\n \t}\n \n \tvcpu-\u003earch.time = system_time;\n-\tkvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu);\n \n \t/* we verify if the enable bit is set... */\n-\tif (system_time \u0026 1)\n+\tif (system_time \u0026 1) {\n \t\tkvm_gpc_activate(\u0026vcpu-\u003earch.pv_time, system_time \u0026 ~1ULL,\n \t\t\t\t sizeof(struct pvclock_vcpu_time_info));\n-\telse\n+\t\tkvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);\n+\t} else\n \t\tkvm_gpc_deactivate(\u0026vcpu-\u003earch.pv_time);\n \n \treturn;\n@@ -2480,32 +2425,57 @@ static uint32_t div_frac(uint32_t dividend, uint32_t divisor)\n \treturn dividend;\n }\n \n-static void kvm_get_time_scale(uint64_t scaled_hz, uint64_t base_hz,\n+static void kvm_get_time_scale(u64 scaled_hz, u64 base_hz,\n \t\t\t s8 *pshift, u32 *pmultiplier)\n {\n-\tuint64_t scaled64;\n-\tint32_t shift = 0;\n-\tuint64_t tps64;\n-\tuint32_t tps32;\n+\tu64 scaled_hz_u64 = scaled_hz;\n+\ts32 shift = 0;\n+\tu64 base_hz_u64;\n+\tu32 base32;\n \n-\ttps64 = base_hz;\n-\tscaled64 = scaled_hz;\n-\twhile (tps64 \u003e scaled64*2 || tps64 \u0026 0xffffffff00000000ULL) {\n-\t\ttps64 \u003e\u003e= 1;\n+\t/*\n+\t * This function calculates a fixed-point multiplier and shift such\n+\t * that:\n+\t * time_ns = (tsc_cycles \u003c\u003c shift) * multiplier \u003e\u003e 32\n+\t *\n+\t * Where tsc_cycles tick at base_hz, and time_ns should count at\n+\t * scaled_hz (typically NSEC_PER_SEC for a TSC→nanoseconds conversion).\n+\t *\n+\t * The multiplier is: (scaled_hz \u003c\u003c 32) / base_hz, adjusted by shift\n+\t * to keep everything in range.\n+\t */\n+\n+\tbase_hz_u64 = base_hz;\n+\n+\t/*\n+\t * Start by shifting base_hz right until it fits in 32 bits, and\n+\t * is lower than double the target rate. This introduces a negative\n+\t * shift value which would result in pvclock_scale_delta() shifting\n+\t * the actual tick count right before performing the multiplication.\n+\t */\n+\twhile (base_hz_u64 \u003e scaled_hz_u64 * 2 || base_hz_u64 \u003e\u003e 32) {\n+\t\tbase_hz_u64 \u003e\u003e= 1;\n \t\tshift--;\n \t}\n \n-\ttps32 = (uint32_t)tps64;\n-\twhile (tps32 \u003c= scaled64 || scaled64 \u0026 0xffffffff00000000ULL) {\n-\t\tif (scaled64 \u0026 0xffffffff00000000ULL || tps32 \u0026 0x80000000)\n-\t\t\tscaled64 \u003e\u003e= 1;\n+\t/* Now the shifted base_hz fits in 32 bits. */\n+\tbase32 = (u32)base_hz_u64;\n+\n+\t/*\n+\t * Next, shift scaled_hz right until it fits in 32 bits, and ensure\n+\t * that the shifted base_hz is strictly larger (so that the result of the\n+\t * final division also fits in 32 bits).\n+\t */\n+\twhile (base32 \u003c= scaled_hz_u64 || scaled_hz_u64 \u003e\u003e 32) {\n+\t\tif (scaled_hz_u64 \u003e\u003e 32 || base32 \u0026 BIT(31))\n+\t\t\tscaled_hz_u64 \u003e\u003e= 1;\n \t\telse\n-\t\t\ttps32 \u003c\u003c= 1;\n+\t\t\tbase32 \u003c\u003c= 1;\n \t\tshift++;\n \t}\n \n \t*pshift = shift;\n-\t*pmultiplier = div_frac(scaled64, tps32);\n+\t*pmultiplier = div_frac(scaled_hz_u64, base32);\n }\n \n #ifdef CONFIG_X86_64\n@@ -2564,6 +2534,7 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)\n {\n \tu32 thresh_lo, thresh_hi;\n \tint use_scaling = 0;\n+\tu64 tsc;\n \n \t/* tsc_khz can be zero if TSC calibration fails */\n \tif (user_tsc_khz == 0) {\n@@ -2591,55 +2562,125 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)\n \t\t\t user_tsc_khz, thresh_lo, thresh_hi);\n \t\tuse_scaling = 1;\n \t}\n-\treturn set_tsc_khz(vcpu, user_tsc_khz, use_scaling);\n+\t/*\n+\t * Read the guest TSC before changing the ratio, so we can\n+\t * re-synchronize to preserve continuity across the change.\n+\t */\n+\ttsc = kvm_read_l1_tsc(vcpu, rdtsc());\n+\n+\tif (set_tsc_khz(vcpu, user_tsc_khz, use_scaling))\n+\t\treturn -1;\n+\n+\tkvm_synchronize_tsc(vcpu, \u0026tsc);\n+\treturn 0;\n }\n \n static u64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns)\n {\n-\tu64 tsc = pvclock_scale_delta(kernel_ns-vcpu-\u003earch.this_tsc_nsec,\n-\t\t\t\t vcpu-\u003earch.virtual_tsc_mult,\n-\t\t\t\t vcpu-\u003earch.virtual_tsc_shift);\n-\ttsc += vcpu-\u003earch.this_tsc_write;\n-\treturn tsc;\n+\ts64 delta_ns = kernel_ns - vcpu-\u003earch.this_tsc_nsec;\n+\tu64 tsc;\n+\n+\t/* Handle negative deltas gracefully (master clock ref may be earlier) */\n+\tif (delta_ns \u003c 0) {\n+\t\ttsc = pvclock_scale_delta(-delta_ns,\n+\t\t\t\t\t vcpu-\u003earch.virtual_tsc_mult,\n+\t\t\t\t\t vcpu-\u003earch.virtual_tsc_shift);\n+\t\treturn vcpu-\u003earch.this_tsc_write - tsc;\n+\t}\n+\n+\ttsc = pvclock_scale_delta(delta_ns,\n+\t\t\t\t vcpu-\u003earch.virtual_tsc_mult,\n+\t\t\t\t vcpu-\u003earch.virtual_tsc_shift);\n+\treturn vcpu-\u003earch.this_tsc_write + tsc;\n }\n \n #ifdef CONFIG_X86_64\n+static int kvm_host_vclock_mode;\n+\n static inline bool gtod_is_based_on_tsc(int mode)\n {\n \treturn mode == VDSO_CLOCKMODE_TSC || mode == VDSO_CLOCKMODE_HVCLOCK;\n }\n-#endif\n \n-static void kvm_track_tsc_matching(struct kvm_vcpu *vcpu, bool new_generation)\n+static bool kvm_use_master_clock(struct kvm *kvm)\n+{\n+\tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n+\n+\t/*\n+\t * The 'old kvmclock' check is a workaround (from 2015) for a\n+\t * SUSE 2.6.16 kernel that didn't boot if the system_time in\n+\t * its kvmclock was too far behind the current time. So the\n+\t * mode of just setting the reference point and allowing time\n+\t * to proceed linearly from there makes it fail to boot.\n+\t * Despite that being kind of the *point* of the way the clock\n+\t * is exposed to the guest. By coincidence, the offending\n+\t * kernels used the old MSR_KVM_SYSTEM_TIME, which was moved\n+\t * only because it resided in the wrong number range. So the\n+\t * workaround is activated for *all* guests using the old MSR.\n+\t */\n+\treturn ka-\u003eall_vcpus_matched_freq \u0026\u0026\n+\t\t!ka-\u003ebackwards_tsc_observed \u0026\u0026\n+\t\t!ka-\u003eboot_vcpu_runs_old_kvmclock;\n+}\n+\n+static void kvm_track_tsc_matching(struct kvm_vcpu *vcpu, bool update_mclock)\n {\n-#ifdef CONFIG_X86_64\n \tstruct kvm_arch *ka = \u0026vcpu-\u003ekvm-\u003earch;\n-\tstruct pvclock_gtod_data *gtod = \u0026pvclock_gtod_data;\n+\tbool prev_matched_tsc = ka-\u003eall_vcpus_matched_tsc;\n+\n+\t/*\n+\t * Track whether all vCPUs have matching TSC offsets (for\n+\t * PVCLOCK_TSC_STABLE_BIT) and matching frequencies (for\n+\t * master clock eligibility).\n+\t */\n+\n+\t/*\n+\t * A new vCPU might already have incremented -\u003eonline_vcpus\n+\t * and cause a temporary false negative here. But will then\n+\t * call kvm_synchronize_tsc() from kvm_arch_vcpu_postcreate()\n+\t * and finish the job.\n+\t */\n+\tint online = atomic_read(\u0026vcpu-\u003ekvm-\u003eonline_vcpus);\n+\tka-\u003eall_vcpus_matched_tsc = (ka-\u003enr_vcpus_matched_tsc \u003e= online);\n+\n+\tupdate_mclock |= (prev_matched_tsc != ka-\u003eall_vcpus_matched_tsc);\n+\n+\t/*\n+\t * all_vcpus_matched_freq starts true and is cleared when\n+\t * __kvm_synchronize_tsc() detects a frequency mismatch.\n+\t * Re-enable when all vCPUs have synced with matching frequency.\n+\t * If all offsets also match, that implies frequencies match too.\n+\t */\n+\tif (ka-\u003eall_vcpus_matched_tsc ||\n+\t ka-\u003enr_vcpus_matched_freq \u003e= online)\n+\t\tka-\u003eall_vcpus_matched_freq = true;\n \n \t/*\n \t * To use the masterclock, the host clocksource must be based on TSC\n-\t * and all vCPUs must have matching TSCs. Note, the count for matching\n-\t * vCPUs doesn't include the reference vCPU, hence \"+1\".\n+\t * and all vCPUs must have matching TSC *frequency*. Different offsets\n+\t * are fine — each vCPU's pvclock has its own tsc_timestamp that\n+\t * accounts for its offset.\n \t */\n-\tbool use_master_clock = (ka-\u003enr_vcpus_matched_tsc + 1 ==\n-\t\t\t\t atomic_read(\u0026vcpu-\u003ekvm-\u003eonline_vcpus)) \u0026\u0026\n-\t\t\t\tgtod_is_based_on_tsc(gtod-\u003eclock.vclock_mode);\n+\tbool use_master_clock = kvm_use_master_clock(vcpu-\u003ekvm) \u0026\u0026\n+\t\t\t\tgtod_is_based_on_tsc(kvm_host_vclock_mode);\n \n \t/*\n-\t * Request a masterclock update if the masterclock needs to be toggled\n-\t * on/off, or when starting a new generation and the masterclock is\n-\t * enabled (compute_guest_tsc() requires the masterclock snapshot to be\n-\t * taken _after_ the new generation is created).\n+\t * Request a masterclock update if needed: toggling master clock,\n+\t * new generation requiring a fresh snapshot, or a change in\n+\t * all_vcpus_matched_tsc (to broadcast PVCLOCK_TSC_STABLE_BIT).\n \t */\n-\tif ((ka-\u003euse_master_clock \u0026\u0026 new_generation) ||\n+\tif ((ka-\u003euse_master_clock \u0026\u0026 update_mclock) ||\n \t (ka-\u003euse_master_clock != use_master_clock))\n \t\tkvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);\n \n \ttrace_kvm_track_tsc(vcpu-\u003evcpu_id, ka-\u003enr_vcpus_matched_tsc,\n \t\t\t atomic_read(\u0026vcpu-\u003ekvm-\u003eonline_vcpus),\n-\t\t ka-\u003euse_master_clock, gtod-\u003eclock.vclock_mode);\n-#endif\n+\t\t ka-\u003euse_master_clock, kvm_host_vclock_mode);\n }\n+#else\n+static inline void kvm_track_tsc_matching(struct kvm_vcpu *vcpu,\n+\t\t\t\t\t bool update_mclock) {}\n+#endif\n \n /*\n * Multiply tsc by a fixed point number represented by ratio.\n@@ -2666,11 +2707,12 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio)\n \treturn _tsc;\n }\n \n-static u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc)\n+static u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,\n+\t\t\t\t u64 target_tsc)\n {\n \tu64 tsc;\n \n-\ttsc = kvm_scale_tsc(rdtsc(), vcpu-\u003earch.l1_tsc_scaling_ratio);\n+\ttsc = kvm_scale_tsc(host_tsc, vcpu-\u003earch.l1_tsc_scaling_ratio);\n \n \treturn target_tsc - tsc;\n }\n@@ -2757,7 +2799,7 @@ static inline bool kvm_check_tsc_unstable(void)\n \t * TSC is marked unstable when we're running on Hyper-V,\n \t * 'TSC page' clocksource is good.\n \t */\n-\tif (pvclock_gtod_data.clock.vclock_mode == VDSO_CLOCKMODE_HVCLOCK)\n+\tif (kvm_host_vclock_mode == VDSO_CLOCKMODE_HVCLOCK)\n \t\treturn false;\n #endif\n \treturn check_tsc_unstable();\n@@ -2782,13 +2824,27 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,\n \t\tvcpu-\u003ekvm-\u003earch.user_set_tsc = true;\n \n \t/*\n-\t * We also track th most recent recorded KHZ, write and time to\n-\t * allow the matching interval to be extended at each write.\n+\t * Track the TSC frequency, scaling ratio, and offset for the current\n+\t * generation. These are used to detect matching TSC writes and to\n+\t * compute the guest TSC from the host clock.\n+\t *\n+\t * If the frequency changed, master clock mode can no longer be used\n+\t * since the kvmclock scaling factors differ between vCPUs.\n \t */\n-\tkvm-\u003earch.last_tsc_nsec = ns;\n-\tkvm-\u003earch.last_tsc_write = tsc;\n-\tkvm-\u003earch.last_tsc_khz = vcpu-\u003earch.virtual_tsc_khz;\n-\tkvm-\u003earch.last_tsc_offset = offset;\n+\tif (vcpu-\u003earch.virtual_tsc_khz != kvm-\u003earch.cur_tsc_khz) {\n+\t\tkvm-\u003earch.cur_tsc_freq_generation++;\n+\t\tkvm-\u003earch.all_vcpus_matched_freq = false;\n+\t\tkvm-\u003earch.nr_vcpus_matched_freq = 0;\n+\t}\n+\n+\t/* Count each vCPU once per freq generation */\n+\tif (vcpu-\u003earch.this_tsc_freq_generation != kvm-\u003earch.cur_tsc_freq_generation) {\n+\t\tvcpu-\u003earch.this_tsc_freq_generation = kvm-\u003earch.cur_tsc_freq_generation;\n+\t\tkvm-\u003earch.nr_vcpus_matched_freq++;\n+\t}\n+\n+\tkvm-\u003earch.cur_tsc_khz = vcpu-\u003earch.virtual_tsc_khz;\n+\tkvm-\u003earch.cur_tsc_scaling_ratio = vcpu-\u003earch.l1_tsc_scaling_ratio;\n \n \tvcpu-\u003earch.last_guest_tsc = tsc;\n \n@@ -2801,20 +2857,20 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,\n \t\t * nanosecond time, offset, and write, so if TSCs are in\n \t\t * sync, we can match exact offset, and if not, we can match\n \t\t * exact software computation in compute_guest_tsc()\n-\t\t *\n-\t\t * These values are tracked in kvm-\u003earch.cur_xxx variables.\n \t\t */\n \t\tkvm-\u003earch.cur_tsc_generation++;\n+\t\tkvm-\u003earch.all_vcpus_matched_tsc = false;\n+\t\tkvm-\u003earch.nr_vcpus_matched_tsc = 0;\n \t\tkvm-\u003earch.cur_tsc_nsec = ns;\n \t\tkvm-\u003earch.cur_tsc_write = tsc;\n \t\tkvm-\u003earch.cur_tsc_offset = offset;\n-\t\tkvm-\u003earch.nr_vcpus_matched_tsc = 0;\n-\t} else if (vcpu-\u003earch.this_tsc_generation != kvm-\u003earch.cur_tsc_generation) {\n+\t}\n+\n+\tif (vcpu-\u003earch.this_tsc_generation != kvm-\u003earch.cur_tsc_generation) {\n+\t\tvcpu-\u003earch.this_tsc_generation = kvm-\u003earch.cur_tsc_generation;\n \t\tkvm-\u003earch.nr_vcpus_matched_tsc++;\n \t}\n \n-\t/* Keep track of which generation this VCPU has synchronized to */\n-\tvcpu-\u003earch.this_tsc_generation = kvm-\u003earch.cur_tsc_generation;\n \tvcpu-\u003earch.this_tsc_nsec = kvm-\u003earch.cur_tsc_nsec;\n \tvcpu-\u003earch.this_tsc_write = kvm-\u003earch.cur_tsc_write;\n \n@@ -2825,15 +2881,24 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\n {\n \tu64 data = user_value ? *user_value : 0;\n \tstruct kvm *kvm = vcpu-\u003ekvm;\n-\tu64 offset, ns, elapsed;\n+\tu64 offset, host_tsc, elapsed;\n+\ts64 ns;\n \tunsigned long flags;\n \tbool matched = false;\n \tbool synchronizing = false;\n \n \traw_spin_lock_irqsave(\u0026kvm-\u003earch.tsc_write_lock, flags);\n-\toffset = kvm_compute_l1_tsc_offset(vcpu, data);\n-\tns = get_kvmclock_base_ns();\n-\telapsed = ns - kvm-\u003earch.last_tsc_nsec;\n+\n+#ifdef CONFIG_X86_64\n+\tif (!kvm_get_time_and_clockread(\u0026ns, \u0026host_tsc))\n+#endif\n+\t{\n+\t\thost_tsc = rdtsc();\n+\t\tns = get_kvmclock_base_ns();\n+\t}\n+\n+\toffset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);\n+\telapsed = ns - kvm-\u003earch.cur_tsc_nsec;\n \n \tif (vcpu-\u003earch.virtual_tsc_khz) {\n \t\tif (data == 0) {\n@@ -2843,7 +2908,7 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\n \t\t\t */\n \t\t\tsynchronizing = true;\n \t\t} else if (kvm-\u003earch.user_set_tsc) {\n-\t\t\tu64 tsc_exp = kvm-\u003earch.last_tsc_write +\n+\t\t\tu64 tsc_exp = kvm-\u003earch.cur_tsc_write +\n \t\t\t\t\t\tnsec_to_cycles(vcpu, elapsed);\n \t\t\tu64 tsc_hz = vcpu-\u003earch.virtual_tsc_khz * 1000LL;\n \t\t\t/*\n@@ -2874,14 +2939,19 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\n \t * it's better to try to match offsets from the beginning.\n */\n \tif (synchronizing \u0026\u0026\n-\t vcpu-\u003earch.virtual_tsc_khz == kvm-\u003earch.last_tsc_khz) {\n-\t\tif (!kvm_check_tsc_unstable()) {\n+\t vcpu-\u003earch.virtual_tsc_khz == kvm-\u003earch.cur_tsc_khz) {\n+\t\t/*\n+\t\t * If synchronizing, advance the reference point to \"now\"\n+\t\t * so the matching window slides forward with each vCPU.\n+\t\t */\n+\t\tu64 delta = nsec_to_cycles(vcpu, elapsed);\n+\n+\t\tdata = kvm-\u003earch.cur_tsc_write + delta;\n+\n+\t\tif (!kvm_check_tsc_unstable())\n \t\t\toffset = kvm-\u003earch.cur_tsc_offset;\n-\t\t} else {\n-\t\t\tu64 delta = nsec_to_cycles(vcpu, elapsed);\n-\t\t\tdata += delta;\n-\t\t\toffset = kvm_compute_l1_tsc_offset(vcpu, data);\n-\t\t}\n+\t\telse\n+\t\t\toffset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);\n \t\tmatched = true;\n \t}\n \n@@ -2907,141 +2977,44 @@ static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment)\n \n #ifdef CONFIG_X86_64\n \n-static u64 read_tsc(void)\n+/*\n+ * Calculates the kvmclock_base_ns (CLOCK_MONOTONIC_RAW + boot time) and\n+ * reports the TSC value from which it do so. Returns true if host is\n+ * using TSC based clocksource.\n+ */\n+static bool kvm_snapshot_has_tsc(struct system_time_snapshot *snap,\n+\t\t\t\tu64 *tsc_timestamp)\n {\n-\tu64 ret = (u64)rdtsc_ordered();\n-\tu64 last = pvclock_gtod_data.clock.cycle_last;\n-\n-\tif (likely(ret \u003e= last))\n-\t\treturn ret;\n-\n \t/*\n-\t * GCC likes to generate cmov here, but this branch is extremely\n-\t * predictable (it's just a function of time and the likely is\n-\t * very likely) and there's a data dependence, so force GCC\n-\t * to generate a branch instead. I don't barrier() because\n-\t * we don't actually need a barrier, and if this function\n-\t * ever gets inlined it will generate worse code.\n+\t * ktime_get_snapshot_id() cannot fail for standard clock IDs\n+\t * (only for invalid/aux clocks or during suspend, with a WARN).\n \t */\n-\tasm volatile (\"\");\n-\treturn last;\n-}\n-\n-static inline u64 vgettsc(struct pvclock_clock *clock, u64 *tsc_timestamp,\n-\t\t\t int *mode)\n-{\n-\tu64 tsc_pg_val;\n-\tlong v;\n+\tif (!snap-\u003evalid)\n+\t\treturn false;\n \n-\tswitch (clock-\u003evclock_mode) {\n-\tcase VDSO_CLOCKMODE_HVCLOCK:\n-\t\tif (hv_read_tsc_page_tsc(hv_get_tsc_page(),\n-\t\t\t\t\t tsc_timestamp, \u0026tsc_pg_val)) {\n-\t\t\t/* TSC page valid */\n-\t\t\t*mode = VDSO_CLOCKMODE_HVCLOCK;\n-\t\t\tv = (tsc_pg_val - clock-\u003ecycle_last) \u0026\n-\t\t\t\tclock-\u003emask;\n-\t\t} else {\n-\t\t\t/* TSC page invalid */\n-\t\t\t*mode = VDSO_CLOCKMODE_NONE;\n-\t\t}\n-\t\tbreak;\n-\tcase VDSO_CLOCKMODE_TSC:\n-\t\t*mode = VDSO_CLOCKMODE_TSC;\n-\t\t*tsc_timestamp = read_tsc();\n-\t\tv = (*tsc_timestamp - clock-\u003ecycle_last) \u0026\n-\t\t\tclock-\u003emask;\n-\t\tbreak;\n-\tdefault:\n-\t\t*mode = VDSO_CLOCKMODE_NONE;\n+\tif (snap-\u003ecs_id == CSID_X86_TSC) {\n+\t\t*tsc_timestamp = snap-\u003ecycles;\n+\t\treturn true;\n \t}\n \n-\tif (*mode == VDSO_CLOCKMODE_NONE)\n-\t\t*tsc_timestamp = v = 0;\n-\n-\treturn v * clock-\u003emult;\n-}\n-\n-/*\n- * As with get_kvmclock_base_ns(), this counts from boot time, at the\n- * frequency of CLOCK_MONOTONIC_RAW (hence adding gtos-\u003eoffs_boot).\n- */\n-static int do_kvmclock_base(s64 *t, u64 *tsc_timestamp)\n-{\n-\tstruct pvclock_gtod_data *gtod = \u0026pvclock_gtod_data;\n-\tunsigned long seq;\n-\tint mode;\n-\tu64 ns;\n-\n-\tdo {\n-\t\tseq = read_seqcount_begin(\u0026gtod-\u003eseq);\n-\t\tns = gtod-\u003eraw_clock.base_cycles;\n-\t\tns += vgettsc(\u0026gtod-\u003eraw_clock, tsc_timestamp, \u0026mode);\n-\t\tns \u003e\u003e= gtod-\u003eraw_clock.shift;\n-\t\tns += ktime_to_ns(ktime_add(gtod-\u003eraw_clock.offset, gtod-\u003eoffs_boot));\n-\t} while (unlikely(read_seqcount_retry(\u0026gtod-\u003eseq, seq)));\n-\t*t = ns;\n-\n-\treturn mode;\n-}\n-\n-/*\n- * This calculates CLOCK_MONOTONIC at the time of the TSC snapshot, with\n- * no boot time offset.\n- */\n-static int do_monotonic(s64 *t, u64 *tsc_timestamp)\n-{\n-\tstruct pvclock_gtod_data *gtod = \u0026pvclock_gtod_data;\n-\tunsigned long seq;\n-\tint mode;\n-\tu64 ns;\n-\n-\tdo {\n-\t\tseq = read_seqcount_begin(\u0026gtod-\u003eseq);\n-\t\tns = gtod-\u003eclock.base_cycles;\n-\t\tns += vgettsc(\u0026gtod-\u003eclock, tsc_timestamp, \u0026mode);\n-\t\tns \u003e\u003e= gtod-\u003eclock.shift;\n-\t\tns += ktime_to_ns(gtod-\u003eclock.offset);\n-\t} while (unlikely(read_seqcount_retry(\u0026gtod-\u003eseq, seq)));\n-\t*t = ns;\n-\n-\treturn mode;\n-}\n-\n-static int do_realtime(struct timespec64 *ts, u64 *tsc_timestamp)\n-{\n-\tstruct pvclock_gtod_data *gtod = \u0026pvclock_gtod_data;\n-\tunsigned long seq;\n-\tint mode;\n-\tu64 ns;\n-\n-\tdo {\n-\t\tseq = read_seqcount_begin(\u0026gtod-\u003eseq);\n-\t\tts-\u003etv_sec = gtod-\u003ewall_time_sec;\n-\t\tns = gtod-\u003eclock.base_cycles;\n-\t\tns += vgettsc(\u0026gtod-\u003eclock, tsc_timestamp, \u0026mode);\n-\t\tns \u003e\u003e= gtod-\u003eclock.shift;\n-\t} while (unlikely(read_seqcount_retry(\u0026gtod-\u003eseq, seq)));\n-\n-\tts-\u003etv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, \u0026ns);\n-\tts-\u003etv_nsec = ns;\n+\tif (snap-\u003ehw_csid == CSID_X86_TSC \u0026\u0026 snap-\u003ehw_cycles) {\n+\t\t*tsc_timestamp = snap-\u003ehw_cycles;\n+\t\treturn true;\n+\t}\n \n-\treturn mode;\n+\treturn false;\n }\n \n-/*\n- * Calculates the kvmclock_base_ns (CLOCK_MONOTONIC_RAW + boot time) and\n- * reports the TSC value from which it do so. Returns true if host is\n- * using TSC based clocksource.\n- */\n static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)\n {\n-\t/* checked again under seqlock below */\n-\tif (!gtod_is_based_on_tsc(pvclock_gtod_data.clock.vclock_mode))\n+\tstruct system_time_snapshot snap = {};\n+\n+\tktime_get_snapshot_id(CLOCK_MONOTONIC_RAW, \u0026snap);\n+\tif (!kvm_snapshot_has_tsc(\u0026snap, tsc_timestamp))\n \t\treturn false;\n \n-\treturn gtod_is_based_on_tsc(do_kvmclock_base(kernel_ns,\n-\t\t\t\t\t\t tsc_timestamp));\n+\t*kernel_ns = ktime_to_ns(ktime_mono_to_any(snap.systime, TK_OFFS_BOOT));\n+\treturn true;\n }\n \n /*\n@@ -3050,12 +3023,14 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)\n */\n bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)\n {\n-\t/* checked again under seqlock below */\n-\tif (!gtod_is_based_on_tsc(pvclock_gtod_data.clock.vclock_mode))\n+\tstruct system_time_snapshot snap = {};\n+\n+\tktime_get_snapshot_id(CLOCK_MONOTONIC, \u0026snap);\n+\tif (!kvm_snapshot_has_tsc(\u0026snap, tsc_timestamp))\n \t\treturn false;\n \n-\treturn gtod_is_based_on_tsc(do_monotonic(kernel_ns,\n-\t\t\t\t\t\t tsc_timestamp));\n+\t*kernel_ns = ktime_to_ns(snap.systime);\n+\treturn true;\n }\n \n /*\n@@ -3068,11 +3043,14 @@ bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)\n static bool kvm_get_walltime_and_clockread(struct timespec64 *ts,\n \t\t\t\t\t u64 *tsc_timestamp)\n {\n-\t/* checked again under seqlock below */\n-\tif (!gtod_is_based_on_tsc(pvclock_gtod_data.clock.vclock_mode))\n+\tstruct system_time_snapshot snap = {};\n+\n+\tktime_get_snapshot_id(CLOCK_REALTIME, \u0026snap);\n+\tif (!kvm_snapshot_has_tsc(\u0026snap, tsc_timestamp))\n \t\treturn false;\n \n-\treturn gtod_is_based_on_tsc(do_realtime(ts, tsc_timestamp));\n+\t*ts = ktime_to_timespec64(snap.systime);\n+\treturn true;\n }\n #endif\n \n@@ -3117,16 +3095,15 @@ static bool kvm_get_walltime_and_clockread(struct timespec64 *ts,\n *\n */\n \n+static unsigned long get_cpu_tsc_khz(void);\n+\n static void pvclock_update_vm_gtod_copy(struct kvm *kvm)\n {\n #ifdef CONFIG_X86_64\n \tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n-\tint vclock_mode;\n-\tbool host_tsc_clocksource, vcpus_matched;\n+\tbool host_tsc_clocksource;\n \n \tlockdep_assert_held(\u0026kvm-\u003earch.tsc_write_lock);\n-\tvcpus_matched = (ka-\u003enr_vcpus_matched_tsc + 1 ==\n-\t\t\tatomic_read(\u0026kvm-\u003eonline_vcpus));\n \n \t/*\n \t * If the host uses TSC clock, then passthrough TSC as stable\n@@ -3136,16 +3113,36 @@ static void pvclock_update_vm_gtod_copy(struct kvm *kvm)\n \t\t\t\t\t\u0026ka-\u003emaster_kernel_ns,\n \t\t\t\t\t\u0026ka-\u003emaster_cycle_now);\n \n-\tka-\u003euse_master_clock = host_tsc_clocksource \u0026\u0026 vcpus_matched\n-\t\t\t\t\u0026\u0026 !ka-\u003ebackwards_tsc_observed\n-\t\t\t\t\u0026\u0026 !ka-\u003eboot_vcpu_runs_old_kvmclock;\n+\tka-\u003euse_master_clock = host_tsc_clocksource \u0026\u0026\n+\t\t\t\tkvm_use_master_clock(kvm);\n+\n+\tif (ka-\u003euse_master_clock) {\n+\t\tu64 tsc_hz;\n \n-\tif (ka-\u003euse_master_clock)\n \t\tatomic_set(\u0026kvm_guest_has_master_clock, 1);\n \n-\tvclock_mode = pvclock_gtod_data.clock.vclock_mode;\n-\ttrace_kvm_update_master_clock(ka-\u003euse_master_clock, vclock_mode,\n-\t\t\t\t\tvcpus_matched);\n+\t\t/*\n+\t\t * Copy the scaling ratio and precompute the mul/shift for\n+\t\t * converting guest TSC to nanoseconds. These are used by\n+\t\t * get_kvmclock() to compute kvmclock from the host TSC\n+\t\t * without needing a vCPU reference.\n+\t\t */\n+\t\tka-\u003emaster_tsc_scaling_ratio = ka-\u003ecur_tsc_scaling_ratio;\n+\t\ttsc_hz = (u64)get_cpu_tsc_khz() * 1000;\n+\t\tif (tsc_hz \u0026\u0026 kvm_caps.has_tsc_control)\n+\t\t\ttsc_hz = kvm_scale_tsc(tsc_hz,\n+\t\t\t\t\t ka-\u003emaster_tsc_scaling_ratio);\n+\t\tif (tsc_hz)\n+\t\t\tkvm_get_time_scale(NSEC_PER_SEC, tsc_hz,\n+\t\t\t\t\t \u0026ka-\u003emaster_tsc_shift,\n+\t\t\t\t\t \u0026ka-\u003emaster_tsc_mul);\n+\t\telse\n+\t\t\tka-\u003euse_master_clock = false;\n+\t}\n+\n+\ttrace_kvm_update_master_clock(ka-\u003euse_master_clock,\n+\t\t\t\t kvm_host_vclock_mode,\n+\t\t\t\t ka-\u003eall_vcpus_matched_freq);\n #endif\n }\n \n@@ -3154,10 +3151,39 @@ static void kvm_make_mclock_inprogress_request(struct kvm *kvm)\n \tkvm_make_all_cpus_request(kvm, KVM_REQ_MCLOCK_INPROGRESS);\n }\n \n-static void __kvm_start_pvclock_update(struct kvm *kvm)\n+static void kvm_clear_mclock_inprogress_request(struct kvm *kvm)\n {\n+\tstruct kvm_vcpu *vcpu;\n+\tunsigned long i;\n+\n+\tkvm_for_each_vcpu(i, vcpu, kvm)\n+\t\tkvm_clear_request(KVM_REQ_MCLOCK_INPROGRESS, vcpu);\n+}\n+\n+static bool __kvm_start_pvclock_update(struct kvm *kvm, struct kvm_vcpu *requesting_vcpu)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tunsigned long i;\n+\n \traw_spin_lock_irq(\u0026kvm-\u003earch.tsc_write_lock);\n+\n+\t/*\n+\t * If another vCPU already did the update while we were waiting\n+\t * for the lock, our request will have been cleared. Bail out.\n+\t */\n+\tif (requesting_vcpu \u0026\u0026\n+\t !kvm_test_request(KVM_REQ_MASTERCLOCK_UPDATE, requesting_vcpu)) {\n+\t\tkvm_clear_mclock_inprogress_request(kvm);\n+\t\traw_spin_unlock_irq(\u0026kvm-\u003earch.tsc_write_lock);\n+\t\treturn false;\n+\t}\n+\n+\t/* The update is VM-wide; prevent other vCPUs from redoing it. */\n+\tkvm_for_each_vcpu(i, vcpu, kvm)\n+\t\tkvm_clear_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);\n+\n \twrite_seqcount_begin(\u0026kvm-\u003earch.pvclock_sc);\n+\treturn true;\n }\n \n static void kvm_start_pvclock_update(struct kvm *kvm)\n@@ -3165,7 +3191,7 @@ static void kvm_start_pvclock_update(struct kvm *kvm)\n \tkvm_make_mclock_inprogress_request(kvm);\n \n \t/* no guest entries from this point */\n-\t__kvm_start_pvclock_update(kvm);\n+\t__kvm_start_pvclock_update(kvm, NULL);\n }\n \n static void kvm_end_pvclock_update(struct kvm *kvm)\n@@ -3174,22 +3200,25 @@ static void kvm_end_pvclock_update(struct kvm *kvm)\n \tstruct kvm_vcpu *vcpu;\n \tunsigned long i;\n \n-\twrite_seqcount_end(\u0026ka-\u003epvclock_sc);\n-\traw_spin_unlock_irq(\u0026ka-\u003etsc_write_lock);\n \tkvm_for_each_vcpu(i, vcpu, kvm)\n \t\tkvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);\n \n \t/* guest entries allowed */\n-\tkvm_for_each_vcpu(i, vcpu, kvm)\n-\t\tkvm_clear_request(KVM_REQ_MCLOCK_INPROGRESS, vcpu);\n+\tkvm_clear_mclock_inprogress_request(kvm);\n+\n+\twrite_seqcount_end(\u0026ka-\u003epvclock_sc);\n+\traw_spin_unlock_irq(\u0026ka-\u003etsc_write_lock);\n }\n \n-static void kvm_update_masterclock(struct kvm *kvm)\n+static void kvm_update_masterclock(struct kvm *kvm, struct kvm_vcpu *vcpu)\n {\n \tkvm_hv_request_tsc_page_update(kvm);\n-\tkvm_start_pvclock_update(kvm);\n-\tpvclock_update_vm_gtod_copy(kvm);\n-\tkvm_end_pvclock_update(kvm);\n+\tkvm_make_mclock_inprogress_request(kvm);\n+\n+\tif (__kvm_start_pvclock_update(kvm, vcpu)) {\n+\t\tpvclock_update_vm_gtod_copy(kvm);\n+\t\tkvm_end_pvclock_update(kvm);\n+\t}\n }\n \n /*\n@@ -3208,50 +3237,59 @@ static unsigned long get_cpu_tsc_khz(void)\n \t\treturn __this_cpu_read(cpu_tsc_khz);\n }\n \n-/* Called within read_seqcount_begin/retry for kvm-\u003epvclock_sc. */\n-static void __get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)\n+static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)\n {\n \tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n-\tstruct pvclock_vcpu_time_info hv_clock;\n+\tunsigned int seq;\n \n-\t/* both __this_cpu_read() and rdtsc() should be on the same cpu */\n-\tget_cpu();\n+\tdo {\n+\t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n \n-\tdata-\u003eflags = 0;\n-\tif (ka-\u003euse_master_clock \u0026\u0026\n-\t (static_cpu_has(X86_FEATURE_CONSTANT_TSC) || __this_cpu_read(cpu_tsc_khz))) {\n+\t\tdata-\u003eflags = 0;\n #ifdef CONFIG_X86_64\n-\t\tstruct timespec64 ts;\n+\t\tif (ka-\u003euse_master_clock) {\n+\t\t\tstruct pvclock_vcpu_time_info hv_clock;\n+\t\t\tstruct timespec64 ts;\n \n-\t\tif (kvm_get_walltime_and_clockread(\u0026ts, \u0026data-\u003ehost_tsc)) {\n-\t\t\tdata-\u003erealtime = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec;\n-\t\t\tdata-\u003eflags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC;\n-\t\t} else\n-#endif\n-\t\tdata-\u003ehost_tsc = rdtsc();\n+\t\t\tif (kvm_get_walltime_and_clockread(\u0026ts, \u0026data-\u003ehost_tsc)) {\n+\t\t\t\tdata-\u003erealtime = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec;\n+\t\t\t\tdata-\u003eflags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC | KVM_CLOCK_TSC_STABLE;\n \n-\t\tdata-\u003eflags |= KVM_CLOCK_TSC_STABLE;\n-\t\thv_clock.tsc_timestamp = ka-\u003emaster_cycle_now;\n-\t\thv_clock.system_time = ka-\u003emaster_kernel_ns + ka-\u003ekvmclock_offset;\n-\t\tkvm_get_time_scale(NSEC_PER_SEC, get_cpu_tsc_khz() * 1000LL,\n-\t\t\t\t \u0026hv_clock.tsc_shift,\n-\t\t\t\t \u0026hv_clock.tsc_to_system_mul);\n-\t\tdata-\u003eclock = __pvclock_read_cycles(\u0026hv_clock, data-\u003ehost_tsc);\n-\t} else {\n-\t\tdata-\u003eclock = get_kvmclock_base_ns() + ka-\u003ekvmclock_offset;\n-\t}\n-\n-\tput_cpu();\n-}\n+\t\t\t\thv_clock.tsc_timestamp = ka-\u003emaster_cycle_now;\n+\t\t\t\thv_clock.system_time = ka-\u003emaster_kernel_ns + ka-\u003ekvmclock_offset;\n \n-static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)\n-{\n-\tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n-\tunsigned seq;\n+\t\t\t\t/*\n+\t\t\t\t * Use the precomputed guest-TSC-based mul/shift\n+\t\t\t\t * so that the kvmclock value matches what the\n+\t\t\t\t * guest computes from its own TSC.\n+\t\t\t\t */\n+\t\t\t\thv_clock.tsc_shift = ka-\u003emaster_tsc_shift;\n+\t\t\t\thv_clock.tsc_to_system_mul = ka-\u003emaster_tsc_mul;\n+\n+\t\t\t\tif (kvm_caps.has_tsc_control) {\n+\t\t\t\t\tu64 tsc_delta = data-\u003ehost_tsc - ka-\u003emaster_cycle_now;\n+\n+\t\t\t\t\ttsc_delta = kvm_scale_tsc(tsc_delta,\n+\t\t\t\t\t\t\t\t ka-\u003emaster_tsc_scaling_ratio);\n+\t\t\t\t\tdata-\u003eclock = hv_clock.system_time +\n+\t\t\t\t\t\tpvclock_scale_delta(tsc_delta,\n+\t\t\t\t\t\t\t\t hv_clock.tsc_to_system_mul,\n+\t\t\t\t\t\t\t\t hv_clock.tsc_shift);\n+\t\t\t\t} else {\n+\t\t\t\t\tdata-\u003eclock = __pvclock_read_cycles(\u0026hv_clock,\n+\t\t\t\t\t\t\t\t\t data-\u003ehost_tsc);\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t}\n \n-\tdo {\n-\t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n-\t\t__get_kvmclock(kvm, data);\n+\t\t\t/*\n+\t\t\t * Clock read failed (e.g. clocksource is transitioning\n+\t\t\t * away from TSC). Fall back to the non-master-clock path\n+\t\t\t * rather than spinning.\n+\t\t\t */\n+\t\t}\n+#endif\n+\t\tdata-\u003eclock = get_kvmclock_base_ns() + ka-\u003ekvmclock_offset;\n \t} while (read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq));\n }\n \n@@ -3314,44 +3352,60 @@ static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock,\n int kvm_guest_time_update(struct kvm_vcpu *v)\n {\n \tstruct pvclock_vcpu_time_info hv_clock = {};\n-\tunsigned long flags, tgt_tsc_khz;\n+\tu64 tgt_tsc_hz;\n \tunsigned seq;\n \tstruct kvm_vcpu_arch *vcpu = \u0026v-\u003earch;\n \tstruct kvm_arch *ka = \u0026v-\u003ekvm-\u003earch;\n \ts64 kernel_ns;\n \tu64 tsc_timestamp, host_tsc;\n+\tu64 master_host_tsc = 0;\n+\ts64 master_kernel_ns = 0;\n+\ts64 kvmclock_offset = 0;\n \tbool use_master_clock;\n \n-\tkernel_ns = 0;\n-\thost_tsc = 0;\n-\n \t/*\n \t * If the host uses TSC clock, then passthrough TSC as stable\n \t * to the guest.\n \t */\n \tdo {\n \t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n+\n \t\tuse_master_clock = ka-\u003euse_master_clock;\n+\n+\t\t/*\n+\t\t * The TSC read and the call to get_cpu_tsc_khz() must happen\n+\t\t * on the same CPU.\n+\t\t */\n+\t\tget_cpu();\n+\n+\t\ttgt_tsc_hz = (u64)get_cpu_tsc_khz() * 1000;\n+\n+#ifdef CONFIG_X86_64\n+\t\tif (use_master_clock \u0026\u0026\n+\t\t !kvm_get_time_and_clockread(\u0026kernel_ns, \u0026host_tsc) \u0026\u0026\n+\t\t !read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq))\n+\t\t\tuse_master_clock = false;\n+#endif\n+\n+\t\tput_cpu();\n+\n \t\tif (use_master_clock) {\n-\t\t\thost_tsc = ka-\u003emaster_cycle_now;\n-\t\t\tkernel_ns = ka-\u003emaster_kernel_ns;\n+\t\t\tmaster_host_tsc = ka-\u003emaster_cycle_now;\n+\t\t\tmaster_kernel_ns = ka-\u003emaster_kernel_ns;\n+\t\t} else {\n+\t\t\tlocal_irq_disable();\n+\t\t\thost_tsc = rdtsc();\n+\t\t\tkernel_ns = get_kvmclock_base_ns();\n+\t\t\tlocal_irq_enable();\n \t\t}\n+\n+\t\tkvmclock_offset = ka-\u003ekvmclock_offset;\n \t} while (read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq));\n \n-\t/* Keep irq disabled to prevent changes to the clock */\n-\tlocal_irq_save(flags);\n-\ttgt_tsc_khz = get_cpu_tsc_khz();\n-\tif (unlikely(tgt_tsc_khz == 0)) {\n-\t\tlocal_irq_restore(flags);\n+\tif (unlikely(tgt_tsc_hz == 0)) {\n \t\tkvm_make_request(KVM_REQ_CLOCK_UPDATE, v);\n \t\treturn 1;\n \t}\n-\tif (!use_master_clock) {\n-\t\thost_tsc = rdtsc();\n-\t\tkernel_ns = get_kvmclock_base_ns();\n-\t}\n-\n-\ttsc_timestamp = kvm_read_l1_tsc(v, host_tsc);\n \n \t/*\n \t * We may have to catch up the TSC to match elapsed wall clock\n@@ -3361,42 +3415,57 @@ int kvm_guest_time_update(struct kvm_vcpu *v)\n \t * entry to avoid unknown leaps of TSC even when running\n \t * again on the same CPU. This may cause apparent elapsed\n \t * time to disappear, and the guest to stand still or run\n-\t *\tvery slowly.\n+\t * very slowly.\n \t */\n \tif (vcpu-\u003etsc_catchup) {\n-\t\tu64 tsc = compute_guest_tsc(v, kernel_ns);\n-\t\tif (tsc \u003e tsc_timestamp) {\n-\t\t\tadjust_tsc_offset_guest(v, tsc - tsc_timestamp);\n-\t\t\ttsc_timestamp = tsc;\n-\t\t}\n+\t\ts64 adjustment;\n+\n+\t\t/*\n+\t\t * Calculate the delta between what the guest TSC *should* be\n+\t\t * and what it actually is according to kvm_read_l1_tsc().\n+\t\t */\n+\t\tadjustment = compute_guest_tsc(v, kernel_ns) -\n+\t\t\t kvm_read_l1_tsc(v, host_tsc);\n+\t\tif (adjustment \u003e 0)\n+\t\t\tadjust_tsc_offset_guest(v, adjustment);\n \t}\n \n-\tlocal_irq_restore(flags);\n+\t/*\n+\t * Now that TSC upscaling is out of the way, the remaining calculations\n+\t * are all relative to the reference time that's placed in hv_clock.\n+\t * If the master clock is NOT in use, the reference time is \"now\". If\n+\t * master clock is in use, the reference time comes from there.\n+\t */\n+\tif (use_master_clock) {\n+\t\thost_tsc = master_host_tsc;\n+\t\tkernel_ns = master_kernel_ns;\n+\t}\n+\ttsc_timestamp = kvm_read_l1_tsc(v, host_tsc);\n \n \t/* With all the info we got, fill in the values */\n \n \tif (kvm_caps.has_tsc_control) {\n-\t\ttgt_tsc_khz = kvm_scale_tsc(tgt_tsc_khz,\n+\t\ttgt_tsc_hz = kvm_scale_tsc(tgt_tsc_hz,\n \t\t\t\t\t v-\u003earch.l1_tsc_scaling_ratio);\n-\t\ttgt_tsc_khz = tgt_tsc_khz ? : 1;\n+\t\ttgt_tsc_hz = tgt_tsc_hz ? : 1;\n \t}\n \n-\tif (unlikely(vcpu-\u003ehw_tsc_khz != tgt_tsc_khz)) {\n-\t\tkvm_get_time_scale(NSEC_PER_SEC, tgt_tsc_khz * 1000LL,\n+\tif (unlikely(vcpu-\u003ehw_tsc_hz != tgt_tsc_hz)) {\n+\t\tkvm_get_time_scale(NSEC_PER_SEC, tgt_tsc_hz,\n \t\t\t\t \u0026vcpu-\u003epvclock_tsc_shift,\n \t\t\t\t \u0026vcpu-\u003epvclock_tsc_mul);\n-\t\tvcpu-\u003ehw_tsc_khz = tgt_tsc_khz;\n+\t\tvcpu-\u003ehw_tsc_hz = tgt_tsc_hz;\n \t}\n \n \thv_clock.tsc_shift = vcpu-\u003epvclock_tsc_shift;\n \thv_clock.tsc_to_system_mul = vcpu-\u003epvclock_tsc_mul;\n \thv_clock.tsc_timestamp = tsc_timestamp;\n-\thv_clock.system_time = kernel_ns + v-\u003ekvm-\u003earch.kvmclock_offset;\n+\thv_clock.system_time = kernel_ns + kvmclock_offset;\n \tvcpu-\u003elast_guest_tsc = tsc_timestamp;\n \n \t/* If the host uses TSC clocksource, then it is stable */\n \thv_clock.flags = 0;\n-\tif (use_master_clock)\n+\tif (use_master_clock \u0026\u0026 ka-\u003eall_vcpus_matched_tsc)\n \t\thv_clock.flags |= PVCLOCK_TSC_STABLE_BIT;\n \n \tif (vcpu-\u003epv_time.active) {\n@@ -3454,63 +3523,22 @@ int kvm_guest_time_update(struct kvm_vcpu *v)\n * wallclock and kvmclock times, and subtracting one from the other.\n *\n * Fall back to using their values at slightly different moments by\n- * calling ktime_get_real_ns() and get_kvmclock_ns() separately.\n+ * calling ktime_get_real_ns() and get_kvmclock() separately.\n */\n uint64_t kvm_get_wall_clock_epoch(struct kvm *kvm)\n {\n-#ifdef CONFIG_X86_64\n-\tstruct pvclock_vcpu_time_info hv_clock;\n-\tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n-\tunsigned long seq, local_tsc_khz;\n-\tstruct timespec64 ts;\n-\tuint64_t host_tsc;\n-\n-\tdo {\n-\t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n-\n-\t\tlocal_tsc_khz = 0;\n-\t\tif (!ka-\u003euse_master_clock)\n-\t\t\tbreak;\n-\n-\t\t/*\n-\t\t * The TSC read and the call to get_cpu_tsc_khz() must happen\n-\t\t * on the same CPU.\n-\t\t */\n-\t\tget_cpu();\n-\n-\t\tlocal_tsc_khz = get_cpu_tsc_khz();\n-\n-\t\tif (local_tsc_khz \u0026\u0026\n-\t\t !kvm_get_walltime_and_clockread(\u0026ts, \u0026host_tsc))\n-\t\t\tlocal_tsc_khz = 0; /* Fall back to old method */\n-\n-\t\tput_cpu();\n-\n-\t\t/*\n-\t\t * These values must be snapshotted within the seqcount loop.\n-\t\t * After that, it's just mathematics which can happen on any\n-\t\t * CPU at any time.\n-\t\t */\n-\t\thv_clock.tsc_timestamp = ka-\u003emaster_cycle_now;\n-\t\thv_clock.system_time = ka-\u003emaster_kernel_ns + ka-\u003ekvmclock_offset;\n+\tstruct kvm_clock_data data;\n \n-\t} while (read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq));\n+\tget_kvmclock(kvm, \u0026data);\n \n \t/*\n-\t * If the conditions were right, and obtaining the wallclock+TSC was\n-\t * successful, calculate the KVM clock at the corresponding time and\n-\t * subtract one from the other to get the guest's epoch in nanoseconds\n-\t * since 1970-01-01.\n+\t * If get_kvmclock() captured both wallclock and kvmclock from the\n+\t * same TSC reading, use them for a precise epoch calculation.\n \t */\n-\tif (local_tsc_khz) {\n-\t\tkvm_get_time_scale(NSEC_PER_SEC, local_tsc_khz * NSEC_PER_USEC,\n-\t\t\t\t \u0026hv_clock.tsc_shift,\n-\t\t\t\t \u0026hv_clock.tsc_to_system_mul);\n-\t\treturn ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec -\n-\t\t\t__pvclock_read_cycles(\u0026hv_clock, host_tsc);\n-\t}\n-#endif\n-\treturn ktime_get_real_ns() - get_kvmclock_ns(kvm);\n+\tif (data.flags \u0026 KVM_CLOCK_REALTIME)\n+\t\treturn data.realtime - data.clock;\n+\n+\treturn ktime_get_real_ns() - data.clock;\n }\n \n /*\n@@ -4089,7 +4117,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n \t\tif (msr_info-\u003ehost_initiated) {\n \t\t\tkvm_synchronize_tsc(vcpu, \u0026data);\n \t\t} else if (!vcpu-\u003earch.guest_tsc_protected) {\n-\t\t\tu64 adj = kvm_compute_l1_tsc_offset(vcpu, data) - vcpu-\u003earch.l1_tsc_offset;\n+\t\t\tu64 adj = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data) -\n+\t\t\t\t vcpu-\u003earch.l1_tsc_offset;\n \t\t\tadjust_tsc_offset_guest(vcpu, adj);\n \t\t\tvcpu-\u003earch.ia32_tsc_adjust_msr += adj;\n \t\t}\n@@ -5213,7 +5242,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \t\t\tmark_tsc_unstable(\"KVM discovered backwards TSC\");\n \n \t\tif (kvm_check_tsc_unstable()) {\n-\t\t\tu64 offset = kvm_compute_l1_tsc_offset(vcpu,\n+\t\t\tu64 offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(),\n \t\t\t\t\t\tvcpu-\u003earch.last_guest_tsc);\n \t\t\tkvm_vcpu_write_tsc_offset(vcpu, offset);\n \t\t\tif (!vcpu-\u003earch.guest_tsc_protected)\n@@ -5227,13 +5256,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \t\t * On a host with synchronized TSC, there is no need to update\n \t\t * kvmclock on vcpu-\u003ecpu migration\n \t\t */\n-\t\tif (!vcpu-\u003ekvm-\u003earch.use_master_clock || vcpu-\u003ecpu == -1) {\n+\t\tif (!vcpu-\u003ekvm-\u003earch.use_master_clock) {\n \t\t\tif (__ratelimit(\u0026vcpu-\u003ekvm-\u003earch.kvmclock_update_rs))\n \t\t\t\tkvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu);\n \t\t\telse\n \t\t\t\tkvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);\n-\t\t}\n-\n+\t\t} else if (vcpu-\u003ecpu == -1)\n+\t\t\tkvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);\n \t\tif (vcpu-\u003ecpu != cpu)\n \t\t\tkvm_make_request(KVM_REQ_MIGRATE_TIMER, vcpu);\n \t\tvcpu-\u003ecpu = cpu;\n@@ -5934,6 +5963,9 @@ static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,\n \tcase KVM_VCPU_TSC_OFFSET:\n \t\tr = 0;\n \t\tbreak;\n+\tcase KVM_VCPU_TSC_SCALE:\n+\t\tr = kvm_caps.has_tsc_control ? 0 : -ENXIO;\n+\t\tbreak;\n \tdefault:\n \t\tr = -ENXIO;\n \t}\n@@ -5954,6 +5986,21 @@ static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,\n \t\t\tbreak;\n \t\tr = 0;\n \t\tbreak;\n+\tcase KVM_VCPU_TSC_SCALE: {\n+\t\tstruct kvm_vcpu_tsc_scale scale;\n+\n+\t\tr = -ENXIO;\n+\t\tif (!kvm_caps.has_tsc_control)\n+\t\t\tbreak;\n+\n+\t\tscale.tsc_ratio = vcpu-\u003earch.l1_tsc_scaling_ratio;\n+\t\tscale.tsc_frac_bits = kvm_caps.tsc_scaling_ratio_frac_bits;\n+\t\tr = -EFAULT;\n+\t\tif (copy_to_user(uaddr, \u0026scale, sizeof(scale)))\n+\t\t\tbreak;\n+\t\tr = 0;\n+\t\tbreak;\n+\t}\n \tdefault:\n \t\tr = -ENXIO;\n \t}\n@@ -5981,8 +6028,8 @@ static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,\n \t\traw_spin_lock_irqsave(\u0026kvm-\u003earch.tsc_write_lock, flags);\n \n \t\tmatched = (vcpu-\u003earch.virtual_tsc_khz \u0026\u0026\n-\t\t\t kvm-\u003earch.last_tsc_khz == vcpu-\u003earch.virtual_tsc_khz \u0026\u0026\n-\t\t\t kvm-\u003earch.last_tsc_offset == offset);\n+\t\t\t kvm-\u003earch.cur_tsc_khz == vcpu-\u003earch.virtual_tsc_khz \u0026\u0026\n+\t\t\t kvm-\u003earch.cur_tsc_offset == offset);\n \n \t\ttsc = kvm_scale_tsc(rdtsc(), vcpu-\u003earch.l1_tsc_scaling_ratio) + offset;\n \t\tns = get_kvmclock_base_ns();\n@@ -5993,6 +6040,9 @@ static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,\n \t\tr = 0;\n \t\tbreak;\n \t}\n+\tcase KVM_VCPU_TSC_SCALE:\n+\t\tr = kvm_caps.has_tsc_control ? -EINVAL : -ENXIO;\n+\t\tbreak;\n \tdefault:\n \t\tr = -ENXIO;\n \t}\n@@ -6209,6 +6259,169 @@ static int kvm_get_reg_list(struct kvm_vcpu *vcpu,\n \treturn 0;\n }\n \n+#ifdef CONFIG_X86_64\n+static int kvm_vcpu_ioctl_get_clock_guest(struct kvm_vcpu *v, void __user *argp)\n+{\n+\tstruct pvclock_vcpu_time_info hv_clock = {};\n+\tstruct kvm_vcpu_arch *vcpu = \u0026v-\u003earch;\n+\tstruct kvm_arch *ka = \u0026v-\u003ekvm-\u003earch;\n+\tunsigned int seq;\n+\n+\t/*\n+\t * If KVM_REQ_CLOCK_UPDATE is already pending, or if the pvclock\n+\t * has never been generated at all, call kvm_guest_time_update().\n+\t */\n+\tif (kvm_check_request(KVM_REQ_CLOCK_UPDATE, v) || !vcpu-\u003ehw_tsc_hz) {\n+\t\tint idx = srcu_read_lock(\u0026v-\u003ekvm-\u003esrcu);\n+\t\tint ret = kvm_guest_time_update(v);\n+\n+\t\tsrcu_read_unlock(\u0026v-\u003ekvm-\u003esrcu, idx);\n+\t\tif (ret)\n+\t\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * Reconstruct the pvclock from the master clock state, matching\n+\t * exactly what kvm_guest_time_update() writes to the guest.\n+\t */\n+\tdo {\n+\t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n+\n+\t\tif (!ka-\u003euse_master_clock)\n+\t\t\treturn -EINVAL;\n+\n+\t\thv_clock.tsc_timestamp = kvm_read_l1_tsc(v, ka-\u003emaster_cycle_now);\n+\t\thv_clock.system_time = ka-\u003emaster_kernel_ns + ka-\u003ekvmclock_offset;\n+\t} while (read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq));\n+\n+\thv_clock.tsc_shift = vcpu-\u003epvclock_tsc_shift;\n+\thv_clock.tsc_to_system_mul = vcpu-\u003epvclock_tsc_mul;\n+\thv_clock.flags = ka-\u003eall_vcpus_matched_tsc ? PVCLOCK_TSC_STABLE_BIT : 0;\n+\n+\tif (copy_to_user(argp, \u0026hv_clock, sizeof(hv_clock)))\n+\t\treturn -EFAULT;\n+\n+\treturn 0;\n+}\n+\n+/*\n+ * Reverse the calculation in the hv_clock definition.\n+ *\n+ * time_ns = ( (cycles \u003c\u003c shift) * mul ) \u003e\u003e 32;\n+ * (although shift can be negative, so that's bad C)\n+ *\n+ * So for a single second,\n+ * NSEC_PER_SEC = ( ( FREQ_HZ \u003c\u003c shift) * mul ) \u003e\u003e 32\n+ * NSEC_PER_SEC \u003c\u003c 32 = ( FREQ_HZ \u003c\u003c shift ) * mul\n+ * ( NSEC_PER_SEC \u003c\u003c 32 ) / mul = FREQ_HZ \u003c\u003c shift\n+ * ( NSEC_PER_SEC \u003c\u003c 32 ) / mul ) \u003e\u003e shift = FREQ_HZ\n+ */\n+static u64 hvclock_to_hz(u32 mul, s8 shift)\n+{\n+\tu64 tm = NSEC_PER_SEC \u003c\u003c 32;\n+\n+\t/* Maximise precision. Shift right until the top bit is set */\n+\ttm \u003c\u003c= 2;\n+\tshift += 2;\n+\n+\t/* While 'mul' is even, increase the shift *after* the division */\n+\twhile (!(mul \u0026 1)) {\n+\t\tshift++;\n+\t\tmul \u003e\u003e= 1;\n+\t}\n+\n+\ttm /= mul;\n+\n+\tif (shift \u003e= 64)\n+\t\treturn 0;\n+\tif (shift \u003e 0)\n+\t\treturn tm \u003e\u003e shift;\n+\tif (shift \u003c= -64)\n+\t\treturn 0;\n+\treturn tm \u003c\u003c -shift;\n+}\n+\n+static int kvm_vcpu_ioctl_set_clock_guest(struct kvm_vcpu *v, void __user *argp)\n+{\n+\tstruct pvclock_vcpu_time_info user_hv_clock;\n+\tstruct kvm *kvm = v-\u003ekvm;\n+\tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n+\tu64 curr_tsc_hz, user_tsc_hz;\n+\tu64 user_clk_ns;\n+\tu64 guest_tsc;\n+\tint rc = 0;\n+\n+\tif (copy_from_user(\u0026user_hv_clock, argp, sizeof(user_hv_clock)))\n+\t\treturn -EFAULT;\n+\n+\tif (user_hv_clock.pad0 || user_hv_clock.pad[0] || user_hv_clock.pad[1])\n+\t\treturn -EINVAL;\n+\n+\tif (!user_hv_clock.tsc_to_system_mul)\n+\t\treturn -EINVAL;\n+\n+\tif (user_hv_clock.tsc_shift \u003c -31 || user_hv_clock.tsc_shift \u003e 31)\n+\t\treturn -EINVAL;\n+\n+\tuser_tsc_hz = hvclock_to_hz(user_hv_clock.tsc_to_system_mul,\n+\t\t\t\t user_hv_clock.tsc_shift);\n+\n+\tkvm_hv_request_tsc_page_update(kvm);\n+\n+\t/*\n+\t * kvm_start_pvclock_update() takes tsc_write_lock and opens\n+\t * the pvclock seqcount; kvm_end_pvclock_update() closes both.\n+\t * All clock state modifications between them are atomic with\n+\t * respect to readers in kvm_guest_time_update().\n+\t */\n+\tkvm_start_pvclock_update(kvm);\n+\tpvclock_update_vm_gtod_copy(kvm);\n+\n+\tif (!ka-\u003euse_master_clock) {\n+\t\trc = -EINVAL;\n+\t\tgoto out;\n+\t}\n+\n+\tcurr_tsc_hz = (u64)get_cpu_tsc_khz() * 1000;\n+\tif (unlikely(curr_tsc_hz == 0)) {\n+\t\trc = -EINVAL;\n+\t\tgoto out;\n+\t}\n+\n+\tif (kvm_caps.has_tsc_control)\n+\t\tcurr_tsc_hz = kvm_scale_tsc(curr_tsc_hz,\n+\t\t\t\t\t v-\u003earch.l1_tsc_scaling_ratio);\n+\n+\t/*\n+\t * Allow for a discrepancy of 1 kHz either way between the TSC\n+\t * frequency used to generate the user's pvclock and the current\n+\t * host's measured frequency, since they may not precisely match.\n+\t */\n+\tif (user_tsc_hz \u003c curr_tsc_hz - 1000 ||\n+\t user_tsc_hz \u003e curr_tsc_hz + 1000) {\n+\t\trc = -ERANGE;\n+\t\tgoto out;\n+\t}\n+\n+\t/*\n+\t * Calculate the guest TSC at the new reference point, and the\n+\t * corresponding KVM clock value according to user_hv_clock.\n+\t * Adjust kvmclock_offset so both definitions agree.\n+\t */\n+\tguest_tsc = kvm_read_l1_tsc(v, ka-\u003emaster_cycle_now);\n+\tif (guest_tsc \u003c user_hv_clock.tsc_timestamp) {\n+\t\trc = -EINVAL;\n+\t\tgoto out;\n+\t}\n+\tuser_clk_ns = __pvclock_read_cycles(\u0026user_hv_clock, guest_tsc);\n+\tka-\u003ekvmclock_offset = user_clk_ns - ka-\u003emaster_kernel_ns;\n+\n+out:\n+\tkvm_end_pvclock_update(kvm);\n+\treturn rc;\n+}\n+#endif\n+\n long kvm_arch_vcpu_ioctl(struct file *filp,\n \t\t\t unsigned int ioctl, unsigned long arg)\n {\n@@ -6609,6 +6822,14 @@ long kvm_arch_vcpu_ioctl(struct file *filp,\n \t\tsrcu_read_unlock(\u0026vcpu-\u003ekvm-\u003esrcu, idx);\n \t\tbreak;\n \t}\n+#ifdef CONFIG_X86_64\n+\tcase KVM_SET_CLOCK_GUEST:\n+\t\tr = kvm_vcpu_ioctl_set_clock_guest(vcpu, argp);\n+\t\tbreak;\n+\tcase KVM_GET_CLOCK_GUEST:\n+\t\tr = kvm_vcpu_ioctl_get_clock_guest(vcpu, argp);\n+\t\tbreak;\n+#endif\n #ifdef CONFIG_KVM_HYPERV\n \tcase KVM_GET_SUPPORTED_HV_CPUID:\n \t\tr = kvm_ioctl_get_supported_hv_cpuid(vcpu, argp);\n@@ -9824,7 +10045,7 @@ static void kvm_hyperv_tsc_notifier(void)\n \tkvm_caps.max_guest_tsc_khz = tsc_khz;\n \n \tlist_for_each_entry(kvm, \u0026vm_list, vm_list) {\n-\t\t__kvm_start_pvclock_update(kvm);\n+\t\t__kvm_start_pvclock_update(kvm, NULL);\n \t\tpvclock_update_vm_gtod_copy(kvm);\n \t\tkvm_end_pvclock_update(kvm);\n \t}\n@@ -9991,6 +10212,7 @@ static void pvclock_irq_work_fn(struct irq_work *w)\n }\n \n static DEFINE_IRQ_WORK(pvclock_irq_work, pvclock_irq_work_fn);\n+#endif\n \n /*\n * Notification about pvclock gtod data update.\n@@ -9998,26 +10220,27 @@ static DEFINE_IRQ_WORK(pvclock_irq_work, pvclock_irq_work_fn);\n static int pvclock_gtod_notify(struct notifier_block *nb, unsigned long unused,\n \t\t\t void *priv)\n {\n-\tstruct pvclock_gtod_data *gtod = \u0026pvclock_gtod_data;\n+#ifdef CONFIG_X86_64\n \tstruct timekeeper *tk = priv;\n \n-\tupdate_pvclock_gtod(tk);\n+\tkvm_host_vclock_mode =\n+\t\ttk-\u003etkr_mono.clock-\u003evdso_clock_mode;\n \n \t/*\n \t * Disable master clock if host does not trust, or does not use,\n \t * TSC based clocksource. Delegate queue_work() to irq_work as\n \t * this is invoked with tk_core.seq write held.\n \t */\n-\tif (!gtod_is_based_on_tsc(gtod-\u003eclock.vclock_mode) \u0026\u0026\n+\tif (!gtod_is_based_on_tsc(kvm_host_vclock_mode) \u0026\u0026\n \t atomic_read(\u0026kvm_guest_has_master_clock) != 0)\n \t\tirq_work_queue(\u0026pvclock_irq_work);\n+#endif\n \treturn 0;\n }\n \n static struct notifier_block pvclock_gtod_notifier = {\n \t.notifier_call = pvclock_gtod_notify,\n };\n-#endif\n \n void kvm_setup_xss_caps(void)\n {\n@@ -10206,9 +10429,9 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)\n \n \tif (pi_inject_timer == -1)\n \t\tpi_inject_timer = housekeeping_enabled(HK_TYPE_TIMER);\n-#ifdef CONFIG_X86_64\n \tpvclock_gtod_register_notifier(\u0026pvclock_gtod_notifier);\n \n+#ifdef CONFIG_X86_64\n \tif (hypervisor_is_type(X86_HYPER_MS_HYPERV))\n \t\tset_hv_tscchange_cb(kvm_hyperv_tsc_notifier);\n #endif\n@@ -10265,8 +10488,8 @@ void kvm_x86_vendor_exit(void)\n \t\t\t\t\t CPUFREQ_TRANSITION_NOTIFIER);\n \t\tcpuhp_remove_state_nocalls(CPUHP_AP_X86_KVM_CLK_ONLINE);\n \t}\n-#ifdef CONFIG_X86_64\n \tpvclock_gtod_unregister_notifier(\u0026pvclock_gtod_notifier);\n+#ifdef CONFIG_X86_64\n \tirq_work_sync(\u0026pvclock_irq_work);\n \tcancel_work_sync(\u0026pvclock_gtod_work);\n #endif\n@@ -11201,8 +11424,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)\n \t\t\tkvm_mmu_free_obsolete_roots(vcpu);\n \t\tif (kvm_check_request(KVM_REQ_MIGRATE_TIMER, vcpu))\n \t\t\t__kvm_migrate_timers(vcpu);\n-\t\tif (kvm_check_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu))\n-\t\t\tkvm_update_masterclock(vcpu-\u003ekvm);\n+\t\tif (kvm_test_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu))\n+\t\t\tkvm_update_masterclock(vcpu-\u003ekvm, vcpu);\n \t\tif (kvm_check_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu))\n \t\t\tkvm_gen_kvmclock_update(vcpu);\n \t\tif (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu)) {\n@@ -13187,7 +13410,7 @@ int kvm_arch_enable_virtualization_cpu(void)\n {\n \tstruct kvm *kvm;\n \tstruct kvm_vcpu *vcpu;\n-\tunsigned long i;\n+\tunsigned long i, flags;\n \tint ret;\n \tu64 local_tsc;\n \tu64 max_tsc = 0;\n@@ -13266,13 +13489,18 @@ int kvm_arch_enable_virtualization_cpu(void)\n \t\t\t}\n \n \t\t\t/*\n-\t\t\t * We have to disable TSC offset matching.. if you were\n-\t\t\t * booting a VM while issuing an S4 host suspend....\n-\t\t\t * you may have some problem. Solving this issue is\n-\t\t\t * left as an exercise to the reader.\n+\t\t\t * Adjust the TSC matching reference by the same\n+\t\t\t * delta applied to each vCPU's offset, so that\n+\t\t\t * future KVM_SET_TSC / vCPU creation still matches\n+\t\t\t * correctly against the adjusted TSC timeline.\n+\t\t\t * Scale from host to guest TSC rate.\n \t\t\t */\n-\t\t\tkvm-\u003earch.last_tsc_nsec = 0;\n-\t\t\tkvm-\u003earch.last_tsc_write = 0;\n+\t\t\traw_spin_lock_irqsave(\u0026kvm-\u003earch.tsc_write_lock, flags);\n+\t\t\tkvm-\u003earch.cur_tsc_write -=\n+\t\t\t\tkvm_scale_tsc(delta_cyc,\n+\t\t\t\t\t kvm-\u003earch.cur_tsc_scaling_ratio);\n+\t\t\tkvm-\u003earch.cur_tsc_offset += delta_cyc;\n+\t\t\traw_spin_unlock_irqrestore(\u0026kvm-\u003earch.tsc_write_lock, flags);\n \t\t}\n \n \t}\n@@ -13342,6 +13570,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n {\n \tint ret;\n \tunsigned long flags;\n+\tu64 kvmclock_host_tsc;\n+\ts64 kvmclock_ns;\n \n \tif (!kvm_is_vm_type_supported(type))\n \t\treturn -EINVAL;\n@@ -13373,17 +13603,47 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)\n \tseqcount_raw_spinlock_init(\u0026kvm-\u003earch.pvclock_sc, \u0026kvm-\u003earch.tsc_write_lock);\n \tratelimit_state_init(\u0026kvm-\u003earch.kvmclock_update_rs, HZ, 10);\n \tratelimit_set_flags(\u0026kvm-\u003earch.kvmclock_update_rs, RATELIMIT_MSG_ON_RELEASE);\n-\tkvm-\u003earch.kvmclock_offset = -get_kvmclock_base_ns();\n-\n-\traw_spin_lock_irqsave(\u0026kvm-\u003earch.tsc_write_lock, flags);\n-\tpvclock_update_vm_gtod_copy(kvm);\n-\traw_spin_unlock_irqrestore(\u0026kvm-\u003earch.tsc_write_lock, flags);\n \n \tkvm-\u003earch.default_tsc_khz = max_tsc_khz ? : tsc_khz;\n \tkvm-\u003earch.apic_bus_cycle_ns = APIC_BUS_CYCLE_NS_DEFAULT;\n \tkvm-\u003earch.guest_can_read_msr_platform_info = true;\n \tkvm-\u003earch.enable_pmu = enable_pmu;\n \n+\tkvm-\u003earch.all_vcpus_matched_freq = true;\n+\tkvm-\u003earch.all_vcpus_matched_tsc = true;\n+\tkvm-\u003earch.cur_tsc_scaling_ratio = kvm_caps.default_tsc_scaling_ratio;\n+\tkvm-\u003earch.cur_tsc_khz = kvm-\u003earch.default_tsc_khz;\n+\n+\traw_spin_lock_irqsave(\u0026kvm-\u003earch.tsc_write_lock, flags);\n+\n+\t/*\n+\t * Establish the initial TSC generation for synchronization logic.\n+\t * The first vCPU will sync to this, starting its guest TSC at the\n+\t * number of cycles elapsed since VM creation rather than at zero.\n+\t * This ensures coherence with the kvmclock epoch (also set here)\n+\t * and means the master clock snapshot taken below is immediately\n+\t * valid — no need to redo it at first vCPU creation.\n+\t */\n+#ifdef CONFIG_X86_64\n+\tif (!kvm_get_time_and_clockread(\u0026kvmclock_ns, \u0026kvmclock_host_tsc))\n+#endif\n+\t{\n+\t\t/*\n+\t\t * If not simultaneous, we want the TSC to be zero slightly\n+\t\t * *after* the kvmclock is zero.\n+\t\t */\n+\t\tkvmclock_ns = get_kvmclock_base_ns();\n+\t\tkvmclock_host_tsc = rdtsc();\n+\t}\n+\tkvm-\u003earch.cur_tsc_write = 0;\n+\tkvm-\u003earch.cur_tsc_offset = -(s64)kvmclock_host_tsc;\n+\tkvm-\u003earch.cur_tsc_nsec = kvmclock_ns;\n+\tkvm-\u003earch.cur_tsc_generation = 1;\n+\tkvm-\u003earch.kvmclock_offset = -kvmclock_ns;\n+\n+\tpvclock_update_vm_gtod_copy(kvm);\n+\traw_spin_unlock_irqrestore(\u0026kvm-\u003earch.tsc_write_lock, flags);\n+\n #if IS_ENABLED(CONFIG_HYPERV)\n \tspin_lock_init(\u0026kvm-\u003earch.hv_root_tdp_lock);\n \tkvm-\u003earch.hv_root_tdp = INVALID_PAGE;\ndiff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c\nindex 91fd3673c09a2e..b1d67ece5db301 100644\n--- a/arch/x86/kvm/xen.c\n+++ b/arch/x86/kvm/xen.c\n@@ -98,8 +98,6 @@ static int kvm_xen_shared_info_init(struct kvm *kvm)\n \twc-\u003eversion = wc_version + 1;\n \tread_unlock_irq(\u0026gpc-\u003elock);\n \n-\tkvm_make_all_cpus_request(kvm, KVM_REQ_MASTERCLOCK_UPDATE);\n-\n out:\n \tsrcu_read_unlock(\u0026kvm-\u003esrcu, idx);\n \treturn ret;\n@@ -588,29 +586,45 @@ void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)\n {\n \tstruct kvm_vcpu_xen *vx = \u0026v-\u003earch.xen;\n \tu64 now = get_kvmclock_ns(v-\u003ekvm);\n-\tu64 delta_ns = now - vx-\u003erunstate_entry_time;\n \tu64 run_delay = current-\u003esched_info.run_delay;\n+\ts64 delta_ns = now - vx-\u003erunstate_entry_time;\n+\ts64 steal_ns = run_delay - vx-\u003elast_steal;\n \n+\t/*\n+\t * If the vCPU was never run before, its prior state should\n+\t * be considered RUNSTATE_offline.\n+\t */\n \tif (unlikely(!vx-\u003erunstate_entry_time))\n \t\tvx-\u003ecurrent_runstate = RUNSTATE_offline;\n \n+\t/*\n+\t * If KVM clock went backwards, just update the current runstate\n+\t * but don't account any time. Leave entry_time unchanged so the\n+\t * next positive delta covers the full period once the clock\n+\t * catches up. Update last_steal every time so stolen time only\n+\t * reflects the interval since the most recent call.\n+\t */\n+\tif (delta_ns \u003c 0)\n+\t\tgoto update_guest;\n+\n \t/*\n \t * Time waiting for the scheduler isn't \"stolen\" if the\n \t * vCPU wasn't running anyway.\n \t */\n-\tif (vx-\u003ecurrent_runstate == RUNSTATE_running) {\n-\t\tu64 steal_ns = run_delay - vx-\u003elast_steal;\n+\tif (vx-\u003ecurrent_runstate == RUNSTATE_running \u0026\u0026 steal_ns \u003e 0) {\n+\t\tif (steal_ns \u003e delta_ns)\n+\t\t\tsteal_ns = delta_ns;\n \n \t\tdelta_ns -= steal_ns;\n-\n \t\tvx-\u003erunstate_times[RUNSTATE_runnable] += steal_ns;\n \t}\n-\tvx-\u003elast_steal = run_delay;\n \n \tvx-\u003erunstate_times[vx-\u003ecurrent_runstate] += delta_ns;\n-\tvx-\u003ecurrent_runstate = state;\n \tvx-\u003erunstate_entry_time = now;\n \n+ update_guest:\n+\tvx-\u003ecurrent_runstate = state;\n+\tvx-\u003elast_steal = run_delay;\n \tif (vx-\u003erunstate_cache.active)\n \t\tkvm_xen_update_runstate_guest(v, state == RUNSTATE_runnable);\n }\ndiff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h\nindex 59e6128a7bd3c2..f372855857a889 100644\n--- a/arch/x86/kvm/xen.h\n+++ b/arch/x86/kvm/xen.h\n@@ -50,14 +50,6 @@ static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)\n \t\tkvm_xen_inject_vcpu_vector(vcpu);\n }\n \n-static inline bool kvm_xen_is_tsc_leaf(struct kvm_vcpu *vcpu, u32 function)\n-{\n-\treturn static_branch_unlikely(\u0026kvm_xen_enabled.key) \u0026\u0026\n-\t vcpu-\u003earch.xen.cpuid.base \u0026\u0026\n-\t function \u003c= vcpu-\u003earch.xen.cpuid.limit \u0026\u0026\n-\t function == (vcpu-\u003earch.xen.cpuid.base | XEN_CPUID_LEAF(3));\n-}\n-\n static inline bool kvm_xen_msr_enabled(struct kvm *kvm)\n {\n \treturn static_branch_unlikely(\u0026kvm_xen_enabled.key) \u0026\u0026\n@@ -177,11 +169,6 @@ static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)\n {\n \treturn false;\n }\n-\n-static inline bool kvm_xen_is_tsc_leaf(struct kvm_vcpu *vcpu, u32 function)\n-{\n-\treturn false;\n-}\n #endif\n \n int kvm_xen_hypercall(struct kvm_vcpu *vcpu);\ndiff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h\nindex 6c8afa2047bf33..9b50191b859cd7 100644\n--- a/include/uapi/linux/kvm.h\n+++ b/include/uapi/linux/kvm.h\n@@ -1669,4 +1669,7 @@ struct kvm_pre_fault_memory {\n \t__u64 padding[5];\n };\n \n+#define KVM_SET_CLOCK_GUEST\t_IOW(KVMIO, 0xd6, struct pvclock_vcpu_time_info)\n+#define KVM_GET_CLOCK_GUEST\t_IOR(KVMIO, 0xd7, struct pvclock_vcpu_time_info)\n+\n #endif /* __LINUX_KVM_H */\ndiff --git a/scripts/xen-hypercalls.sh b/scripts/xen-hypercalls.sh\nindex f18b00843df3ff..51a722198997da 100755\n--- a/scripts/xen-hypercalls.sh\n+++ b/scripts/xen-hypercalls.sh\n@@ -5,7 +5,7 @@ shift\n in=\"$@\"\n \n for i in $in; do\n-\teval $CPP $LINUXINCLUDE -dD -imacros \"$i\" -x c /dev/null\n+\teval $CPP -D__KERNEL__ $LINUXINCLUDE -dD -imacros \"$i\" -x c /dev/null\n done | \\\n awk '$1 == \"#define\" \u0026\u0026 $2 ~ /__HYPERVISOR_[a-z][a-z_0-9]*/ { v[$3] = $2 }\n \tEND { print \"/* auto-generated by scripts/xen-hypercall.sh */\"\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 9118a5a51b89fd..f155683bd7f8d1 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -105,6 +105,9 @@ TEST_GEN_PROGS_x86 += x86/pmu_counters_test\n TEST_GEN_PROGS_x86 += x86/pmu_event_filter_test\n TEST_GEN_PROGS_x86 += x86/private_mem_conversions_test\n TEST_GEN_PROGS_x86 += x86/private_mem_kvm_exits_test\n+TEST_GEN_PROGS_x86 += x86/pvclock_test\n+TEST_GEN_PROGS_x86 += x86/masterclock_offset_test\n+TEST_GEN_PROGS_x86 += x86/pvclock_migration_test\n TEST_GEN_PROGS_x86 += x86/set_boot_cpu_id\n TEST_GEN_PROGS_x86 += x86/set_sregs_test\n TEST_GEN_PROGS_x86 += x86/smaller_maxphyaddr_emulation_test\n@@ -138,6 +141,8 @@ TEST_GEN_PROGS_x86 += x86/xss_msr_test\n TEST_GEN_PROGS_x86 += x86/debug_regs\n TEST_GEN_PROGS_x86 += x86/tsc_msrs_test\n TEST_GEN_PROGS_x86 += x86/vmx_pmu_caps_test\n+TEST_GEN_PROGS_x86 += x86/xen_cpuid_timing_test\n+TEST_GEN_PROGS_x86 += x86/xen_migration_test\n TEST_GEN_PROGS_x86 += x86/xen_shinfo_test\n TEST_GEN_PROGS_x86 += x86/xen_vmcall_test\n TEST_GEN_PROGS_x86 += x86/sev_init2_tests\ndiff --git a/tools/testing/selftests/kvm/x86/masterclock_offset_test.c b/tools/testing/selftests/kvm/x86/masterclock_offset_test.c\nnew file mode 100644\nindex 00000000000000..88e2bd2edab516\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/masterclock_offset_test.c\n@@ -0,0 +1,180 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Test that KVM master clock mode works with different TSC offsets\n+ * as long as all vCPUs have the same TSC frequency.\n+ */\n+#include \u003cstdint.h\u003e\n+#include \u003cstring.h\u003e\n+\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+\n+#include \u003casm/pvclock-abi.h\u003e\n+\n+#define KVMCLOCK_GPA\t0xc0000000ull\n+#define TSC_OFFSET\t(1000000000ULL)\n+\n+static uint64_t pvclock_calc(struct pvclock_vcpu_time_info *pvti, uint64_t guest_tsc)\n+{\n+\tuint64_t delta = guest_tsc - pvti-\u003etsc_timestamp;\n+\n+\tif (pvti-\u003etsc_shift \u003e= 0)\n+\t\tdelta \u003c\u003c= pvti-\u003etsc_shift;\n+\telse\n+\t\tdelta \u003e\u003e= -(int)pvti-\u003etsc_shift;\n+\n+\treturn pvti-\u003esystem_time + ((__uint128_t)delta * pvti-\u003etsc_to_system_mul \u003e\u003e 32);\n+}\n+\n+static void guest_code(void)\n+{\n+\twrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | KVM_MSR_ENABLED);\n+\tfor (;;)\n+\t\tGUEST_SYNC(0);\n+}\n+\n+int main(void)\n+{\n+\tstruct kvm_vcpu *vcpus[3];\n+\tstruct kvm_clock_data clock;\n+\tstruct pvclock_vcpu_time_info pvti[3];\n+\tstruct kvm_vm *vm;\n+\tuint64_t offset0, host_tsc, clk0, clk2;\n+\tint i;\n+\n+\tTEST_REQUIRE(sys_clocksource_is_based_on_tsc());\n+\n+\tvm = vm_create_with_vcpus(3, guest_code, vcpus);\n+\n+\tTEST_REQUIRE(!__vcpu_has_device_attr(vcpus[0], KVM_VCPU_TSC_CTRL,\n+\t\t\t\t\t KVM_VCPU_TSC_OFFSET));\n+\n+\tvm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,\n+\t\t\t\t KVMCLOCK_GPA, 1,\n+\t\t\t\t vm_calc_num_guest_pages(VM_MODE_DEFAULT,\n+\t\t\t\t\t\t\t getpagesize()), 0);\n+\tvirt_map(vm, KVMCLOCK_GPA, KVMCLOCK_GPA,\n+\t\t vm_calc_num_guest_pages(VM_MODE_DEFAULT, getpagesize()));\n+\n+\t/* Get vCPU 0's default offset and set vCPU 2's offset higher */\n+\tvcpu_device_attr_get(vcpus[0], KVM_VCPU_TSC_CTRL,\n+\t\t\t KVM_VCPU_TSC_OFFSET, \u0026offset0);\n+\tuint64_t offset2 = offset0 + TSC_OFFSET;\n+\tvcpu_device_attr_set(vcpus[2], KVM_VCPU_TSC_CTRL,\n+\t\t\t KVM_VCPU_TSC_OFFSET, \u0026offset2);\n+\n+\t/* Run each vCPU to enable kvmclock (with offset already set) */\n+\tfor (i = 0; i \u003c 3; i++) {\n+\t\tvcpu_run(vcpus[i]);\n+\t\tTEST_ASSERT_KVM_EXIT_REASON(vcpus[i], KVM_EXIT_IO);\n+\t}\n+\n+\t/* Check master clock is active */\n+\tmemset(\u0026clock, 0, sizeof(clock));\n+\tvm_ioctl(vm, KVM_GET_CLOCK, \u0026clock);\n+\tpr_info(\"KVM_GET_CLOCK flags: 0x%x\\n\", clock.flags);\n+\tTEST_ASSERT(clock.flags \u0026 KVM_CLOCK_HOST_TSC,\n+\t\t \"Master clock should be active, flags=0x%x\", clock.flags);\n+\tTEST_ASSERT(clock.flags \u0026 KVM_CLOCK_TSC_STABLE,\n+\t\t \"KVM_CLOCK_TSC_STABLE should be set, flags=0x%x\", clock.flags);\n+\n+\t/* Get per-vCPU pvclock in order 0, 2, 1 */\n+\tint order[] = {0, 2, 1};\n+\tfor (i = 0; i \u003c 3; i++) {\n+\t\tint idx = order[i];\n+\t\t__vcpu_ioctl(vcpus[idx], KVM_GET_CLOCK_GUEST, \u0026pvti[idx]);\n+\t\tpr_info(\"vCPU %d: tsc_timestamp=%lu system_time=%lu \"\n+\t\t\t\"mul=%u shift=%d flags=0x%x\\n\",\n+\t\t\tidx, (unsigned long)pvti[idx].tsc_timestamp,\n+\t\t\t(unsigned long)pvti[idx].system_time,\n+\t\t\tpvti[idx].tsc_to_system_mul, pvti[idx].tsc_shift,\n+\t\t\tpvti[idx].flags);\n+\t}\n+\n+\t/* Read guest TSCs: should see (0+OFF) \u003c 2 \u003c (1+OFF) */\n+\tuint64_t gtsc0 = vcpu_get_msr(vcpus[0], MSR_IA32_TSC);\n+\tuint64_t gtsc2 = vcpu_get_msr(vcpus[2], MSR_IA32_TSC);\n+\tuint64_t gtsc1 = vcpu_get_msr(vcpus[1], MSR_IA32_TSC);\n+\tpr_info(\"Guest TSCs: vcpu0=%lu vcpu2=%lu vcpu1=%lu\\n\",\n+\t\t(unsigned long)gtsc0, (unsigned long)gtsc2, (unsigned long)gtsc1);\n+\tpr_info(\"vcpu0+OFF=%lu vcpu1+OFF=%lu\\n\",\n+\t\t(unsigned long)(gtsc0 + TSC_OFFSET),\n+\t\t(unsigned long)(gtsc1 + TSC_OFFSET));\n+\tTEST_ASSERT(gtsc0 + TSC_OFFSET \u003c gtsc2 \u0026\u0026 gtsc2 \u003c gtsc1 + TSC_OFFSET,\n+\t\t \"Expected (vcpu0+OFF) \u003c vcpu2 \u003c (vcpu1+OFF)\");\n+\n+\t/* PVCLOCK_TSC_STABLE_BIT should NOT be set (offsets differ) */\n+\tTEST_ASSERT(!(pvti[2].flags \u0026 PVCLOCK_TSC_STABLE_BIT),\n+\t\t \"PVCLOCK_TSC_STABLE_BIT should NOT be set, flags=0x%x\",\n+\t\t pvti[2].flags);\n+\n+\t/* Same mul/shift */\n+\tTEST_ASSERT(pvti[0].tsc_to_system_mul == pvti[2].tsc_to_system_mul \u0026\u0026\n+\t\t pvti[0].tsc_shift == pvti[2].tsc_shift,\n+\t\t \"All vCPUs should have same mul/shift\");\n+\n+\t/*\n+\t * Read host TSC once. At this instant:\n+\t * vCPU 0 guest TSC = host_tsc + offset0\n+\t * vCPU 2 guest TSC = host_tsc + offset0 + TSC_OFFSET\n+\t * Feed each through its pvclock. Expect the same kvmclock.\n+\t */\n+\thost_tsc = rdtsc();\n+\tclk0 = pvclock_calc(\u0026pvti[0], host_tsc + offset0);\n+\tclk2 = pvclock_calc(\u0026pvti[2], host_tsc + offset0 + TSC_OFFSET);\n+\n+\tpr_info(\"kvmclock via vCPU 0: %lu ns\\n\", (unsigned long)clk0);\n+\tpr_info(\"kvmclock via vCPU 2: %lu ns\\n\", (unsigned long)clk2);\n+\tTEST_ASSERT(clk0 == clk2,\n+\t\t \"kvmclock from offset vCPUs should match exactly, \"\n+\t\t \"diff=%ld ns\", (long)(clk2 - clk0));\n+\n+\tpr_info(\"PASSED: pvclock consistent across offset vCPUs\\n\");\n+\n+\t/*\n+\t * Now add an hour to the VM kvmclock via KVM_SET_CLOCK, run each\n+\t * vCPU to pick up the update, and check they're still in sync.\n+\t */\n+\t{\n+#define ONE_HOUR_NS (3600ULL * NSEC_PER_SEC)\n+\t\tstruct kvm_clock_data setclk = { .clock = clock.clock + ONE_HOUR_NS };\n+\n+\t\tvm_ioctl(vm, KVM_SET_CLOCK, \u0026setclk);\n+\t}\n+\n+\t/* Guest code does GUEST_SYNC then exits — run each to see update */\n+\tfor (i = 0; i \u003c 3; i++) {\n+\t\tvcpu_run(vcpus[order[i]]);\n+\t\tTEST_ASSERT_KVM_EXIT_REASON(vcpus[order[i]], KVM_EXIT_IO);\n+\t}\n+\n+\t/* Re-read pvclocks */\n+\tfor (i = 0; i \u003c 3; i++)\n+\t\t__vcpu_ioctl(vcpus[order[i]], KVM_GET_CLOCK_GUEST, \u0026pvti[order[i]]);\n+\n+\tpr_info(\"After +1h: vCPU 0 system_time=%lu, vCPU 2 system_time=%lu\\n\",\n+\t\t(unsigned long)pvti[0].system_time,\n+\t\t(unsigned long)pvti[2].system_time);\n+\tTEST_ASSERT(pvti[0].system_time == pvti[2].system_time,\n+\t\t \"system_time should still match after KVM_SET_CLOCK\");\n+\n+\thost_tsc = rdtsc();\n+\tclk0 = pvclock_calc(\u0026pvti[0], host_tsc + offset0);\n+\tclk2 = pvclock_calc(\u0026pvti[2], host_tsc + offset0 + TSC_OFFSET);\n+\n+\tpr_info(\"After +1h: kvmclock via vCPU 0: %lu ns\\n\", (unsigned long)clk0);\n+\tpr_info(\"After +1h: kvmclock via vCPU 2: %lu ns\\n\", (unsigned long)clk2);\n+\tTEST_ASSERT(clk0 == clk2,\n+\t\t \"After +1h: kvmclock should still match, diff=%ld ns\",\n+\t\t (long)(clk2 - clk0));\n+\n+\t/* Verify the clock actually moved by ~1 hour */\n+\tTEST_ASSERT(clk0 \u003e ONE_HOUR_NS,\n+\t\t \"Clock should be \u003e 1 hour after set, got %lu ns\",\n+\t\t (unsigned long)clk0);\n+\n+\tpr_info(\"PASSED: pvclock still consistent after KVM_SET_CLOCK +1h\\n\");\n+\tkvm_vm_free(vm);\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/pvclock_migration_test.c b/tools/testing/selftests/kvm/x86/pvclock_migration_test.c\nnew file mode 100644\nindex 00000000000000..3b675827aec346\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/pvclock_migration_test.c\n@@ -0,0 +1,383 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Test KVM clock precision across simulated live migration.\n+ *\n+ * Verifies that the documented TSC migration procedure (using\n+ * KVM_VCPU_TSC_OFFSET, KVM_VCPU_TSC_SCALE, KVM_GET_CLOCK, and\n+ * KVM_SET_CLOCK_GUEST) preserves the kvmclock's relationship to\n+ * CLOCK_MONOTONIC_RAW.\n+ *\n+ * The test:\n+ * 1. Creates a VM, runs the guest to enable kvmclock\n+ * 2. Does a PTP-like ABA measurement of kvmclock vs CLOCK_MONOTONIC_RAW\n+ * 3. Follows the documented migration procedure (same host, 1s pause)\n+ * 4. Does the same ABA measurement on the destination VM\n+ * 5. Verifies the kvmclock-vs-monotonic delta is preserved\n+ */\n+#include \u003cinttypes.h\u003e\n+#include \u003cstdint.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003ctime.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+\n+#include \u003casm/pvclock-abi.h\u003e\n+\n+#define KVMCLOCK_GPA\t0xc0000000ULL\n+\n+static void guest_code(void)\n+{\n+\twrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | 1);\n+\tGUEST_SYNC(0);\n+\tGUEST_SYNC(1);\n+}\n+\n+static uint64_t read_kvmclock_ns(struct kvm_vm *vm)\n+{\n+\tstruct kvm_clock_data data = {};\n+\n+\tvm_ioctl(vm, KVM_GET_CLOCK, \u0026data);\n+\treturn data.clock;\n+}\n+\n+static uint64_t pvclock_read_cycles(struct pvclock_vcpu_time_info *src,\n+\t\t\t\t uint64_t tsc)\n+{\n+\tuint64_t delta = tsc - src-\u003etsc_timestamp;\n+\tuint64_t ns;\n+\n+\tif (src-\u003etsc_shift \u003e= 0)\n+\t\tdelta \u003c\u003c= src-\u003etsc_shift;\n+\telse\n+\t\tdelta \u003e\u003e= -(int32_t)src-\u003etsc_shift;\n+\n+\tns = (unsigned __int128)delta * src-\u003etsc_to_system_mul \u003e\u003e 32;\n+\treturn src-\u003esystem_time + ns;\n+}\n+\n+/*\n+ * ABA measurement: read CLOCK_MONOTONIC_RAW, kvmclock, CLOCK_MONOTONIC_RAW.\n+ * Repeat 3 times, keep the reading with the smallest spread.\n+ */\n+static void aba_reading(struct kvm_vm *vm, uint64_t *lo, uint64_t *kvm_ns,\n+\t\t\tuint64_t *hi)\n+{\n+\tuint64_t best_spread = UINT64_MAX;\n+\tint i;\n+\n+\tfor (i = 0; i \u003c 3; i++) {\n+\t\tstruct timespec ts1, ts2;\n+\t\tuint64_t m1, m2, clk;\n+\n+\t\tclock_gettime(CLOCK_MONOTONIC_RAW, \u0026ts1);\n+\t\tclk = read_kvmclock_ns(vm);\n+\t\tclock_gettime(CLOCK_MONOTONIC_RAW, \u0026ts2);\n+\n+\t\tm1 = ts1.tv_sec * 1000000000ULL + ts1.tv_nsec;\n+\t\tm2 = ts2.tv_sec * 1000000000ULL + ts2.tv_nsec;\n+\n+\t\tif (m2 - m1 \u003c best_spread) {\n+\t\t\tbest_spread = m2 - m1;\n+\t\t\t*lo = m1;\n+\t\t\t*kvm_ns = clk;\n+\t\t\t*hi = m2;\n+\t\t}\n+\t}\n+}\n+\n+static struct kvm_vm *create_vm(struct kvm_vcpu **vcpu)\n+{\n+\tstruct kvm_vm *vm = vm_create_with_one_vcpu(vcpu, guest_code);\n+\n+\tvm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,\n+\t\t\t\t KVMCLOCK_GPA, 1, 1, 0);\n+\tvirt_map(vm, KVMCLOCK_GPA, KVMCLOCK_GPA, 1);\n+\treturn vm;\n+}\n+\n+int main(void)\n+{\n+\tstruct pvclock_vcpu_time_info pvti_src;\n+\tstruct kvm_clock_data clock_src, clock_dst;\n+\tstruct kvm_vcpu_tsc_scale scale_src, scale_dst;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct ucall uc;\n+\tuint64_t mono_before, kvm_before, kvm_after;\n+\tint64_t delta_before;\n+\tuint64_t ofs_src, tsc_src, tsc_dst, raw_dst, ofs_dst;\n+\tuint64_t host_tsc_src, host_tsc_dst;\n+\tuint64_t time_src, time_dst;\n+\tint64_t delta_t;\n+\tuint32_t freq_khz = 1500000; /* 1.5 GHz — forces TSC scaling */\n+\tint ret;\n+\n+\tTEST_REQUIRE(sys_clocksource_is_based_on_tsc());\n+\tTEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL));\n+\n+\t/* === SOURCE SIDE === */\n+\tpr_info(\"=== Source VM ===\\n\");\n+\tvm = create_vm(\u0026vcpu);\n+\n+\t/* Set guest TSC frequency (may trigger scaling) */\n+\tvcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)freq_khz);\n+\n+\t/* Run guest to enable kvmclock */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\tTEST_ASSERT_EQ(get_ucall(vcpu, \u0026uc), UCALL_SYNC);\n+\n+\t/* ABA measurement: kvmclock vs CLOCK_MONOTONIC_RAW */\n+\tuint64_t src_mono_lo, src_mono_hi;\n+\taba_reading(vm, \u0026src_mono_lo, \u0026kvm_before, \u0026src_mono_hi);\n+\tmono_before = (src_mono_lo + src_mono_hi) / 2;\n+\tdelta_before = (int64_t)(kvm_before - mono_before);\n+\tpr_info(\" kvmclock - MONOTONIC_RAW = %\" PRId64 \" ns (±%\" PRIu64 \" ns)\\n\",\n+\t\tdelta_before, (src_mono_hi - src_mono_lo) / 2);\n+\n+\t/* Step 1: KVM_GET_CLOCK for atomic {host_tsc, realtime} */\n+\tmemset(\u0026clock_src, 0, sizeof(clock_src));\n+\tclock_src.flags = KVM_CLOCK_REALTIME;\n+\tvm_ioctl(vm, KVM_GET_CLOCK, \u0026clock_src);\n+\thost_tsc_src = clock_src.host_tsc;\n+\ttime_src = clock_src.realtime;\n+\n+\t/* Step 2: Save TSC offset and scale */\n+\t{\n+\t\tstruct kvm_device_attr attr = {\n+\t\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t\t.attr = KVM_VCPU_TSC_OFFSET,\n+\t\t\t.addr = (uint64_t)(uintptr_t)\u0026ofs_src,\n+\t\t};\n+\t\tvcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, \u0026attr);\n+\t}\n+\t{\n+\t\tstruct kvm_device_attr attr = {\n+\t\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t\t.attr = KVM_VCPU_TSC_SCALE,\n+\t\t\t.addr = (uint64_t)(uintptr_t)\u0026scale_src,\n+\t\t};\n+\t\tmemset(\u0026scale_src, 0, sizeof(scale_src));\n+\t\t__vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, \u0026attr);\n+\t}\n+\n+\t/* Compute guest TSC at Tsrc */\n+\tif (scale_src.tsc_frac_bits)\n+\t\ttsc_src = ((unsigned __int128)host_tsc_src * scale_src.tsc_ratio\n+\t\t\t \u003e\u003e scale_src.tsc_frac_bits) + ofs_src;\n+\telse\n+\t\ttsc_src = host_tsc_src + ofs_src;\n+\n+\t/* Step 3: KVM_GET_CLOCK_GUEST */\n+\tret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, \u0026pvti_src);\n+\tTEST_ASSERT(!ret, \"KVM_GET_CLOCK_GUEST failed\");\n+\n+\tpr_info(\" TSC freq=%u kHz, offset=%\" PRId64 \"\\n\", freq_khz, (int64_t)ofs_src);\n+\n+\tkvm_vm_release(vm);\n+\n+\t/* === PAUSE (simulate migration) === */\n+\tpr_info(\"=== Pausing 1 second ===\\n\");\n+\tsleep(1);\n+\n+\t/* === DESTINATION SIDE === */\n+\tpr_info(\"=== Destination VM ===\\n\");\n+\tvm = create_vm(\u0026vcpu);\n+\n+\t/* Step 4: KVM_SET_TSC_KHZ */\n+\tvcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)freq_khz);\n+\n+\t/* Step 5: KVM_GET_CLOCK for atomic {host_tsc, realtime} pair.\n+\t * Master clock is active from vCPU creation.\n+\t */\n+\tmemset(\u0026clock_dst, 0, sizeof(clock_dst));\n+\tvm_ioctl(vm, KVM_GET_CLOCK, \u0026clock_dst);\n+\thost_tsc_dst = clock_dst.host_tsc;\n+\ttime_dst = clock_dst.realtime;\n+\n+\t/* Step 6: ΔT */\n+\tdelta_t = (int64_t)(time_dst - time_src);\n+\n+\t/* Step 7: Compute destination offset */\n+\t{\n+\t\tstruct kvm_device_attr attr = {\n+\t\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t\t.attr = KVM_VCPU_TSC_SCALE,\n+\t\t\t.addr = (uint64_t)(uintptr_t)\u0026scale_dst,\n+\t\t};\n+\t\tmemset(\u0026scale_dst, 0, sizeof(scale_dst));\n+\t\t__vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, \u0026attr);\n+\t}\n+\n+\ttsc_dst = tsc_src + (uint64_t)((int64_t)freq_khz * delta_t / 1000000LL);\n+\n+\tif (scale_dst.tsc_frac_bits)\n+\t\traw_dst = (unsigned __int128)host_tsc_dst * scale_dst.tsc_ratio\n+\t\t\t \u003e\u003e scale_dst.tsc_frac_bits;\n+\telse\n+\t\traw_dst = host_tsc_dst;\n+\n+\tofs_dst = tsc_dst - raw_dst;\n+\n+\t/*\n+\t * The TSC offset delta introduced by using CLOCK_REALTIME to\n+\t * estimate elapsed time. On same host, the correct offset is\n+\t * ofs_src; the difference is the CLOCK_REALTIME-vs-TSC error.\n+\t */\n+\tint64_t tsc_ofs_delta = (int64_t)(ofs_dst - ofs_src);\n+\tint64_t tsc_ofs_delta_ns = tsc_ofs_delta * 1000000LL / freq_khz;\n+\tpr_info(\" Destination TSC offset=%\" PRId64\n+\t\t\", imprecision from CLOCK_REALTIME: %\" PRId64 \" cycles = %\"\n+\t\tPRId64 \" ns\\n\", (int64_t)ofs_dst, tsc_ofs_delta, tsc_ofs_delta_ns);\n+\n+\t/* Set TSC offset */\n+\t{\n+\t\tstruct kvm_device_attr attr = {\n+\t\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t\t.attr = KVM_VCPU_TSC_OFFSET,\n+\t\t\t.addr = (uint64_t)(uintptr_t)\u0026ofs_dst,\n+\t\t};\n+\t\tvcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, \u0026attr);\n+\t}\n+\n+\t/* Step 8: KVM_SET_CLOCK_GUEST */\n+\tret = __vcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, \u0026pvti_src);\n+\tTEST_ASSERT(!ret, \"KVM_SET_CLOCK_GUEST failed: errno %d\", errno);\n+\n+\t/* Run guest to update pvclock page on destination */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\tTEST_ASSERT_EQ(get_ucall(vcpu, \u0026uc), UCALL_SYNC);\n+\n+\t/* ABA measurement on destination */\n+\tuint64_t mono_lo, mono_hi;\n+\taba_reading(vm, \u0026mono_lo, \u0026kvm_after, \u0026mono_hi);\n+\n+\t/*\n+\t * The kvmclock is tied to the guest TSC via SET_CLOCK_GUEST.\n+\t * The guest TSC is offset from the correct value by tsc_ofs_delta_ns\n+\t * (due to CLOCK_REALTIME imprecision). So the kvmclock should be\n+\t * offset from CLOCK_MONOTONIC_RAW by exactly:\n+\t * (original delta) + tsc_ofs_delta_ns\n+\t *\n+\t * The \"original delta\" has uncertainty from the source ABA spread,\n+\t * and the measurement has uncertainty from the destination ABA spread.\n+\t * Verify the expected value falls within the combined bounds.\n+\t */\n+\tint64_t delta_before_lo = (int64_t)(kvm_before - src_mono_hi);\n+\tint64_t delta_before_hi = (int64_t)(kvm_before - src_mono_lo);\n+\tint64_t expected_lo = delta_before_lo + tsc_ofs_delta_ns;\n+\tint64_t expected_hi = delta_before_hi + tsc_ofs_delta_ns;\n+\tint64_t actual_lo = (int64_t)(kvm_after - mono_hi);\n+\tint64_t actual_hi = (int64_t)(kvm_after - mono_lo);\n+\n+\t/* Show the shift relative to the source measurement */\n+\tint64_t expected_mid = tsc_ofs_delta_ns;\n+\tint64_t expected_err = (int64_t)(src_mono_hi - src_mono_lo) / 2;\n+\tint64_t actual_mid = ((actual_lo + actual_hi) / 2) - delta_before;\n+\tint64_t actual_err = (int64_t)(mono_hi - mono_lo) / 2;\n+\tpr_info(\" kvmclock-mono shift: expected %\" PRId64 \" ns (±%\" PRId64\n+\t\t\"), measured %\" PRId64 \" ns (±%\" PRId64 \")\\n\",\n+\t\texpected_mid, expected_err, actual_mid, actual_err);\n+\n+\t/* The ranges must overlap */\n+\tTEST_ASSERT(expected_hi \u003e= actual_lo \u0026\u0026 expected_lo \u003c= actual_hi,\n+\t\t \"Ranges don't overlap: expected [%\" PRId64 \", %\" PRId64\n+\t\t \"] measured [%\" PRId64 \", %\" PRId64 \"]\",\n+\t\t expected_lo, expected_hi, actual_lo, actual_hi);\n+\n+\t/*\n+\t * Direct pvclock verification: read the destination pvclock page\n+\t * and verify that computing kvmclock from pvti_src and pvti_dst\n+\t * at the same guest TSC gives the same result.\n+\t *\n+\t * Get an atomic {host_tsc, kvmclock} pair, scale host_tsc to\n+\t * guest TSC using KVM_VCPU_TSC_SCALE, then compute kvmclock\n+\t * from both pvclock structs.\n+\t */\n+\tstruct kvm_clock_data clock_now = {};\n+\tvm_ioctl(vm, KVM_GET_CLOCK, \u0026clock_now);\n+\n+\tstruct pvclock_vcpu_time_info *pvti_dst = addr_gpa2hva(vm, KVMCLOCK_GPA);\n+\tuint64_t host_tsc_now = clock_now.host_tsc;\n+\tuint64_t guest_tsc_now;\n+\n+\tif (scale_dst.tsc_frac_bits)\n+\t\tguest_tsc_now = ((unsigned __int128)host_tsc_now *\n+\t\t\t\t scale_dst.tsc_ratio \u003e\u003e scale_dst.tsc_frac_bits)\n+\t\t\t\t+ ofs_dst;\n+\telse\n+\t\tguest_tsc_now = host_tsc_now + ofs_dst;\n+\n+\tuint64_t clk_from_src = pvclock_read_cycles(\u0026pvti_src, guest_tsc_now);\n+\tuint64_t clk_from_dst = pvclock_read_cycles(pvti_dst, guest_tsc_now);\n+\tint64_t pvclock_delta = (int64_t)(clk_from_src - clk_from_dst);\n+\n+\tpr_info(\" Pvclock direct: src=%\" PRIu64 \" dst=%\" PRIu64\n+\t\t\" delta=%\" PRId64 \" ns\\n\", clk_from_src, clk_from_dst, pvclock_delta);\n+\tpr_info(\" KVM_GET_CLOCK: %\" PRIu64 \" ns\\n\", (uint64_t)clock_now.clock);\n+\n+\tTEST_ASSERT(pvclock_delta \u003e= -2 \u0026\u0026 pvclock_delta \u003c= 2,\n+\t\t \"pvclock src vs dst disagree by %\" PRId64 \" ns\", pvclock_delta);\n+\n+\t/*\n+\t * Tight ABA: compare pvclock_read() directly (no ioctl) against\n+\t * CLOCK_MONOTONIC_RAW. The spread should be much smaller since\n+\t * there's no syscall between the two clock_gettime calls — just\n+\t * rdtsc + userspace mul/shift.\n+\t */\n+\tuint64_t tight_mono_lo = 0, tight_mono_hi = 0, tight_kvm = 0;\n+\tuint64_t tight_best_spread = UINT64_MAX;\n+\tfor (int i = 0; i \u003c 3; i++) {\n+\t\tstruct timespec ts1, ts2;\n+\t\tuint64_t m1, m2, tsc, clk;\n+\n+\t\tclock_gettime(CLOCK_MONOTONIC_RAW, \u0026ts1);\n+\t\ttsc = rdtsc();\n+\t\tclock_gettime(CLOCK_MONOTONIC_RAW, \u0026ts2);\n+\n+\t\tm1 = ts1.tv_sec * 1000000000ULL + ts1.tv_nsec;\n+\t\tm2 = ts2.tv_sec * 1000000000ULL + ts2.tv_nsec;\n+\n+\t\t/* Scale host TSC to guest TSC */\n+\t\tif (scale_dst.tsc_frac_bits)\n+\t\t\ttsc = ((unsigned __int128)tsc * scale_dst.tsc_ratio\n+\t\t\t \u003e\u003e scale_dst.tsc_frac_bits) + ofs_dst;\n+\t\telse\n+\t\t\ttsc += ofs_dst;\n+\n+\t\tclk = pvclock_read_cycles(pvti_dst, tsc);\n+\n+\t\tif (m2 - m1 \u003c tight_best_spread) {\n+\t\t\ttight_best_spread = m2 - m1;\n+\t\t\ttight_mono_lo = m1;\n+\t\t\ttight_mono_hi = m2;\n+\t\t\ttight_kvm = clk;\n+\t\t}\n+\t}\n+\tpr_info(\" Tight ABA spread: %\" PRIu64 \" ns (best of 3)\\n\", tight_best_spread);\n+\n+\tint64_t tight_expected_lo = delta_before_lo + tsc_ofs_delta_ns;\n+\tint64_t tight_expected_hi = delta_before_hi + tsc_ofs_delta_ns;\n+\tint64_t tight_actual_lo = (int64_t)(tight_kvm - tight_mono_hi);\n+\tint64_t tight_actual_hi = (int64_t)(tight_kvm - tight_mono_lo);\n+\tint64_t tight_actual_mid = ((tight_actual_lo + tight_actual_hi) / 2) - delta_before;\n+\tint64_t tight_actual_err = (int64_t)(tight_mono_hi - tight_mono_lo) / 2;\n+\n+\tpr_info(\" Tight kvmclock-mono shift: expected %\" PRId64\n+\t\t\" ns (±%\" PRId64 \"), measured %\" PRId64 \" ns (±%\" PRId64 \")\\n\",\n+\t\texpected_mid, expected_err, tight_actual_mid, tight_actual_err);\n+\n+\tTEST_ASSERT(tight_expected_hi \u003e= tight_actual_lo \u0026\u0026\n+\t\t tight_expected_lo \u003c= tight_actual_hi,\n+\t\t \"Tight ABA ranges don't overlap\");\n+\n+\tkvm_vm_release(vm);\n+\tpr_info(\"PASS: kvmclock offset matches TSC delta from CLOCK_REALTIME\"\n+\t\t\" (%\" PRId64 \" ns) within ABA bounds\\n\", tsc_ofs_delta_ns);\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/pvclock_test.c b/tools/testing/selftests/kvm/x86/pvclock_test.c\nnew file mode 100644\nindex 00000000000000..f2b917ed5deada\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/pvclock_test.c\n@@ -0,0 +1,443 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Copyright © Amazon.com, Inc. or its affiliates.\n+ *\n+ * Tests for pvclock API\n+ * KVM_SET_CLOCK_GUEST/KVM_GET_CLOCK_GUEST\n+ */\n+#include \u003cgetopt.h\u003e\n+#include \u003cstdint.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003ctime.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+\n+#include \u003casm/pvclock-abi.h\u003e\n+\n+/*\n+ * Reproduce the pvclock calculation the guest uses to convert TSC to\n+ * nanoseconds. This must match the kernel's __pvclock_read_cycles().\n+ */\n+static inline uint64_t pvclock_scale_delta(uint64_t delta, uint32_t mul,\n+\t\t\t\t\t int8_t shift)\n+{\n+\tif (shift \u003c 0)\n+\t\tdelta \u003e\u003e= -shift;\n+\telse\n+\t\tdelta \u003c\u003c= shift;\n+\treturn ((__uint128_t)delta * mul) \u003e\u003e 32;\n+}\n+\n+static inline uint64_t pvclock_read_cycles(struct pvclock_vcpu_time_info *src,\n+\t\t\t\t\t uint64_t tsc)\n+{\n+\tuint64_t delta = tsc - src-\u003etsc_timestamp;\n+\n+\treturn src-\u003esystem_time + pvclock_scale_delta(delta,\n+\t\t\t\t\t\t src-\u003etsc_to_system_mul,\n+\t\t\t\t\t\t src-\u003etsc_shift);\n+}\n+\n+static inline void pvti_snapshot(struct pvclock_vcpu_time_info *dst,\n+\t\t\t\t volatile struct pvclock_vcpu_time_info *src)\n+{\n+\tuint32_t version;\n+\n+\tdo {\n+\t\tversion = src-\u003eversion;\n+\t\t__asm__ __volatile__(\"\" ::: \"memory\");\n+\t\t*dst = *src;\n+\t\t__asm__ __volatile__(\"\" ::: \"memory\");\n+\t} while ((src-\u003eversion \u0026 1) || src-\u003eversion != version);\n+}\n+\n+enum {\n+\tSTAGE_FIRST_BOOT,\n+\tSTAGE_UNCORRECTED,\n+\tSTAGE_CORRECTED\n+};\n+\n+#define KVMCLOCK_GPA\t0xc0000000ull\n+#define KVMCLOCK_SIZE\tsizeof(struct pvclock_vcpu_time_info)\n+\n+static void trigger_pvti_update(void)\n+{\n+\t/*\n+\t * Toggle between KVM's old and new system time methods to coerce KVM\n+\t * into updating the fields in the PV time info struct.\n+\t */\n+\twrmsr(MSR_KVM_SYSTEM_TIME, KVMCLOCK_GPA | KVM_MSR_ENABLED);\n+\twrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | KVM_MSR_ENABLED);\n+}\n+\n+static void guest_code(void)\n+{\n+\tstruct pvclock_vcpu_time_info *pvti =\n+\t\t(void *)(unsigned long)KVMCLOCK_GPA;\n+\tstruct pvclock_vcpu_time_info pvti_boot;\n+\tstruct pvclock_vcpu_time_info pvti_uncorrected;\n+\tstruct pvclock_vcpu_time_info pvti_corrected;\n+\tuint64_t tsc_guest;\n+\tuint64_t clk_boot, clk_uncorrected, clk_corrected;\n+\tint64_t delta_corrected;\n+\n+\t/* Set up kvmclock and snapshot the initial pvclock parameters. */\n+\twrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | KVM_MSR_ENABLED);\n+\tpvti_snapshot(\u0026pvti_boot, pvti);\n+\tGUEST_SYNC(STAGE_FIRST_BOOT);\n+\n+\t/*\n+\t * Trigger an update of the PVTI. Calculating the KVM clock using this\n+\t * updated structure will show a delta from the original.\n+\t */\n+\ttrigger_pvti_update();\n+\tpvti_snapshot(\u0026pvti_uncorrected, pvti);\n+\tGUEST_SYNC(STAGE_UNCORRECTED);\n+\n+\t/*\n+\t * Snapshot the corrected time (the host does KVM_SET_CLOCK_GUEST when\n+\t * handling STAGE_UNCORRECTED).\n+\t */\n+\tpvti_snapshot(\u0026pvti_corrected, pvti);\n+\n+\t/*\n+\t * Sample the TSC at a single point in time, then calculate the\n+\t * effective KVM clock using the PVTI from each stage. Verify that the\n+\t * corrected clock matches the boot clock to within ±2ns.\n+\t */\n+\ttsc_guest = rdtsc();\n+\n+\tclk_boot = pvclock_read_cycles(\u0026pvti_boot, tsc_guest);\n+\tclk_uncorrected = pvclock_read_cycles(\u0026pvti_uncorrected, tsc_guest);\n+\tclk_corrected = pvclock_read_cycles(\u0026pvti_corrected, tsc_guest);\n+\n+\tdelta_corrected = clk_boot - clk_corrected;\n+\n+\t__GUEST_ASSERT(delta_corrected \u003e= -2 \u0026\u0026 delta_corrected \u003c= 2,\n+\t\t \"corrected delta %ld out of range (boot=%lu uncorrected=%lu corrected=%lu)\",\n+\t\t delta_corrected, clk_boot, clk_uncorrected, clk_corrected);\n+\n+\tGUEST_SYNC(STAGE_CORRECTED);\n+}\n+\n+static void run_test(struct kvm_vm *vm, struct kvm_vcpu *vcpu,\n+\t\t unsigned int sleep_sec)\n+{\n+\tstruct pvclock_vcpu_time_info pvti_before;\n+\tstruct ucall uc;\n+\n+\tfor (;;) {\n+\t\tvcpu_run(vcpu);\n+\t\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\n+\t\tswitch (get_ucall(vcpu, \u0026uc)) {\n+\t\tcase UCALL_ABORT:\n+\t\t\tREPORT_GUEST_ASSERT(uc);\n+\t\t\tbreak;\n+\t\tcase UCALL_SYNC:\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tTEST_FAIL(\"Unexpected ucall\");\n+\t\t}\n+\n+\t\tswitch (uc.args[1]) {\n+\t\tcase STAGE_FIRST_BOOT:\n+\t\t\t/* Save the pvclock parameters before the update. */\n+\t\t\tvcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, \u0026pvti_before);\n+\n+\t\t\t/* Sleep to let the clocks diverge. */\n+\t\t\tsleep(sleep_sec);\n+\t\t\tbreak;\n+\n+\t\tcase STAGE_UNCORRECTED:\n+\t\t\t/* Restore the original pvclock parameters. */\n+\t\t\tvcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, \u0026pvti_before);\n+\t\t\tbreak;\n+\n+\t\tcase STAGE_CORRECTED:\n+\t\t\t/* Guest verified the delta in-guest. */\n+\t\t\treturn;\n+\n+\t\tdefault:\n+\t\t\tTEST_FAIL(\"Unknown stage %lu\", uc.args[1]);\n+\t\t}\n+\t}\n+}\n+\n+static void configure_pvclock(struct kvm_vm *vm)\n+{\n+\tunsigned int nr_pages;\n+\n+\tnr_pages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, getpagesize());\n+\tvm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,\n+\t\t\t\t KVMCLOCK_GPA, 1, nr_pages, 0);\n+\tvirt_map(vm, KVMCLOCK_GPA, KVMCLOCK_GPA, nr_pages);\n+}\n+\n+static void run_at_frequency(uint64_t tsc_khz, unsigned int sleep_sec)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\n+\tpr_info(\"Testing at TSC frequency %lu kHz\\n\", tsc_khz);\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code);\n+\tconfigure_pvclock(vm);\n+\tvcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)tsc_khz);\n+\trun_test(vm, vcpu, sleep_sec);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void test_tsc_stable_bit(void);\n+static void test_clock_guest_with_offsets(void);\n+\n+static void usage(const char *name)\n+{\n+\tprintf(\"Usage: %s [options]\\n\"\n+\t \" -s, --sleep SEC sleep duration between snapshots (default: 2)\\n\"\n+\t \" -h, --help show this help\\n\", name);\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tstatic const struct option long_opts[] = {\n+\t\t{ \"sleep\", required_argument, NULL, 's' },\n+\t\t{ \"help\", no_argument, NULL, 'h' },\n+\t\t{ NULL, 0, NULL, 0 },\n+\t};\n+\tunsigned int sleep_sec = 2;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tuint64_t host_khz;\n+\tuint64_t freq;\n+\tint opt;\n+\n+\twhile ((opt = getopt_long(argc, argv, \"s:h\", long_opts, NULL)) != -1) {\n+\t\tswitch (opt) {\n+\t\tcase 's':\n+\t\t\tsleep_sec = atoi(optarg);\n+\t\t\tbreak;\n+\t\tcase 'h':\n+\t\tdefault:\n+\t\t\tusage(argv[0]);\n+\t\t\treturn opt == 'h' ? 0 : 1;\n+\t\t}\n+\t}\n+\n+\tTEST_REQUIRE(sys_clocksource_is_based_on_tsc());\n+\tTEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL));\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code);\n+\tconfigure_pvclock(vm);\n+\n+\t/* Check KVM_GET_CLOCK_GUEST is supported */\n+\t{\n+\t\tstruct pvclock_vcpu_time_info tmp;\n+\t\tint ret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, \u0026tmp);\n+\t\tTEST_REQUIRE(ret == 0);\n+\t}\n+\n+\t/* First run at native frequency (no scaling). */\n+\trun_test(vm, vcpu, sleep_sec);\n+\n+\t/*\n+\t * Then enumerate a range of TSC frequencies crossing the 32-bit\n+\t * boundary, to exercise the scaling path at various ratios.\n+\t */\n+\thost_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);\n+\tkvm_vm_free(vm);\n+\n+\tfor (freq = 1000000; freq \u003c= 5000000; freq += 500000) {\n+\t\tif (freq == host_khz)\n+\t\t\tcontinue;\n+\t\trun_at_frequency(freq, sleep_sec);\n+\t}\n+\n+\ttest_tsc_stable_bit();\n+\ttest_clock_guest_with_offsets();\n+\n+\treturn 0;\n+}\n+\n+static volatile uint32_t vcpu_counter;\n+static void guest_code_stable_bit(void)\n+{\n+\tuint32_t idx = __atomic_fetch_add(\u0026vcpu_counter, 1, __ATOMIC_SEQ_CST);\n+\tuint64_t gpa = KVMCLOCK_GPA + idx * sizeof(struct pvclock_vcpu_time_info);\n+\n+\twrmsr(MSR_KVM_SYSTEM_TIME_NEW, gpa | KVM_MSR_ENABLED);\n+\tGUEST_SYNC(0);\n+\tGUEST_SYNC(0);\n+\tGUEST_SYNC(0);\n+}\n+\n+static void set_tsc_offset(struct kvm_vcpu *vcpu, uint64_t offset)\n+{\n+\tstruct kvm_device_attr attr = {\n+\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t.attr = KVM_VCPU_TSC_OFFSET,\n+\t\t.addr = (__u64)(uintptr_t)\u0026offset,\n+\t};\n+\n+\tTEST_REQUIRE(__vcpu_has_device_attr(vcpu, KVM_VCPU_TSC_CTRL,\n+\t\t\t\t\t KVM_VCPU_TSC_OFFSET) == 0);\n+\tvcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, \u0026attr);\n+}\n+\n+static void run_vcpu_once(struct kvm_vcpu *vcpu)\n+{\n+\tstruct ucall uc;\n+\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\tswitch (get_ucall(vcpu, \u0026uc)) {\n+\tcase UCALL_ABORT:\n+\t\tREPORT_GUEST_ASSERT(uc);\n+\t\tbreak;\n+\tcase UCALL_SYNC:\n+\t\tbreak;\n+\tdefault:\n+\t\tTEST_FAIL(\"Unexpected ucall\");\n+\t}\n+}\n+\n+static void test_tsc_stable_bit(void)\n+{\n+\tstruct pvclock_vcpu_time_info pvti;\n+\tstruct kvm_vcpu *vcpus[2];\n+\tstruct kvm_vm *vm;\n+\tint ret;\n+\n+\tpr_info(\"Testing PVCLOCK_TSC_STABLE_BIT with matched/unmatched TSCs\\n\");\n+\n+\tvm = vm_create_with_vcpus(2, guest_code_stable_bit, vcpus);\n+\tconfigure_pvclock(vm);\n+\n+\t/*\n+\t * Case 1: All TSCs matched (same frequency and offset).\n+\t * Master clock should be active, PVCLOCK_TSC_STABLE_BIT set.\n+\t */\n+\trun_vcpu_once(vcpus[0]);\n+\n+\tret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, \u0026pvti);\n+\tTEST_ASSERT(!ret, \"GET_CLOCK_GUEST should succeed with matched TSCs\");\n+\tTEST_ASSERT(pvti.flags \u0026 PVCLOCK_TSC_STABLE_BIT,\n+\t\t \"PVCLOCK_TSC_STABLE_BIT should be set with matched TSCs\");\n+\n+\t/*\n+\t * Case 2: Different TSC offset, same frequency.\n+\t * Master clock should still be active (frequency matches), but\n+\t * PVCLOCK_TSC_STABLE_BIT should be cleared (offsets differ).\n+\t */\n+\tset_tsc_offset(vcpus[1], 12345678);\n+\trun_vcpu_once(vcpus[1]);\n+\trun_vcpu_once(vcpus[0]);\n+\n+\tret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, \u0026pvti);\n+\tif (ret) {\n+\t\t/* Master clock disabled by offset mismatch — old kernel */\n+\t\tpr_info(\" Skipping offset tests (master clock requires matched offsets)\\n\");\n+\t\tgoto out_stable;\n+\t}\n+\tTEST_ASSERT(!(pvti.flags \u0026 PVCLOCK_TSC_STABLE_BIT),\n+\t\t \"PVCLOCK_TSC_STABLE_BIT should be clear with offset-mismatched TSCs\");\n+\n+\t/*\n+\t * Case 3: Different TSC frequency.\n+\t * Master clock should be disabled entirely.\n+\t */\n+\tvcpu_ioctl(vcpus[1], KVM_SET_TSC_KHZ,\n+\t\t (void *)(unsigned long)(__vcpu_ioctl(vcpus[1], KVM_GET_TSC_KHZ, NULL) / 2));\n+\t/* Write TSC to trigger kvm_synchronize_tsc / kvm_track_tsc_matching */\n+\tvcpu_set_msr(vcpus[1], MSR_IA32_TSC, 0);\n+\trun_vcpu_once(vcpus[1]);\n+\n+\tret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, \u0026pvti);\n+\tTEST_ASSERT(ret \u0026\u0026 errno == EINVAL,\n+\t\t \"GET_CLOCK_GUEST should fail with frequency-mismatched TSCs, got %d (errno %d)\",\n+\t\t ret, errno);\n+\n+out_stable:\n+\tkvm_vm_free(vm);\n+}\n+\n+static void test_clock_guest_with_offsets(void)\n+{\n+\tstruct pvclock_vcpu_time_info pvti0, pvti1, pvti1_after;\n+\tstruct kvm_vcpu *vcpus[2];\n+\tstruct kvm_vm *vm;\n+\tint64_t delta;\n+\tint ret;\n+\n+\tpr_info(\"Testing KVM_[GS]ET_CLOCK_GUEST with different TSC offsets\\n\");\n+\n+\tvm = vm_create_with_vcpus(2, guest_code_stable_bit, vcpus);\n+\tconfigure_pvclock(vm);\n+\n+\t/* Set different TSC offsets on the two vCPUs */\n+\tset_tsc_offset(vcpus[0], 0);\n+\tset_tsc_offset(vcpus[1], 1000000000ull);\n+\n+\t/* Run both to establish kvmclock */\n+\trun_vcpu_once(vcpus[0]);\n+\trun_vcpu_once(vcpus[1]);\n+\n+\t/* GET_CLOCK_GUEST on both — should succeed (master clock active) */\n+\tret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, \u0026pvti0);\n+\tif (ret) {\n+\t\tpr_info(\" Skipping (master clock requires matched offsets on this kernel)\\n\");\n+\t\tkvm_vm_free(vm);\n+\t\treturn;\n+\t}\n+\tret = __vcpu_ioctl(vcpus[1], KVM_GET_CLOCK_GUEST, \u0026pvti1);\n+\tTEST_ASSERT(!ret, \"GET_CLOCK_GUEST on vcpu1 failed\");\n+\n+\t/* The tsc_timestamps should differ (different offsets) */\n+\tTEST_ASSERT(pvti0.tsc_timestamp != pvti1.tsc_timestamp,\n+\t\t \"tsc_timestamps should differ with different offsets\");\n+\n+\t/* Sleep to let time elapse, then restore vcpu0's clock */\n+\tsleep(1);\n+\tvcpu_ioctl(vcpus[0], KVM_SET_CLOCK_GUEST, \u0026pvti0);\n+\n+\t/* Run vcpu0 to process the clock update */\n+\trun_vcpu_once(vcpus[0]);\n+\n+\t/* GET_CLOCK_GUEST on vcpu1 — should reflect the correction */\n+\tret = __vcpu_ioctl(vcpus[1], KVM_GET_CLOCK_GUEST, \u0026pvti1_after);\n+\tTEST_ASSERT(!ret, \"GET_CLOCK_GUEST on vcpu1 after SET failed\");\n+\n+\t/*\n+\t * After SET on vcpu0, verify the correction worked by getting\n+\t * the clock on vcpu0 again. The mul/shift should be the same,\n+\t * and computing kvmclock at the same TSC should give the same\n+\t * result as the original (within ±2ns).\n+\t */\n+\t{\n+\t\tstruct pvclock_vcpu_time_info pvti0_after;\n+\t\tuint64_t tsc_now, clk_from_old, clk_from_new;\n+\n+\t\tret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, \u0026pvti0_after);\n+\t\tTEST_ASSERT(!ret, \"GET_CLOCK_GUEST on vcpu0 after SET failed\");\n+\n+\t\ttsc_now = pvti0_after.tsc_timestamp;\n+\t\tclk_from_old = pvclock_read_cycles(\u0026pvti0, tsc_now);\n+\t\tclk_from_new = pvclock_read_cycles(\u0026pvti0_after, tsc_now);\n+\n+\t\tdelta = (int64_t)clk_from_new - (int64_t)clk_from_old;\n+\t\tTEST_ASSERT(delta \u003e= -2 \u0026\u0026 delta \u003c= 2,\n+\t\t\t \"clock correction delta should be \u003c=2ns, got %ld ns\",\n+\t\t\t delta);\n+\t}\n+\n+\t/*\n+\t * Also verify that vcpu1's clock is still accessible (master\n+\t * clock still active with different offsets).\n+\t */\n+\tret = __vcpu_ioctl(vcpus[1], KVM_GET_CLOCK_GUEST, \u0026pvti1_after);\n+\tTEST_ASSERT(!ret, \"GET_CLOCK_GUEST on vcpu1 after SET failed\");\n+\n+\tkvm_vm_free(vm);\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c b/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c\nnew file mode 100644\nindex 00000000000000..2098cb511ef021\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c\n@@ -0,0 +1,230 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Test that userspace can correctly populate Xen and generic CPUID\n+ * timing leaves using KVM_GET_TSC_KHZ and KVM_VCPU_TSC_SCALE.\n+ *\n+ * This validates that the removal of KVM's runtime Xen CPUID modification\n+ * doesn't break guests, because userspace has all the information needed.\n+ */\n+#include \u003cstdint.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+\n+#include \u003casm/pvclock-abi.h\u003e\n+\n+#define XEN_CPUID_BASE\t\t0x40000100\n+#define XEN_CPUID_LEAF(n)\t(XEN_CPUID_BASE + (n))\n+#define GENERIC_TIMING_LEAF\t0x40000010\n+\n+/* Values set by host, verified by guest */\n+static uint32_t expected_tsc_khz;\n+static uint32_t expected_bus_khz;\n+static uint32_t expected_tsc_mul;\n+static int8_t expected_tsc_shift;\n+static uint64_t host_khz;\n+\n+static void guest_code(void)\n+{\n+\tuint32_t eax, ebx, ecx, edx;\n+\n+\t/* Check generic timing leaf 0x40000010 */\n+\t__cpuid(GENERIC_TIMING_LEAF, 0, \u0026eax, \u0026ebx, \u0026ecx, \u0026edx);\n+\tGUEST_ASSERT_EQ(eax, expected_tsc_khz);\n+\tGUEST_ASSERT_EQ(ebx, expected_bus_khz);\n+\n+\t/* Check Xen leaf 3, sub-leaf 0: ECX = guest TSC frequency */\n+\t__cpuid(XEN_CPUID_LEAF(3), 0, \u0026eax, \u0026ebx, \u0026ecx, \u0026edx);\n+\tGUEST_ASSERT_EQ(ecx, expected_tsc_khz);\n+\n+\t/* Check Xen leaf 3, sub-leaf 1: ECX = mul, EDX = shift */\n+\t__cpuid(XEN_CPUID_LEAF(3), 1, \u0026eax, \u0026ebx, \u0026ecx, \u0026edx);\n+\tGUEST_ASSERT_EQ(ecx, expected_tsc_mul);\n+\tGUEST_ASSERT_EQ((int8_t)edx, expected_tsc_shift);\n+\n+\tGUEST_SYNC(0);\n+}\n+\n+static void add_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function,\n+\t\t\t uint32_t index, uint32_t eax, uint32_t ebx,\n+\t\t\t uint32_t ecx, uint32_t edx)\n+{\n+\tstruct kvm_cpuid2 *cpuid = vcpu-\u003ecpuid;\n+\tstruct kvm_cpuid_entry2 *entry;\n+\tint n = cpuid-\u003enent;\n+\n+\tvcpu-\u003ecpuid = realloc(vcpu-\u003ecpuid,\n+\t\t\t sizeof(*cpuid) + (n + 1) * sizeof(*entry));\n+\tcpuid = vcpu-\u003ecpuid;\n+\tcpuid-\u003enent = n + 1;\n+\n+\tentry = \u0026cpuid-\u003eentries[n];\n+\tmemset(entry, 0, sizeof(*entry));\n+\tentry-\u003efunction = function;\n+\tentry-\u003eindex = index;\n+\tentry-\u003eflags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;\n+\tentry-\u003eeax = eax;\n+\tentry-\u003eebx = ebx;\n+\tentry-\u003eecx = ecx;\n+\tentry-\u003eedx = edx;\n+}\n+\n+/*\n+ * Compute pvclock mul/shift from frequency, matching kvm_get_time_scale().\n+ */\n+static void compute_tsc_mul_shift(uint64_t tsc_hz, uint32_t *mul, int8_t *shift)\n+{\n+\tuint64_t scaled = 1000000000ULL;\n+\tuint64_t base = tsc_hz;\n+\tint32_t s = 0;\n+\tuint32_t base32;\n+\n+\twhile (base \u003e scaled * 2 || base \u003e\u003e 32) {\n+\t\tbase \u003e\u003e= 1;\n+\t\ts--;\n+\t}\n+\tbase32 = (uint32_t)base;\n+\twhile (base32 \u003c= scaled || scaled \u003e\u003e 32) {\n+\t\tif (scaled \u003e\u003e 32 || base32 \u0026 (1U \u003c\u003c 31))\n+\t\t\tscaled \u003e\u003e= 1;\n+\t\telse\n+\t\t\tbase32 \u003c\u003c= 1;\n+\t\ts++;\n+\t}\n+\t*mul = (uint32_t)((scaled \u003c\u003c 32) / base32);\n+\t*shift = (int8_t)s;\n+}\n+\n+static void run_test(uint64_t tsc_khz)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct ucall uc;\n+\tuint32_t effective_tsc_khz, effective_bus_khz;\n+\tint bus_cycle_ns;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code);\n+\n+\tif (tsc_khz) {\n+\t\tpr_info(\"Testing at TSC frequency %lu kHz\\n\", tsc_khz);\n+\t\tvcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)tsc_khz);\n+\t} else {\n+\t\tpr_info(\"Testing at native TSC frequency\\n\");\n+\t}\n+\n+\teffective_tsc_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);\n+\tbus_cycle_ns = vm_check_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS);\n+\teffective_bus_khz = bus_cycle_ns \u003e 0 ? 1000000 / bus_cycle_ns : 1000000;\n+\n+\t/* If scaling wasn't applied, skip this frequency */\n+\tif (tsc_khz \u0026\u0026 effective_tsc_khz == host_khz) {\n+\t\tpr_info(\" TSC scaling not available, skipping\\n\");\n+\t\tkvm_vm_free(vm);\n+\t\treturn;\n+\t}\n+\n+\tpr_info(\" Effective TSC: %u kHz, Bus: %u kHz\\n\", effective_tsc_khz, effective_bus_khz);\n+\n+\t/* Also exercise KVM_VCPU_TSC_SCALE if available */\n+\t{\n+\t\tstruct { uint64_t ratio; uint64_t frac_bits; } scale;\n+\t\tstruct kvm_device_attr scale_attr = {\n+\t\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t\t.attr = KVM_VCPU_TSC_SCALE,\n+\t\t\t.addr = (uint64_t)(uintptr_t)\u0026scale,\n+\t\t};\n+\n+\t\tif (!__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, \u0026scale_attr)) {\n+\t\t\tvcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, \u0026scale_attr);\n+\t\t\tpr_info(\" TSC scale: ratio=%lu frac_bits=%lu\\n\",\n+\t\t\t\tscale.ratio, scale.frac_bits);\n+\n+\t\t\t/*\n+\t\t\t * Verify: applying the ratio to the host TSC frequency\n+\t\t\t * should give approximately the effective frequency.\n+\t\t\t */\n+\t\t\tif (tsc_khz) {\n+\t\t\t\tuint64_t computed = ((__uint128_t)host_khz * scale.ratio) \u003e\u003e scale.frac_bits;\n+\t\t\t\tint64_t diff = (int64_t)computed - (int64_t)effective_tsc_khz;\n+\n+\t\t\t\tTEST_ASSERT(diff \u003e= -1 \u0026\u0026 diff \u003c= 1,\n+\t\t\t\t\t \"TSC_SCALE ratio mismatch: computed %lu vs effective %u (diff %ld)\",\n+\t\t\t\t\t computed, effective_tsc_khz, diff);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tcompute_tsc_mul_shift((uint64_t)effective_tsc_khz * 1000,\n+\t\t\t \u0026expected_tsc_mul, \u0026expected_tsc_shift);\n+\n+\texpected_tsc_khz = effective_tsc_khz;\n+\texpected_bus_khz = effective_bus_khz;\n+\n+\tsync_global_to_guest(vm, expected_tsc_khz);\n+\tsync_global_to_guest(vm, expected_bus_khz);\n+\tsync_global_to_guest(vm, expected_tsc_mul);\n+\tsync_global_to_guest(vm, expected_tsc_shift);\n+\n+\t/* Populate CPUID leaves as a VMM would */\n+\tadd_cpuid_entry(vcpu, GENERIC_TIMING_LEAF, 0,\n+\t\t\teffective_tsc_khz, effective_bus_khz, 0, 0);\n+\tadd_cpuid_entry(vcpu, XEN_CPUID_LEAF(3), 0,\n+\t\t\t0, 0, effective_tsc_khz, 0);\n+\tadd_cpuid_entry(vcpu, XEN_CPUID_LEAF(3), 1,\n+\t\t\t0, 0, expected_tsc_mul,\n+\t\t\t(uint32_t)(uint8_t)expected_tsc_shift);\n+\n+\tvcpu_set_cpuid(vcpu);\n+\n+\tpr_info(\" pvclock mul=%u shift=%d\\n\", expected_tsc_mul, expected_tsc_shift);\n+\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\n+\tswitch (get_ucall(vcpu, \u0026uc)) {\n+\tcase UCALL_ABORT:\n+\t\tREPORT_GUEST_ASSERT(uc);\n+\t\tbreak;\n+\tcase UCALL_SYNC:\n+\t\tbreak;\n+\tdefault:\n+\t\tTEST_FAIL(\"Unexpected ucall\");\n+\t}\n+\n+\tkvm_vm_free(vm);\n+}\n+\n+int main(void)\n+{\n+\tuint64_t freq;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct kvm_device_attr attr = {\n+\t\t.group = KVM_VCPU_TSC_CTRL,\n+\t\t.attr = KVM_VCPU_TSC_SCALE,\n+\t};\n+\n+\tTEST_REQUIRE(sys_clocksource_is_based_on_tsc());\n+\n+\t/* Check KVM_VCPU_TSC_SCALE is supported (implies TSC scaling) */\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code);\n+\tTEST_REQUIRE(!__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, \u0026attr));\n+\thost_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);\n+\tkvm_vm_free(vm);\n+\n+\t/* Native frequency */\n+\trun_test(0);\n+\n+\t/* Scaled frequencies — skip if TSC scaling not available */\n+\tfor (freq = 1000000; freq \u003c= 4000000; freq += 1000000) {\n+\t\tif (freq == host_khz)\n+\t\t\tcontinue;\n+\t\trun_test(freq);\n+\t}\n+\n+\tpr_info(\"PASS: All CPUID timing leaf tests passed\\n\");\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/xen_migration_test.c b/tools/testing/selftests/kvm/x86/xen_migration_test.c\nnew file mode 100644\nindex 00000000000000..4a041d36f1b9f3\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/xen_migration_test.c\n@@ -0,0 +1,194 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Test Xen runstate (steal time) preservation across simulated migration.\n+ *\n+ * Verifies that the kernel correctly accounts the migration gap as\n+ * steal time (runnable) when runstate data is saved and restored\n+ * precisely, but real time elapses during the migration.\n+ *\n+ * The key insight: userspace saves the runstate with state=RUNSTATE_runnable\n+ * (the vCPU is not running during migration). On restore, the kernel sees\n+ * that kvmclock has advanced past state_entry_time, and accounts the\n+ * difference as time spent in the runnable state.\n+ */\n+#include \u003cinttypes.h\u003e\n+#include \u003cstdint.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+\n+#include \u003casm/pvclock-abi.h\u003e\n+\n+#define SHINFO_GPA\t0xc0000000ULL\n+#define RUNSTATE_GPA\t(SHINFO_GPA + 0x1000)\n+\n+#define RUNSTATE_running 0\n+#define RUNSTATE_runnable 1\n+#define RUNSTATE_blocked 2\n+#define RUNSTATE_offline 3\n+\n+struct vcpu_runstate_info {\n+\tuint32_t state;\n+\tuint64_t state_entry_time;\n+\tuint64_t time[4];\n+} __attribute__((packed));\n+\n+static void guest_code(void)\n+{\n+\tvolatile struct vcpu_runstate_info *rs =\n+\t\t(void *)(unsigned long)RUNSTATE_GPA;\n+\n+\t/* Report runstate times — no need to enable kvmclock MSR,\n+\t * the kernel writes runstate using its internal kvmclock. */\n+\tGUEST_SYNC_ARGS(0, rs-\u003etime[RUNSTATE_runnable],\n+\t\t\trs-\u003etime[RUNSTATE_running], 0, 0);\n+}\n+\n+static struct kvm_vm *create_xen_vm(struct kvm_vcpu **vcpu)\n+{\n+\tstruct kvm_vm *vm;\n+\tint xen_caps;\n+\n+\tvm = vm_create_with_one_vcpu(vcpu, guest_code);\n+\n+\txen_caps = kvm_check_cap(KVM_CAP_XEN_HVM);\n+\tTEST_REQUIRE(xen_caps \u0026 KVM_XEN_HVM_CONFIG_SHARED_INFO);\n+\tTEST_REQUIRE(xen_caps \u0026 KVM_XEN_HVM_CONFIG_RUNSTATE);\n+\n+\t/* Map pages */\n+\tvm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,\n+\t\t\t\t SHINFO_GPA, 1, 2, 0);\n+\tvirt_map(vm, SHINFO_GPA, SHINFO_GPA, 2);\n+\n+\t/* Enable Xen HVM with MSR interception (enables runstate tracking) */\n+\tstruct kvm_xen_hvm_config cfg = {\n+\t\t.flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,\n+\t\t.msr = 0x40000000,\n+\t};\n+\tvm_ioctl(vm, KVM_XEN_HVM_CONFIG, \u0026cfg);\n+\n+\t/* Set shared_info */\n+\tstruct kvm_xen_hvm_attr ha = {\n+\t\t.type = KVM_XEN_ATTR_TYPE_SHARED_INFO,\n+\t\t.u.shared_info.gfn = SHINFO_GPA \u003e\u003e 12,\n+\t};\n+\tvm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, \u0026ha);\n+\n+\t/* Set runstate address */\n+\tstruct kvm_xen_vcpu_attr rs_addr = {\n+\t\t.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR,\n+\t\t.u.gpa = RUNSTATE_GPA,\n+\t};\n+\tvcpu_ioctl(*vcpu, KVM_XEN_VCPU_SET_ATTR, \u0026rs_addr);\n+\n+\treturn vm;\n+}\n+\n+int main(void)\n+{\n+\tstruct pvclock_vcpu_time_info pvti;\n+\tstruct kvm_xen_vcpu_attr runstate_save;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct ucall uc;\n+\tuint64_t tsc_offset;\n+\tint ret;\n+\n+\t/* === SOURCE SIDE === */\n+\tpr_info(\"=== Source: create VM and run guest ===\\n\");\n+\tvm = create_xen_vm(\u0026vcpu);\n+\n+\t/* Run guest once to accumulate some runstate time */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\tTEST_ASSERT_EQ(get_ucall(vcpu, \u0026uc), UCALL_SYNC);\n+\n+\tpr_info(\" Guest sees: runnable=%\" PRIu64 \" running=%\" PRIu64 \"\\n\",\n+\t\tuc.args[2], uc.args[3]);\n+\n+\t/* Save clock state */\n+\tret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, \u0026pvti);\n+\tTEST_ASSERT(!ret, \"KVM_GET_CLOCK_GUEST failed\");\n+\n+\t/* Save TSC offset */\n+\ttsc_offset = vcpu_get_msr(vcpu, MSR_IA32_TSC_ADJUST);\n+\n+\t/* Save runstate — the vCPU is now \"runnable\" (not running) */\n+\trunstate_save.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;\n+\tvcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, \u0026runstate_save);\n+\n+\t/*\n+\t * Transition to runnable state before saving — the vCPU is\n+\t * not running during migration.\n+\t */\n+\trunstate_save.u.runstate.state = RUNSTATE_runnable;\n+\n+\tpr_info(\" Saved runstate: running=%\" PRIu64 \" runnable=%\" PRIu64\n+\t\t\" entry=%\" PRIu64 \"\\n\",\n+\t\t(uint64_t)runstate_save.u.runstate.time_running,\n+\t\t(uint64_t)runstate_save.u.runstate.time_runnable,\n+\t\t(uint64_t)runstate_save.u.runstate.state_entry_time);\n+\n+\tuint64_t saved_runnable = runstate_save.u.runstate.time_runnable;\n+\n+\tkvm_vm_free(vm);\n+\n+\t/* === MIGRATION GAP === */\n+\tpr_info(\"=== Simulating migration (sleeping 10ms) ===\\n\");\n+\tusleep(10000);\n+\n+\t/* === DESTINATION SIDE === */\n+\tpr_info(\"=== Destination: create new VM and restore ===\\n\");\n+\tvm = create_xen_vm(\u0026vcpu);\n+\n+\t/* Restore TSC offset */\n+\tvcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, tsc_offset);\n+\n+\t/* Restore clock — kvmclock will now be ~10ms ahead of the snapshot */\n+\tvcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, \u0026pvti);\n+\n+\t/* Restore runstate exactly as saved (state=runnable) */\n+\trunstate_save.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;\n+\tret = __vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, \u0026runstate_save);\n+\tTEST_ASSERT(!ret, \"Restore runstate failed: errno %d\", errno);\n+\n+\t/*\n+\t * Run the guest. When the vCPU enters vcpu_run, the kernel\n+\t * transitions from RUNSTATE_runnable to RUNSTATE_running.\n+\t * It computes: delta = kvmclock_now - state_entry_time\n+\t * This delta (which includes the migration gap) is added to\n+\t * time_runnable (steal time).\n+\t */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\tTEST_ASSERT_EQ(get_ucall(vcpu, \u0026uc), UCALL_SYNC);\n+\n+\tuint64_t guest_runnable = uc.args[2];\n+\tuint64_t guest_running = uc.args[3];\n+\n+\tpr_info(\" Guest sees: runnable=%\" PRIu64 \" running=%\" PRIu64 \"\\n\",\n+\t\tguest_runnable, guest_running);\n+\n+\tuint64_t steal_increase = guest_runnable - saved_runnable;\n+\tpr_info(\" Steal time increase: %\" PRIu64 \" ns (migration gap)\\n\",\n+\t\tsteal_increase);\n+\n+\t/*\n+\t * The steal time increase should be at least 10ms (the sleep)\n+\t * but not more than 5s (allowing for VM creation overhead).\n+\t * The actual gap is from the source's state_entry_time to the\n+\t * destination's kvmclock \"now\" at vcpu_load time.\n+\t */\n+\tTEST_ASSERT(steal_increase \u003e= 10000000ULL \u0026\u0026\n+\t\t steal_increase \u003c 5000000000ULL,\n+\t\t \"Steal time increase %\" PRIu64 \" ns not in expected range \"\n+\t\t \"[10ms, 5s]\", steal_increase);\n+\n+\tkvm_vm_free(vm);\n+\tpr_info(\"PASS: Migration gap correctly accounted as steal time\\n\");\n+\treturn 0;\n+}\ndiff --git a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c\nindex 5076f6a7545575..2f241ca4f096f6 100644\n--- a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c\n+++ b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c\n@@ -98,22 +98,7 @@ struct sched_poll {\n \tu64 timeout;\n };\n \n-struct pvclock_vcpu_time_info {\n-\tu32 version;\n-\tu32 pad0;\n-\tu64 tsc_timestamp;\n-\tu64 system_time;\n-\tu32 tsc_to_system_mul;\n-\ts8 tsc_shift;\n-\tu8 flags;\n-\tu8 pad[2];\n-} __attribute__((__packed__)); /* 32 bytes */\n-\n-struct pvclock_wall_clock {\n-\tu32 version;\n-\tu32 sec;\n-\tu32 nsec;\n-} __attribute__((__packed__));\n+#include \u003casm/pvclock-abi.h\u003e\n \n struct vcpu_runstate_info {\n \tu32 state;\n"
} |
| 2/1 | 2026/07/03 22:02 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_vcpu_ioctl_get_clock_guest",
"kvm_vcpu_ioctl_set_clock_guest",
"kvm_arch_tsc_get_attr",
"kvm_arch_tsc_set_attr",
"kvm_arch_tsc_has_attr",
"kvm_guest_time_update",
"kvm_synchronize_tsc",
"kvm_update_masterclock"
],
"Reasoning": "The patch introduces new KVM ioctls (KVM_GET_CLOCK_GUEST, KVM_SET_CLOCK_GUEST) and a new device attribute (KVM_VCPU_TSC_SCALE) for managing guest clocks and TSC scaling. It also modifies core KVM timekeeping functions like kvm_guest_time_update and kvm_synchronize_tsc. These are functional changes to reachable core kernel logic that should be fuzzed.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit d0600dd63912d02580f09d082e72314faa4a3434
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Jul 3 22:02:36 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 52bbbb553ce107..2268b4442df614 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -6553,6 +6553,43 @@ KVM_S390_KEYOP_SSKE
Sets the storage key for the guest address ``guest_addr`` to the key
specified in ``key``, returning the previous value in ``key``.
+4.145 KVM_GET_CLOCK_GUEST
+----------------------------
+
+:Capability: none
+:Architectures: x86_64
+:Type: vcpu ioctl
+:Parameters: struct pvclock_vcpu_time_info (out)
+:Returns: 0 on success, <0 on error
+
+Retrieves the current time information structure used for KVM/PV clocks,
+in precisely the form advertised to the guest vCPU, which gives parameters
+for a direct conversion from a guest TSC value to nanoseconds.
+
+When the KVM clock is not in "master clock" mode, for example because the
+host TSC is unreliable or the guest TSCs are oddly configured, the KVM clock
+is actually defined by the host CLOCK_MONOTONIC_RAW instead of the guest TSC.
+In this case, the KVM_GET_CLOCK_GUEST ioctl returns -EINVAL.
+
+4.146 KVM_SET_CLOCK_GUEST
+----------------------------
+
+:Capability: none
+:Architectures: x86_64
+:Type: vcpu ioctl
+:Parameters: struct pvclock_vcpu_time_info (in)
+:Returns: 0 on success, <0 on error
+
+Sets the KVM clock (for the whole VM) in terms of the vCPU TSC, using the
+pvclock structure as returned by KVM_GET_CLOCK_GUEST. This allows the precise
+arithmetic relationship between guest TSC and KVM clock to be preserved by
+userspace across migration.
+
+When the KVM clock is not in "master clock" mode, and the KVM clock is actually
+defined by the host CLOCK_MONOTONIC_RAW, this ioctl returns -EINVAL. Userspace
+may choose to set the clock using the less precise KVM_SET_CLOCK ioctl, or may
+choose to fail, denying migration to a host whose TSC is misbehaving.
+
.. _kvm_run:
5. The kvm_run structure
diff --git a/Documentation/virt/kvm/devices/vcpu.rst b/Documentation/virt/kvm/devices/vcpu.rst
index 5e38058200105e..de9f58f75eb931 100644
--- a/Documentation/virt/kvm/devices/vcpu.rst
+++ b/Documentation/virt/kvm/devices/vcpu.rst
@@ -229,6 +229,7 @@ including the layout of the stolen time structure.
:Architectures: x86
4.1 ATTRIBUTE: KVM_VCPU_TSC_OFFSET
+----------------------------------
:Parameters: 64-bit unsigned TSC offset
@@ -243,7 +244,10 @@ Returns:
Specifies the guest's TSC offset relative to the host's TSC. The guest's
TSC is then derived by the following equation:
- guest_tsc = host_tsc + KVM_VCPU_TSC_OFFSET
+ guest_tsc = ((host_tsc * tsc_ratio) >> tsc_frac_bits) + KVM_VCPU_TSC_OFFSET
+
+The values of tsc_ratio and tsc_frac_bits can be obtained using
+the KVM_VCPU_TSC_SCALE attribute.
This attribute is useful to adjust the guest's TSC on live migration,
so that the TSC counts the time during which the VM was paused. The
@@ -251,44 +255,100 @@ following describes a possible algorithm to use for this purpose.
From the source VMM process:
-1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (tsc_src),
+1. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (host_tsc_src),
kvmclock nanoseconds (guest_src), and host CLOCK_REALTIME nanoseconds
- (host_src).
+ (time_src) at a given moment (Tsrc).
+
+2. For each vCPU[i]:
+
+ a. Read the KVM_VCPU_TSC_OFFSET attribute to record the guest TSC offset
+ (ofs_src[i]).
-2. Read the KVM_VCPU_TSC_OFFSET attribute for every vCPU to record the
- guest TSC offset (ofs_src[i]).
+ b. Read the KVM_VCPU_TSC_SCALE attribute to record the guest TSC scaling
+ ratio (ratio_src[i], frac_bits_src[i]).
-3. Invoke the KVM_GET_TSC_KHZ ioctl to record the frequency of the
- guest's TSC (freq).
+ c. Use host_tsc_src and the scaling/offset factors to calculate this
+ vCPU's TSC at time Tsrc:
+
+ tsc_src[i] = ((host_tsc_src * ratio_src[i]) >> frac_bits_src[i]) + ofs_src[i]
+
+3. Invoke the KVM_GET_CLOCK_GUEST ioctl on the boot vCPU to return the KVM
+ clock as a function of the guest TSC (pvti_src). (This ioctl may not
+ succeed if the host and guest TSCs are not consistent and well-behaved.)
From the destination VMM process:
-4. Invoke the KVM_SET_CLOCK ioctl, providing the source nanoseconds from
- kvmclock (guest_src) and CLOCK_REALTIME (host_src) in their respective
- fields. Ensure that the KVM_CLOCK_REALTIME flag is set in the provided
- structure.
+4. Before creating the vCPUs, invoke the KVM_SET_TSC_KHZ ioctl on the VM, to
+ set the scaled frequency of the guest's TSC (freq).
+
+5. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (host_tsc_dst) and
+ host CLOCK_REALTIME nanoseconds (time_dst) at a given moment (Tdst).
+
+6. Calculate the number of nanoseconds elapsed between Tsrc and Tdst:
+
+ ΔT = time_dst - time_src
+
+7. As each vCPU[i] is created:
+
+ a. Read the KVM_VCPU_TSC_SCALE attribute to record the guest TSC scaling
+ ratio (ratio_dst[i], frac_bits_dst[i]).
+
+ b. Calculate the intended guest TSC value at time Tdst:
+
+ tsc_dst[i] = tsc_src[i] + (ΔT * freq[i])
- KVM will advance the VM's kvmclock to account for elapsed time since
- recording the clock values. Note that this will cause problems in
- the guest (e.g., timeouts) unless CLOCK_REALTIME is synchronized
- between the source and destination, and a reasonably short time passes
- between the source pausing the VMs and the destination executing
- steps 4-7.
+ c. Use host_tsc_dst and the scaling factors to calculate this vCPU's
+ raw scaled TSC at time Tdst without offsetting:
+
+ raw_dst[i] = ((host_tsc_dst * ratio_dst[i]) >> frac_bits_dst[i])
+
+ d. Calculate ofs_dst[i] = tsc_dst[i] - raw_dst[i] and set the resulting
+ offset using the KVM_VCPU_TSC_OFFSET attribute.
+
+8. If pvti_src was provided, invoke the KVM_SET_CLOCK_GUEST ioctl on the boot
+ vCPU to restore the KVM clock as a precise function of the guest TSC.
+
+9. If KVM_SET_CLOCK_GUEST was not available or failed (e.g. because the
+ master clock is not active), fall back to the KVM_SET_CLOCK ioctl,
+ providing the source nanoseconds from kvmclock (guest_src) and
+ CLOCK_REALTIME (time_src) in their respective fields. Ensure that the
+ KVM_CLOCK_REALTIME flag is set in the provided structure.
+
+ KVM will restore the VM's kvmclock, accounting for elapsed time since
+ the clock values were recorded. Note that this will cause problems in
+ the guest (e.g., timeouts) unless CLOCK_REALTIME is synchronized between
+ the source and destination, and a reasonably short time passes between
+ the source pausing the VMs and the destination resuming them.
+ Due to the KVM_[SG]ET_CLOCK API using CLOCK_REALTIME instead of
+ CLOCK_TAI, leap seconds during the migration may also introduce errors.
+
+4.2 ATTRIBUTE: KVM_VCPU_TSC_SCALE
+---------------------------------
+
+:Parameters: struct kvm_vcpu_tsc_scale
+
+Returns:
+
+ ======= ======================================
+ -EFAULT Error reading the provided parameter
+ address.
+ -ENXIO Attribute not supported (no TSC scaling)
+ -EINVAL Invalid request to write the attribute
+ ======= ======================================
-5. Invoke the KVM_GET_CLOCK ioctl to record the host TSC (tsc_dest) and
- kvmclock nanoseconds (guest_dest).
+This read-only attribute reports the guest's TSC scaling factor, in the form
+of a fixed-point number represented by the following structure::
-6. Adjust the guest TSC offsets for every vCPU to account for (1) time
- elapsed since recording state and (2) difference in TSCs between the
- source and destination machine:
+ struct kvm_vcpu_tsc_scale {
+ __u64 tsc_ratio;
+ __u64 tsc_frac_bits;
+ };
- ofs_dst[i] = ofs_src[i] -
- (guest_src - guest_dest) * freq +
- (tsc_src - tsc_dest)
+The tsc_frac_bits field indicates the location of the fixed point, such that
+host TSC values are converted to guest TSC using the formula:
- ("ofs[i] + tsc - guest * freq" is the guest TSC value corresponding to
- a time of 0 in kvmclock. The above formula ensures that it is the
- same on the destination as it was on the source).
+ guest_tsc = ((host_tsc * tsc_ratio) >> tsc_frac_bits) + offset
-7. Write the KVM_VCPU_TSC_OFFSET attribute for every vCPU with the
- respective value derived in the previous step.
+Userspace can use this to precisely calculate the guest TSC from the host
+TSC at any given moment. This is needed for accurate migration of guests,
+as described in the documentation for the KVM_VCPU_TSC_OFFSET attribute.
diff --git a/MAINTAINERS b/MAINTAINERS
index 905c29fe499642..057ccef8d1fa5c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14420,7 +14420,7 @@ S: Supported
T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git
F: arch/um/include/asm/kvm_para.h
F: arch/x86/include/asm/kvm_para.h
-F: arch/x86/include/asm/pvclock-abi.h
+F: arch/x86/include/uapi/asm/pvclock-abi.h
F: arch/x86/include/uapi/asm/kvm_para.h
F: arch/x86/kernel/kvm.c
F: arch/x86/kernel/kvmclock.c
@@ -29105,7 +29105,7 @@ R: Boris Ostrovsky <boris.ostrovsky@oracle.com>
L: xen-devel@lists.xenproject.org (moderated for non-subscribers)
S: Supported
F: arch/x86/configs/xen.config
-F: arch/x86/include/asm/pvclock-abi.h
+F: arch/x86/include/uapi/asm/pvclock-abi.h
F: arch/x86/include/asm/xen/
F: arch/x86/platform/pvh/
F: arch/x86/xen/
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index f14009f25a3b6a..da9141ea7d7b3d 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -950,7 +950,7 @@ struct kvm_vcpu_arch {
gpa_t time;
s8 pvclock_tsc_shift;
u32 pvclock_tsc_mul;
- unsigned int hw_tsc_khz;
+ u64 hw_tsc_hz;
struct gfn_to_pfn_cache pv_time;
/* set guest stopped flag in pvclock flags field */
bool pvclock_set_guest_stopped_request;
@@ -970,6 +970,7 @@ struct kvm_vcpu_arch {
u64 this_tsc_nsec;
u64 this_tsc_write;
u64 this_tsc_generation;
+ u64 this_tsc_freq_generation;
bool tsc_catchup;
bool tsc_always_catchup;
s8 virtual_tsc_shift;
@@ -1486,14 +1487,16 @@ struct kvm_arch {
* preemption-disabled region, so it must be a raw spinlock.
*/
raw_spinlock_t tsc_write_lock;
- u64 last_tsc_nsec;
- u64 last_tsc_write;
- u32 last_tsc_khz;
- u64 last_tsc_offset;
+ u32 cur_tsc_khz;
+ u64 cur_tsc_scaling_ratio;
u64 cur_tsc_nsec;
u64 cur_tsc_write;
u64 cur_tsc_offset;
u64 cur_tsc_generation;
+ bool all_vcpus_matched_tsc;
+ bool all_vcpus_matched_freq;
+ int nr_vcpus_matched_freq;
+ u64 cur_tsc_freq_generation;
int nr_vcpus_matched_tsc;
u32 default_tsc_khz;
@@ -1505,6 +1508,9 @@ struct kvm_arch {
u64 master_kernel_ns;
u64 master_cycle_now;
struct ratelimit_state kvmclock_update_rs;
+ u64 master_tsc_scaling_ratio;
+ s8 master_tsc_shift;
+ u32 master_tsc_mul;
#ifdef CONFIG_KVM_HYPERV
struct kvm_hv hyperv;
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 5f2b30d0405c87..fb38486ff74231 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -12,6 +12,7 @@
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/stddef.h>
+#include <asm/pvclock-abi.h>
#define KVM_PIO_PAGE_OFFSET 1
#define KVM_COALESCED_MMIO_PAGE_OFFSET 2
@@ -961,6 +962,12 @@ struct kvm_hyperv_eventfd {
/* for KVM_{GET,SET,HAS}_DEVICE_ATTR */
#define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */
#define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */
+#define KVM_VCPU_TSC_SCALE 1 /* attribute for TSC scaling factor */
+
+struct kvm_vcpu_tsc_scale {
+ __u64 tsc_ratio;
+ __u64 tsc_frac_bits;
+};
/* x86-specific KVM_EXIT_HYPERCALL flags. */
#define KVM_EXIT_HYPERCALL_LONG_MODE _BITULL(0)
diff --git a/arch/x86/include/asm/pvclock-abi.h b/arch/x86/include/uapi/asm/pvclock-abi.h
similarity index 82%
rename from arch/x86/include/asm/pvclock-abi.h
rename to arch/x86/include/uapi/asm/pvclock-abi.h
index b9fece5fc96d6f..6d70cf64036293 100644
--- a/arch/x86/include/asm/pvclock-abi.h
+++ b/arch/x86/include/uapi/asm/pvclock-abi.h
@@ -1,6 +1,9 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_X86_PVCLOCK_ABI_H
#define _ASM_X86_PVCLOCK_ABI_H
+
+#include <linux/types.h>
+
#ifndef __ASSEMBLER__
/*
@@ -24,20 +27,20 @@
*/
struct pvclock_vcpu_time_info {
- u32 version;
- u32 pad0;
- u64 tsc_timestamp;
- u64 system_time;
- u32 tsc_to_system_mul;
- s8 tsc_shift;
- u8 flags;
- u8 pad[2];
+ __u32 version;
+ __u32 pad0;
+ __u64 tsc_timestamp;
+ __u64 system_time;
+ __u32 tsc_to_system_mul;
+ __s8 tsc_shift;
+ __u8 flags;
+ __u8 pad[2];
} __attribute__((__packed__)); /* 32 bytes */
struct pvclock_wall_clock {
- u32 version;
- u32 sec;
- u32 nsec;
+ __u32 version;
+ __u32 sec;
+ __u32 nsec;
} __attribute__((__packed__));
#define PVCLOCK_TSC_STABLE_BIT (1 << 0)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index e69156b54cfffc..826637a0b72d6f 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -2117,22 +2117,6 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
} else if (function == 0x80000007) {
if (kvm_hv_invtsc_suppressed(vcpu))
*edx &= ~feature_bit(CONSTANT_TSC);
- } else if (IS_ENABLED(CONFIG_KVM_XEN) &&
- kvm_xen_is_tsc_leaf(vcpu, function)) {
- /*
- * Update guest TSC frequency information if necessary.
- * Ignore failures, there is no sane value that can be
- * provided if KVM can't get the TSC frequency.
- */
- if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu))
- kvm_guest_time_update(vcpu);
-
- if (index == 1) {
- *ecx = vcpu->arch.pvclock_tsc_mul;
- *edx = vcpu->arch.pvclock_tsc_shift;
- } else if (index == 2) {
- *eax = vcpu->arch.hw_tsc_khz;
- }
}
} else {
*eax = *ebx = *ecx = *edx = 0;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index e02a38da5296e3..c46a34aeb3dfb8 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5557,7 +5557,8 @@ static __init int svm_hardware_setup(void)
XFEATURE_MASK_BNDCSR);
if (tsc_scaling) {
- if (!boot_cpu_has(X86_FEATURE_TSCRATEMSR)) {
+ if (!boot_cpu_has(X86_FEATURE_TSCRATEMSR) ||
+ !boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) {
tsc_scaling = false;
} else {
pr_info("TSC scaling supported\n");
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index b9103de01428eb..54e92d94155ed9 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2792,6 +2792,16 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
if (!cpu_has_sgx())
_cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_ENCLS_EXITING;
+ /*
+ * KVM doesn't re-derive the TSC scaling ratio when the host TSC
+ * frequency changes, so TSC scaling is only usable with a constant
+ * TSC. Clear the control here rather than in vmx_hardware_setup() so
+ * that the per-CPU configs recomputed by vmx_check_processor_compat()
+ * stay consistent with the golden vmcs_config.
+ */
+ if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
+ _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_TSC_SCALING;
+
if (_cpu_based_exec_control & CPU_BASED_ACTIVATE_TERTIARY_CONTROLS)
_cpu_based_3rd_exec_control =
adjust_vmx_controls64(KVM_OPTIONAL_VMX_TERTIARY_VM_EXEC_CONTROL,
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0550359ed798fc..d35f2ac3ef494c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -35,6 +35,7 @@
#include "smm.h"
#include <linux/clocksource.h>
+#include <linux/timekeeping.h>
#include <linux/interrupt.h>
#include <linux/kvm.h>
#include <linux/fs.h>
@@ -203,6 +204,10 @@ module_param(mitigate_smt_rsb, bool, 0444);
* usermode, e.g. SYSCALL MSRs and TSC_AUX, can be deferred until the CPU
* returns to userspace, i.e. the kernel can run with the guest's value.
*/
+#ifdef CONFIG_X86_64
+static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);
+#endif
+static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value);
#define KVM_MAX_NR_USER_RETURN_MSRS 16
struct kvm_user_return_msrs {
@@ -2342,71 +2347,11 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
return kvm_set_msr_ignored_check(vcpu, index, *data, true);
}
-#ifdef CONFIG_X86_64
-struct pvclock_clock {
- int vclock_mode;
- u64 cycle_last;
- u64 mask;
- u32 mult;
- u32 shift;
- u64 base_cycles;
- u64 offset;
-};
-
-struct pvclock_gtod_data {
- seqcount_t seq;
-
- struct pvclock_clock clock; /* extract of a clocksource struct */
- struct pvclock_clock raw_clock; /* extract of a clocksource struct */
-
- ktime_t offs_boot;
- u64 wall_time_sec;
-};
-
-static struct pvclock_gtod_data pvclock_gtod_data;
-
-static void update_pvclock_gtod(struct timekeeper *tk)
-{
- struct pvclock_gtod_data *vdata = &pvclock_gtod_data;
-
- write_seqcount_begin(&vdata->seq);
-
- /* copy pvclock gtod data */
- vdata->clock.vclock_mode = tk->tkr_mono.clock->vdso_clock_mode;
- vdata->clock.cycle_last = tk->tkr_mono.cycle_last;
- vdata->clock.mask = tk->tkr_mono.mask;
- vdata->clock.mult = tk->tkr_mono.mult;
- vdata->clock.shift = tk->tkr_mono.shift;
- vdata->clock.base_cycles = tk->tkr_mono.xtime_nsec;
- vdata->clock.offset = tk->tkr_mono.base;
-
- vdata->raw_clock.vclock_mode = tk->tkr_raw.clock->vdso_clock_mode;
- vdata->raw_clock.cycle_last = tk->tkr_raw.cycle_last;
- vdata->raw_clock.mask = tk->tkr_raw.mask;
- vdata->raw_clock.mult = tk->tkr_raw.mult;
- vdata->raw_clock.shift = tk->tkr_raw.shift;
- vdata->raw_clock.base_cycles = tk->tkr_raw.xtime_nsec;
- vdata->raw_clock.offset = tk->tkr_raw.base;
-
- vdata->wall_time_sec = tk->xtime_sec;
-
- vdata->offs_boot = tk->offs_boot;
-
- write_seqcount_end(&vdata->seq);
-}
-
static s64 get_kvmclock_base_ns(void)
{
/* Count up from boot time, but with the frequency of the raw clock. */
- return ktime_to_ns(ktime_add(ktime_get_raw(), pvclock_gtod_data.offs_boot));
+ return ktime_to_ns(ktime_mono_to_any(ktime_get_raw(), TK_OFFS_BOOT));
}
-#else
-static s64 get_kvmclock_base_ns(void)
-{
- /* Master clock not used, so we can just use CLOCK_BOOTTIME. */
- return ktime_get_boottime_ns();
-}
-#endif
static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock, int sec_hi_ofs)
{
@@ -2462,13 +2407,13 @@ static void kvm_write_system_time(struct kvm_vcpu *vcpu, gpa_t system_time,
}
vcpu->arch.time = system_time;
- kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu);
/* we verify if the enable bit is set... */
- if (system_time & 1)
+ if (system_time & 1) {
kvm_gpc_activate(&vcpu->arch.pv_time, system_time & ~1ULL,
sizeof(struct pvclock_vcpu_time_info));
- else
+ kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
+ } else
kvm_gpc_deactivate(&vcpu->arch.pv_time);
return;
@@ -2480,32 +2425,57 @@ static uint32_t div_frac(uint32_t dividend, uint32_t divisor)
return dividend;
}
-static void kvm_get_time_scale(uint64_t scaled_hz, uint64_t base_hz,
+static void kvm_get_time_scale(u64 scaled_hz, u64 base_hz,
s8 *pshift, u32 *pmultiplier)
{
- uint64_t scaled64;
- int32_t shift = 0;
- uint64_t tps64;
- uint32_t tps32;
+ u64 scaled_hz_u64 = scaled_hz;
+ s32 shift = 0;
+ u64 base_hz_u64;
+ u32 base32;
- tps64 = base_hz;
- scaled64 = scaled_hz;
- while (tps64 > scaled64*2 || tps64 & 0xffffffff00000000ULL) {
- tps64 >>= 1;
+ /*
+ * This function calculates a fixed-point multiplier and shift such
+ * that:
+ * time_ns = (tsc_cycles << shift) * multiplier >> 32
+ *
+ * Where tsc_cycles tick at base_hz, and time_ns should count at
+ * scaled_hz (typically NSEC_PER_SEC for a TSC→nanoseconds conversion).
+ *
+ * The multiplier is: (scaled_hz << 32) / base_hz, adjusted by shift
+ * to keep everything in range.
+ */
+
+ base_hz_u64 = base_hz;
+
+ /*
+ * Start by shifting base_hz right until it fits in 32 bits, and
+ * is lower than double the target rate. This introduces a negative
+ * shift value which would result in pvclock_scale_delta() shifting
+ * the actual tick count right before performing the multiplication.
+ */
+ while (base_hz_u64 > scaled_hz_u64 * 2 || base_hz_u64 >> 32) {
+ base_hz_u64 >>= 1;
shift--;
}
- tps32 = (uint32_t)tps64;
- while (tps32 <= scaled64 || scaled64 & 0xffffffff00000000ULL) {
- if (scaled64 & 0xffffffff00000000ULL || tps32 & 0x80000000)
- scaled64 >>= 1;
+ /* Now the shifted base_hz fits in 32 bits. */
+ base32 = (u32)base_hz_u64;
+
+ /*
+ * Next, shift scaled_hz right until it fits in 32 bits, and ensure
+ * that the shifted base_hz is strictly larger (so that the result of the
+ * final division also fits in 32 bits).
+ */
+ while (base32 <= scaled_hz_u64 || scaled_hz_u64 >> 32) {
+ if (scaled_hz_u64 >> 32 || base32 & BIT(31))
+ scaled_hz_u64 >>= 1;
else
- tps32 <<= 1;
+ base32 <<= 1;
shift++;
}
*pshift = shift;
- *pmultiplier = div_frac(scaled64, tps32);
+ *pmultiplier = div_frac(scaled_hz_u64, base32);
}
#ifdef CONFIG_X86_64
@@ -2564,6 +2534,7 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
{
u32 thresh_lo, thresh_hi;
int use_scaling = 0;
+ u64 tsc;
/* tsc_khz can be zero if TSC calibration fails */
if (user_tsc_khz == 0) {
@@ -2591,55 +2562,125 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
user_tsc_khz, thresh_lo, thresh_hi);
use_scaling = 1;
}
- return set_tsc_khz(vcpu, user_tsc_khz, use_scaling);
+ /*
+ * Read the guest TSC before changing the ratio, so we can
+ * re-synchronize to preserve continuity across the change.
+ */
+ tsc = kvm_read_l1_tsc(vcpu, rdtsc());
+
+ if (set_tsc_khz(vcpu, user_tsc_khz, use_scaling))
+ return -1;
+
+ kvm_synchronize_tsc(vcpu, &tsc);
+ return 0;
}
static u64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns)
{
- u64 tsc = pvclock_scale_delta(kernel_ns-vcpu->arch.this_tsc_nsec,
- vcpu->arch.virtual_tsc_mult,
- vcpu->arch.virtual_tsc_shift);
- tsc += vcpu->arch.this_tsc_write;
- return tsc;
+ s64 delta_ns = kernel_ns - vcpu->arch.this_tsc_nsec;
+ u64 tsc;
+
+ /* Handle negative deltas gracefully (master clock ref may be earlier) */
+ if (delta_ns < 0) {
+ tsc = pvclock_scale_delta(-delta_ns,
+ vcpu->arch.virtual_tsc_mult,
+ vcpu->arch.virtual_tsc_shift);
+ return vcpu->arch.this_tsc_write - tsc;
+ }
+
+ tsc = pvclock_scale_delta(delta_ns,
+ vcpu->arch.virtual_tsc_mult,
+ vcpu->arch.virtual_tsc_shift);
+ return vcpu->arch.this_tsc_write + tsc;
}
#ifdef CONFIG_X86_64
+static int kvm_host_vclock_mode;
+
static inline bool gtod_is_based_on_tsc(int mode)
{
return mode == VDSO_CLOCKMODE_TSC || mode == VDSO_CLOCKMODE_HVCLOCK;
}
-#endif
-static void kvm_track_tsc_matching(struct kvm_vcpu *vcpu, bool new_generation)
+static bool kvm_use_master_clock(struct kvm *kvm)
+{
+ struct kvm_arch *ka = &kvm->arch;
+
+ /*
+ * The 'old kvmclock' check is a workaround (from 2015) for a
+ * SUSE 2.6.16 kernel that didn't boot if the system_time in
+ * its kvmclock was too far behind the current time. So the
+ * mode of just setting the reference point and allowing time
+ * to proceed linearly from there makes it fail to boot.
+ * Despite that being kind of the *point* of the way the clock
+ * is exposed to the guest. By coincidence, the offending
+ * kernels used the old MSR_KVM_SYSTEM_TIME, which was moved
+ * only because it resided in the wrong number range. So the
+ * workaround is activated for *all* guests using the old MSR.
+ */
+ return ka->all_vcpus_matched_freq &&
+ !ka->backwards_tsc_observed &&
+ !ka->boot_vcpu_runs_old_kvmclock;
+}
+
+static void kvm_track_tsc_matching(struct kvm_vcpu *vcpu, bool update_mclock)
{
-#ifdef CONFIG_X86_64
struct kvm_arch *ka = &vcpu->kvm->arch;
- struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
+ bool prev_matched_tsc = ka->all_vcpus_matched_tsc;
+
+ /*
+ * Track whether all vCPUs have matching TSC offsets (for
+ * PVCLOCK_TSC_STABLE_BIT) and matching frequencies (for
+ * master clock eligibility).
+ */
+
+ /*
+ * A new vCPU might already have incremented ->online_vcpus
+ * and cause a temporary false negative here. But will then
+ * call kvm_synchronize_tsc() from kvm_arch_vcpu_postcreate()
+ * and finish the job.
+ */
+ int online = atomic_read(&vcpu->kvm->online_vcpus);
+ ka->all_vcpus_matched_tsc = (ka->nr_vcpus_matched_tsc >= online);
+
+ update_mclock |= (prev_matched_tsc != ka->all_vcpus_matched_tsc);
+
+ /*
+ * all_vcpus_matched_freq starts true and is cleared when
+ * __kvm_synchronize_tsc() detects a frequency mismatch.
+ * Re-enable when all vCPUs have synced with matching frequency.
+ * If all offsets also match, that implies frequencies match too.
+ */
+ if (ka->all_vcpus_matched_tsc ||
+ ka->nr_vcpus_matched_freq >= online)
+ ka->all_vcpus_matched_freq = true;
/*
* To use the masterclock, the host clocksource must be based on TSC
- * and all vCPUs must have matching TSCs. Note, the count for matching
- * vCPUs doesn't include the reference vCPU, hence "+1".
+ * and all vCPUs must have matching TSC *frequency*. Different offsets
+ * are fine — each vCPU's pvclock has its own tsc_timestamp that
+ * accounts for its offset.
*/
- bool use_master_clock = (ka->nr_vcpus_matched_tsc + 1 ==
- atomic_read(&vcpu->kvm->online_vcpus)) &&
- gtod_is_based_on_tsc(gtod->clock.vclock_mode);
+ bool use_master_clock = kvm_use_master_clock(vcpu->kvm) &&
+ gtod_is_based_on_tsc(kvm_host_vclock_mode);
/*
- * Request a masterclock update if the masterclock needs to be toggled
- * on/off, or when starting a new generation and the masterclock is
- * enabled (compute_guest_tsc() requires the masterclock snapshot to be
- * taken _after_ the new generation is created).
+ * Request a masterclock update if needed: toggling master clock,
+ * new generation requiring a fresh snapshot, or a change in
+ * all_vcpus_matched_tsc (to broadcast PVCLOCK_TSC_STABLE_BIT).
*/
- if ((ka->use_master_clock && new_generation) ||
+ if ((ka->use_master_clock && update_mclock) ||
(ka->use_master_clock != use_master_clock))
kvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);
trace_kvm_track_tsc(vcpu->vcpu_id, ka->nr_vcpus_matched_tsc,
atomic_read(&vcpu->kvm->online_vcpus),
- ka->use_master_clock, gtod->clock.vclock_mode);
-#endif
+ ka->use_master_clock, kvm_host_vclock_mode);
}
+#else
+static inline void kvm_track_tsc_matching(struct kvm_vcpu *vcpu,
+ bool update_mclock) {}
+#endif
/*
* Multiply tsc by a fixed point number represented by ratio.
@@ -2666,11 +2707,12 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio)
return _tsc;
}
-static u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc)
+static u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,
+ u64 target_tsc)
{
u64 tsc;
- tsc = kvm_scale_tsc(rdtsc(), vcpu->arch.l1_tsc_scaling_ratio);
+ tsc = kvm_scale_tsc(host_tsc, vcpu->arch.l1_tsc_scaling_ratio);
return target_tsc - tsc;
}
@@ -2757,7 +2799,7 @@ static inline bool kvm_check_tsc_unstable(void)
* TSC is marked unstable when we're running on Hyper-V,
* 'TSC page' clocksource is good.
*/
- if (pvclock_gtod_data.clock.vclock_mode == VDSO_CLOCKMODE_HVCLOCK)
+ if (kvm_host_vclock_mode == VDSO_CLOCKMODE_HVCLOCK)
return false;
#endif
return check_tsc_unstable();
@@ -2782,13 +2824,27 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,
vcpu->kvm->arch.user_set_tsc = true;
/*
- * We also track th most recent recorded KHZ, write and time to
- * allow the matching interval to be extended at each write.
+ * Track the TSC frequency, scaling ratio, and offset for the current
+ * generation. These are used to detect matching TSC writes and to
+ * compute the guest TSC from the host clock.
+ *
+ * If the frequency changed, master clock mode can no longer be used
+ * since the kvmclock scaling factors differ between vCPUs.
*/
- kvm->arch.last_tsc_nsec = ns;
- kvm->arch.last_tsc_write = tsc;
- kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz;
- kvm->arch.last_tsc_offset = offset;
+ if (vcpu->arch.virtual_tsc_khz != kvm->arch.cur_tsc_khz) {
+ kvm->arch.cur_tsc_freq_generation++;
+ kvm->arch.all_vcpus_matched_freq = false;
+ kvm->arch.nr_vcpus_matched_freq = 0;
+ }
+
+ /* Count each vCPU once per freq generation */
+ if (vcpu->arch.this_tsc_freq_generation != kvm->arch.cur_tsc_freq_generation) {
+ vcpu->arch.this_tsc_freq_generation = kvm->arch.cur_tsc_freq_generation;
+ kvm->arch.nr_vcpus_matched_freq++;
+ }
+
+ kvm->arch.cur_tsc_khz = vcpu->arch.virtual_tsc_khz;
+ kvm->arch.cur_tsc_scaling_ratio = vcpu->arch.l1_tsc_scaling_ratio;
vcpu->arch.last_guest_tsc = tsc;
@@ -2801,20 +2857,20 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,
* nanosecond time, offset, and write, so if TSCs are in
* sync, we can match exact offset, and if not, we can match
* exact software computation in compute_guest_tsc()
- *
- * These values are tracked in kvm->arch.cur_xxx variables.
*/
kvm->arch.cur_tsc_generation++;
+ kvm->arch.all_vcpus_matched_tsc = false;
+ kvm->arch.nr_vcpus_matched_tsc = 0;
kvm->arch.cur_tsc_nsec = ns;
kvm->arch.cur_tsc_write = tsc;
kvm->arch.cur_tsc_offset = offset;
- kvm->arch.nr_vcpus_matched_tsc = 0;
- } else if (vcpu->arch.this_tsc_generation != kvm->arch.cur_tsc_generation) {
+ }
+
+ if (vcpu->arch.this_tsc_generation != kvm->arch.cur_tsc_generation) {
+ vcpu->arch.this_tsc_generation = kvm->arch.cur_tsc_generation;
kvm->arch.nr_vcpus_matched_tsc++;
}
- /* Keep track of which generation this VCPU has synchronized to */
- vcpu->arch.this_tsc_generation = kvm->arch.cur_tsc_generation;
vcpu->arch.this_tsc_nsec = kvm->arch.cur_tsc_nsec;
vcpu->arch.this_tsc_write = kvm->arch.cur_tsc_write;
@@ -2825,15 +2881,24 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)
{
u64 data = user_value ? *user_value : 0;
struct kvm *kvm = vcpu->kvm;
- u64 offset, ns, elapsed;
+ u64 offset, host_tsc, elapsed;
+ s64 ns;
unsigned long flags;
bool matched = false;
bool synchronizing = false;
raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
- offset = kvm_compute_l1_tsc_offset(vcpu, data);
- ns = get_kvmclock_base_ns();
- elapsed = ns - kvm->arch.last_tsc_nsec;
+
+#ifdef CONFIG_X86_64
+ if (!kvm_get_time_and_clockread(&ns, &host_tsc))
+#endif
+ {
+ host_tsc = rdtsc();
+ ns = get_kvmclock_base_ns();
+ }
+
+ offset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);
+ elapsed = ns - kvm->arch.cur_tsc_nsec;
if (vcpu->arch.virtual_tsc_khz) {
if (data == 0) {
@@ -2843,7 +2908,7 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)
*/
synchronizing = true;
} else if (kvm->arch.user_set_tsc) {
- u64 tsc_exp = kvm->arch.last_tsc_write +
+ u64 tsc_exp = kvm->arch.cur_tsc_write +
nsec_to_cycles(vcpu, elapsed);
u64 tsc_hz = vcpu->arch.virtual_tsc_khz * 1000LL;
/*
@@ -2874,14 +2939,19 @@ static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)
* it's better to try to match offsets from the beginning.
*/
if (synchronizing &&
- vcpu->arch.virtual_tsc_khz == kvm->arch.last_tsc_khz) {
- if (!kvm_check_tsc_unstable()) {
+ vcpu->arch.virtual_tsc_khz == kvm->arch.cur_tsc_khz) {
+ /*
+ * If synchronizing, advance the reference point to "now"
+ * so the matching window slides forward with each vCPU.
+ */
+ u64 delta = nsec_to_cycles(vcpu, elapsed);
+
+ data = kvm->arch.cur_tsc_write + delta;
+
+ if (!kvm_check_tsc_unstable())
offset = kvm->arch.cur_tsc_offset;
- } else {
- u64 delta = nsec_to_cycles(vcpu, elapsed);
- data += delta;
- offset = kvm_compute_l1_tsc_offset(vcpu, data);
- }
+ else
+ offset = kvm_compute_l1_tsc_offset(vcpu, host_tsc, data);
matched = true;
}
@@ -2907,141 +2977,44 @@ static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment)
#ifdef CONFIG_X86_64
-static u64 read_tsc(void)
+/*
+ * Calculates the kvmclock_base_ns (CLOCK_MONOTONIC_RAW + boot time) and
+ * reports the TSC value from which it do so. Returns true if host is
+ * using TSC based clocksource.
+ */
+static bool kvm_snapshot_has_tsc(struct system_time_snapshot *snap,
+ u64 *tsc_timestamp)
{
- u64 ret = (u64)rdtsc_ordered();
- u64 last = pvclock_gtod_data.clock.cycle_last;
-
- if (likely(ret >= last))
- return ret;
-
/*
- * GCC likes to generate cmov here, but this branch is extremely
- * predictable (it's just a function of time and the likely is
- * very likely) and there's a data dependence, so force GCC
- * to generate a branch instead. I don't barrier() because
- * we don't actually need a barrier, and if this function
- * ever gets inlined it will generate worse code.
+ * ktime_get_snapshot_id() cannot fail for standard clock IDs
+ * (only for invalid/aux clocks or during suspend, with a WARN).
*/
- asm volatile ("");
- return last;
-}
-
-static inline u64 vgettsc(struct pvclock_clock *clock, u64 *tsc_timestamp,
- int *mode)
-{
- u64 tsc_pg_val;
- long v;
+ if (!snap->valid)
+ return false;
- switch (clock->vclock_mode) {
- case VDSO_CLOCKMODE_HVCLOCK:
- if (hv_read_tsc_page_tsc(hv_get_tsc_page(),
- tsc_timestamp, &tsc_pg_val)) {
- /* TSC page valid */
- *mode = VDSO_CLOCKMODE_HVCLOCK;
- v = (tsc_pg_val - clock->cycle_last) &
- clock->mask;
- } else {
- /* TSC page invalid */
- *mode = VDSO_CLOCKMODE_NONE;
- }
- break;
- case VDSO_CLOCKMODE_TSC:
- *mode = VDSO_CLOCKMODE_TSC;
- *tsc_timestamp = read_tsc();
- v = (*tsc_timestamp - clock->cycle_last) &
- clock->mask;
- break;
- default:
- *mode = VDSO_CLOCKMODE_NONE;
+ if (snap->cs_id == CSID_X86_TSC) {
+ *tsc_timestamp = snap->cycles;
+ return true;
}
- if (*mode == VDSO_CLOCKMODE_NONE)
- *tsc_timestamp = v = 0;
-
- return v * clock->mult;
-}
-
-/*
- * As with get_kvmclock_base_ns(), this counts from boot time, at the
- * frequency of CLOCK_MONOTONIC_RAW (hence adding gtos->offs_boot).
- */
-static int do_kvmclock_base(s64 *t, u64 *tsc_timestamp)
-{
- struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
- unsigned long seq;
- int mode;
- u64 ns;
-
- do {
- seq = read_seqcount_begin(>od->seq);
- ns = gtod->raw_clock.base_cycles;
- ns += vgettsc(>od->raw_clock, tsc_timestamp, &mode);
- ns >>= gtod->raw_clock.shift;
- ns += ktime_to_ns(ktime_add(gtod->raw_clock.offset, gtod->offs_boot));
- } while (unlikely(read_seqcount_retry(>od->seq, seq)));
- *t = ns;
-
- return mode;
-}
-
-/*
- * This calculates CLOCK_MONOTONIC at the time of the TSC snapshot, with
- * no boot time offset.
- */
-static int do_monotonic(s64 *t, u64 *tsc_timestamp)
-{
- struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
- unsigned long seq;
- int mode;
- u64 ns;
-
- do {
- seq = read_seqcount_begin(>od->seq);
- ns = gtod->clock.base_cycles;
- ns += vgettsc(>od->clock, tsc_timestamp, &mode);
- ns >>= gtod->clock.shift;
- ns += ktime_to_ns(gtod->clock.offset);
- } while (unlikely(read_seqcount_retry(>od->seq, seq)));
- *t = ns;
-
- return mode;
-}
-
-static int do_realtime(struct timespec64 *ts, u64 *tsc_timestamp)
-{
- struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
- unsigned long seq;
- int mode;
- u64 ns;
-
- do {
- seq = read_seqcount_begin(>od->seq);
- ts->tv_sec = gtod->wall_time_sec;
- ns = gtod->clock.base_cycles;
- ns += vgettsc(>od->clock, tsc_timestamp, &mode);
- ns >>= gtod->clock.shift;
- } while (unlikely(read_seqcount_retry(>od->seq, seq)));
-
- ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
- ts->tv_nsec = ns;
+ if (snap->hw_csid == CSID_X86_TSC && snap->hw_cycles) {
+ *tsc_timestamp = snap->hw_cycles;
+ return true;
+ }
- return mode;
+ return false;
}
-/*
- * Calculates the kvmclock_base_ns (CLOCK_MONOTONIC_RAW + boot time) and
- * reports the TSC value from which it do so. Returns true if host is
- * using TSC based clocksource.
- */
static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)
{
- /* checked again under seqlock below */
- if (!gtod_is_based_on_tsc(pvclock_gtod_data.clock.vclock_mode))
+ struct system_time_snapshot snap = {};
+
+ ktime_get_snapshot_id(CLOCK_MONOTONIC_RAW, &snap);
+ if (!kvm_snapshot_has_tsc(&snap, tsc_timestamp))
return false;
- return gtod_is_based_on_tsc(do_kvmclock_base(kernel_ns,
- tsc_timestamp));
+ *kernel_ns = ktime_to_ns(ktime_mono_to_any(snap.systime, TK_OFFS_BOOT));
+ return true;
}
/*
@@ -3050,12 +3023,14 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)
*/
bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)
{
- /* checked again under seqlock below */
- if (!gtod_is_based_on_tsc(pvclock_gtod_data.clock.vclock_mode))
+ struct system_time_snapshot snap = {};
+
+ ktime_get_snapshot_id(CLOCK_MONOTONIC, &snap);
+ if (!kvm_snapshot_has_tsc(&snap, tsc_timestamp))
return false;
- return gtod_is_based_on_tsc(do_monotonic(kernel_ns,
- tsc_timestamp));
+ *kernel_ns = ktime_to_ns(snap.systime);
+ return true;
}
/*
@@ -3068,11 +3043,14 @@ bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp)
static bool kvm_get_walltime_and_clockread(struct timespec64 *ts,
u64 *tsc_timestamp)
{
- /* checked again under seqlock below */
- if (!gtod_is_based_on_tsc(pvclock_gtod_data.clock.vclock_mode))
+ struct system_time_snapshot snap = {};
+
+ ktime_get_snapshot_id(CLOCK_REALTIME, &snap);
+ if (!kvm_snapshot_has_tsc(&snap, tsc_timestamp))
return false;
- return gtod_is_based_on_tsc(do_realtime(ts, tsc_timestamp));
+ *ts = ktime_to_timespec64(snap.systime);
+ return true;
}
#endif
@@ -3117,16 +3095,15 @@ static bool kvm_get_walltime_and_clockread(struct timespec64 *ts,
*
*/
+static unsigned long get_cpu_tsc_khz(void);
+
static void pvclock_update_vm_gtod_copy(struct kvm *kvm)
{
#ifdef CONFIG_X86_64
struct kvm_arch *ka = &kvm->arch;
- int vclock_mode;
- bool host_tsc_clocksource, vcpus_matched;
+ bool host_tsc_clocksource;
lockdep_assert_held(&kvm->arch.tsc_write_lock);
- vcpus_matched = (ka->nr_vcpus_matched_tsc + 1 ==
- atomic_read(&kvm->online_vcpus));
/*
* If the host uses TSC clock, then passthrough TSC as stable
@@ -3136,16 +3113,36 @@ static void pvclock_update_vm_gtod_copy(struct kvm *kvm)
&ka->master_kernel_ns,
&ka->master_cycle_now);
- ka->use_master_clock = host_tsc_clocksource && vcpus_matched
- && !ka->backwards_tsc_observed
- && !ka->boot_vcpu_runs_old_kvmclock;
+ ka->use_master_clock = host_tsc_clocksource &&
+ kvm_use_master_clock(kvm);
+
+ if (ka->use_master_clock) {
+ u64 tsc_hz;
- if (ka->use_master_clock)
atomic_set(&kvm_guest_has_master_clock, 1);
- vclock_mode = pvclock_gtod_data.clock.vclock_mode;
- trace_kvm_update_master_clock(ka->use_master_clock, vclock_mode,
- vcpus_matched);
+ /*
+ * Copy the scaling ratio and precompute the mul/shift for
+ * converting guest TSC to nanoseconds. These are used by
+ * get_kvmclock() to compute kvmclock from the host TSC
+ * without needing a vCPU reference.
+ */
+ ka->master_tsc_scaling_ratio = ka->cur_tsc_scaling_ratio;
+ tsc_hz = (u64)get_cpu_tsc_khz() * 1000;
+ if (tsc_hz && kvm_caps.has_tsc_control)
+ tsc_hz = kvm_scale_tsc(tsc_hz,
+ ka->master_tsc_scaling_ratio);
+ if (tsc_hz)
+ kvm_get_time_scale(NSEC_PER_SEC, tsc_hz,
+ &ka->master_tsc_shift,
+ &ka->master_tsc_mul);
+ else
+ ka->use_master_clock = false;
+ }
+
+ trace_kvm_update_master_clock(ka->use_master_clock,
+ kvm_host_vclock_mode,
+ ka->all_vcpus_matched_freq);
#endif
}
@@ -3154,10 +3151,39 @@ static void kvm_make_mclock_inprogress_request(struct kvm *kvm)
kvm_make_all_cpus_request(kvm, KVM_REQ_MCLOCK_INPROGRESS);
}
-static void __kvm_start_pvclock_update(struct kvm *kvm)
+static void kvm_clear_mclock_inprogress_request(struct kvm *kvm)
{
+ struct kvm_vcpu *vcpu;
+ unsigned long i;
+
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_clear_request(KVM_REQ_MCLOCK_INPROGRESS, vcpu);
+}
+
+static bool __kvm_start_pvclock_update(struct kvm *kvm, struct kvm_vcpu *requesting_vcpu)
+{
+ struct kvm_vcpu *vcpu;
+ unsigned long i;
+
raw_spin_lock_irq(&kvm->arch.tsc_write_lock);
+
+ /*
+ * If another vCPU already did the update while we were waiting
+ * for the lock, our request will have been cleared. Bail out.
+ */
+ if (requesting_vcpu &&
+ !kvm_test_request(KVM_REQ_MASTERCLOCK_UPDATE, requesting_vcpu)) {
+ kvm_clear_mclock_inprogress_request(kvm);
+ raw_spin_unlock_irq(&kvm->arch.tsc_write_lock);
+ return false;
+ }
+
+ /* The update is VM-wide; prevent other vCPUs from redoing it. */
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_clear_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu);
+
write_seqcount_begin(&kvm->arch.pvclock_sc);
+ return true;
}
static void kvm_start_pvclock_update(struct kvm *kvm)
@@ -3165,7 +3191,7 @@ static void kvm_start_pvclock_update(struct kvm *kvm)
kvm_make_mclock_inprogress_request(kvm);
/* no guest entries from this point */
- __kvm_start_pvclock_update(kvm);
+ __kvm_start_pvclock_update(kvm, NULL);
}
static void kvm_end_pvclock_update(struct kvm *kvm)
@@ -3174,22 +3200,25 @@ static void kvm_end_pvclock_update(struct kvm *kvm)
struct kvm_vcpu *vcpu;
unsigned long i;
- write_seqcount_end(&ka->pvclock_sc);
- raw_spin_unlock_irq(&ka->tsc_write_lock);
kvm_for_each_vcpu(i, vcpu, kvm)
kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
/* guest entries allowed */
- kvm_for_each_vcpu(i, vcpu, kvm)
- kvm_clear_request(KVM_REQ_MCLOCK_INPROGRESS, vcpu);
+ kvm_clear_mclock_inprogress_request(kvm);
+
+ write_seqcount_end(&ka->pvclock_sc);
+ raw_spin_unlock_irq(&ka->tsc_write_lock);
}
-static void kvm_update_masterclock(struct kvm *kvm)
+static void kvm_update_masterclock(struct kvm *kvm, struct kvm_vcpu *vcpu)
{
kvm_hv_request_tsc_page_update(kvm);
- kvm_start_pvclock_update(kvm);
- pvclock_update_vm_gtod_copy(kvm);
- kvm_end_pvclock_update(kvm);
+ kvm_make_mclock_inprogress_request(kvm);
+
+ if (__kvm_start_pvclock_update(kvm, vcpu)) {
+ pvclock_update_vm_gtod_copy(kvm);
+ kvm_end_pvclock_update(kvm);
+ }
}
/*
@@ -3208,50 +3237,59 @@ static unsigned long get_cpu_tsc_khz(void)
return __this_cpu_read(cpu_tsc_khz);
}
-/* Called within read_seqcount_begin/retry for kvm->pvclock_sc. */
-static void __get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)
+static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)
{
struct kvm_arch *ka = &kvm->arch;
- struct pvclock_vcpu_time_info hv_clock;
+ unsigned int seq;
- /* both __this_cpu_read() and rdtsc() should be on the same cpu */
- get_cpu();
+ do {
+ seq = read_seqcount_begin(&ka->pvclock_sc);
- data->flags = 0;
- if (ka->use_master_clock &&
- (static_cpu_has(X86_FEATURE_CONSTANT_TSC) || __this_cpu_read(cpu_tsc_khz))) {
+ data->flags = 0;
#ifdef CONFIG_X86_64
- struct timespec64 ts;
+ if (ka->use_master_clock) {
+ struct pvclock_vcpu_time_info hv_clock;
+ struct timespec64 ts;
- if (kvm_get_walltime_and_clockread(&ts, &data->host_tsc)) {
- data->realtime = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec;
- data->flags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC;
- } else
-#endif
- data->host_tsc = rdtsc();
+ if (kvm_get_walltime_and_clockread(&ts, &data->host_tsc)) {
+ data->realtime = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec;
+ data->flags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC | KVM_CLOCK_TSC_STABLE;
- data->flags |= KVM_CLOCK_TSC_STABLE;
- hv_clock.tsc_timestamp = ka->master_cycle_now;
- hv_clock.system_time = ka->master_kernel_ns + ka->kvmclock_offset;
- kvm_get_time_scale(NSEC_PER_SEC, get_cpu_tsc_khz() * 1000LL,
- &hv_clock.tsc_shift,
- &hv_clock.tsc_to_system_mul);
- data->clock = __pvclock_read_cycles(&hv_clock, data->host_tsc);
- } else {
- data->clock = get_kvmclock_base_ns() + ka->kvmclock_offset;
- }
-
- put_cpu();
-}
+ hv_clock.tsc_timestamp = ka->master_cycle_now;
+ hv_clock.system_time = ka->master_kernel_ns + ka->kvmclock_offset;
-static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)
-{
- struct kvm_arch *ka = &kvm->arch;
- unsigned seq;
+ /*
+ * Use the precomputed guest-TSC-based mul/shift
+ * so that the kvmclock value matches what the
+ * guest computes from its own TSC.
+ */
+ hv_clock.tsc_shift = ka->master_tsc_shift;
+ hv_clock.tsc_to_system_mul = ka->master_tsc_mul;
+
+ if (kvm_caps.has_tsc_control) {
+ u64 tsc_delta = data->host_tsc - ka->master_cycle_now;
+
+ tsc_delta = kvm_scale_tsc(tsc_delta,
+ ka->master_tsc_scaling_ratio);
+ data->clock = hv_clock.system_time +
+ pvclock_scale_delta(tsc_delta,
+ hv_clock.tsc_to_system_mul,
+ hv_clock.tsc_shift);
+ } else {
+ data->clock = __pvclock_read_cycles(&hv_clock,
+ data->host_tsc);
+ }
+ continue;
+ }
- do {
- seq = read_seqcount_begin(&ka->pvclock_sc);
- __get_kvmclock(kvm, data);
+ /*
+ * Clock read failed (e.g. clocksource is transitioning
+ * away from TSC). Fall back to the non-master-clock path
+ * rather than spinning.
+ */
+ }
+#endif
+ data->clock = get_kvmclock_base_ns() + ka->kvmclock_offset;
} while (read_seqcount_retry(&ka->pvclock_sc, seq));
}
@@ -3314,44 +3352,60 @@ static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock,
int kvm_guest_time_update(struct kvm_vcpu *v)
{
struct pvclock_vcpu_time_info hv_clock = {};
- unsigned long flags, tgt_tsc_khz;
+ u64 tgt_tsc_hz;
unsigned seq;
struct kvm_vcpu_arch *vcpu = &v->arch;
struct kvm_arch *ka = &v->kvm->arch;
s64 kernel_ns;
u64 tsc_timestamp, host_tsc;
+ u64 master_host_tsc = 0;
+ s64 master_kernel_ns = 0;
+ s64 kvmclock_offset = 0;
bool use_master_clock;
- kernel_ns = 0;
- host_tsc = 0;
-
/*
* If the host uses TSC clock, then passthrough TSC as stable
* to the guest.
*/
do {
seq = read_seqcount_begin(&ka->pvclock_sc);
+
use_master_clock = ka->use_master_clock;
+
+ /*
+ * The TSC read and the call to get_cpu_tsc_khz() must happen
+ * on the same CPU.
+ */
+ get_cpu();
+
+ tgt_tsc_hz = (u64)get_cpu_tsc_khz() * 1000;
+
+#ifdef CONFIG_X86_64
+ if (use_master_clock &&
+ !kvm_get_time_and_clockread(&kernel_ns, &host_tsc) &&
+ !read_seqcount_retry(&ka->pvclock_sc, seq))
+ use_master_clock = false;
+#endif
+
+ put_cpu();
+
if (use_master_clock) {
- host_tsc = ka->master_cycle_now;
- kernel_ns = ka->master_kernel_ns;
+ master_host_tsc = ka->master_cycle_now;
+ master_kernel_ns = ka->master_kernel_ns;
+ } else {
+ local_irq_disable();
+ host_tsc = rdtsc();
+ kernel_ns = get_kvmclock_base_ns();
+ local_irq_enable();
}
+
+ kvmclock_offset = ka->kvmclock_offset;
} while (read_seqcount_retry(&ka->pvclock_sc, seq));
- /* Keep irq disabled to prevent changes to the clock */
- local_irq_save(flags);
- tgt_tsc_khz = get_cpu_tsc_khz();
- if (unlikely(tgt_tsc_khz == 0)) {
- local_irq_restore(flags);
+ if (unlikely(tgt_tsc_hz == 0)) {
kvm_make_request(KVM_REQ_CLOCK_UPDATE, v);
return 1;
}
- if (!use_master_clock) {
- host_tsc = rdtsc();
- kernel_ns = get_kvmclock_base_ns();
- }
-
- tsc_timestamp = kvm_read_l1_tsc(v, host_tsc);
/*
* We may have to catch up the TSC to match elapsed wall clock
@@ -3361,42 +3415,57 @@ int kvm_guest_time_update(struct kvm_vcpu *v)
* entry to avoid unknown leaps of TSC even when running
* again on the same CPU. This may cause apparent elapsed
* time to disappear, and the guest to stand still or run
- * very slowly.
+ * very slowly.
*/
if (vcpu->tsc_catchup) {
- u64 tsc = compute_guest_tsc(v, kernel_ns);
- if (tsc > tsc_timestamp) {
- adjust_tsc_offset_guest(v, tsc - tsc_timestamp);
- tsc_timestamp = tsc;
- }
+ s64 adjustment;
+
+ /*
+ * Calculate the delta between what the guest TSC *should* be
+ * and what it actually is according to kvm_read_l1_tsc().
+ */
+ adjustment = compute_guest_tsc(v, kernel_ns) -
+ kvm_read_l1_tsc(v, host_tsc);
+ if (adjustment > 0)
+ adjust_tsc_offset_guest(v, adjustment);
}
- local_irq_restore(flags);
+ /*
+ * Now that TSC upscaling is out of the way, the remaining calculations
+ * are all relative to the reference time that's placed in hv_clock.
+ * If the master clock is NOT in use, the reference time is "now". If
+ * master clock is in use, the reference time comes from there.
+ */
+ if (use_master_clock) {
+ host_tsc = master_host_tsc;
+ kernel_ns = master_kernel_ns;
+ }
+ tsc_timestamp = kvm_read_l1_tsc(v, host_tsc);
/* With all the info we got, fill in the values */
if (kvm_caps.has_tsc_control) {
- tgt_tsc_khz = kvm_scale_tsc(tgt_tsc_khz,
+ tgt_tsc_hz = kvm_scale_tsc(tgt_tsc_hz,
v->arch.l1_tsc_scaling_ratio);
- tgt_tsc_khz = tgt_tsc_khz ? : 1;
+ tgt_tsc_hz = tgt_tsc_hz ? : 1;
}
- if (unlikely(vcpu->hw_tsc_khz != tgt_tsc_khz)) {
- kvm_get_time_scale(NSEC_PER_SEC, tgt_tsc_khz * 1000LL,
+ if (unlikely(vcpu->hw_tsc_hz != tgt_tsc_hz)) {
+ kvm_get_time_scale(NSEC_PER_SEC, tgt_tsc_hz,
&vcpu->pvclock_tsc_shift,
&vcpu->pvclock_tsc_mul);
- vcpu->hw_tsc_khz = tgt_tsc_khz;
+ vcpu->hw_tsc_hz = tgt_tsc_hz;
}
hv_clock.tsc_shift = vcpu->pvclock_tsc_shift;
hv_clock.tsc_to_system_mul = vcpu->pvclock_tsc_mul;
hv_clock.tsc_timestamp = tsc_timestamp;
- hv_clock.system_time = kernel_ns + v->kvm->arch.kvmclock_offset;
+ hv_clock.system_time = kernel_ns + kvmclock_offset;
vcpu->last_guest_tsc = tsc_timestamp;
/* If the host uses TSC clocksource, then it is stable */
hv_clock.flags = 0;
- if (use_master_clock)
+ if (use_master_clock && ka->all_vcpus_matched_tsc)
hv_clock.flags |= PVCLOCK_TSC_STABLE_BIT;
if (vcpu->pv_time.active) {
@@ -3454,63 +3523,22 @@ int kvm_guest_time_update(struct kvm_vcpu *v)
* wallclock and kvmclock times, and subtracting one from the other.
*
* Fall back to using their values at slightly different moments by
- * calling ktime_get_real_ns() and get_kvmclock_ns() separately.
+ * calling ktime_get_real_ns() and get_kvmclock() separately.
*/
uint64_t kvm_get_wall_clock_epoch(struct kvm *kvm)
{
-#ifdef CONFIG_X86_64
- struct pvclock_vcpu_time_info hv_clock;
- struct kvm_arch *ka = &kvm->arch;
- unsigned long seq, local_tsc_khz;
- struct timespec64 ts;
- uint64_t host_tsc;
-
- do {
- seq = read_seqcount_begin(&ka->pvclock_sc);
-
- local_tsc_khz = 0;
- if (!ka->use_master_clock)
- break;
-
- /*
- * The TSC read and the call to get_cpu_tsc_khz() must happen
- * on the same CPU.
- */
- get_cpu();
-
- local_tsc_khz = get_cpu_tsc_khz();
-
- if (local_tsc_khz &&
- !kvm_get_walltime_and_clockread(&ts, &host_tsc))
- local_tsc_khz = 0; /* Fall back to old method */
-
- put_cpu();
-
- /*
- * These values must be snapshotted within the seqcount loop.
- * After that, it's just mathematics which can happen on any
- * CPU at any time.
- */
- hv_clock.tsc_timestamp = ka->master_cycle_now;
- hv_clock.system_time = ka->master_kernel_ns + ka->kvmclock_offset;
+ struct kvm_clock_data data;
- } while (read_seqcount_retry(&ka->pvclock_sc, seq));
+ get_kvmclock(kvm, &data);
/*
- * If the conditions were right, and obtaining the wallclock+TSC was
- * successful, calculate the KVM clock at the corresponding time and
- * subtract one from the other to get the guest's epoch in nanoseconds
- * since 1970-01-01.
+ * If get_kvmclock() captured both wallclock and kvmclock from the
+ * same TSC reading, use them for a precise epoch calculation.
*/
- if (local_tsc_khz) {
- kvm_get_time_scale(NSEC_PER_SEC, local_tsc_khz * NSEC_PER_USEC,
- &hv_clock.tsc_shift,
- &hv_clock.tsc_to_system_mul);
- return ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec -
- __pvclock_read_cycles(&hv_clock, host_tsc);
- }
-#endif
- return ktime_get_real_ns() - get_kvmclock_ns(kvm);
+ if (data.flags & KVM_CLOCK_REALTIME)
+ return data.realtime - data.clock;
+
+ return ktime_get_real_ns() - data.clock;
}
/*
@@ -4089,7 +4117,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
if (msr_info->host_initiated) {
kvm_synchronize_tsc(vcpu, &data);
} else if (!vcpu->arch.guest_tsc_protected) {
- u64 adj = kvm_compute_l1_tsc_offset(vcpu, data) - vcpu->arch.l1_tsc_offset;
+ u64 adj = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data) -
+ vcpu->arch.l1_tsc_offset;
adjust_tsc_offset_guest(vcpu, adj);
vcpu->arch.ia32_tsc_adjust_msr += adj;
}
@@ -5213,7 +5242,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
mark_tsc_unstable("KVM discovered backwards TSC");
if (kvm_check_tsc_unstable()) {
- u64 offset = kvm_compute_l1_tsc_offset(vcpu,
+ u64 offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(),
vcpu->arch.last_guest_tsc);
kvm_vcpu_write_tsc_offset(vcpu, offset);
if (!vcpu->arch.guest_tsc_protected)
@@ -5227,13 +5256,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
* On a host with synchronized TSC, there is no need to update
* kvmclock on vcpu->cpu migration
*/
- if (!vcpu->kvm->arch.use_master_clock || vcpu->cpu == -1) {
+ if (!vcpu->kvm->arch.use_master_clock) {
if (__ratelimit(&vcpu->kvm->arch.kvmclock_update_rs))
kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu);
else
kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
- }
-
+ } else if (vcpu->cpu == -1)
+ kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
if (vcpu->cpu != cpu)
kvm_make_request(KVM_REQ_MIGRATE_TIMER, vcpu);
vcpu->cpu = cpu;
@@ -5934,6 +5963,9 @@ static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,
case KVM_VCPU_TSC_OFFSET:
r = 0;
break;
+ case KVM_VCPU_TSC_SCALE:
+ r = kvm_caps.has_tsc_control ? 0 : -ENXIO;
+ break;
default:
r = -ENXIO;
}
@@ -5954,6 +5986,21 @@ static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,
break;
r = 0;
break;
+ case KVM_VCPU_TSC_SCALE: {
+ struct kvm_vcpu_tsc_scale scale;
+
+ r = -ENXIO;
+ if (!kvm_caps.has_tsc_control)
+ break;
+
+ scale.tsc_ratio = vcpu->arch.l1_tsc_scaling_ratio;
+ scale.tsc_frac_bits = kvm_caps.tsc_scaling_ratio_frac_bits;
+ r = -EFAULT;
+ if (copy_to_user(uaddr, &scale, sizeof(scale)))
+ break;
+ r = 0;
+ break;
+ }
default:
r = -ENXIO;
}
@@ -5981,8 +6028,8 @@ static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,
raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
matched = (vcpu->arch.virtual_tsc_khz &&
- kvm->arch.last_tsc_khz == vcpu->arch.virtual_tsc_khz &&
- kvm->arch.last_tsc_offset == offset);
+ kvm->arch.cur_tsc_khz == vcpu->arch.virtual_tsc_khz &&
+ kvm->arch.cur_tsc_offset == offset);
tsc = kvm_scale_tsc(rdtsc(), vcpu->arch.l1_tsc_scaling_ratio) + offset;
ns = get_kvmclock_base_ns();
@@ -5993,6 +6040,9 @@ static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,
r = 0;
break;
}
+ case KVM_VCPU_TSC_SCALE:
+ r = kvm_caps.has_tsc_control ? -EINVAL : -ENXIO;
+ break;
default:
r = -ENXIO;
}
@@ -6209,6 +6259,169 @@ static int kvm_get_reg_list(struct kvm_vcpu *vcpu,
return 0;
}
+#ifdef CONFIG_X86_64
+static int kvm_vcpu_ioctl_get_clock_guest(struct kvm_vcpu *v, void __user *argp)
+{
+ struct pvclock_vcpu_time_info hv_clock = {};
+ struct kvm_vcpu_arch *vcpu = &v->arch;
+ struct kvm_arch *ka = &v->kvm->arch;
+ unsigned int seq;
+
+ /*
+ * If KVM_REQ_CLOCK_UPDATE is already pending, or if the pvclock
+ * has never been generated at all, call kvm_guest_time_update().
+ */
+ if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, v) || !vcpu->hw_tsc_hz) {
+ int idx = srcu_read_lock(&v->kvm->srcu);
+ int ret = kvm_guest_time_update(v);
+
+ srcu_read_unlock(&v->kvm->srcu, idx);
+ if (ret)
+ return -EINVAL;
+ }
+
+ /*
+ * Reconstruct the pvclock from the master clock state, matching
+ * exactly what kvm_guest_time_update() writes to the guest.
+ */
+ do {
+ seq = read_seqcount_begin(&ka->pvclock_sc);
+
+ if (!ka->use_master_clock)
+ return -EINVAL;
+
+ hv_clock.tsc_timestamp = kvm_read_l1_tsc(v, ka->master_cycle_now);
+ hv_clock.system_time = ka->master_kernel_ns + ka->kvmclock_offset;
+ } while (read_seqcount_retry(&ka->pvclock_sc, seq));
+
+ hv_clock.tsc_shift = vcpu->pvclock_tsc_shift;
+ hv_clock.tsc_to_system_mul = vcpu->pvclock_tsc_mul;
+ hv_clock.flags = ka->all_vcpus_matched_tsc ? PVCLOCK_TSC_STABLE_BIT : 0;
+
+ if (copy_to_user(argp, &hv_clock, sizeof(hv_clock)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Reverse the calculation in the hv_clock definition.
+ *
+ * time_ns = ( (cycles << shift) * mul ) >> 32;
+ * (although shift can be negative, so that's bad C)
+ *
+ * So for a single second,
+ * NSEC_PER_SEC = ( ( FREQ_HZ << shift) * mul ) >> 32
+ * NSEC_PER_SEC << 32 = ( FREQ_HZ << shift ) * mul
+ * ( NSEC_PER_SEC << 32 ) / mul = FREQ_HZ << shift
+ * ( NSEC_PER_SEC << 32 ) / mul ) >> shift = FREQ_HZ
+ */
+static u64 hvclock_to_hz(u32 mul, s8 shift)
+{
+ u64 tm = NSEC_PER_SEC << 32;
+
+ /* Maximise precision. Shift right until the top bit is set */
+ tm <<= 2;
+ shift += 2;
+
+ /* While 'mul' is even, increase the shift *after* the division */
+ while (!(mul & 1)) {
+ shift++;
+ mul >>= 1;
+ }
+
+ tm /= mul;
+
+ if (shift >= 64)
+ return 0;
+ if (shift > 0)
+ return tm >> shift;
+ if (shift <= -64)
+ return 0;
+ return tm << -shift;
+}
+
+static int kvm_vcpu_ioctl_set_clock_guest(struct kvm_vcpu *v, void __user *argp)
+{
+ struct pvclock_vcpu_time_info user_hv_clock;
+ struct kvm *kvm = v->kvm;
+ struct kvm_arch *ka = &kvm->arch;
+ u64 curr_tsc_hz, user_tsc_hz;
+ u64 user_clk_ns;
+ u64 guest_tsc;
+ int rc = 0;
+
+ if (copy_from_user(&user_hv_clock, argp, sizeof(user_hv_clock)))
+ return -EFAULT;
+
+ if (user_hv_clock.pad0 || user_hv_clock.pad[0] || user_hv_clock.pad[1])
+ return -EINVAL;
+
+ if (!user_hv_clock.tsc_to_system_mul)
+ return -EINVAL;
+
+ if (user_hv_clock.tsc_shift < -31 || user_hv_clock.tsc_shift > 31)
+ return -EINVAL;
+
+ user_tsc_hz = hvclock_to_hz(user_hv_clock.tsc_to_system_mul,
+ user_hv_clock.tsc_shift);
+
+ kvm_hv_request_tsc_page_update(kvm);
+
+ /*
+ * kvm_start_pvclock_update() takes tsc_write_lock and opens
+ * the pvclock seqcount; kvm_end_pvclock_update() closes both.
+ * All clock state modifications between them are atomic with
+ * respect to readers in kvm_guest_time_update().
+ */
+ kvm_start_pvclock_update(kvm);
+ pvclock_update_vm_gtod_copy(kvm);
+
+ if (!ka->use_master_clock) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ curr_tsc_hz = (u64)get_cpu_tsc_khz() * 1000;
+ if (unlikely(curr_tsc_hz == 0)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (kvm_caps.has_tsc_control)
+ curr_tsc_hz = kvm_scale_tsc(curr_tsc_hz,
+ v->arch.l1_tsc_scaling_ratio);
+
+ /*
+ * Allow for a discrepancy of 1 kHz either way between the TSC
+ * frequency used to generate the user's pvclock and the current
+ * host's measured frequency, since they may not precisely match.
+ */
+ if (user_tsc_hz < curr_tsc_hz - 1000 ||
+ user_tsc_hz > curr_tsc_hz + 1000) {
+ rc = -ERANGE;
+ goto out;
+ }
+
+ /*
+ * Calculate the guest TSC at the new reference point, and the
+ * corresponding KVM clock value according to user_hv_clock.
+ * Adjust kvmclock_offset so both definitions agree.
+ */
+ guest_tsc = kvm_read_l1_tsc(v, ka->master_cycle_now);
+ if (guest_tsc < user_hv_clock.tsc_timestamp) {
+ rc = -EINVAL;
+ goto out;
+ }
+ user_clk_ns = __pvclock_read_cycles(&user_hv_clock, guest_tsc);
+ ka->kvmclock_offset = user_clk_ns - ka->master_kernel_ns;
+
+out:
+ kvm_end_pvclock_update(kvm);
+ return rc;
+}
+#endif
+
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -6609,6 +6822,14 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
srcu_read_unlock(&vcpu->kvm->srcu, idx);
break;
}
+#ifdef CONFIG_X86_64
+ case KVM_SET_CLOCK_GUEST:
+ r = kvm_vcpu_ioctl_set_clock_guest(vcpu, argp);
+ break;
+ case KVM_GET_CLOCK_GUEST:
+ r = kvm_vcpu_ioctl_get_clock_guest(vcpu, argp);
+ break;
+#endif
#ifdef CONFIG_KVM_HYPERV
case KVM_GET_SUPPORTED_HV_CPUID:
r = kvm_ioctl_get_supported_hv_cpuid(vcpu, argp);
@@ -9824,7 +10045,7 @@ static void kvm_hyperv_tsc_notifier(void)
kvm_caps.max_guest_tsc_khz = tsc_khz;
list_for_each_entry(kvm, &vm_list, vm_list) {
- __kvm_start_pvclock_update(kvm);
+ __kvm_start_pvclock_update(kvm, NULL);
pvclock_update_vm_gtod_copy(kvm);
kvm_end_pvclock_update(kvm);
}
@@ -9991,6 +10212,7 @@ static void pvclock_irq_work_fn(struct irq_work *w)
}
static DEFINE_IRQ_WORK(pvclock_irq_work, pvclock_irq_work_fn);
+#endif
/*
* Notification about pvclock gtod data update.
@@ -9998,26 +10220,27 @@ static DEFINE_IRQ_WORK(pvclock_irq_work, pvclock_irq_work_fn);
static int pvclock_gtod_notify(struct notifier_block *nb, unsigned long unused,
void *priv)
{
- struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
+#ifdef CONFIG_X86_64
struct timekeeper *tk = priv;
- update_pvclock_gtod(tk);
+ kvm_host_vclock_mode =
+ tk->tkr_mono.clock->vdso_clock_mode;
/*
* Disable master clock if host does not trust, or does not use,
* TSC based clocksource. Delegate queue_work() to irq_work as
* this is invoked with tk_core.seq write held.
*/
- if (!gtod_is_based_on_tsc(gtod->clock.vclock_mode) &&
+ if (!gtod_is_based_on_tsc(kvm_host_vclock_mode) &&
atomic_read(&kvm_guest_has_master_clock) != 0)
irq_work_queue(&pvclock_irq_work);
+#endif
return 0;
}
static struct notifier_block pvclock_gtod_notifier = {
.notifier_call = pvclock_gtod_notify,
};
-#endif
void kvm_setup_xss_caps(void)
{
@@ -10206,9 +10429,9 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
if (pi_inject_timer == -1)
pi_inject_timer = housekeeping_enabled(HK_TYPE_TIMER);
-#ifdef CONFIG_X86_64
pvclock_gtod_register_notifier(&pvclock_gtod_notifier);
+#ifdef CONFIG_X86_64
if (hypervisor_is_type(X86_HYPER_MS_HYPERV))
set_hv_tscchange_cb(kvm_hyperv_tsc_notifier);
#endif
@@ -10265,8 +10488,8 @@ void kvm_x86_vendor_exit(void)
CPUFREQ_TRANSITION_NOTIFIER);
cpuhp_remove_state_nocalls(CPUHP_AP_X86_KVM_CLK_ONLINE);
}
-#ifdef CONFIG_X86_64
pvclock_gtod_unregister_notifier(&pvclock_gtod_notifier);
+#ifdef CONFIG_X86_64
irq_work_sync(&pvclock_irq_work);
cancel_work_sync(&pvclock_gtod_work);
#endif
@@ -11201,8 +11424,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
kvm_mmu_free_obsolete_roots(vcpu);
if (kvm_check_request(KVM_REQ_MIGRATE_TIMER, vcpu))
__kvm_migrate_timers(vcpu);
- if (kvm_check_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu))
- kvm_update_masterclock(vcpu->kvm);
+ if (kvm_test_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu))
+ kvm_update_masterclock(vcpu->kvm, vcpu);
if (kvm_check_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu))
kvm_gen_kvmclock_update(vcpu);
if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu)) {
@@ -13187,7 +13410,7 @@ int kvm_arch_enable_virtualization_cpu(void)
{
struct kvm *kvm;
struct kvm_vcpu *vcpu;
- unsigned long i;
+ unsigned long i, flags;
int ret;
u64 local_tsc;
u64 max_tsc = 0;
@@ -13266,13 +13489,18 @@ int kvm_arch_enable_virtualization_cpu(void)
}
/*
- * We have to disable TSC offset matching.. if you were
- * booting a VM while issuing an S4 host suspend....
- * you may have some problem. Solving this issue is
- * left as an exercise to the reader.
+ * Adjust the TSC matching reference by the same
+ * delta applied to each vCPU's offset, so that
+ * future KVM_SET_TSC / vCPU creation still matches
+ * correctly against the adjusted TSC timeline.
+ * Scale from host to guest TSC rate.
*/
- kvm->arch.last_tsc_nsec = 0;
- kvm->arch.last_tsc_write = 0;
+ raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
+ kvm->arch.cur_tsc_write -=
+ kvm_scale_tsc(delta_cyc,
+ kvm->arch.cur_tsc_scaling_ratio);
+ kvm->arch.cur_tsc_offset += delta_cyc;
+ raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags);
}
}
@@ -13342,6 +13570,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
int ret;
unsigned long flags;
+ u64 kvmclock_host_tsc;
+ s64 kvmclock_ns;
if (!kvm_is_vm_type_supported(type))
return -EINVAL;
@@ -13373,17 +13603,47 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
seqcount_raw_spinlock_init(&kvm->arch.pvclock_sc, &kvm->arch.tsc_write_lock);
ratelimit_state_init(&kvm->arch.kvmclock_update_rs, HZ, 10);
ratelimit_set_flags(&kvm->arch.kvmclock_update_rs, RATELIMIT_MSG_ON_RELEASE);
- kvm->arch.kvmclock_offset = -get_kvmclock_base_ns();
-
- raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
- pvclock_update_vm_gtod_copy(kvm);
- raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags);
kvm->arch.default_tsc_khz = max_tsc_khz ? : tsc_khz;
kvm->arch.apic_bus_cycle_ns = APIC_BUS_CYCLE_NS_DEFAULT;
kvm->arch.guest_can_read_msr_platform_info = true;
kvm->arch.enable_pmu = enable_pmu;
+ kvm->arch.all_vcpus_matched_freq = true;
+ kvm->arch.all_vcpus_matched_tsc = true;
+ kvm->arch.cur_tsc_scaling_ratio = kvm_caps.default_tsc_scaling_ratio;
+ kvm->arch.cur_tsc_khz = kvm->arch.default_tsc_khz;
+
+ raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
+
+ /*
+ * Establish the initial TSC generation for synchronization logic.
+ * The first vCPU will sync to this, starting its guest TSC at the
+ * number of cycles elapsed since VM creation rather than at zero.
+ * This ensures coherence with the kvmclock epoch (also set here)
+ * and means the master clock snapshot taken below is immediately
+ * valid — no need to redo it at first vCPU creation.
+ */
+#ifdef CONFIG_X86_64
+ if (!kvm_get_time_and_clockread(&kvmclock_ns, &kvmclock_host_tsc))
+#endif
+ {
+ /*
+ * If not simultaneous, we want the TSC to be zero slightly
+ * *after* the kvmclock is zero.
+ */
+ kvmclock_ns = get_kvmclock_base_ns();
+ kvmclock_host_tsc = rdtsc();
+ }
+ kvm->arch.cur_tsc_write = 0;
+ kvm->arch.cur_tsc_offset = -(s64)kvmclock_host_tsc;
+ kvm->arch.cur_tsc_nsec = kvmclock_ns;
+ kvm->arch.cur_tsc_generation = 1;
+ kvm->arch.kvmclock_offset = -kvmclock_ns;
+
+ pvclock_update_vm_gtod_copy(kvm);
+ raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags);
+
#if IS_ENABLED(CONFIG_HYPERV)
spin_lock_init(&kvm->arch.hv_root_tdp_lock);
kvm->arch.hv_root_tdp = INVALID_PAGE;
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index 91fd3673c09a2e..b1d67ece5db301 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -98,8 +98,6 @@ static int kvm_xen_shared_info_init(struct kvm *kvm)
wc->version = wc_version + 1;
read_unlock_irq(&gpc->lock);
- kvm_make_all_cpus_request(kvm, KVM_REQ_MASTERCLOCK_UPDATE);
-
out:
srcu_read_unlock(&kvm->srcu, idx);
return ret;
@@ -588,29 +586,45 @@ void kvm_xen_update_runstate(struct kvm_vcpu *v, int state)
{
struct kvm_vcpu_xen *vx = &v->arch.xen;
u64 now = get_kvmclock_ns(v->kvm);
- u64 delta_ns = now - vx->runstate_entry_time;
u64 run_delay = current->sched_info.run_delay;
+ s64 delta_ns = now - vx->runstate_entry_time;
+ s64 steal_ns = run_delay - vx->last_steal;
+ /*
+ * If the vCPU was never run before, its prior state should
+ * be considered RUNSTATE_offline.
+ */
if (unlikely(!vx->runstate_entry_time))
vx->current_runstate = RUNSTATE_offline;
+ /*
+ * If KVM clock went backwards, just update the current runstate
+ * but don't account any time. Leave entry_time unchanged so the
+ * next positive delta covers the full period once the clock
+ * catches up. Update last_steal every time so stolen time only
+ * reflects the interval since the most recent call.
+ */
+ if (delta_ns < 0)
+ goto update_guest;
+
/*
* Time waiting for the scheduler isn't "stolen" if the
* vCPU wasn't running anyway.
*/
- if (vx->current_runstate == RUNSTATE_running) {
- u64 steal_ns = run_delay - vx->last_steal;
+ if (vx->current_runstate == RUNSTATE_running && steal_ns > 0) {
+ if (steal_ns > delta_ns)
+ steal_ns = delta_ns;
delta_ns -= steal_ns;
-
vx->runstate_times[RUNSTATE_runnable] += steal_ns;
}
- vx->last_steal = run_delay;
vx->runstate_times[vx->current_runstate] += delta_ns;
- vx->current_runstate = state;
vx->runstate_entry_time = now;
+ update_guest:
+ vx->current_runstate = state;
+ vx->last_steal = run_delay;
if (vx->runstate_cache.active)
kvm_xen_update_runstate_guest(v, state == RUNSTATE_runnable);
}
diff --git a/arch/x86/kvm/xen.h b/arch/x86/kvm/xen.h
index 59e6128a7bd3c2..f372855857a889 100644
--- a/arch/x86/kvm/xen.h
+++ b/arch/x86/kvm/xen.h
@@ -50,14 +50,6 @@ static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
kvm_xen_inject_vcpu_vector(vcpu);
}
-static inline bool kvm_xen_is_tsc_leaf(struct kvm_vcpu *vcpu, u32 function)
-{
- return static_branch_unlikely(&kvm_xen_enabled.key) &&
- vcpu->arch.xen.cpuid.base &&
- function <= vcpu->arch.xen.cpuid.limit &&
- function == (vcpu->arch.xen.cpuid.base | XEN_CPUID_LEAF(3));
-}
-
static inline bool kvm_xen_msr_enabled(struct kvm *kvm)
{
return static_branch_unlikely(&kvm_xen_enabled.key) &&
@@ -177,11 +169,6 @@ static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)
{
return false;
}
-
-static inline bool kvm_xen_is_tsc_leaf(struct kvm_vcpu *vcpu, u32 function)
-{
- return false;
-}
#endif
int kvm_xen_hypercall(struct kvm_vcpu *vcpu);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6c8afa2047bf33..9b50191b859cd7 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1669,4 +1669,7 @@ struct kvm_pre_fault_memory {
__u64 padding[5];
};
+#define KVM_SET_CLOCK_GUEST _IOW(KVMIO, 0xd6, struct pvclock_vcpu_time_info)
+#define KVM_GET_CLOCK_GUEST _IOR(KVMIO, 0xd7, struct pvclock_vcpu_time_info)
+
#endif /* __LINUX_KVM_H */
diff --git a/scripts/xen-hypercalls.sh b/scripts/xen-hypercalls.sh
index f18b00843df3ff..51a722198997da 100755
--- a/scripts/xen-hypercalls.sh
+++ b/scripts/xen-hypercalls.sh
@@ -5,7 +5,7 @@ shift
in="$@"
for i in $in; do
- eval $CPP $LINUXINCLUDE -dD -imacros "$i" -x c /dev/null
+ eval $CPP -D__KERNEL__ $LINUXINCLUDE -dD -imacros "$i" -x c /dev/null
done | \
awk '$1 == "#define" && $2 ~ /__HYPERVISOR_[a-z][a-z_0-9]*/ { v[$3] = $2 }
END { print "/* auto-generated by scripts/xen-hypercall.sh */"
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 9118a5a51b89fd..f155683bd7f8d1 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -105,6 +105,9 @@ TEST_GEN_PROGS_x86 += x86/pmu_counters_test
TEST_GEN_PROGS_x86 += x86/pmu_event_filter_test
TEST_GEN_PROGS_x86 += x86/private_mem_conversions_test
TEST_GEN_PROGS_x86 += x86/private_mem_kvm_exits_test
+TEST_GEN_PROGS_x86 += x86/pvclock_test
+TEST_GEN_PROGS_x86 += x86/masterclock_offset_test
+TEST_GEN_PROGS_x86 += x86/pvclock_migration_test
TEST_GEN_PROGS_x86 += x86/set_boot_cpu_id
TEST_GEN_PROGS_x86 += x86/set_sregs_test
TEST_GEN_PROGS_x86 += x86/smaller_maxphyaddr_emulation_test
@@ -138,6 +141,8 @@ TEST_GEN_PROGS_x86 += x86/xss_msr_test
TEST_GEN_PROGS_x86 += x86/debug_regs
TEST_GEN_PROGS_x86 += x86/tsc_msrs_test
TEST_GEN_PROGS_x86 += x86/vmx_pmu_caps_test
+TEST_GEN_PROGS_x86 += x86/xen_cpuid_timing_test
+TEST_GEN_PROGS_x86 += x86/xen_migration_test
TEST_GEN_PROGS_x86 += x86/xen_shinfo_test
TEST_GEN_PROGS_x86 += x86/xen_vmcall_test
TEST_GEN_PROGS_x86 += x86/sev_init2_tests
diff --git a/tools/testing/selftests/kvm/x86/masterclock_offset_test.c b/tools/testing/selftests/kvm/x86/masterclock_offset_test.c
new file mode 100644
index 00000000000000..88e2bd2edab516
--- /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 <stdint.h>
+#include <string.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+#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 = guest_tsc - pvti->tsc_timestamp;
+
+ if (pvti->tsc_shift >= 0)
+ delta <<= pvti->tsc_shift;
+ else
+ delta >>= -(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 = 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 = 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 = 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=0x%x", clock.flags);
+ TEST_ASSERT(clock.flags & KVM_CLOCK_TSC_STABLE,
+ "KVM_CLOCK_TSC_STABLE should be set, flags=0x%x", clock.flags);
+
+ /* Get per-vCPU pvclock in order 0, 2, 1 */
+ int order[] = {0, 2, 1};
+ for (i = 0; i < 3; i++) {
+ int idx = order[i];
+ __vcpu_ioctl(vcpus[idx], KVM_GET_CLOCK_GUEST, &pvti[idx]);
+ pr_info("vCPU %d: tsc_timestamp=%lu system_time=%lu "
+ "mul=%u shift=%d flags=0x%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 = vcpu_get_msr(vcpus[0], MSR_IA32_TSC);
+ uint64_t gtsc2 = vcpu_get_msr(vcpus[2], MSR_IA32_TSC);
+ uint64_t gtsc1 = vcpu_get_msr(vcpus[1], MSR_IA32_TSC);
+ pr_info("Guest TSCs: vcpu0=%lu vcpu2=%lu vcpu1=%lu\n",
+ (unsigned long)gtsc0, (unsigned long)gtsc2, (unsigned long)gtsc1);
+ pr_info("vcpu0+OFF=%lu vcpu1+OFF=%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=0x%x",
+ pvti[2].flags);
+
+ /* Same mul/shift */
+ TEST_ASSERT(pvti[0].tsc_to_system_mul == pvti[2].tsc_to_system_mul &&
+ pvti[0].tsc_shift == pvti[2].tsc_shift,
+ "All vCPUs should have same mul/shift");
+
+ /*
+ * Read host TSC once. At this instant:
+ * vCPU 0 guest TSC = host_tsc + offset0
+ * vCPU 2 guest TSC = host_tsc + offset0 + TSC_OFFSET
+ * Feed each through its pvclock. Expect the same kvmclock.
+ */
+ host_tsc = rdtsc();
+ clk0 = pvclock_calc(&pvti[0], host_tsc + offset0);
+ clk2 = 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 == clk2,
+ "kvmclock from offset vCPUs should match exactly, "
+ "diff=%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 = { .clock = clock.clock + ONE_HOUR_NS };
+
+ vm_ioctl(vm, KVM_SET_CLOCK, &setclk);
+ }
+
+ /* Guest code does GUEST_SYNC then exits — run each to see update */
+ for (i = 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 = 0; i < 3; i++)
+ __vcpu_ioctl(vcpus[order[i]], KVM_GET_CLOCK_GUEST, &pvti[order[i]]);
+
+ pr_info("After +1h: vCPU 0 system_time=%lu, vCPU 2 system_time=%lu\n",
+ (unsigned long)pvti[0].system_time,
+ (unsigned long)pvti[2].system_time);
+ TEST_ASSERT(pvti[0].system_time == pvti[2].system_time,
+ "system_time should still match after KVM_SET_CLOCK");
+
+ host_tsc = rdtsc();
+ clk0 = pvclock_calc(&pvti[0], host_tsc + offset0);
+ clk2 = 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 == clk2,
+ "After +1h: kvmclock should still match, diff=%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;
+}
diff --git a/tools/testing/selftests/kvm/x86/pvclock_migration_test.c b/tools/testing/selftests/kvm/x86/pvclock_migration_test.c
new file mode 100644
index 00000000000000..3b675827aec346
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/pvclock_migration_test.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test KVM clock precision across simulated live migration.
+ *
+ * Verifies that the documented TSC migration procedure (using
+ * KVM_VCPU_TSC_OFFSET, KVM_VCPU_TSC_SCALE, KVM_GET_CLOCK, and
+ * KVM_SET_CLOCK_GUEST) preserves the kvmclock's relationship to
+ * CLOCK_MONOTONIC_RAW.
+ *
+ * The test:
+ * 1. Creates a VM, runs the guest to enable kvmclock
+ * 2. Does a PTP-like ABA measurement of kvmclock vs CLOCK_MONOTONIC_RAW
+ * 3. Follows the documented migration procedure (same host, 1s pause)
+ * 4. Does the same ABA measurement on the destination VM
+ * 5. Verifies the kvmclock-vs-monotonic delta is preserved
+ */
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+#define KVMCLOCK_GPA 0xc0000000ULL
+
+static void guest_code(void)
+{
+ wrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | 1);
+ GUEST_SYNC(0);
+ GUEST_SYNC(1);
+}
+
+static uint64_t read_kvmclock_ns(struct kvm_vm *vm)
+{
+ struct kvm_clock_data data = {};
+
+ vm_ioctl(vm, KVM_GET_CLOCK, &data);
+ return data.clock;
+}
+
+static uint64_t pvclock_read_cycles(struct pvclock_vcpu_time_info *src,
+ uint64_t tsc)
+{
+ uint64_t delta = tsc - src->tsc_timestamp;
+ uint64_t ns;
+
+ if (src->tsc_shift >= 0)
+ delta <<= src->tsc_shift;
+ else
+ delta >>= -(int32_t)src->tsc_shift;
+
+ ns = (unsigned __int128)delta * src->tsc_to_system_mul >> 32;
+ return src->system_time + ns;
+}
+
+/*
+ * ABA measurement: read CLOCK_MONOTONIC_RAW, kvmclock, CLOCK_MONOTONIC_RAW.
+ * Repeat 3 times, keep the reading with the smallest spread.
+ */
+static void aba_reading(struct kvm_vm *vm, uint64_t *lo, uint64_t *kvm_ns,
+ uint64_t *hi)
+{
+ uint64_t best_spread = UINT64_MAX;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ struct timespec ts1, ts2;
+ uint64_t m1, m2, clk;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
+ clk = read_kvmclock_ns(vm);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
+
+ m1 = ts1.tv_sec * 1000000000ULL + ts1.tv_nsec;
+ m2 = ts2.tv_sec * 1000000000ULL + ts2.tv_nsec;
+
+ if (m2 - m1 < best_spread) {
+ best_spread = m2 - m1;
+ *lo = m1;
+ *kvm_ns = clk;
+ *hi = m2;
+ }
+ }
+}
+
+static struct kvm_vm *create_vm(struct kvm_vcpu **vcpu)
+{
+ struct kvm_vm *vm = vm_create_with_one_vcpu(vcpu, guest_code);
+
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ KVMCLOCK_GPA, 1, 1, 0);
+ virt_map(vm, KVMCLOCK_GPA, KVMCLOCK_GPA, 1);
+ return vm;
+}
+
+int main(void)
+{
+ struct pvclock_vcpu_time_info pvti_src;
+ struct kvm_clock_data clock_src, clock_dst;
+ struct kvm_vcpu_tsc_scale scale_src, scale_dst;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ uint64_t mono_before, kvm_before, kvm_after;
+ int64_t delta_before;
+ uint64_t ofs_src, tsc_src, tsc_dst, raw_dst, ofs_dst;
+ uint64_t host_tsc_src, host_tsc_dst;
+ uint64_t time_src, time_dst;
+ int64_t delta_t;
+ uint32_t freq_khz = 1500000; /* 1.5 GHz — forces TSC scaling */
+ int ret;
+
+ TEST_REQUIRE(sys_clocksource_is_based_on_tsc());
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL));
+
+ /* === SOURCE SIDE === */
+ pr_info("=== Source VM ===\n");
+ vm = create_vm(&vcpu);
+
+ /* Set guest TSC frequency (may trigger scaling) */
+ vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)freq_khz);
+
+ /* Run guest to enable kvmclock */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+
+ /* ABA measurement: kvmclock vs CLOCK_MONOTONIC_RAW */
+ uint64_t src_mono_lo, src_mono_hi;
+ aba_reading(vm, &src_mono_lo, &kvm_before, &src_mono_hi);
+ mono_before = (src_mono_lo + src_mono_hi) / 2;
+ delta_before = (int64_t)(kvm_before - mono_before);
+ pr_info(" kvmclock - MONOTONIC_RAW = %" PRId64 " ns (±%" PRIu64 " ns)\n",
+ delta_before, (src_mono_hi - src_mono_lo) / 2);
+
+ /* Step 1: KVM_GET_CLOCK for atomic {host_tsc, realtime} */
+ memset(&clock_src, 0, sizeof(clock_src));
+ clock_src.flags = KVM_CLOCK_REALTIME;
+ vm_ioctl(vm, KVM_GET_CLOCK, &clock_src);
+ host_tsc_src = clock_src.host_tsc;
+ time_src = clock_src.realtime;
+
+ /* Step 2: Save TSC offset and scale */
+ {
+ struct kvm_device_attr attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_OFFSET,
+ .addr = (uint64_t)(uintptr_t)&ofs_src,
+ };
+ vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, &attr);
+ }
+ {
+ struct kvm_device_attr attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_SCALE,
+ .addr = (uint64_t)(uintptr_t)&scale_src,
+ };
+ memset(&scale_src, 0, sizeof(scale_src));
+ __vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, &attr);
+ }
+
+ /* Compute guest TSC at Tsrc */
+ if (scale_src.tsc_frac_bits)
+ tsc_src = ((unsigned __int128)host_tsc_src * scale_src.tsc_ratio
+ >> scale_src.tsc_frac_bits) + ofs_src;
+ else
+ tsc_src = host_tsc_src + ofs_src;
+
+ /* Step 3: KVM_GET_CLOCK_GUEST */
+ ret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, &pvti_src);
+ TEST_ASSERT(!ret, "KVM_GET_CLOCK_GUEST failed");
+
+ pr_info(" TSC freq=%u kHz, offset=%" PRId64 "\n", freq_khz, (int64_t)ofs_src);
+
+ kvm_vm_release(vm);
+
+ /* === PAUSE (simulate migration) === */
+ pr_info("=== Pausing 1 second ===\n");
+ sleep(1);
+
+ /* === DESTINATION SIDE === */
+ pr_info("=== Destination VM ===\n");
+ vm = create_vm(&vcpu);
+
+ /* Step 4: KVM_SET_TSC_KHZ */
+ vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)freq_khz);
+
+ /* Step 5: KVM_GET_CLOCK for atomic {host_tsc, realtime} pair.
+ * Master clock is active from vCPU creation.
+ */
+ memset(&clock_dst, 0, sizeof(clock_dst));
+ vm_ioctl(vm, KVM_GET_CLOCK, &clock_dst);
+ host_tsc_dst = clock_dst.host_tsc;
+ time_dst = clock_dst.realtime;
+
+ /* Step 6: ΔT */
+ delta_t = (int64_t)(time_dst - time_src);
+
+ /* Step 7: Compute destination offset */
+ {
+ struct kvm_device_attr attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_SCALE,
+ .addr = (uint64_t)(uintptr_t)&scale_dst,
+ };
+ memset(&scale_dst, 0, sizeof(scale_dst));
+ __vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, &attr);
+ }
+
+ tsc_dst = tsc_src + (uint64_t)((int64_t)freq_khz * delta_t / 1000000LL);
+
+ if (scale_dst.tsc_frac_bits)
+ raw_dst = (unsigned __int128)host_tsc_dst * scale_dst.tsc_ratio
+ >> scale_dst.tsc_frac_bits;
+ else
+ raw_dst = host_tsc_dst;
+
+ ofs_dst = tsc_dst - raw_dst;
+
+ /*
+ * The TSC offset delta introduced by using CLOCK_REALTIME to
+ * estimate elapsed time. On same host, the correct offset is
+ * ofs_src; the difference is the CLOCK_REALTIME-vs-TSC error.
+ */
+ int64_t tsc_ofs_delta = (int64_t)(ofs_dst - ofs_src);
+ int64_t tsc_ofs_delta_ns = tsc_ofs_delta * 1000000LL / freq_khz;
+ pr_info(" Destination TSC offset=%" PRId64
+ ", imprecision from CLOCK_REALTIME: %" PRId64 " cycles = %"
+ PRId64 " ns\n", (int64_t)ofs_dst, tsc_ofs_delta, tsc_ofs_delta_ns);
+
+ /* Set TSC offset */
+ {
+ struct kvm_device_attr attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_OFFSET,
+ .addr = (uint64_t)(uintptr_t)&ofs_dst,
+ };
+ vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &attr);
+ }
+
+ /* Step 8: KVM_SET_CLOCK_GUEST */
+ ret = __vcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, &pvti_src);
+ TEST_ASSERT(!ret, "KVM_SET_CLOCK_GUEST failed: errno %d", errno);
+
+ /* Run guest to update pvclock page on destination */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+
+ /* ABA measurement on destination */
+ uint64_t mono_lo, mono_hi;
+ aba_reading(vm, &mono_lo, &kvm_after, &mono_hi);
+
+ /*
+ * The kvmclock is tied to the guest TSC via SET_CLOCK_GUEST.
+ * The guest TSC is offset from the correct value by tsc_ofs_delta_ns
+ * (due to CLOCK_REALTIME imprecision). So the kvmclock should be
+ * offset from CLOCK_MONOTONIC_RAW by exactly:
+ * (original delta) + tsc_ofs_delta_ns
+ *
+ * The "original delta" has uncertainty from the source ABA spread,
+ * and the measurement has uncertainty from the destination ABA spread.
+ * Verify the expected value falls within the combined bounds.
+ */
+ int64_t delta_before_lo = (int64_t)(kvm_before - src_mono_hi);
+ int64_t delta_before_hi = (int64_t)(kvm_before - src_mono_lo);
+ int64_t expected_lo = delta_before_lo + tsc_ofs_delta_ns;
+ int64_t expected_hi = delta_before_hi + tsc_ofs_delta_ns;
+ int64_t actual_lo = (int64_t)(kvm_after - mono_hi);
+ int64_t actual_hi = (int64_t)(kvm_after - mono_lo);
+
+ /* Show the shift relative to the source measurement */
+ int64_t expected_mid = tsc_ofs_delta_ns;
+ int64_t expected_err = (int64_t)(src_mono_hi - src_mono_lo) / 2;
+ int64_t actual_mid = ((actual_lo + actual_hi) / 2) - delta_before;
+ int64_t actual_err = (int64_t)(mono_hi - mono_lo) / 2;
+ pr_info(" kvmclock-mono shift: expected %" PRId64 " ns (±%" PRId64
+ "), measured %" PRId64 " ns (±%" PRId64 ")\n",
+ expected_mid, expected_err, actual_mid, actual_err);
+
+ /* The ranges must overlap */
+ TEST_ASSERT(expected_hi >= actual_lo && expected_lo <= actual_hi,
+ "Ranges don't overlap: expected [%" PRId64 ", %" PRId64
+ "] measured [%" PRId64 ", %" PRId64 "]",
+ expected_lo, expected_hi, actual_lo, actual_hi);
+
+ /*
+ * Direct pvclock verification: read the destination pvclock page
+ * and verify that computing kvmclock from pvti_src and pvti_dst
+ * at the same guest TSC gives the same result.
+ *
+ * Get an atomic {host_tsc, kvmclock} pair, scale host_tsc to
+ * guest TSC using KVM_VCPU_TSC_SCALE, then compute kvmclock
+ * from both pvclock structs.
+ */
+ struct kvm_clock_data clock_now = {};
+ vm_ioctl(vm, KVM_GET_CLOCK, &clock_now);
+
+ struct pvclock_vcpu_time_info *pvti_dst = addr_gpa2hva(vm, KVMCLOCK_GPA);
+ uint64_t host_tsc_now = clock_now.host_tsc;
+ uint64_t guest_tsc_now;
+
+ if (scale_dst.tsc_frac_bits)
+ guest_tsc_now = ((unsigned __int128)host_tsc_now *
+ scale_dst.tsc_ratio >> scale_dst.tsc_frac_bits)
+ + ofs_dst;
+ else
+ guest_tsc_now = host_tsc_now + ofs_dst;
+
+ uint64_t clk_from_src = pvclock_read_cycles(&pvti_src, guest_tsc_now);
+ uint64_t clk_from_dst = pvclock_read_cycles(pvti_dst, guest_tsc_now);
+ int64_t pvclock_delta = (int64_t)(clk_from_src - clk_from_dst);
+
+ pr_info(" Pvclock direct: src=%" PRIu64 " dst=%" PRIu64
+ " delta=%" PRId64 " ns\n", clk_from_src, clk_from_dst, pvclock_delta);
+ pr_info(" KVM_GET_CLOCK: %" PRIu64 " ns\n", (uint64_t)clock_now.clock);
+
+ TEST_ASSERT(pvclock_delta >= -2 && pvclock_delta <= 2,
+ "pvclock src vs dst disagree by %" PRId64 " ns", pvclock_delta);
+
+ /*
+ * Tight ABA: compare pvclock_read() directly (no ioctl) against
+ * CLOCK_MONOTONIC_RAW. The spread should be much smaller since
+ * there's no syscall between the two clock_gettime calls — just
+ * rdtsc + userspace mul/shift.
+ */
+ uint64_t tight_mono_lo = 0, tight_mono_hi = 0, tight_kvm = 0;
+ uint64_t tight_best_spread = UINT64_MAX;
+ for (int i = 0; i < 3; i++) {
+ struct timespec ts1, ts2;
+ uint64_t m1, m2, tsc, clk;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
+ tsc = rdtsc();
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts2);
+
+ m1 = ts1.tv_sec * 1000000000ULL + ts1.tv_nsec;
+ m2 = ts2.tv_sec * 1000000000ULL + ts2.tv_nsec;
+
+ /* Scale host TSC to guest TSC */
+ if (scale_dst.tsc_frac_bits)
+ tsc = ((unsigned __int128)tsc * scale_dst.tsc_ratio
+ >> scale_dst.tsc_frac_bits) + ofs_dst;
+ else
+ tsc += ofs_dst;
+
+ clk = pvclock_read_cycles(pvti_dst, tsc);
+
+ if (m2 - m1 < tight_best_spread) {
+ tight_best_spread = m2 - m1;
+ tight_mono_lo = m1;
+ tight_mono_hi = m2;
+ tight_kvm = clk;
+ }
+ }
+ pr_info(" Tight ABA spread: %" PRIu64 " ns (best of 3)\n", tight_best_spread);
+
+ int64_t tight_expected_lo = delta_before_lo + tsc_ofs_delta_ns;
+ int64_t tight_expected_hi = delta_before_hi + tsc_ofs_delta_ns;
+ int64_t tight_actual_lo = (int64_t)(tight_kvm - tight_mono_hi);
+ int64_t tight_actual_hi = (int64_t)(tight_kvm - tight_mono_lo);
+ int64_t tight_actual_mid = ((tight_actual_lo + tight_actual_hi) / 2) - delta_before;
+ int64_t tight_actual_err = (int64_t)(tight_mono_hi - tight_mono_lo) / 2;
+
+ pr_info(" Tight kvmclock-mono shift: expected %" PRId64
+ " ns (±%" PRId64 "), measured %" PRId64 " ns (±%" PRId64 ")\n",
+ expected_mid, expected_err, tight_actual_mid, tight_actual_err);
+
+ TEST_ASSERT(tight_expected_hi >= tight_actual_lo &&
+ tight_expected_lo <= tight_actual_hi,
+ "Tight ABA ranges don't overlap");
+
+ kvm_vm_release(vm);
+ pr_info("PASS: kvmclock offset matches TSC delta from CLOCK_REALTIME"
+ " (%" PRId64 " ns) within ABA bounds\n", tsc_ofs_delta_ns);
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/pvclock_test.c b/tools/testing/selftests/kvm/x86/pvclock_test.c
new file mode 100644
index 00000000000000..f2b917ed5deada
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/pvclock_test.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright © Amazon.com, Inc. or its affiliates.
+ *
+ * Tests for pvclock API
+ * KVM_SET_CLOCK_GUEST/KVM_GET_CLOCK_GUEST
+ */
+#include <getopt.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+/*
+ * Reproduce the pvclock calculation the guest uses to convert TSC to
+ * nanoseconds. This must match the kernel's __pvclock_read_cycles().
+ */
+static inline uint64_t pvclock_scale_delta(uint64_t delta, uint32_t mul,
+ int8_t shift)
+{
+ if (shift < 0)
+ delta >>= -shift;
+ else
+ delta <<= shift;
+ return ((__uint128_t)delta * mul) >> 32;
+}
+
+static inline uint64_t pvclock_read_cycles(struct pvclock_vcpu_time_info *src,
+ uint64_t tsc)
+{
+ uint64_t delta = tsc - src->tsc_timestamp;
+
+ return src->system_time + pvclock_scale_delta(delta,
+ src->tsc_to_system_mul,
+ src->tsc_shift);
+}
+
+static inline void pvti_snapshot(struct pvclock_vcpu_time_info *dst,
+ volatile struct pvclock_vcpu_time_info *src)
+{
+ uint32_t version;
+
+ do {
+ version = src->version;
+ __asm__ __volatile__("" ::: "memory");
+ *dst = *src;
+ __asm__ __volatile__("" ::: "memory");
+ } while ((src->version & 1) || src->version != version);
+}
+
+enum {
+ STAGE_FIRST_BOOT,
+ STAGE_UNCORRECTED,
+ STAGE_CORRECTED
+};
+
+#define KVMCLOCK_GPA 0xc0000000ull
+#define KVMCLOCK_SIZE sizeof(struct pvclock_vcpu_time_info)
+
+static void trigger_pvti_update(void)
+{
+ /*
+ * Toggle between KVM's old and new system time methods to coerce KVM
+ * into updating the fields in the PV time info struct.
+ */
+ wrmsr(MSR_KVM_SYSTEM_TIME, KVMCLOCK_GPA | KVM_MSR_ENABLED);
+ wrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | KVM_MSR_ENABLED);
+}
+
+static void guest_code(void)
+{
+ struct pvclock_vcpu_time_info *pvti =
+ (void *)(unsigned long)KVMCLOCK_GPA;
+ struct pvclock_vcpu_time_info pvti_boot;
+ struct pvclock_vcpu_time_info pvti_uncorrected;
+ struct pvclock_vcpu_time_info pvti_corrected;
+ uint64_t tsc_guest;
+ uint64_t clk_boot, clk_uncorrected, clk_corrected;
+ int64_t delta_corrected;
+
+ /* Set up kvmclock and snapshot the initial pvclock parameters. */
+ wrmsr(MSR_KVM_SYSTEM_TIME_NEW, KVMCLOCK_GPA | KVM_MSR_ENABLED);
+ pvti_snapshot(&pvti_boot, pvti);
+ GUEST_SYNC(STAGE_FIRST_BOOT);
+
+ /*
+ * Trigger an update of the PVTI. Calculating the KVM clock using this
+ * updated structure will show a delta from the original.
+ */
+ trigger_pvti_update();
+ pvti_snapshot(&pvti_uncorrected, pvti);
+ GUEST_SYNC(STAGE_UNCORRECTED);
+
+ /*
+ * Snapshot the corrected time (the host does KVM_SET_CLOCK_GUEST when
+ * handling STAGE_UNCORRECTED).
+ */
+ pvti_snapshot(&pvti_corrected, pvti);
+
+ /*
+ * Sample the TSC at a single point in time, then calculate the
+ * effective KVM clock using the PVTI from each stage. Verify that the
+ * corrected clock matches the boot clock to within ±2ns.
+ */
+ tsc_guest = rdtsc();
+
+ clk_boot = pvclock_read_cycles(&pvti_boot, tsc_guest);
+ clk_uncorrected = pvclock_read_cycles(&pvti_uncorrected, tsc_guest);
+ clk_corrected = pvclock_read_cycles(&pvti_corrected, tsc_guest);
+
+ delta_corrected = clk_boot - clk_corrected;
+
+ __GUEST_ASSERT(delta_corrected >= -2 && delta_corrected <= 2,
+ "corrected delta %ld out of range (boot=%lu uncorrected=%lu corrected=%lu)",
+ delta_corrected, clk_boot, clk_uncorrected, clk_corrected);
+
+ GUEST_SYNC(STAGE_CORRECTED);
+}
+
+static void run_test(struct kvm_vm *vm, struct kvm_vcpu *vcpu,
+ unsigned int sleep_sec)
+{
+ struct pvclock_vcpu_time_info pvti_before;
+ struct ucall uc;
+
+ for (;;) {
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_SYNC:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall");
+ }
+
+ switch (uc.args[1]) {
+ case STAGE_FIRST_BOOT:
+ /* Save the pvclock parameters before the update. */
+ vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, &pvti_before);
+
+ /* Sleep to let the clocks diverge. */
+ sleep(sleep_sec);
+ break;
+
+ case STAGE_UNCORRECTED:
+ /* Restore the original pvclock parameters. */
+ vcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, &pvti_before);
+ break;
+
+ case STAGE_CORRECTED:
+ /* Guest verified the delta in-guest. */
+ return;
+
+ default:
+ TEST_FAIL("Unknown stage %lu", uc.args[1]);
+ }
+ }
+}
+
+static void configure_pvclock(struct kvm_vm *vm)
+{
+ unsigned int nr_pages;
+
+ nr_pages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, getpagesize());
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ KVMCLOCK_GPA, 1, nr_pages, 0);
+ virt_map(vm, KVMCLOCK_GPA, KVMCLOCK_GPA, nr_pages);
+}
+
+static void run_at_frequency(uint64_t tsc_khz, unsigned int sleep_sec)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ pr_info("Testing at TSC frequency %lu kHz\n", tsc_khz);
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ configure_pvclock(vm);
+ vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)tsc_khz);
+ run_test(vm, vcpu, sleep_sec);
+ kvm_vm_free(vm);
+}
+
+static void test_tsc_stable_bit(void);
+static void test_clock_guest_with_offsets(void);
+
+static void usage(const char *name)
+{
+ printf("Usage: %s [options]\n"
+ " -s, --sleep SEC sleep duration between snapshots (default: 2)\n"
+ " -h, --help show this help\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option long_opts[] = {
+ { "sleep", required_argument, NULL, 's' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ unsigned int sleep_sec = 2;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t host_khz;
+ uint64_t freq;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "s:h", long_opts, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ sleep_sec = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage(argv[0]);
+ return opt == 'h' ? 0 : 1;
+ }
+ }
+
+ TEST_REQUIRE(sys_clocksource_is_based_on_tsc());
+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL));
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ configure_pvclock(vm);
+
+ /* Check KVM_GET_CLOCK_GUEST is supported */
+ {
+ struct pvclock_vcpu_time_info tmp;
+ int ret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, &tmp);
+ TEST_REQUIRE(ret == 0);
+ }
+
+ /* First run at native frequency (no scaling). */
+ run_test(vm, vcpu, sleep_sec);
+
+ /*
+ * Then enumerate a range of TSC frequencies crossing the 32-bit
+ * boundary, to exercise the scaling path at various ratios.
+ */
+ host_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);
+ kvm_vm_free(vm);
+
+ for (freq = 1000000; freq <= 5000000; freq += 500000) {
+ if (freq == host_khz)
+ continue;
+ run_at_frequency(freq, sleep_sec);
+ }
+
+ test_tsc_stable_bit();
+ test_clock_guest_with_offsets();
+
+ return 0;
+}
+
+static volatile uint32_t vcpu_counter;
+static void guest_code_stable_bit(void)
+{
+ uint32_t idx = __atomic_fetch_add(&vcpu_counter, 1, __ATOMIC_SEQ_CST);
+ uint64_t gpa = KVMCLOCK_GPA + idx * sizeof(struct pvclock_vcpu_time_info);
+
+ wrmsr(MSR_KVM_SYSTEM_TIME_NEW, gpa | KVM_MSR_ENABLED);
+ GUEST_SYNC(0);
+ GUEST_SYNC(0);
+ GUEST_SYNC(0);
+}
+
+static void set_tsc_offset(struct kvm_vcpu *vcpu, uint64_t offset)
+{
+ struct kvm_device_attr attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_OFFSET,
+ .addr = (__u64)(uintptr_t)&offset,
+ };
+
+ TEST_REQUIRE(__vcpu_has_device_attr(vcpu, KVM_VCPU_TSC_CTRL,
+ KVM_VCPU_TSC_OFFSET) == 0);
+ vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &attr);
+}
+
+static void run_vcpu_once(struct kvm_vcpu *vcpu)
+{
+ struct ucall uc;
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_SYNC:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall");
+ }
+}
+
+static void test_tsc_stable_bit(void)
+{
+ struct pvclock_vcpu_time_info pvti;
+ struct kvm_vcpu *vcpus[2];
+ struct kvm_vm *vm;
+ int ret;
+
+ pr_info("Testing PVCLOCK_TSC_STABLE_BIT with matched/unmatched TSCs\n");
+
+ vm = vm_create_with_vcpus(2, guest_code_stable_bit, vcpus);
+ configure_pvclock(vm);
+
+ /*
+ * Case 1: All TSCs matched (same frequency and offset).
+ * Master clock should be active, PVCLOCK_TSC_STABLE_BIT set.
+ */
+ run_vcpu_once(vcpus[0]);
+
+ ret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, &pvti);
+ TEST_ASSERT(!ret, "GET_CLOCK_GUEST should succeed with matched TSCs");
+ TEST_ASSERT(pvti.flags & PVCLOCK_TSC_STABLE_BIT,
+ "PVCLOCK_TSC_STABLE_BIT should be set with matched TSCs");
+
+ /*
+ * Case 2: Different TSC offset, same frequency.
+ * Master clock should still be active (frequency matches), but
+ * PVCLOCK_TSC_STABLE_BIT should be cleared (offsets differ).
+ */
+ set_tsc_offset(vcpus[1], 12345678);
+ run_vcpu_once(vcpus[1]);
+ run_vcpu_once(vcpus[0]);
+
+ ret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, &pvti);
+ if (ret) {
+ /* Master clock disabled by offset mismatch — old kernel */
+ pr_info(" Skipping offset tests (master clock requires matched offsets)\n");
+ goto out_stable;
+ }
+ TEST_ASSERT(!(pvti.flags & PVCLOCK_TSC_STABLE_BIT),
+ "PVCLOCK_TSC_STABLE_BIT should be clear with offset-mismatched TSCs");
+
+ /*
+ * Case 3: Different TSC frequency.
+ * Master clock should be disabled entirely.
+ */
+ vcpu_ioctl(vcpus[1], KVM_SET_TSC_KHZ,
+ (void *)(unsigned long)(__vcpu_ioctl(vcpus[1], KVM_GET_TSC_KHZ, NULL) / 2));
+ /* Write TSC to trigger kvm_synchronize_tsc / kvm_track_tsc_matching */
+ vcpu_set_msr(vcpus[1], MSR_IA32_TSC, 0);
+ run_vcpu_once(vcpus[1]);
+
+ ret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, &pvti);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "GET_CLOCK_GUEST should fail with frequency-mismatched TSCs, got %d (errno %d)",
+ ret, errno);
+
+out_stable:
+ kvm_vm_free(vm);
+}
+
+static void test_clock_guest_with_offsets(void)
+{
+ struct pvclock_vcpu_time_info pvti0, pvti1, pvti1_after;
+ struct kvm_vcpu *vcpus[2];
+ struct kvm_vm *vm;
+ int64_t delta;
+ int ret;
+
+ pr_info("Testing KVM_[GS]ET_CLOCK_GUEST with different TSC offsets\n");
+
+ vm = vm_create_with_vcpus(2, guest_code_stable_bit, vcpus);
+ configure_pvclock(vm);
+
+ /* Set different TSC offsets on the two vCPUs */
+ set_tsc_offset(vcpus[0], 0);
+ set_tsc_offset(vcpus[1], 1000000000ull);
+
+ /* Run both to establish kvmclock */
+ run_vcpu_once(vcpus[0]);
+ run_vcpu_once(vcpus[1]);
+
+ /* GET_CLOCK_GUEST on both — should succeed (master clock active) */
+ ret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, &pvti0);
+ if (ret) {
+ pr_info(" Skipping (master clock requires matched offsets on this kernel)\n");
+ kvm_vm_free(vm);
+ return;
+ }
+ ret = __vcpu_ioctl(vcpus[1], KVM_GET_CLOCK_GUEST, &pvti1);
+ TEST_ASSERT(!ret, "GET_CLOCK_GUEST on vcpu1 failed");
+
+ /* The tsc_timestamps should differ (different offsets) */
+ TEST_ASSERT(pvti0.tsc_timestamp != pvti1.tsc_timestamp,
+ "tsc_timestamps should differ with different offsets");
+
+ /* Sleep to let time elapse, then restore vcpu0's clock */
+ sleep(1);
+ vcpu_ioctl(vcpus[0], KVM_SET_CLOCK_GUEST, &pvti0);
+
+ /* Run vcpu0 to process the clock update */
+ run_vcpu_once(vcpus[0]);
+
+ /* GET_CLOCK_GUEST on vcpu1 — should reflect the correction */
+ ret = __vcpu_ioctl(vcpus[1], KVM_GET_CLOCK_GUEST, &pvti1_after);
+ TEST_ASSERT(!ret, "GET_CLOCK_GUEST on vcpu1 after SET failed");
+
+ /*
+ * After SET on vcpu0, verify the correction worked by getting
+ * the clock on vcpu0 again. The mul/shift should be the same,
+ * and computing kvmclock at the same TSC should give the same
+ * result as the original (within ±2ns).
+ */
+ {
+ struct pvclock_vcpu_time_info pvti0_after;
+ uint64_t tsc_now, clk_from_old, clk_from_new;
+
+ ret = __vcpu_ioctl(vcpus[0], KVM_GET_CLOCK_GUEST, &pvti0_after);
+ TEST_ASSERT(!ret, "GET_CLOCK_GUEST on vcpu0 after SET failed");
+
+ tsc_now = pvti0_after.tsc_timestamp;
+ clk_from_old = pvclock_read_cycles(&pvti0, tsc_now);
+ clk_from_new = pvclock_read_cycles(&pvti0_after, tsc_now);
+
+ delta = (int64_t)clk_from_new - (int64_t)clk_from_old;
+ TEST_ASSERT(delta >= -2 && delta <= 2,
+ "clock correction delta should be <=2ns, got %ld ns",
+ delta);
+ }
+
+ /*
+ * Also verify that vcpu1's clock is still accessible (master
+ * clock still active with different offsets).
+ */
+ ret = __vcpu_ioctl(vcpus[1], KVM_GET_CLOCK_GUEST, &pvti1_after);
+ TEST_ASSERT(!ret, "GET_CLOCK_GUEST on vcpu1 after SET failed");
+
+ kvm_vm_free(vm);
+}
diff --git a/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c b/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c
new file mode 100644
index 00000000000000..2098cb511ef021
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/xen_cpuid_timing_test.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test that userspace can correctly populate Xen and generic CPUID
+ * timing leaves using KVM_GET_TSC_KHZ and KVM_VCPU_TSC_SCALE.
+ *
+ * This validates that the removal of KVM's runtime Xen CPUID modification
+ * doesn't break guests, because userspace has all the information needed.
+ */
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+#define XEN_CPUID_BASE 0x40000100
+#define XEN_CPUID_LEAF(n) (XEN_CPUID_BASE + (n))
+#define GENERIC_TIMING_LEAF 0x40000010
+
+/* Values set by host, verified by guest */
+static uint32_t expected_tsc_khz;
+static uint32_t expected_bus_khz;
+static uint32_t expected_tsc_mul;
+static int8_t expected_tsc_shift;
+static uint64_t host_khz;
+
+static void guest_code(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ /* Check generic timing leaf 0x40000010 */
+ __cpuid(GENERIC_TIMING_LEAF, 0, &eax, &ebx, &ecx, &edx);
+ GUEST_ASSERT_EQ(eax, expected_tsc_khz);
+ GUEST_ASSERT_EQ(ebx, expected_bus_khz);
+
+ /* Check Xen leaf 3, sub-leaf 0: ECX = guest TSC frequency */
+ __cpuid(XEN_CPUID_LEAF(3), 0, &eax, &ebx, &ecx, &edx);
+ GUEST_ASSERT_EQ(ecx, expected_tsc_khz);
+
+ /* Check Xen leaf 3, sub-leaf 1: ECX = mul, EDX = shift */
+ __cpuid(XEN_CPUID_LEAF(3), 1, &eax, &ebx, &ecx, &edx);
+ GUEST_ASSERT_EQ(ecx, expected_tsc_mul);
+ GUEST_ASSERT_EQ((int8_t)edx, expected_tsc_shift);
+
+ GUEST_SYNC(0);
+}
+
+static void add_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function,
+ uint32_t index, uint32_t eax, uint32_t ebx,
+ uint32_t ecx, uint32_t edx)
+{
+ struct kvm_cpuid2 *cpuid = vcpu->cpuid;
+ struct kvm_cpuid_entry2 *entry;
+ int n = cpuid->nent;
+
+ vcpu->cpuid = realloc(vcpu->cpuid,
+ sizeof(*cpuid) + (n + 1) * sizeof(*entry));
+ cpuid = vcpu->cpuid;
+ cpuid->nent = n + 1;
+
+ entry = &cpuid->entries[n];
+ memset(entry, 0, sizeof(*entry));
+ entry->function = function;
+ entry->index = index;
+ entry->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+ entry->eax = eax;
+ entry->ebx = ebx;
+ entry->ecx = ecx;
+ entry->edx = edx;
+}
+
+/*
+ * Compute pvclock mul/shift from frequency, matching kvm_get_time_scale().
+ */
+static void compute_tsc_mul_shift(uint64_t tsc_hz, uint32_t *mul, int8_t *shift)
+{
+ uint64_t scaled = 1000000000ULL;
+ uint64_t base = tsc_hz;
+ int32_t s = 0;
+ uint32_t base32;
+
+ while (base > scaled * 2 || base >> 32) {
+ base >>= 1;
+ s--;
+ }
+ base32 = (uint32_t)base;
+ while (base32 <= scaled || scaled >> 32) {
+ if (scaled >> 32 || base32 & (1U << 31))
+ scaled >>= 1;
+ else
+ base32 <<= 1;
+ s++;
+ }
+ *mul = (uint32_t)((scaled << 32) / base32);
+ *shift = (int8_t)s;
+}
+
+static void run_test(uint64_t tsc_khz)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ uint32_t effective_tsc_khz, effective_bus_khz;
+ int bus_cycle_ns;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+
+ if (tsc_khz) {
+ pr_info("Testing at TSC frequency %lu kHz\n", tsc_khz);
+ vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *)(unsigned long)tsc_khz);
+ } else {
+ pr_info("Testing at native TSC frequency\n");
+ }
+
+ effective_tsc_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);
+ bus_cycle_ns = vm_check_cap(vm, KVM_CAP_X86_APIC_BUS_CYCLES_NS);
+ effective_bus_khz = bus_cycle_ns > 0 ? 1000000 / bus_cycle_ns : 1000000;
+
+ /* If scaling wasn't applied, skip this frequency */
+ if (tsc_khz && effective_tsc_khz == host_khz) {
+ pr_info(" TSC scaling not available, skipping\n");
+ kvm_vm_free(vm);
+ return;
+ }
+
+ pr_info(" Effective TSC: %u kHz, Bus: %u kHz\n", effective_tsc_khz, effective_bus_khz);
+
+ /* Also exercise KVM_VCPU_TSC_SCALE if available */
+ {
+ struct { uint64_t ratio; uint64_t frac_bits; } scale;
+ struct kvm_device_attr scale_attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_SCALE,
+ .addr = (uint64_t)(uintptr_t)&scale,
+ };
+
+ if (!__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &scale_attr)) {
+ vcpu_ioctl(vcpu, KVM_GET_DEVICE_ATTR, &scale_attr);
+ pr_info(" TSC scale: ratio=%lu frac_bits=%lu\n",
+ scale.ratio, scale.frac_bits);
+
+ /*
+ * Verify: applying the ratio to the host TSC frequency
+ * should give approximately the effective frequency.
+ */
+ if (tsc_khz) {
+ uint64_t computed = ((__uint128_t)host_khz * scale.ratio) >> scale.frac_bits;
+ int64_t diff = (int64_t)computed - (int64_t)effective_tsc_khz;
+
+ TEST_ASSERT(diff >= -1 && diff <= 1,
+ "TSC_SCALE ratio mismatch: computed %lu vs effective %u (diff %ld)",
+ computed, effective_tsc_khz, diff);
+ }
+ }
+ }
+
+ compute_tsc_mul_shift((uint64_t)effective_tsc_khz * 1000,
+ &expected_tsc_mul, &expected_tsc_shift);
+
+ expected_tsc_khz = effective_tsc_khz;
+ expected_bus_khz = effective_bus_khz;
+
+ sync_global_to_guest(vm, expected_tsc_khz);
+ sync_global_to_guest(vm, expected_bus_khz);
+ sync_global_to_guest(vm, expected_tsc_mul);
+ sync_global_to_guest(vm, expected_tsc_shift);
+
+ /* Populate CPUID leaves as a VMM would */
+ add_cpuid_entry(vcpu, GENERIC_TIMING_LEAF, 0,
+ effective_tsc_khz, effective_bus_khz, 0, 0);
+ add_cpuid_entry(vcpu, XEN_CPUID_LEAF(3), 0,
+ 0, 0, effective_tsc_khz, 0);
+ add_cpuid_entry(vcpu, XEN_CPUID_LEAF(3), 1,
+ 0, 0, expected_tsc_mul,
+ (uint32_t)(uint8_t)expected_tsc_shift);
+
+ vcpu_set_cpuid(vcpu);
+
+ pr_info(" pvclock mul=%u shift=%d\n", expected_tsc_mul, expected_tsc_shift);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_SYNC:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall");
+ }
+
+ kvm_vm_free(vm);
+}
+
+int main(void)
+{
+ uint64_t freq;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct kvm_device_attr attr = {
+ .group = KVM_VCPU_TSC_CTRL,
+ .attr = KVM_VCPU_TSC_SCALE,
+ };
+
+ TEST_REQUIRE(sys_clocksource_is_based_on_tsc());
+
+ /* Check KVM_VCPU_TSC_SCALE is supported (implies TSC scaling) */
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ TEST_REQUIRE(!__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &attr));
+ host_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL);
+ kvm_vm_free(vm);
+
+ /* Native frequency */
+ run_test(0);
+
+ /* Scaled frequencies — skip if TSC scaling not available */
+ for (freq = 1000000; freq <= 4000000; freq += 1000000) {
+ if (freq == host_khz)
+ continue;
+ run_test(freq);
+ }
+
+ pr_info("PASS: All CPUID timing leaf tests passed\n");
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/xen_migration_test.c b/tools/testing/selftests/kvm/x86/xen_migration_test.c
new file mode 100644
index 00000000000000..4a041d36f1b9f3
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/xen_migration_test.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Test Xen runstate (steal time) preservation across simulated migration.
+ *
+ * Verifies that the kernel correctly accounts the migration gap as
+ * steal time (runnable) when runstate data is saved and restored
+ * precisely, but real time elapses during the migration.
+ *
+ * The key insight: userspace saves the runstate with state=RUNSTATE_runnable
+ * (the vCPU is not running during migration). On restore, the kernel sees
+ * that kvmclock has advanced past state_entry_time, and accounts the
+ * difference as time spent in the runnable state.
+ */
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+#include <asm/pvclock-abi.h>
+
+#define SHINFO_GPA 0xc0000000ULL
+#define RUNSTATE_GPA (SHINFO_GPA + 0x1000)
+
+#define RUNSTATE_running 0
+#define RUNSTATE_runnable 1
+#define RUNSTATE_blocked 2
+#define RUNSTATE_offline 3
+
+struct vcpu_runstate_info {
+ uint32_t state;
+ uint64_t state_entry_time;
+ uint64_t time[4];
+} __attribute__((packed));
+
+static void guest_code(void)
+{
+ volatile struct vcpu_runstate_info *rs =
+ (void *)(unsigned long)RUNSTATE_GPA;
+
+ /* Report runstate times — no need to enable kvmclock MSR,
+ * the kernel writes runstate using its internal kvmclock. */
+ GUEST_SYNC_ARGS(0, rs->time[RUNSTATE_runnable],
+ rs->time[RUNSTATE_running], 0, 0);
+}
+
+static struct kvm_vm *create_xen_vm(struct kvm_vcpu **vcpu)
+{
+ struct kvm_vm *vm;
+ int xen_caps;
+
+ vm = vm_create_with_one_vcpu(vcpu, guest_code);
+
+ xen_caps = kvm_check_cap(KVM_CAP_XEN_HVM);
+ TEST_REQUIRE(xen_caps & KVM_XEN_HVM_CONFIG_SHARED_INFO);
+ TEST_REQUIRE(xen_caps & KVM_XEN_HVM_CONFIG_RUNSTATE);
+
+ /* Map pages */
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+ SHINFO_GPA, 1, 2, 0);
+ virt_map(vm, SHINFO_GPA, SHINFO_GPA, 2);
+
+ /* Enable Xen HVM with MSR interception (enables runstate tracking) */
+ struct kvm_xen_hvm_config cfg = {
+ .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,
+ .msr = 0x40000000,
+ };
+ vm_ioctl(vm, KVM_XEN_HVM_CONFIG, &cfg);
+
+ /* Set shared_info */
+ struct kvm_xen_hvm_attr ha = {
+ .type = KVM_XEN_ATTR_TYPE_SHARED_INFO,
+ .u.shared_info.gfn = SHINFO_GPA >> 12,
+ };
+ vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &ha);
+
+ /* Set runstate address */
+ struct kvm_xen_vcpu_attr rs_addr = {
+ .type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR,
+ .u.gpa = RUNSTATE_GPA,
+ };
+ vcpu_ioctl(*vcpu, KVM_XEN_VCPU_SET_ATTR, &rs_addr);
+
+ return vm;
+}
+
+int main(void)
+{
+ struct pvclock_vcpu_time_info pvti;
+ struct kvm_xen_vcpu_attr runstate_save;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ uint64_t tsc_offset;
+ int ret;
+
+ /* === SOURCE SIDE === */
+ pr_info("=== Source: create VM and run guest ===\n");
+ vm = create_xen_vm(&vcpu);
+
+ /* Run guest once to accumulate some runstate time */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+
+ pr_info(" Guest sees: runnable=%" PRIu64 " running=%" PRIu64 "\n",
+ uc.args[2], uc.args[3]);
+
+ /* Save clock state */
+ ret = __vcpu_ioctl(vcpu, KVM_GET_CLOCK_GUEST, &pvti);
+ TEST_ASSERT(!ret, "KVM_GET_CLOCK_GUEST failed");
+
+ /* Save TSC offset */
+ tsc_offset = vcpu_get_msr(vcpu, MSR_IA32_TSC_ADJUST);
+
+ /* Save runstate — the vCPU is now "runnable" (not running) */
+ runstate_save.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
+ vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &runstate_save);
+
+ /*
+ * Transition to runnable state before saving — the vCPU is
+ * not running during migration.
+ */
+ runstate_save.u.runstate.state = RUNSTATE_runnable;
+
+ pr_info(" Saved runstate: running=%" PRIu64 " runnable=%" PRIu64
+ " entry=%" PRIu64 "\n",
+ (uint64_t)runstate_save.u.runstate.time_running,
+ (uint64_t)runstate_save.u.runstate.time_runnable,
+ (uint64_t)runstate_save.u.runstate.state_entry_time);
+
+ uint64_t saved_runnable = runstate_save.u.runstate.time_runnable;
+
+ kvm_vm_free(vm);
+
+ /* === MIGRATION GAP === */
+ pr_info("=== Simulating migration (sleeping 10ms) ===\n");
+ usleep(10000);
+
+ /* === DESTINATION SIDE === */
+ pr_info("=== Destination: create new VM and restore ===\n");
+ vm = create_xen_vm(&vcpu);
+
+ /* Restore TSC offset */
+ vcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, tsc_offset);
+
+ /* Restore clock — kvmclock will now be ~10ms ahead of the snapshot */
+ vcpu_ioctl(vcpu, KVM_SET_CLOCK_GUEST, &pvti);
+
+ /* Restore runstate exactly as saved (state=runnable) */
+ runstate_save.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA;
+ ret = __vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &runstate_save);
+ TEST_ASSERT(!ret, "Restore runstate failed: errno %d", errno);
+
+ /*
+ * Run the guest. When the vCPU enters vcpu_run, the kernel
+ * transitions from RUNSTATE_runnable to RUNSTATE_running.
+ * It computes: delta = kvmclock_now - state_entry_time
+ * This delta (which includes the migration gap) is added to
+ * time_runnable (steal time).
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC);
+
+ uint64_t guest_runnable = uc.args[2];
+ uint64_t guest_running = uc.args[3];
+
+ pr_info(" Guest sees: runnable=%" PRIu64 " running=%" PRIu64 "\n",
+ guest_runnable, guest_running);
+
+ uint64_t steal_increase = guest_runnable - saved_runnable;
+ pr_info(" Steal time increase: %" PRIu64 " ns (migration gap)\n",
+ steal_increase);
+
+ /*
+ * The steal time increase should be at least 10ms (the sleep)
+ * but not more than 5s (allowing for VM creation overhead).
+ * The actual gap is from the source's state_entry_time to the
+ * destination's kvmclock "now" at vcpu_load time.
+ */
+ TEST_ASSERT(steal_increase >= 10000000ULL &&
+ steal_increase < 5000000000ULL,
+ "Steal time increase %" PRIu64 " ns not in expected range "
+ "[10ms, 5s]", steal_increase);
+
+ kvm_vm_free(vm);
+ pr_info("PASS: Migration gap correctly accounted as steal time\n");
+ return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c
index 5076f6a7545575..2f241ca4f096f6 100644
--- a/tools/testing/selftests/kvm/x86/xen_shinfo_test.c
+++ b/tools/testing/selftests/kvm/x86/xen_shinfo_test.c
@@ -98,22 +98,7 @@ struct sched_poll {
u64 timeout;
};
-struct pvclock_vcpu_time_info {
- u32 version;
- u32 pad0;
- u64 tsc_timestamp;
- u64 system_time;
- u32 tsc_to_system_mul;
- s8 tsc_shift;
- u8 flags;
- u8 pad[2];
-} __attribute__((__packed__)); /* 32 bytes */
-
-struct pvclock_wall_clock {
- u32 version;
- u32 sec;
- u32 nsec;
-} __attribute__((__packed__));
+#include <asm/pvclock-abi.h>
struct vcpu_runstate_info {
u32 state;
|
| 3/2 | 2026/07/03 22:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 52030 output: 27 thoughts: 563 Thoughts: **Focusing the Fuzzing Lens on KVM Clock and TSC Changes** Okay, so I've got this patch series introducing a couple of new ioctls to KVM: `KVM_GET_CLOCK_GUEST` and `KVM_SET_CLOCK_GUEST`. We're also adding a new `KVM_VCPU_TSC_SCALE` attribute to `KVM_VCPU_TSC_CTRL`. Seems like we're touching quite a few files – `arch/x86/kvm/x86.c`, the Xen-specific code, and the VMX and SVM implementations – so we need to be really careful. And, of course, there are new tests in `tools/testing/selftests/kvm/x86/`, which is a good sign, but doesn’t make us safe. My focus right now is figuring out the most critical areas to target with fuzzing. Let me break down the functions that have been modified or added: * `kvm_vcpu_ioctl_get_clock_guest` * `kvm_vcpu_ioctl_set_clock_guest` * `kvm_arch_tsc_get_attr` (modified for the new TSC scale) * `kvm_arch_tsc_set_attr` (modified for the new TSC scale) * `kvm_arch_tsc_has_attr` (modified for the new TSC scale) * `kvm_guest_time_update` * `kvm_synchronize_tsc` * `kvm_update_masterclock` * `kvm_xen_update_runstate` Let's dive in and see where these functions live in the KVM ecosystem. `kvm_vcpu_ioctl_get_clock_guest` and `kvm_vcpu_ioctl_set_clock_guest` are going to be called from `kvm_arch_vcpu_ioctl` when the userspace calls `KVM_GET_CLOCK_GUEST` and `KVM_SET_CLOCK_GUEST` ioctls. I should keep an eye on how these are handled and if there’s any opportunity for interesting feedback. Then there are `kvm_arch_tsc_get_attr` and `kvm_arch_tsc_set_attr`. These are related to the new TSC scale attribute, and I can see these being called from `kvm_vcpu_ioctl_device_attr`, so I'll need to check the interactions there. `kvm_guest_time_update` is a good candidate for further analysis, too. Of course, the whole thing is gated by `CONFIG_KVM` and `CONFIG_X86_64` because the clock ioctls are behind the x86-64 guard, so those configurations will have to be active when running the tests. Alright, time to verify those configurations and then dig into the code and start identifying potential fuzzing targets, keeping these functions firmly in mind! |
| 4/2 | 2026/07/03 22:02 | tool | grepper |
0mArgs:{
"Expression": "kvm_vcpu_ioctl_get_clock_guest"
}Results: {
"Output": "arch/x86/kvm/x86.c=6240=static int kvm_get_reg_list(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/x86.c-6262-#ifdef CONFIG_X86_64\narch/x86/kvm/x86.c:6263:static int kvm_vcpu_ioctl_get_clock_guest(struct kvm_vcpu *v, void __user *argp)\narch/x86/kvm/x86.c-6264-{\n--\narch/x86/kvm/x86.c=6425=long kvm_arch_vcpu_ioctl(struct file *filp,\n--\narch/x86/kvm/x86.c-6829-\tcase KVM_GET_CLOCK_GUEST:\narch/x86/kvm/x86.c:6830:\t\tr = kvm_vcpu_ioctl_get_clock_guest(vcpu, argp);\narch/x86/kvm/x86.c-6831-\t\tbreak;\n"
} |
| 5/2 | 2026/07/03 22:02 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 53592 output: 24 thoughts: 196 Thoughts: **Fuzzing KVM Clock and TSC Attributes** Okay, so I've been looking at some recent KVM changes, and these new functions, `kvm_vcpu_ioctl_get_clock_guest` and `kvm_vcpu_ioctl_set_clock_guest`, they're definitely prime candidates for fuzzing. They're brand new, so there's a good chance they'll reveal some interesting bugs. I'll need to add them to the fuzzing campaign. Then there's the modification to `kvm_arch_tsc_get_attr` and `kvm_arch_tsc_set_attr` to support `KVM_VCPU_TSC_SCALE`. Anytime the TSC is touched, that’s a potential trouble spot, so I'll want to target these too. I’ll make sure the fuzzer covers this area well. And that `kvm_guest_time_update` change? Yeah, I should keep an eye on that one. Now, config-wise, `KVM` is already enabled in syzkaller, which is good. Let me see... are there any other config dependencies I need to be aware of? Ah, the ioctls are under `CONFIG_X86_64`, which is basically standard for x86_64 fuzzing, so I don't think I need to do anything special here. No, I think I'm good on that front. I’ll just list `KVM` as a specific config just to be explicit. Right, I need to take a closer look at `kvm_arch_tsc_get_attr` and `kvm_arch_tsc_set_attr` again and make sure my understanding of the changes is solid. |
| 6/2 | 2026/07/03 22:02 | tool | grepper |
0mArgs:{
"Expression": "kvm_arch_tsc_get_attr"
}Results: {
"Output": "arch/x86/kvm/x86.c=5957=static int kvm_arch_tsc_has_attr(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/x86.c-5975-\narch/x86/kvm/x86.c:5976:static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,\narch/x86/kvm/x86.c-5977-\t\t\t\t struct kvm_device_attr *attr)\n--\narch/x86/kvm/x86.c=6053=static int kvm_vcpu_ioctl_device_attr(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/x86.c-6070-\tcase KVM_GET_DEVICE_ATTR:\narch/x86/kvm/x86.c:6071:\t\tr = kvm_arch_tsc_get_attr(vcpu, \u0026attr);\narch/x86/kvm/x86.c-6072-\t\tbreak;\n"
} |
| 7/2 | 2026/07/03 22:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 54494 output: 24 thoughts: 16 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person: **Diving into `kvm_arch_tsc_set_attr`** Okay, time to dig into this `kvm_arch_tsc_set_attr` function. I need to refresh my memory on exactly how KVM handles TSC configuration at the architecture level. First, I'm expecting this function deals with setting attributes related to the Time Stamp Counter. I assume it's responsible for things like setting the scaling factors, handling frequency adjustments, and possibly implementing TSC-related features if the architecture has them. I'll be looking for how it interacts with the underlying hardware, how it's integrated with the VCPU's state, and how it handles potential errors. I'll need to pay close attention to the lock usage here, especially if it involves any global locks or per-VCPU locks, to ensure it's thread-safe and doesn't introduce any contention issues. Given the importance of the TSC for timing within a VM, I'm going to be especially critical of the performance implications of any operations performed here. |
| 8/2 | 2026/07/03 22:03 | tool | grepper |
0mArgs:{
"Expression": "kvm_arch_tsc_set_attr"
}Results: {
"Output": "arch/x86/kvm/x86.c=5976=static int kvm_arch_tsc_get_attr(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/x86.c-6010-\narch/x86/kvm/x86.c:6011:static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,\narch/x86/kvm/x86.c-6012-\t\t\t\t struct kvm_device_attr *attr)\n--\narch/x86/kvm/x86.c=6053=static int kvm_vcpu_ioctl_device_attr(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/x86.c-6073-\tcase KVM_SET_DEVICE_ATTR:\narch/x86/kvm/x86.c:6074:\t\tr = kvm_arch_tsc_set_attr(vcpu, \u0026attr);\narch/x86/kvm/x86.c-6075-\t\tbreak;\n"
} |
| 9/2 | 2026/07/03 22:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 55078 output: 22 thoughts: 14 Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own: **Investigating `kvm_guest_time_update`** Alright, time to get to it. My initial thought is to zero in on `kvm_guest_time_update`. That function name has a particular ring to it. It sounds like the core of the guest time synchronization within KVM, so it's probably *the* place to look if something isn't behaving as expected. I need to understand its role in the overall timekeeping architecture. Is it responsible for pushing updates, receiving them, applying them, or is it merely a cog in a more complex machine? I need to analyze how its behavior can be impacted by the environment, be that the hardware the hypervisor runs on or the other virtual machines that run on that hardware. I'll need to figure out the inputs, outputs, and any potential bottlenecks or race conditions. Knowing its place in the grand scheme of things will allow me to assess its efficacy. It's the starting point. Let's see what we can find. |
| 10/2 | 2026/07/03 22:03 | tool | grepper |
0mArgs:{
"Expression": "kvm_guest_time_update"
}Results: {
"Output": "arch/x86/kvm/x86.c=3304=static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock,\n--\narch/x86/kvm/x86.c-3351-\narch/x86/kvm/x86.c:3352:int kvm_guest_time_update(struct kvm_vcpu *v)\narch/x86/kvm/x86.c-3353-{\n--\narch/x86/kvm/x86.c=6263=static int kvm_vcpu_ioctl_get_clock_guest(struct kvm_vcpu *v, void __user *argp)\n--\narch/x86/kvm/x86.c-6271-\t * If KVM_REQ_CLOCK_UPDATE is already pending, or if the pvclock\narch/x86/kvm/x86.c:6272:\t * has never been generated at all, call kvm_guest_time_update().\narch/x86/kvm/x86.c-6273-\t */\n--\narch/x86/kvm/x86.c-6275-\t\tint idx = srcu_read_lock(\u0026v-\u003ekvm-\u003esrcu);\narch/x86/kvm/x86.c:6276:\t\tint ret = kvm_guest_time_update(v);\narch/x86/kvm/x86.c-6277-\n--\narch/x86/kvm/x86.c-6284-\t * Reconstruct the pvclock from the master clock state, matching\narch/x86/kvm/x86.c:6285:\t * exactly what kvm_guest_time_update() writes to the guest.\narch/x86/kvm/x86.c-6286-\t */\n--\narch/x86/kvm/x86.c=6344=static int kvm_vcpu_ioctl_set_clock_guest(struct kvm_vcpu *v, void __user *argp)\n--\narch/x86/kvm/x86.c-6374-\t * All clock state modifications between them are atomic with\narch/x86/kvm/x86.c:6375:\t * respect to readers in kvm_guest_time_update().\narch/x86/kvm/x86.c-6376-\t */\n--\narch/x86/kvm/x86.c=7439=static int kvm_vm_ioctl_set_clock(struct kvm *kvm, void __user *argp)\n--\narch/x86/kvm/x86.c-7459-\t/*\narch/x86/kvm/x86.c:7460:\t * This pairs with kvm_guest_time_update(): when masterclock is\narch/x86/kvm/x86.c-7461-\t * in use, we use master_kernel_ns + kvmclock_offset to set\n--\narch/x86/kvm/x86.c=11395=static int vcpu_enter_guest(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/x86.c-11431-\t\tif (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu)) {\narch/x86/kvm/x86.c:11432:\t\t\tr = kvm_guest_time_update(vcpu);\narch/x86/kvm/x86.c-11433-\t\t\tif (unlikely(r))\n--\narch/x86/kvm/x86.h=462=bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);\narch/x86/kvm/x86.h:463:int kvm_guest_time_update(struct kvm_vcpu *v);\narch/x86/kvm/x86.h-464-\n"
} |
| 11/2 | 2026/07/03 22:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 56427 output: 233 thoughts: 258 Thoughts: **KVM Clock and TSC Scaling Logic Update** Okay, so this patch is digging into KVM's clock and TSC scaling. It looks like we're getting some new ioctls: `KVM_GET_CLOCK_GUEST` and `KVM_SET_CLOCK_GUEST`. That's interesting, suggesting direct access for guests, which I presume implies more granular control over clock behavior. And there's a new attribute, `KVM_VCPU_TSC_SCALE`. I'll need to see the details, but my initial thought is this likely allows dynamic scaling of the TSC relative to the host clock. Important implications there for performance and determinism. I see that it's touching several key functions: `kvm_guest_time_update`, `kvm_synchronize_tsc`, and `kvm_update_masterclock`. Definitely core components. `kvm_guest_time_update` is likely where the guest's view of time is maintained. `kvm_synchronize_tsc` suggests we're wrestling with TSC synchronization issues, so this patch likely aims to handle drift and/or offset problems. `kvm_update_masterclock` I’m thinking will be concerned with the authoritative time base. The list of "focus symbols" confirms my initial suspicions. The `kvm_vcpu_ioctl_get/set_clock_guest` functions are the interface for those new ioctls, so I'll be sure to scrutinize them for their interaction with the existing clock infrastructure. Then there's the group of `kvm_arch_tsc_get/set/has_attr` functions, which further reinforce my thinking about attribute-based configuration of the TSC. This all feels very targeted – likely addressing some specific performance bottlenecks or introducing more flexible clock management in KVM. It looks like it directly impacts the fundamental `KVM` configuration. I need to see the exact code changes, but my guess is this set of changes aims to bring more precision or flexibility to the way guest clocks are managed relative to the host TSC, potentially enhancing guest performance or improving the accuracy of guest timekeeping, and if these changes are sound, I'll call set-results. |
| 12/2 | 2026/07/03 22:03 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_vcpu_ioctl_get_clock_guest",
"kvm_vcpu_ioctl_set_clock_guest",
"kvm_arch_tsc_get_attr",
"kvm_arch_tsc_set_attr",
"kvm_arch_tsc_has_attr",
"kvm_guest_time_update",
"kvm_synchronize_tsc",
"kvm_update_masterclock"
],
"Reasoning": "The patch introduces new KVM ioctls (KVM_GET_CLOCK_GUEST, KVM_SET_CLOCK_GUEST) and a new device attribute (KVM_VCPU_TSC_SCALE) for managing guest clocks and TSC scaling. It also modifies core KVM timekeeping functions like kvm_guest_time_update and kvm_synchronize_tsc. These are functional changes to reachable core kernel logic that should be fuzzed.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_vcpu_ioctl_get_clock_guest",
"kvm_vcpu_ioctl_set_clock_guest",
"kvm_arch_tsc_get_attr",
"kvm_arch_tsc_set_attr",
"kvm_arch_tsc_has_attr",
"kvm_guest_time_update",
"kvm_synchronize_tsc",
"kvm_update_masterclock"
],
"Reasoning": "The patch introduces new KVM ioctls (KVM_GET_CLOCK_GUEST, KVM_SET_CLOCK_GUEST) and a new device attribute (KVM_VCPU_TSC_SCALE) for managing guest clocks and TSC scaling. It also modifies core KVM timekeeping functions like kvm_guest_time_update and kvm_synchronize_tsc. These are functional changes to reachable core kernel logic that should be fuzzed.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|