AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/05 00:39 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_synchronize_tsc",
    "kvm_guest_time_update",
    "kvm_get_wall_clock_epoch",
    "kvm_compute_l1_tsc_offset",
    "kvm_arch_vcpu_postcreate"
  ],
  "Reasoning": "The patch modifies KVM x86 TSC (Time Stamp Counter) handling, including TSC scaling, master clock updates, and wall clock epoch calculations. This is core hypervisor logic reachable from guest VMs via MSR writes, CPUID, and KVM ioctls. Fuzzing should focus on the modified functions handling TSC synchronization and guest time updates.",
  "WorthFuzzing": true
}

1/1 2026/08/05 00:39 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 2624444c520b84733e92f65ede26d0ffb5b52f88\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Aug 5 00:39:01 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h\nindex 283847619ff88..2878a5181cd9b 100644\n--- a/arch/x86/include/asm/kvm_host.h\n+++ b/arch/x86/include/asm/kvm_host.h\n@@ -864,7 +864,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@@ -1236,6 +1236,7 @@ struct kvm_arch {\n \tu64 last_tsc_write;\n \tu32 last_tsc_khz;\n \tu64 last_tsc_offset;\n+\tu64 last_tsc_scaling_ratio;\n \tu64 cur_tsc_nsec;\n \tu64 cur_tsc_write;\n \tu64 cur_tsc_offset;\n@@ -1251,6 +1252,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/kvm/cpuid.c b/arch/x86/kvm/cpuid.c\nindex 9e9cf6538a96e..7049f5a5597b4 100644\n--- a/arch/x86/kvm/cpuid.c\n+++ b/arch/x86/kvm/cpuid.c\n@@ -16,6 +16,7 @@\n #include \u003clinux/vmalloc.h\u003e\n #include \u003clinux/uaccess.h\u003e\n #include \u003clinux/sched/stat.h\u003e\n+#include \u003clinux/units.h\u003e\n \n #include \u003casm/processor.h\u003e\n #include \u003casm/user.h\u003e\n@@ -2146,7 +2147,7 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,\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\t*eax = div_u64(vcpu-\u003earch.hw_tsc_hz, HZ_PER_KHZ);\n \t\t\t}\n \t\t}\n \t} else {\ndiff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c\nindex 66fa7140d65d9..22ceb39d41f66 100644\n--- a/arch/x86/kvm/msrs.c\n+++ b/arch/x86/kvm/msrs.c\n@@ -1631,7 +1631,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}\ndiff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c\nindex 9d607b98bd067..3057ca7051db1 100644\n--- a/arch/x86/kvm/svm/svm.c\n+++ b/arch/x86/kvm/svm/svm.c\n@@ -5602,7 +5602,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 e3bfe6aca1a0e..9abd2ed3aeae2 100644\n--- a/arch/x86/kvm/vmx/vmx.c\n+++ b/arch/x86/kvm/vmx/vmx.c\n@@ -2841,6 +2841,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 d94b59140c452..cbe4fd3457b44 100644\n--- a/arch/x86/kvm/x86.c\n+++ b/arch/x86/kvm/x86.c\n@@ -61,6 +61,7 @@\n #include \u003clinux/mem_encrypt.h\u003e\n #include \u003clinux/suspend.h\u003e\n #include \u003clinux/smp.h\u003e\n+#include \u003clinux/units.h\u003e\n \n #include \u003ctrace/events/ipi.h\u003e\n #include \u003ctrace/events/kvm.h\u003e\n@@ -873,7 +874,6 @@ static inline bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu)\n \t       kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending();\n }\n \n-#ifdef CONFIG_X86_64\n struct pvclock_clock {\n \tint vclock_mode;\n \tu64 cycle_last;\n@@ -931,13 +931,6 @@ static s64 get_kvmclock_base_ns(void)\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 }\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 uint32_t div_frac(uint32_t dividend, uint32_t divisor)\n {\n@@ -945,32 +938,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+\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-\ttps64 = base_hz;\n-\tscaled64 = scaled_hz;\n-\twhile (tps64 \u003e scaled64*2 || tps64 \u0026 0xffffffff00000000ULL) {\n-\t\ttps64 \u003e\u003e= 1;\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@@ -1061,11 +1079,15 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)\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+\ttsc = pvclock_scale_delta(abs(delta_ns),\n+\t\t\t\t  vcpu-\u003earch.virtual_tsc_mult,\n+\t\t\t\t  vcpu-\u003earch.virtual_tsc_shift);\n+\n+\treturn vcpu-\u003earch.this_tsc_write + (delta_ns \u003e= 0 ? tsc : -tsc);\n }\n \n #ifdef CONFIG_X86_64\n@@ -1131,11 +1153,12 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio)\n \treturn _tsc;\n }\n \n-u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc)\n+u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,\n+\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@@ -1254,6 +1277,7 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,\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+\tkvm-\u003earch.last_tsc_scaling_ratio = vcpu-\u003earch.l1_tsc_scaling_ratio;\n \n \tvcpu-\u003earch.last_guest_tsc = tsc;\n \n@@ -1296,7 +1320,7 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\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+\toffset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);\n \tns = get_kvmclock_base_ns();\n \telapsed = ns - kvm-\u003earch.last_tsc_nsec;\n \n@@ -1345,7 +1369,7 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\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\toffset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);\n \t\t}\n \t\tmatched = true;\n \t}\n@@ -1566,6 +1590,8 @@ 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@@ -1589,9 +1615,30 @@ static void pvclock_update_vm_gtod_copy(struct kvm *kvm)\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 \n-\tif (ka-\u003euse_master_clock)\n+\tif (ka-\u003euse_master_clock) {\n+\t\tu64 tsc_hz;\n+\n \t\tatomic_set(\u0026kvm_guest_has_master_clock, 1);\n \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-\u003elast_tsc_scaling_ratio;\n+\t\ttsc_hz = (u64)get_cpu_tsc_khz() * HZ_PER_KHZ;\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 \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@@ -1658,39 +1705,49 @@ static unsigned long get_cpu_tsc_khz(void)\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 bool __get_kvmclock_master_clock(struct kvm *kvm,\n+\t\t\t\t\tstruct kvm_clock_data *data)\n {\n+#ifdef CONFIG_X86_64\n \tstruct kvm_arch *ka = \u0026kvm-\u003earch;\n \tstruct pvclock_vcpu_time_info hv_clock;\n+\tstruct timespec64 ts;\n \n-\t/* both __this_cpu_read() and rdtsc() should be on the same cpu */\n-\tget_cpu();\n+\tif (!ka-\u003euse_master_clock)\n+\t\treturn false;\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-#ifdef CONFIG_X86_64\n-\t\tstruct timespec64 ts;\n+\tif (!kvm_get_walltime_and_clockread(\u0026ts, \u0026data-\u003ehost_tsc))\n+\t\treturn false;\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-\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+\tdata-\u003erealtime = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec;\n+\tdata-\u003eflags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC |\n+\t\t       KVM_CLOCK_TSC_STABLE;\n+\n+\thv_clock.tsc_timestamp = ka-\u003emaster_cycle_now;\n+\thv_clock.system_time = ka-\u003emaster_kernel_ns + ka-\u003ekvmclock_offset;\n+\n+\t/*\n+\t * Use the precomputed guest-TSC-based mul/shift so that the kvmclock\n+\t * value matches what the guest computes from its own TSC.\n+\t */\n+\thv_clock.tsc_shift = ka-\u003emaster_tsc_shift;\n+\thv_clock.tsc_to_system_mul = ka-\u003emaster_tsc_mul;\n+\n+\tif (kvm_caps.has_tsc_control) {\n+\t\tu64 tsc_delta = data-\u003ehost_tsc - ka-\u003emaster_cycle_now;\n+\n+\t\ttsc_delta = kvm_scale_tsc(tsc_delta, ka-\u003emaster_tsc_scaling_ratio);\n+\t\tdata-\u003eclock = hv_clock.system_time +\n+\t\t\t      pvclock_scale_delta(tsc_delta,\n+\t\t\t\t\t\t  hv_clock.tsc_to_system_mul,\n+\t\t\t\t\t\t  hv_clock.tsc_shift);\n \t} else {\n-\t\tdata-\u003eclock = get_kvmclock_base_ns() + ka-\u003ekvmclock_offset;\n+\t\tdata-\u003eclock = __pvclock_read_cycles(\u0026hv_clock, data-\u003ehost_tsc);\n \t}\n-\n-\tput_cpu();\n+\treturn true;\n+#else\n+\treturn false;\n+#endif\n }\n \n static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)\n@@ -1699,8 +1756,11 @@ static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)\n \tunsigned seq;\n \n \tdo {\n+\t\tdata-\u003eflags = 0;\n+\n \t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n-\t\t__get_kvmclock(kvm, data);\n+\t\tif (!__get_kvmclock_master_clock(kvm, data))\n+\t\t\tdata-\u003eclock = get_kvmclock_base_ns() + ka-\u003ekvmclock_offset;\n \t} while (read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq));\n }\n \n@@ -1762,36 +1822,45 @@ static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock,\n \n int kvm_guest_time_update(struct kvm_vcpu *v)\n {\n+\tu64 tgt_tsc_hz, tsc_timestamp, host_tsc, master_tsc, master_ns;\n+\tstruct kvm_arch *ka __maybe_unused = \u0026v-\u003ekvm-\u003earch;\n \tstruct pvclock_vcpu_time_info hv_clock = {};\n-\tunsigned long flags, tgt_tsc_khz;\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-\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+#ifdef CONFIG_X86_64\n+\tbool use_master_clock;\n+\tunsigned int seq;\n+\n \tdo {\n \t\tseq = read_seqcount_begin(\u0026ka-\u003epvclock_sc);\n \t\tuse_master_clock = ka-\u003euse_master_clock;\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\tif (!use_master_clock)\n+\t\t\tcontinue;\n+\n+\t\tif (!kvm_get_time_and_clockread(\u0026kernel_ns, \u0026host_tsc)) {\n+\t\t\tuse_master_clock = false;\n+\t\t\tcontinue;\n \t\t}\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+\t\tmaster_tsc = ka-\u003emaster_cycle_now;\n+\t\tmaster_ns = ka-\u003emaster_kernel_ns;\n+\t} while (read_seqcount_retry(\u0026ka-\u003epvclock_sc, seq));\n+#else\n+\tconst bool use_master_clock = false;\n+#endif\n+\t/*\n+\t * Ensure reading the TSC+frequency pair is done on the same CPU.  When\n+\t * NOT using the master clock, the TSC frequency may vary between CPUs.\n+\t */\n+\tpreempt_disable();\n+\ttgt_tsc_hz = (u64)get_cpu_tsc_khz() * HZ_PER_KHZ;\n+\tif (unlikely(tgt_tsc_hz == 0)) {\n+\t\tpreempt_enable();\n \t\tkvm_make_request(KVM_REQ_CLOCK_UPDATE, v);\n \t\treturn 1;\n \t}\n@@ -1820,28 +1889,43 @@ int kvm_guest_time_update(struct kvm_vcpu *v)\n \t\t}\n \t}\n \n-\tlocal_irq_restore(flags);\n+\t/*\n+\t * Refresh L1's last \"observed\" TSC to match the PV clock's timestamp,\n+\t * e.g. so that the guest can't see a TSC that's behind the reference.\n+\t */\n+\tvcpu-\u003elast_guest_tsc = tsc_timestamp;\n+\n+\tpreempt_enable();\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-\tvcpu-\u003elast_guest_tsc = tsc_timestamp;\n+\t/*\n+\t * If the master clock is NOT in use, the reference time placed in the\n+\t * hv_clock is \"now\".  If master clock is in use, the reference time is\n+\t * the master clock's snapshot from some time in the past, not \"now\".\n+\t */\n+\tif (use_master_clock) {\n+\t\thv_clock.tsc_timestamp = kvm_read_l1_tsc(v, master_tsc);\n+\t\thv_clock.system_time = master_ns + v-\u003ekvm-\u003earch.kvmclock_offset;\n+\t} else {\n+\t\thv_clock.tsc_timestamp = tsc_timestamp;\n+\t\thv_clock.system_time = kernel_ns + v-\u003ekvm-\u003earch.kvmclock_offset;\n+\t}\n \n \t/* If the host uses TSC clocksource, then it is stable */\n \thv_clock.flags = 0;\n@@ -1903,63 +1987,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@@ -2583,7 +2626,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@@ -6866,6 +6909,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@@ -6873,26 +6917,26 @@ 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 \tstruct timekeeper *tk = priv;\n \n \tupdate_pvclock_gtod(tk);\n \n+#ifdef CONFIG_X86_64\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(pvclock_gtod_data.clock.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@@ -7111,9 +7155,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@@ -7170,8 +7214,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@@ -9440,6 +9484,8 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)\n \t\treturn;\n \tvcpu_load(vcpu);\n \tkvm_synchronize_tsc(vcpu, NULL);\n+\tif (!vcpu-\u003ekvm-\u003earch.use_master_clock)\n+\t\tkvm_update_masterclock(vcpu-\u003ekvm);\n \tvcpu_put(vcpu);\n \n \t/* poll control enabled by default */\ndiff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h\nindex 0f5919b092e47..1d9a66048b01e 100644\n--- a/arch/x86/kvm/x86.h\n+++ b/arch/x86/kvm/x86.h\n@@ -335,7 +335,8 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio);\n u64 kvm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc);\n u64 kvm_calc_nested_tsc_offset(u64 l1_offset, u64 l2_offset, u64 l2_multiplier);\n u64 kvm_calc_nested_tsc_multiplier(u64 l1_multiplier, u64 l2_multiplier);\n-u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc);\n+u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,\n+\t\t\t      u64 target_tsc);\n void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset);\n \n static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu,\n"
}

2/1 2026/08/05 00:39 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_synchronize_tsc",
    "kvm_guest_time_update",
    "kvm_get_wall_clock_epoch",
    "kvm_compute_l1_tsc_offset",
    "kvm_arch_vcpu_postcreate"
  ],
  "Reasoning": "The patch modifies KVM x86 TSC (Time Stamp Counter) handling, including TSC scaling, master clock updates, and wall clock epoch calculations. This is core hypervisor logic reachable from guest VMs via MSR writes, CPUID, and KVM ioctls. Fuzzing should focus on the modified functions handling TSC synchronization and guest time updates.",
  "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).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

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.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
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.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. 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 2624444c520b84733e92f65ede26d0ffb5b52f88
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Aug 5 00:39:01 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 283847619ff88..2878a5181cd9b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -864,7 +864,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;
@@ -1236,6 +1236,7 @@ struct kvm_arch {
 	u64 last_tsc_write;
 	u32 last_tsc_khz;
 	u64 last_tsc_offset;
+	u64 last_tsc_scaling_ratio;
 	u64 cur_tsc_nsec;
 	u64 cur_tsc_write;
 	u64 cur_tsc_offset;
@@ -1251,6 +1252,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/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 9e9cf6538a96e..7049f5a5597b4 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -16,6 +16,7 @@
 #include <linux/vmalloc.h>
 #include <linux/uaccess.h>
 #include <linux/sched/stat.h>
+#include <linux/units.h>
 
 #include <asm/processor.h>
 #include <asm/user.h>
@@ -2146,7 +2147,7 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
 				*ecx = vcpu->arch.pvclock_tsc_mul;
 				*edx = vcpu->arch.pvclock_tsc_shift;
 			} else if (index == 2) {
-				*eax = vcpu->arch.hw_tsc_khz;
+				*eax = div_u64(vcpu->arch.hw_tsc_hz, HZ_PER_KHZ);
 			}
 		}
 	} else {
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 66fa7140d65d9..22ceb39d41f66 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -1631,7 +1631,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;
 		}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 9d607b98bd067..3057ca7051db1 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -5602,7 +5602,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 e3bfe6aca1a0e..9abd2ed3aeae2 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2841,6 +2841,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 d94b59140c452..cbe4fd3457b44 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -61,6 +61,7 @@
 #include <linux/mem_encrypt.h>
 #include <linux/suspend.h>
 #include <linux/smp.h>
+#include <linux/units.h>
 
 #include <trace/events/ipi.h>
 #include <trace/events/kvm.h>
@@ -873,7 +874,6 @@ static inline bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu)
 	       kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending();
 }
 
-#ifdef CONFIG_X86_64
 struct pvclock_clock {
 	int vclock_mode;
 	u64 cycle_last;
@@ -931,13 +931,6 @@ 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));
 }
-#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 uint32_t div_frac(uint32_t dividend, uint32_t divisor)
 {
@@ -945,32 +938,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;
+
+	/*
+	 * 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.
+	 */
 
-	tps64 = base_hz;
-	scaled64 = scaled_hz;
-	while (tps64 > scaled64*2 || tps64 & 0xffffffff00000000ULL) {
-		tps64 >>= 1;
+	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
@@ -1061,11 +1079,15 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz)
 
 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) */
+	tsc = pvclock_scale_delta(abs(delta_ns),
+				  vcpu->arch.virtual_tsc_mult,
+				  vcpu->arch.virtual_tsc_shift);
+
+	return vcpu->arch.this_tsc_write + (delta_ns >= 0 ? tsc : -tsc);
 }
 
 #ifdef CONFIG_X86_64
@@ -1131,11 +1153,12 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio)
 	return _tsc;
 }
 
-u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc)
+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;
 }
@@ -1254,6 +1277,7 @@ static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,
 	kvm->arch.last_tsc_write = tsc;
 	kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz;
 	kvm->arch.last_tsc_offset = offset;
+	kvm->arch.last_tsc_scaling_ratio = vcpu->arch.l1_tsc_scaling_ratio;
 
 	vcpu->arch.last_guest_tsc = tsc;
 
@@ -1296,7 +1320,7 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)
 	bool synchronizing = false;
 
 	raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags);
-	offset = kvm_compute_l1_tsc_offset(vcpu, data);
+	offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);
 	ns = get_kvmclock_base_ns();
 	elapsed = ns - kvm->arch.last_tsc_nsec;
 
@@ -1345,7 +1369,7 @@ void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)
 		} else {
 			u64 delta = nsec_to_cycles(vcpu, elapsed);
 			data += delta;
-			offset = kvm_compute_l1_tsc_offset(vcpu, data);
+			offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);
 		}
 		matched = true;
 	}
@@ -1566,6 +1590,8 @@ 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
@@ -1589,9 +1615,30 @@ static void pvclock_update_vm_gtod_copy(struct kvm *kvm)
 				&& !ka->backwards_tsc_observed
 				&& !ka->boot_vcpu_runs_old_kvmclock;
 
-	if (ka->use_master_clock)
+	if (ka->use_master_clock) {
+		u64 tsc_hz;
+
 		atomic_set(&kvm_guest_has_master_clock, 1);
 
+		/*
+		 * 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->last_tsc_scaling_ratio;
+		tsc_hz = (u64)get_cpu_tsc_khz() * HZ_PER_KHZ;
+		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;
+	}
+
 	vclock_mode = pvclock_gtod_data.clock.vclock_mode;
 	trace_kvm_update_master_clock(ka->use_master_clock, vclock_mode,
 					vcpus_matched);
@@ -1658,39 +1705,49 @@ static unsigned long get_cpu_tsc_khz(void)
 }
 
 /* Called within read_seqcount_begin/retry for kvm->pvclock_sc.  */
-static void __get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)
+static bool __get_kvmclock_master_clock(struct kvm *kvm,
+					struct kvm_clock_data *data)
 {
+#ifdef CONFIG_X86_64
 	struct kvm_arch *ka = &kvm->arch;
 	struct pvclock_vcpu_time_info hv_clock;
+	struct timespec64 ts;
 
-	/* both __this_cpu_read() and rdtsc() should be on the same cpu */
-	get_cpu();
+	if (!ka->use_master_clock)
+		return false;
 
-	data->flags = 0;
-	if (ka->use_master_clock &&
-	    (static_cpu_has(X86_FEATURE_CONSTANT_TSC) || __this_cpu_read(cpu_tsc_khz))) {
-#ifdef CONFIG_X86_64
-		struct timespec64 ts;
+	if (!kvm_get_walltime_and_clockread(&ts, &data->host_tsc))
+		return false;
 
-		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();
-
-		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);
+	data->realtime = ts.tv_nsec + NSEC_PER_SEC * ts.tv_sec;
+	data->flags |= KVM_CLOCK_REALTIME | KVM_CLOCK_HOST_TSC |
+		       KVM_CLOCK_TSC_STABLE;
+
+	hv_clock.tsc_timestamp = ka->master_cycle_now;
+	hv_clock.system_time = ka->master_kernel_ns + ka->kvmclock_offset;
+
+	/*
+	 * 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 = get_kvmclock_base_ns() + ka->kvmclock_offset;
+		data->clock = __pvclock_read_cycles(&hv_clock, data->host_tsc);
 	}
-
-	put_cpu();
+	return true;
+#else
+	return false;
+#endif
 }
 
 static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)
@@ -1699,8 +1756,11 @@ static void get_kvmclock(struct kvm *kvm, struct kvm_clock_data *data)
 	unsigned seq;
 
 	do {
+		data->flags = 0;
+
 		seq = read_seqcount_begin(&ka->pvclock_sc);
-		__get_kvmclock(kvm, data);
+		if (!__get_kvmclock_master_clock(kvm, data))
+			data->clock = get_kvmclock_base_ns() + ka->kvmclock_offset;
 	} while (read_seqcount_retry(&ka->pvclock_sc, seq));
 }
 
@@ -1762,36 +1822,45 @@ static void kvm_setup_guest_pvclock(struct pvclock_vcpu_time_info *ref_hv_clock,
 
 int kvm_guest_time_update(struct kvm_vcpu *v)
 {
+	u64 tgt_tsc_hz, tsc_timestamp, host_tsc, master_tsc, master_ns;
+	struct kvm_arch *ka __maybe_unused = &v->kvm->arch;
 	struct pvclock_vcpu_time_info hv_clock = {};
-	unsigned long flags, tgt_tsc_khz;
-	unsigned seq;
 	struct kvm_vcpu_arch *vcpu = &v->arch;
-	struct kvm_arch *ka = &v->kvm->arch;
 	s64 kernel_ns;
-	u64 tsc_timestamp, host_tsc;
-	bool use_master_clock;
-
-	kernel_ns = 0;
-	host_tsc = 0;
 
 	/*
 	 * If the host uses TSC clock, then passthrough TSC as stable
 	 * to the guest.
 	 */
+#ifdef CONFIG_X86_64
+	bool use_master_clock;
+	unsigned int seq;
+
 	do {
 		seq = read_seqcount_begin(&ka->pvclock_sc);
 		use_master_clock = ka->use_master_clock;
-		if (use_master_clock) {
-			host_tsc = ka->master_cycle_now;
-			kernel_ns = ka->master_kernel_ns;
+		if (!use_master_clock)
+			continue;
+
+		if (!kvm_get_time_and_clockread(&kernel_ns, &host_tsc)) {
+			use_master_clock = false;
+			continue;
 		}
-	} 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);
+		master_tsc = ka->master_cycle_now;
+		master_ns = ka->master_kernel_ns;
+	} while (read_seqcount_retry(&ka->pvclock_sc, seq));
+#else
+	const bool use_master_clock = false;
+#endif
+	/*
+	 * Ensure reading the TSC+frequency pair is done on the same CPU.  When
+	 * NOT using the master clock, the TSC frequency may vary between CPUs.
+	 */
+	preempt_disable();
+	tgt_tsc_hz = (u64)get_cpu_tsc_khz() * HZ_PER_KHZ;
+	if (unlikely(tgt_tsc_hz == 0)) {
+		preempt_enable();
 		kvm_make_request(KVM_REQ_CLOCK_UPDATE, v);
 		return 1;
 	}
@@ -1820,28 +1889,43 @@ int kvm_guest_time_update(struct kvm_vcpu *v)
 		}
 	}
 
-	local_irq_restore(flags);
+	/*
+	 * Refresh L1's last "observed" TSC to match the PV clock's timestamp,
+	 * e.g. so that the guest can't see a TSC that's behind the reference.
+	 */
+	vcpu->last_guest_tsc = tsc_timestamp;
+
+	preempt_enable();
 
 	/* 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;
-	vcpu->last_guest_tsc = tsc_timestamp;
+	/*
+	 * If the master clock is NOT in use, the reference time placed in the
+	 * hv_clock is "now".  If master clock is in use, the reference time is
+	 * the master clock's snapshot from some time in the past, not "now".
+	 */
+	if (use_master_clock) {
+		hv_clock.tsc_timestamp = kvm_read_l1_tsc(v, master_tsc);
+		hv_clock.system_time = master_ns + v->kvm->arch.kvmclock_offset;
+	} else {
+		hv_clock.tsc_timestamp = tsc_timestamp;
+		hv_clock.system_time = kernel_ns + v->kvm->arch.kvmclock_offset;
+	}
 
 	/* If the host uses TSC clocksource, then it is stable */
 	hv_clock.flags = 0;
@@ -1903,63 +1987,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;
 }
 
 /*
@@ -2583,7 +2626,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)
@@ -6866,6 +6909,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.
@@ -6873,26 +6917,26 @@ 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;
 	struct timekeeper *tk = priv;
 
 	update_pvclock_gtod(tk);
 
+#ifdef CONFIG_X86_64
 	/*
 	 * 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(pvclock_gtod_data.clock.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)
 {
@@ -7111,9 +7155,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
@@ -7170,8 +7214,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
@@ -9440,6 +9484,8 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
 		return;
 	vcpu_load(vcpu);
 	kvm_synchronize_tsc(vcpu, NULL);
+	if (!vcpu->kvm->arch.use_master_clock)
+		kvm_update_masterclock(vcpu->kvm);
 	vcpu_put(vcpu);
 
 	/* poll control enabled by default */
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 0f5919b092e47..1d9a66048b01e 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -335,7 +335,8 @@ u64 kvm_scale_tsc(u64 tsc, u64 ratio);
 u64 kvm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc);
 u64 kvm_calc_nested_tsc_offset(u64 l1_offset, u64 l2_offset, u64 l2_multiplier);
 u64 kvm_calc_nested_tsc_multiplier(u64 l1_multiplier, u64 l2_multiplier);
-u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc);
+u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,
+			      u64 target_tsc);
 void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset);
 
 static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu,

3/2 2026/08/05 00:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11879
							output: 34
							thoughts: 1256
						

Thoughts:
**Analyzing KVM x86 TSC Handling Changes**

Okay, so I'm looking at this patch series that's tweaking KVM's x86 TSC implementation. My initial impression is that this is a fairly deep dive into how KVM handles time-related aspects within the virtualized environment. The changes touch a lot of core KVM code, specifically related to the Time Stamp Counter (TSC).

My first thought is the architectural impact. They've updated data structures like `kvm_vcpu_arch` and `kvm_arch` to accommodate the TSC changes, renaming `hw_tsc_khz` to `hw_tsc_hz`, and introducing new variables related to scaling ratios and shifts. This immediately suggests a shift in how TSC frequency is managed and calculated. I also notice they're now passing the host TSC value to `kvm_compute_l1_tsc_offset`, which hints at improvements in synchronization and offset calculation between host and guest. The changes to `kvm_cpuid` and `kvm_set_msr_common` confirm this focus on refined TSC information exchange.

The dependence on `X86_FEATURE_CONSTANT_TSC` in `svm_hardware_setup` and `setup_vmcs_config` is important – it indicates that these updates are optimized for systems where the TSC is known to be stable, so it's good that they're explicitly checking for that. It also feels like these changes are geared towards more precise guest time keeping. They're refining how the guest TSC is computed, making it handle negative deltas gracefully. Refactoring of the `kvmclock` functions and the use of `tgt_tsc_hz` instead of `tgt_tsc_khz` in `kvm_guest_time_update` further emphasize the focus on increased accuracy and consistency.

Now, as a KVM expert, I need to consider the broader implications. We're looking at core functions, including `kvm_synchronize_tsc`, `kvm_guest_time_update`, `kvm_get_wall_clock_epoch`, `kvm_compute_l1_tsc_offset`, and `kvm_arch_vcpu_postcreate`. Given my knowledge of the codebase, I know these are reachable from user space via ioctls or MSR writes. This means they are reachable from a fuzzer (syzkaller, in my case).

Essentially, these TSC changes are an integral part of KVM, making them a very viable target for fuzzing. With this in mind, the refactoring and argument additions are really important. Any changes to synchronization or offset calculations will be particularly interesting targets.



4/2 2026/08/05 00:39 tool grepper
0m Args:
{
  "Expression": "config KVM",
  "PathPrefix": "arch/x86/kvm/Kconfig"
}

Results:
{
  "Output": "arch/x86/kvm/Kconfig=18=if VIRTUALIZATION\narch/x86/kvm/Kconfig-19-\narch/x86/kvm/Kconfig:20:config KVM_X86\narch/x86/kvm/Kconfig-21-\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n--\narch/x86/kvm/Kconfig-51-\narch/x86/kvm/Kconfig:52:config KVM\narch/x86/kvm/Kconfig-53-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n--\narch/x86/kvm/Kconfig-68-\narch/x86/kvm/Kconfig:69:config KVM_WERROR\narch/x86/kvm/Kconfig-70-\tbool \"Compile KVM with -Werror\"\n--\narch/x86/kvm/Kconfig-82-\narch/x86/kvm/Kconfig:83:config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-84-\tbool \"Enable support for KVM software-protected VMs\"\n--\narch/x86/kvm/Kconfig-95-\narch/x86/kvm/Kconfig:96:config KVM_INTEL\narch/x86/kvm/Kconfig-97-\ttristate \"KVM for Intel (and compatible) processors support\"\n--\narch/x86/kvm/Kconfig-106-\narch/x86/kvm/Kconfig:107:config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-108-        bool \"Check that guests do not receive #VE exceptions\"\n--\narch/x86/kvm/Kconfig=122=config X86_SGX_KVM\n--\narch/x86/kvm/Kconfig-133-\narch/x86/kvm/Kconfig:134:config KVM_INTEL_TDX\narch/x86/kvm/Kconfig-135-\tbool \"Intel Trust Domain Extensions (TDX) support\"\n--\narch/x86/kvm/Kconfig-145-\narch/x86/kvm/Kconfig:146:config KVM_AMD\narch/x86/kvm/Kconfig-147-\ttristate \"KVM for AMD processors support\"\n--\narch/x86/kvm/Kconfig-155-\narch/x86/kvm/Kconfig:156:config KVM_AMD_SEV\narch/x86/kvm/Kconfig-157-\tbool \"AMD Secure Encrypted Virtualization (SEV) support\"\n--\narch/x86/kvm/Kconfig-172-\narch/x86/kvm/Kconfig:173:config KVM_IOAPIC\narch/x86/kvm/Kconfig-174-\tbool \"I/O APIC, PIC, and PIT emulation\"\n--\narch/x86/kvm/Kconfig-182-\narch/x86/kvm/Kconfig:183:config KVM_SMM\narch/x86/kvm/Kconfig-184-\tbool \"System Management Mode emulation\"\n--\narch/x86/kvm/Kconfig-193-\narch/x86/kvm/Kconfig:194:config KVM_HYPERV\narch/x86/kvm/Kconfig-195-\tbool \"Support for Microsoft Hyper-V emulation\"\n--\narch/x86/kvm/Kconfig-207-\narch/x86/kvm/Kconfig:208:config KVM_XEN\narch/x86/kvm/Kconfig-209-\tbool \"Support for Xen hypercall interface\"\n--\narch/x86/kvm/Kconfig-216-\narch/x86/kvm/Kconfig:217:config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-218-\tbool \"Prove KVM MMU correctness\"\n--\narch/x86/kvm/Kconfig-229-\narch/x86/kvm/Kconfig:230:config KVM_EXTERNAL_WRITE_TRACKING\narch/x86/kvm/Kconfig-231-\tbool\narch/x86/kvm/Kconfig-232-\narch/x86/kvm/Kconfig:233:config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-234-\tint \"Maximum number of vCPUs per KVM guest\"\n"
}

5/2 2026/08/05 00:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14952
							output: 36
							thoughts: 388
						

Thoughts:
**Analyzing the KVM x86 Patch**

Alright, let's break down this KVM x86 patch. First, it's pretty clear this is KVM-specific, which is a given, and we'll need to confirm that KVM is indeed enabled. Digging deeper, it looks like this patch is touching `arch/x86/kvm/x86.c`, `arch/x86/kvm/svm/svm.c`, and `arch/x86/kvm/vmx/vmx.c`. Given those files, we're likely looking at changes related to either Intel or AMD virtualization extensions, so just enabling `KVM` should be sufficient for the build requirements.

Now, let's get into the functions the patch is targeting. I see:

*   `kvm_synchronize_tsc`
*   `kvm_guest_time_update`
*   `kvm_get_wall_clock_epoch`
*   `kvm_compute_l1_tsc_offset`
*   `kvm_arch_vcpu_postcreate`

Okay, and now we need to consider if these are hot path functions. It looks like `kvm_guest_time_update` will get called periodically, or when exits occur, probably a pretty common operation. `kvm_synchronize_tsc` is triggered by MSR writes, so anything writing the TSC will be calling that. `kvm_get_wall_clock_epoch` happens on `KVM_GET_CLOCK` ioctls and `kvm_arch_vcpu_postcreate` is tied to vCPU creation, which is also a frequent operation.

Finally, a quick check to see if we're dealing with exported functions or statics. It appears none of these are static, which implies that they're being used outside the scope of their original source files, which means we might be dealing with an API change.



6/2 2026/08/05 00:39 tool grepper
0m Args:
{
  "Expression": "kvm_synchronize_tsc",
  "PathPrefix": "arch/x86/kvm/"
}

Results:
{
  "Output": "arch/x86/kvm/msrs.c=1442=int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1631-\t\tif (msr_info-\u003ehost_initiated) {\narch/x86/kvm/msrs.c:1632:\t\t\tkvm_synchronize_tsc(vcpu, \u0026data);\narch/x86/kvm/msrs.c-1633-\t\t} else if (!vcpu-\u003earch.guest_tsc_protected) {\n--\narch/x86/kvm/x86.c=1241=static inline bool kvm_check_tsc_unstable(void)\n--\narch/x86/kvm/x86.c-1258- */\narch/x86/kvm/x86.c:1259:static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc,\narch/x86/kvm/x86.c-1260-\t\t\t\t  u64 ns, bool matched, bool user_set_tsc)\n--\narch/x86/kvm/x86.c-1312-\narch/x86/kvm/x86.c:1313:void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\narch/x86/kvm/x86.c-1314-{\n--\narch/x86/kvm/x86.c-1376-\narch/x86/kvm/x86.c:1377:\t__kvm_synchronize_tsc(vcpu, offset, data, ns, matched, !!user_value);\narch/x86/kvm/x86.c-1378-\traw_spin_unlock_irqrestore(\u0026kvm-\u003earch.tsc_write_lock, flags);\n--\narch/x86/kvm/x86.c=3338=static int kvm_arch_tsc_set_attr(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/x86.c-3363-\narch/x86/kvm/x86.c:3364:\t\t__kvm_synchronize_tsc(vcpu, offset, tsc, ns, matched, true);\narch/x86/kvm/x86.c-3365-\t\traw_spin_unlock_irqrestore(\u0026kvm-\u003earch.tsc_write_lock, flags);\n--\narch/x86/kvm/x86.c=9481=void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/x86.c-9485-\tvcpu_load(vcpu);\narch/x86/kvm/x86.c:9486:\tkvm_synchronize_tsc(vcpu, NULL);\narch/x86/kvm/x86.c-9487-\tif (!vcpu-\u003ekvm-\u003earch.use_master_clock)\n--\narch/x86/kvm/x86.h=331=int kvm_guest_time_update(struct kvm_vcpu *v);\narch/x86/kvm/x86.h-332-\narch/x86/kvm/x86.h:333:void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value);\narch/x86/kvm/x86.h-334-u64 kvm_scale_tsc(u64 tsc, u64 ratio);\n"
}

7/2 2026/08/05 00:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16790
							output: 39
							thoughts: 231
						

Thoughts:
**My Assessment of the KVM Patch**

Okay, so this patch looks like it's digging into the heart of KVM's TSC scaling and master clock mechanisms. That's a critical area, and the fact that it's functional and reachable means it's definitely fuzzable. Time to dig in.

My focus should be on these key symbols: `kvm_synchronize_tsc`, `kvm_guest_time_update`, `kvm_get_wall_clock_epoch`, `kvm_compute_l1_tsc_offset`, and `kvm_arch_vcpu_postcreate`. They seem central to the changes.

Now, configuration.  I need to make sure the fuzzing setup can actually hit this code. I need to enable some KVM configs. Right, let's start with `KVM`, but I bet for actual execution, I'll also need either `KVM_INTEL` or `KVM_AMD`. The instructions say to only add `KVM`, `KVM_INTEL`, and `KVM_AMD` to `EnableConfigs`, without the `CONFIG_` prefix.

Before I move on, let me check the implementation of `kvm_get_wall_clock_epoch`. That one seems interesting.



8/2 2026/08/05 00:39 tool grepper
0m Args:
{
  "Expression": "kvm_get_wall_clock_epoch",
  "PathPrefix": "arch/x86/kvm/"
}

Results:
{
  "Output": "arch/x86/kvm/msrs.c=1224=static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock, int sec_hi_ofs)\n--\narch/x86/kvm/msrs.c-1246-\narch/x86/kvm/msrs.c:1247:\twall_nsec = kvm_get_wall_clock_epoch(kvm);\narch/x86/kvm/msrs.c-1248-\n--\narch/x86/kvm/x86.c=1823=int kvm_guest_time_update(struct kvm_vcpu *v)\n--\narch/x86/kvm/x86.c-1991- */\narch/x86/kvm/x86.c:1992:uint64_t kvm_get_wall_clock_epoch(struct kvm *kvm)\narch/x86/kvm/x86.c-1993-{\n--\narch/x86/kvm/x86.h=328=u64 get_kvmclock_ns(struct kvm *kvm);\narch/x86/kvm/x86.h:329:uint64_t kvm_get_wall_clock_epoch(struct kvm *kvm);\narch/x86/kvm/x86.h-330-bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);\n--\narch/x86/kvm/xen.c=39=static int kvm_xen_shared_info_init(struct kvm *kvm)\n--\narch/x86/kvm/xen.c-63-\t */\narch/x86/kvm/xen.c:64:\twall_nsec = kvm_get_wall_clock_epoch(kvm);\narch/x86/kvm/xen.c-65-\n"
}

9/2 2026/08/05 00:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 17857
							output: 40
							thoughts: 41
						

Thoughts:
Okay, let's see. This `kvm_get_wall_clock_epoch` function... right, it's used by Xen and interacts with Model-Specific Registers (MSRs). So, it's pretty low-level, likely dealing with timekeeping synchronization between the hypervisor and the guest.

**Digging into Timekeeping: Understanding `kvm_compute_l1_tsc_offset`**

Now, I need to understand exactly *how* it's operating. My next step is definitely going to be diving into `kvm_compute_l1_tsc_offset`. It's almost certain this function figures out the Time Stamp Counter (TSC) offset at the Level 1 (L1) stage. The TSC is the critical hardware register we're talking about here. Knowing how the guest's TSC relates to the host's, especially with virtualization, is absolutely key for correct time management. I'm guessing this function is central to establishing a reliable time base for the guest, despite any hypervisor overhead or interruptions. I suspect it's looking at the guest TSC and comparing it to a known host TSC or a reference time, maybe using some calibration. The offset calculation is crucial to maintaining a consistent time view within the virtual machine. This has implications for a whole load of performance metrics, and it's something I need to fully grasp. I'll need to look carefully at the code to understand the underlying logic.



10/2 2026/08/05 00:39 tool grepper
0m Args:
{
  "Expression": "kvm_compute_l1_tsc_offset",
  "PathPrefix": "arch/x86/kvm/"
}

Results:
{
  "Output": "arch/x86/kvm/msrs.c=1442=int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1633-\t\t} else if (!vcpu-\u003earch.guest_tsc_protected) {\narch/x86/kvm/msrs.c:1634:\t\t\tu64 adj = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data) -\narch/x86/kvm/msrs.c-1635-\t\t\t\t  vcpu-\u003earch.l1_tsc_offset;\n--\narch/x86/kvm/x86.c=1146=u64 kvm_scale_tsc(u64 tsc, u64 ratio)\n--\narch/x86/kvm/x86.c-1155-\narch/x86/kvm/x86.c:1156:u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,\narch/x86/kvm/x86.c-1157-\t\t\t      u64 target_tsc)\n--\narch/x86/kvm/x86.c=1313=void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 *user_value)\n--\narch/x86/kvm/x86.c-1322-\traw_spin_lock_irqsave(\u0026kvm-\u003earch.tsc_write_lock, flags);\narch/x86/kvm/x86.c:1323:\toffset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);\narch/x86/kvm/x86.c-1324-\tns = get_kvmclock_base_ns();\n--\narch/x86/kvm/x86.c-1371-\t\t\tdata += delta;\narch/x86/kvm/x86.c:1372:\t\t\toffset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(), data);\narch/x86/kvm/x86.c-1373-\t\t}\n--\narch/x86/kvm/x86.c=2578=void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n--\narch/x86/kvm/x86.c-2628-\t\tif (kvm_check_tsc_unstable()) {\narch/x86/kvm/x86.c:2629:\t\t\tu64 offset = kvm_compute_l1_tsc_offset(vcpu, rdtsc(),\narch/x86/kvm/x86.c-2630-\t\t\t\t\t\tvcpu-\u003earch.last_guest_tsc);\n--\narch/x86/kvm/x86.h=337=u64 kvm_calc_nested_tsc_multiplier(u64 l1_multiplier, u64 l2_multiplier);\narch/x86/kvm/x86.h:338:u64 kvm_compute_l1_tsc_offset(struct kvm_vcpu *vcpu, u64 host_tsc,\narch/x86/kvm/x86.h-339-\t\t\t      u64 target_tsc);\n"
}

11/2 2026/08/05 00:39 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19146
							output: 182
							thoughts: 180
						

Thoughts:
**My Fuzzing Strategy for This KVM Patch**

Okay, after a quick review, this patch looks like it's definitely worth the effort to fuzz. The modifications it makes to the KVM x86 TSC handling, specifically the TSC scaling, master clock updates, and wall clock epoch calculations, are all prime targets for fuzzing. This is core hypervisor logic, and we know that it's directly accessible from guest VMs through various interfaces like MSR writes, CPUID instructions, and KVM ioctls. That kind of exposure screams vulnerability potential.

Therefore, I'm going to set `WorthFuzzing` to `true`.

My immediate focus will be on the functions that directly handle the TSC synchronization and guest time updates. I'll make sure to add these focus symbols to the fuzzing configuration: `kvm_synchronize_tsc`, `kvm_guest_time_update`, `kvm_get_wall_clock_epoch`, `kvm_compute_l1_tsc_offset`, and `kvm_arch_vcpu_postcreate`. These are the key areas where the patch introduces changes and, consequently, where I anticipate the most interesting fuzzing results.

Finally, I'll enable the `KVM`, `KVM_INTEL`, and `KVM_AMD` configurations. Given the nature of the changes, these are essential to ensure the fuzzer can correctly exercise the modified code paths under different hardware configurations. This should give us a good start towards identifying any potential issues within this patch.



12/2 2026/08/05 00:39 tool set-results
0m Args:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_synchronize_tsc",
    "kvm_guest_time_update",
    "kvm_get_wall_clock_epoch",
    "kvm_compute_l1_tsc_offset",
    "kvm_arch_vcpu_postcreate"
  ],
  "Reasoning": "The patch modifies KVM x86 TSC (Time Stamp Counter) handling, including TSC scaling, master clock updates, and wall clock epoch calculations. This is core hypervisor logic reachable from guest VMs via MSR writes, CPUID, and KVM ioctls. Fuzzing should focus on the modified functions handling TSC synchronization and guest time updates.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "KVM",
    "KVM_INTEL",
    "KVM_AMD"
  ],
  "FocusSymbols": [
    "kvm_synchronize_tsc",
    "kvm_guest_time_update",
    "kvm_get_wall_clock_epoch",
    "kvm_compute_l1_tsc_offset",
    "kvm_arch_vcpu_postcreate"
  ],
  "Reasoning": "The patch modifies KVM x86 TSC (Time Stamp Counter) handling, including TSC scaling, master clock updates, and wall clock epoch calculations. This is core hypervisor logic reachable from guest VMs via MSR writes, CPUID, and KVM ioctls. Fuzzing should focus on the modified functions handling TSC synchronization and guest time updates.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)