* [PATCH v5 0/2] Compute safe bound for VMX preemption timer
@ 2026-07-24 23:49 Jim Mattson
2026-07-24 23:49 ` [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y Jim Mattson
2026-07-24 23:49 ` [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum Jim Mattson
0 siblings, 2 replies; 9+ messages in thread
From: Jim Mattson @ 2026-07-24 23:49 UTC (permalink / raw)
To: seanjc, pbonzini; +Cc: chao.gao, kvm, Jim Mattson
Due to a widespread Intel erratum (e.g. EMR158), programming the VMX
preemption timer with certain large values may cause the timer to expire
earlier than expected. The recommended workaround is to cap the timer value
to strictly less than 2^25 * CPUID.15H:EBX[31:0] / CPUID.15H:EAX[31:0].
v4 -> v5:
- Include Sean's preparatory patch burying VMX preemption timer code under
CONFIG_X86_64=y [Sean]
- Replace div_u64() with direct division (/)
v4: https://lore.kernel.org/all/20260722143024.3938899-1-jmattson@google.com/
v3: https://lore.kernel.org/all/20260722040311.3369898-1-jmattson@google.com/
v2: https://lore.kernel.org/all/20260720231639.1592848-1-jmattson@google.com/
v1: https://lore.kernel.org/all/20260720205230.1457146-1-jmattson@google.com/
Jim Mattson (1):
KVM: VMX: Cap VMX preemption timer to work around Intel erratum
Sean Christopherson (1):
KVM: VMX: Bury all of the VMX preemption timer code under
CONFIG_X86_64=y
arch/x86/kvm/vmx/vmx.c | 152 ++++++++++++++++++++++++++---------------
1 file changed, 96 insertions(+), 56 deletions(-)
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y
2026-07-24 23:49 [PATCH v5 0/2] Compute safe bound for VMX preemption timer Jim Mattson
@ 2026-07-24 23:49 ` Jim Mattson
2026-07-28 5:16 ` Chao Gao
2026-07-28 7:13 ` Binbin Wu
2026-07-24 23:49 ` [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum Jim Mattson
1 sibling, 2 replies; 9+ messages in thread
From: Jim Mattson @ 2026-07-24 23:49 UTC (permalink / raw)
To: seanjc, pbonzini; +Cc: chao.gao, kvm
From: Sean Christopherson <seanjc@google.com>
Double down on using the VMX preemption timer only for 64-bit kernels, and
bury the setup and runtime adjustment code, and all global variables, under
CONFIG_X86_64=y. This will allow addressing various Intel erratum without
running afoul of unused-but-set-variable and __udivdi3() warnings/errors on
32-bit kernels.
No functional change intended.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/vmx/vmx.c | 122 +++++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 53 deletions(-)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index e4b9ac7fed9f..a07faa066ef0 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -150,10 +150,12 @@ module_param(dump_invalid_vmcs, bool, 0644);
#define KVM_VMX_TSC_MULTIPLIER_MAX 0xffffffffffffffffULL
/* Guest_tsc -> host_tsc conversion requires 64-bit division. */
+#ifdef CONFIG_X86_64
static int __read_mostly cpu_preemption_timer_multi;
static bool __read_mostly enable_preemption_timer = 1;
-#ifdef CONFIG_X86_64
module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
+#else
+#define enable_preemption_timer false
#endif
extern bool __read_mostly allow_smaller_maxphyaddr;
@@ -7408,32 +7410,6 @@ static void vmx_refresh_guest_perf_global_control(struct kvm_vcpu *vcpu)
pmu->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL);
}
-static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit)
-{
- struct vcpu_vmx *vmx = to_vmx(vcpu);
- u64 tscl;
- u32 delta_tsc;
-
- if (force_immediate_exit) {
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, 0);
- vmx->loaded_vmcs->hv_timer_soft_disabled = false;
- } else if (vmx->hv_deadline_tsc != -1) {
- tscl = rdtsc();
- if (vmx->hv_deadline_tsc > tscl)
- /* set_hv_timer ensures the delta fits in 32-bits */
- delta_tsc = (u32)((vmx->hv_deadline_tsc - tscl) >>
- cpu_preemption_timer_multi);
- else
- delta_tsc = 0;
-
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
- vmx->loaded_vmcs->hv_timer_soft_disabled = false;
- } else if (!vmx->loaded_vmcs->hv_timer_soft_disabled) {
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, -1);
- vmx->loaded_vmcs->hv_timer_soft_disabled = true;
- }
-}
-
void noinstr vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp)
{
if (unlikely(host_rsp != vmx->loaded_vmcs->host_state.rsp)) {
@@ -7519,6 +7495,8 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
guest_state_exit_irqoff();
}
+static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit);
+
fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
{
bool force_immediate_exit = run_flags & KVM_RUN_FORCE_IMMEDIATE_EXIT;
@@ -8328,6 +8306,36 @@ static inline int u64_shl_div_u64(u64 a, unsigned int shift,
return 0;
}
+static __init void vmx_setup_preemption_timer(void)
+{
+ if (!cpu_has_vmx_preemption_timer())
+ enable_preemption_timer = false;
+
+ if (enable_preemption_timer) {
+ u64 use_timer_freq = 5000ULL * 1000 * 1000;
+
+ cpu_preemption_timer_multi =
+ vmx_misc_preemption_timer_rate(vmcs_config.misc);
+
+ if (tsc_khz)
+ use_timer_freq = (u64)tsc_khz * 1000;
+ use_timer_freq >>= cpu_preemption_timer_multi;
+
+ /*
+ * KVM "disables" the preemption timer by setting it to its max
+ * value. Don't use the timer if it might cause spurious exits
+ * at a rate faster than 0.1 Hz (of uninterrupted guest time).
+ */
+ if (use_timer_freq > 0xffffffffu / 10)
+ enable_preemption_timer = false;
+ }
+
+ if (!enable_preemption_timer) {
+ vt_x86_ops.set_hv_timer = NULL;
+ vt_x86_ops.cancel_hv_timer = NULL;
+ }
+}
+
int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
bool *expired)
{
@@ -8372,6 +8380,39 @@ void vmx_cancel_hv_timer(struct kvm_vcpu *vcpu)
{
to_vmx(vcpu)->hv_deadline_tsc = -1;
}
+
+static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit)
+{
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+ u64 tscl;
+ u32 delta_tsc;
+
+ if (force_immediate_exit) {
+ vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, 0);
+ vmx->loaded_vmcs->hv_timer_soft_disabled = false;
+ } else if (vmx->hv_deadline_tsc != -1) {
+ tscl = rdtsc();
+ if (vmx->hv_deadline_tsc > tscl)
+ /* set_hv_timer ensures the delta fits in 32-bits */
+ delta_tsc = (u32)((vmx->hv_deadline_tsc - tscl) >>
+ cpu_preemption_timer_multi);
+ else
+ delta_tsc = 0;
+
+ vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
+ vmx->loaded_vmcs->hv_timer_soft_disabled = false;
+ } else if (!vmx->loaded_vmcs->hv_timer_soft_disabled) {
+ vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, -1);
+ vmx->loaded_vmcs->hv_timer_soft_disabled = true;
+ }
+}
+#else
+static __init void vmx_setup_preemption_timer(void) { }
+
+static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit)
+{
+ BUILD_BUG_ON(1);
+}
#endif
void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
@@ -8734,32 +8775,7 @@ __init int vmx_hardware_setup(void)
if (!enable_ept || !enable_ept_ad_bits || !cpu_has_vmx_pml())
enable_pml = 0;
- if (!cpu_has_vmx_preemption_timer())
- enable_preemption_timer = false;
-
- if (enable_preemption_timer) {
- u64 use_timer_freq = 5000ULL * 1000 * 1000;
-
- cpu_preemption_timer_multi =
- vmx_misc_preemption_timer_rate(vmcs_config.misc);
-
- if (tsc_khz)
- use_timer_freq = (u64)tsc_khz * 1000;
- use_timer_freq >>= cpu_preemption_timer_multi;
-
- /*
- * KVM "disables" the preemption timer by setting it to its max
- * value. Don't use the timer if it might cause spurious exits
- * at a rate faster than 0.1 Hz (of uninterrupted guest time).
- */
- if (use_timer_freq > 0xffffffffu / 10)
- enable_preemption_timer = false;
- }
-
- if (!enable_preemption_timer) {
- vt_x86_ops.set_hv_timer = NULL;
- vt_x86_ops.cancel_hv_timer = NULL;
- }
+ vmx_setup_preemption_timer();
kvm_caps.supported_mce_cap |= MCG_LMCE_P;
kvm_caps.supported_mce_cap |= MCG_CMCI_P;
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum
2026-07-24 23:49 [PATCH v5 0/2] Compute safe bound for VMX preemption timer Jim Mattson
2026-07-24 23:49 ` [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y Jim Mattson
@ 2026-07-24 23:49 ` Jim Mattson
2026-07-28 7:22 ` Binbin Wu
2026-07-28 15:03 ` Sean Christopherson
1 sibling, 2 replies; 9+ messages in thread
From: Jim Mattson @ 2026-07-24 23:49 UTC (permalink / raw)
To: seanjc, pbonzini; +Cc: chao.gao, kvm, Jim Mattson
Due to a widespread Intel erratum (e.g. EMR158), programming the
VMX-preemption timer with certain large values may cause the timer to
expire earlier than expected. The recommended workaround is to cap the
VMX-preemption timer value to strictly less than 2^25 * CPUID.15H:EBX[31:0]
/ CPUID.15H:EAX[31:0].
Calculate preemption_timer_limit during hardware setup based on CPUID 15H
when available, and return -ERANGE in vmx_set_hv_timer() if the shifted
delta_tsc reaches or exceeds preemption_timer_limit.
Use preemption_timer_limit - 1 instead of 0xffffffff when soft-disabling
the timer and when checking the timer frequency limit in hardware setup.
Reported-by: Sean Christopherson <seanjc@google.com>
Closes: https://lore.kernel.org/all/Zn9X0yFxZi_Mrlnt@google.com/
Suggested-by: Chao Gao <chao.gao@intel.com>
Assisted-by: Gemini:Gemini-Next
Reviewed-by: Chao Gao <chao.gao@intel.com>
Signed-off-by: Jim Mattson <jmattson@google.com>
---
arch/x86/kvm/vmx/vmx.c | 34 +++++++++++++++++++++++++++++-----
1 file changed, 29 insertions(+), 5 deletions(-)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index a07faa066ef0..ebe83a641473 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -153,6 +153,7 @@ module_param(dump_invalid_vmcs, bool, 0644);
#ifdef CONFIG_X86_64
static int __read_mostly cpu_preemption_timer_multi;
static bool __read_mostly enable_preemption_timer = 1;
+static u64 __ro_after_init preemption_timer_limit;
module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
#else
#define enable_preemption_timer false
@@ -8306,6 +8307,26 @@ static inline int u64_shl_div_u64(u64 a, unsigned int shift,
return 0;
}
+/*
+ * Workaround for a widespread Intel erratum (e.g. EMR158) where the
+ * VMX-preemption timer may expire earlier than expected when programmed
+ * with large values. The workaround is to cap the timer value to strictly
+ * less than 2^25 * CPUID.15H:EBX / CPUID.15H:EAX.
+ */
+static __init u64 calc_preemption_timer_limit(void)
+{
+ u32 eax, ebx, ecx, edx;
+
+ if (cpuid_eax(0) < 0x15)
+ return 1ULL << 32;
+
+ cpuid(0x15, &eax, &ebx, &ecx, &edx);
+ if (!eax || !ebx)
+ return 1ULL << 32;
+
+ return min_t(u64, 1ULL << 32, ((u64)ebx << 25) / eax);
+}
+
static __init void vmx_setup_preemption_timer(void)
{
if (!cpu_has_vmx_preemption_timer())
@@ -8317,6 +8338,8 @@ static __init void vmx_setup_preemption_timer(void)
cpu_preemption_timer_multi =
vmx_misc_preemption_timer_rate(vmcs_config.misc);
+ preemption_timer_limit = calc_preemption_timer_limit();
+
if (tsc_khz)
use_timer_freq = (u64)tsc_khz * 1000;
use_timer_freq >>= cpu_preemption_timer_multi;
@@ -8326,7 +8349,8 @@ static __init void vmx_setup_preemption_timer(void)
* value. Don't use the timer if it might cause spurious exits
* at a rate faster than 0.1 Hz (of uninterrupted guest time).
*/
- if (use_timer_freq > 0xffffffffu / 10)
+ if (!preemption_timer_limit ||
+ use_timer_freq > (preemption_timer_limit - 1) / 10)
enable_preemption_timer = false;
}
@@ -8363,12 +8387,12 @@ int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
return -ERANGE;
/*
- * If the delta tsc can't fit in the 32 bit after the multi shift,
- * we can't use the preemption timer.
+ * If the delta tsc exceeds the preemption timer limit after the
+ * multi shift, we can't use the preemption timer.
* It's possible that it fits on later vmentries, but checking
* on every vmentry is costly so we just use an hrtimer.
*/
- if (delta_tsc >> (cpu_preemption_timer_multi + 32))
+ if ((delta_tsc >> cpu_preemption_timer_multi) >= preemption_timer_limit)
return -ERANGE;
vmx->hv_deadline_tsc = tscl + delta_tsc;
@@ -8402,7 +8426,7 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit
vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
vmx->loaded_vmcs->hv_timer_soft_disabled = false;
} else if (!vmx->loaded_vmcs->hv_timer_soft_disabled) {
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, -1);
+ vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preemption_timer_limit - 1);
vmx->loaded_vmcs->hv_timer_soft_disabled = true;
}
}
--
2.55.0.229.g6434b31f56-goog
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y
2026-07-24 23:49 ` [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y Jim Mattson
@ 2026-07-28 5:16 ` Chao Gao
2026-07-28 7:13 ` Binbin Wu
1 sibling, 0 replies; 9+ messages in thread
From: Chao Gao @ 2026-07-28 5:16 UTC (permalink / raw)
To: Jim Mattson; +Cc: seanjc, pbonzini, kvm
On Fri, Jul 24, 2026 at 04:49:13PM -0700, Jim Mattson wrote:
>From: Sean Christopherson <seanjc@google.com>
>
>Double down on using the VMX preemption timer only for 64-bit kernels, and
>bury the setup and runtime adjustment code, and all global variables, under
>CONFIG_X86_64=y. This will allow addressing various Intel erratum without
>running afoul of unused-but-set-variable and __udivdi3() warnings/errors on
>32-bit kernels.
>
>No functional change intended.
>
>Signed-off-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Chao Gao <chao.gao@intel.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y
2026-07-24 23:49 ` [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y Jim Mattson
2026-07-28 5:16 ` Chao Gao
@ 2026-07-28 7:13 ` Binbin Wu
1 sibling, 0 replies; 9+ messages in thread
From: Binbin Wu @ 2026-07-28 7:13 UTC (permalink / raw)
To: Jim Mattson; +Cc: seanjc, pbonzini, chao.gao, kvm
On 7/25/2026 7:49 AM, Jim Mattson wrote:
> From: Sean Christopherson <seanjc@google.com>
>
> Double down on using the VMX preemption timer only for 64-bit kernels, and
> bury the setup and runtime adjustment code, and all global variables, under
> CONFIG_X86_64=y. This will allow addressing various Intel erratum without
Nit: ^
erratum -> errata.
But "various Intel erratum" sounds like there are multiple distinct bugs to me.
How about align the term with patch 2, i.e. "a widespread Intel erratum"
> running afoul of unused-but-set-variable and __udivdi3() warnings/errors on
> 32-bit kernels.
>
> No functional change intended.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum
2026-07-24 23:49 ` [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum Jim Mattson
@ 2026-07-28 7:22 ` Binbin Wu
2026-07-28 15:03 ` Sean Christopherson
1 sibling, 0 replies; 9+ messages in thread
From: Binbin Wu @ 2026-07-28 7:22 UTC (permalink / raw)
To: Jim Mattson; +Cc: seanjc, pbonzini, chao.gao, kvm
On 7/25/2026 7:49 AM, Jim Mattson wrote:
> Due to a widespread Intel erratum (e.g. EMR158), programming the
> VMX-preemption timer with certain large values may cause the timer to
> expire earlier than expected. The recommended workaround is to cap the
> VMX-preemption timer value to strictly less than 2^25 * CPUID.15H:EBX[31:0]
> / CPUID.15H:EAX[31:0].
>
> Calculate preemption_timer_limit during hardware setup based on CPUID 15H
> when available, and return -ERANGE in vmx_set_hv_timer() if the shifted
> delta_tsc reaches or exceeds preemption_timer_limit.
>
> Use preemption_timer_limit - 1 instead of 0xffffffff when soft-disabling
> the timer and when checking the timer frequency limit in hardware setup.
>
> Reported-by: Sean Christopherson <seanjc@google.com>
> Closes: https://lore.kernel.org/all/Zn9X0yFxZi_Mrlnt@google.com/
> Suggested-by: Chao Gao <chao.gao@intel.com>
> Assisted-by: Gemini:Gemini-Next
> Reviewed-by: Chao Gao <chao.gao@intel.com>
> Signed-off-by: Jim Mattson <jmattson@google.com>
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum
2026-07-24 23:49 ` [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum Jim Mattson
2026-07-28 7:22 ` Binbin Wu
@ 2026-07-28 15:03 ` Sean Christopherson
2026-07-28 15:28 ` Jim Mattson
1 sibling, 1 reply; 9+ messages in thread
From: Sean Christopherson @ 2026-07-28 15:03 UTC (permalink / raw)
To: Jim Mattson; +Cc: pbonzini, chao.gao, kvm
On Fri, Jul 24, 2026, Jim Mattson wrote:
Sorry for the late review, I didn't actually look at the code in the previous
versions.
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index a07faa066ef0..ebe83a641473 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -153,6 +153,7 @@ module_param(dump_invalid_vmcs, bool, 0644);
> #ifdef CONFIG_X86_64
> static int __read_mostly cpu_preemption_timer_multi;
> static bool __read_mostly enable_preemption_timer = 1;
> +static u64 __ro_after_init preemption_timer_limit;
> module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
> #else
> #define enable_preemption_timer false
> @@ -8306,6 +8307,26 @@ static inline int u64_shl_div_u64(u64 a, unsigned int shift,
> return 0;
> }
>
> +/*
> + * Workaround for a widespread Intel erratum (e.g. EMR158) where the
> + * VMX-preemption timer may expire earlier than expected when programmed
> + * with large values. The workaround is to cap the timer value to strictly
> + * less than 2^25 * CPUID.15H:EBX / CPUID.15H:EAX.
> + */
> +static __init u64 calc_preemption_timer_limit(void)
> +{
> + u32 eax, ebx, ecx, edx;
> +
> + if (cpuid_eax(0) < 0x15)
> + return 1ULL << 32;
Tracking an exclusive limit is cumbersome because all of KVM's usage deals with
the max value, i.e. it forces all usage to effectively do "- 1". I also think
we should go with "max_value" instead of "limit" so that there's less chance for
confusion around whether the limit is exclusive or inclusive.
And we should provide a local "const u64" for maximum architectural value. The
other option would be to initialize the global to the max architectural value,
and then do early returns here, but I like explicitly setting the global before
checking it against use_timer_freq.
> +
> + cpuid(0x15, &eax, &ebx, &ecx, &edx);
> + if (!eax || !ebx)
> + return 1ULL << 32;
> +
> + return min_t(u64, 1ULL << 32, ((u64)ebx << 25) / eax);
While I appreciate Sashiko's paranoia about "((u64)ebx << 25) / eax)" yielding
zero, I think we should treat that as a WARNable offence. And to avoid a false
positive due to running as a VM of a misconfigured hypervisor, I think we should
only apply the erratum workaround on bare metal, i.e. use the maximum value if
KVM detects X86_FEATURE_HYPERVISOR.
So this as fixup? If this looks good to you, I'm happy to post v6 since I've
already got it locally and tested on CLX, ICX, and EMR.
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index ebe83a641473..dae41842a9ce 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -153,7 +153,7 @@ module_param(dump_invalid_vmcs, bool, 0644);
#ifdef CONFIG_X86_64
static int __read_mostly cpu_preemption_timer_multi;
static bool __read_mostly enable_preemption_timer = 1;
-static u64 __ro_after_init preemption_timer_limit;
+static u64 __ro_after_init preemption_timer_max_value;
module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
#else
#define enable_preemption_timer false
@@ -8313,18 +8313,25 @@ static inline int u64_shl_div_u64(u64 a, unsigned int shift,
* with large values. The workaround is to cap the timer value to strictly
* less than 2^25 * CPUID.15H:EBX / CPUID.15H:EAX.
*/
-static __init u64 calc_preemption_timer_limit(void)
+static __init u64 calc_preemption_timer_max_value(void)
{
+ const u64 ARCHITECTURAL_MAX_VALUE = UINT_MAX;
u32 eax, ebx, ecx, edx;
+ if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
+ return ARCHITECTURAL_MAX_VALUE;
+
if (cpuid_eax(0) < 0x15)
- return 1ULL << 32;
+ return ARCHITECTURAL_MAX_VALUE;
cpuid(0x15, &eax, &ebx, &ecx, &edx);
if (!eax || !ebx)
- return 1ULL << 32;
+ return ARCHITECTURAL_MAX_VALUE;
- return min_t(u64, 1ULL << 32, ((u64)ebx << 25) / eax);
+ if (WARN_ON_ONCE(!(((u64)ebx << 25) / eax)))
+ return ARCHITECTURAL_MAX_VALUE;
+
+ return (((u64)ebx << 25) / eax) - 1;
}
static __init void vmx_setup_preemption_timer(void)
@@ -8338,7 +8345,7 @@ static __init void vmx_setup_preemption_timer(void)
cpu_preemption_timer_multi =
vmx_misc_preemption_timer_rate(vmcs_config.misc);
- preemption_timer_limit = calc_preemption_timer_limit();
+ preemption_timer_max_value = calc_preemption_timer_max_value();
if (tsc_khz)
use_timer_freq = (u64)tsc_khz * 1000;
@@ -8349,8 +8356,7 @@ static __init void vmx_setup_preemption_timer(void)
* value. Don't use the timer if it might cause spurious exits
* at a rate faster than 0.1 Hz (of uninterrupted guest time).
*/
- if (!preemption_timer_limit ||
- use_timer_freq > (preemption_timer_limit - 1) / 10)
+ if (use_timer_freq > preemption_timer_max_value / 10)
enable_preemption_timer = false;
}
@@ -8392,7 +8398,7 @@ int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
* It's possible that it fits on later vmentries, but checking
* on every vmentry is costly so we just use an hrtimer.
*/
- if ((delta_tsc >> cpu_preemption_timer_multi) >= preemption_timer_limit)
+ if ((delta_tsc >> cpu_preemption_timer_multi) > preemption_timer_max_value)
return -ERANGE;
vmx->hv_deadline_tsc = tscl + delta_tsc;
@@ -8426,7 +8432,7 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit
vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
vmx->loaded_vmcs->hv_timer_soft_disabled = false;
} else if (!vmx->loaded_vmcs->hv_timer_soft_disabled) {
- vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preemption_timer_limit - 1);
+ vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preemption_timer_max_value);
vmx->loaded_vmcs->hv_timer_soft_disabled = true;
}
}
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum
2026-07-28 15:03 ` Sean Christopherson
@ 2026-07-28 15:28 ` Jim Mattson
2026-07-28 15:40 ` Sean Christopherson
0 siblings, 1 reply; 9+ messages in thread
From: Jim Mattson @ 2026-07-28 15:28 UTC (permalink / raw)
To: Sean Christopherson; +Cc: pbonzini, chao.gao, kvm
On Tue, Jul 28, 2026 at 8:03 AM Sean Christopherson <seanjc@google.com> wrote:
>
> On Fri, Jul 24, 2026, Jim Mattson wrote:
>
> Sorry for the late review, I didn't actually look at the code in the previous
> versions.
>
> > diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> > index a07faa066ef0..ebe83a641473 100644
> > --- a/arch/x86/kvm/vmx/vmx.c
> > +++ b/arch/x86/kvm/vmx/vmx.c
> > @@ -153,6 +153,7 @@ module_param(dump_invalid_vmcs, bool, 0644);
> > #ifdef CONFIG_X86_64
> > static int __read_mostly cpu_preemption_timer_multi;
> > static bool __read_mostly enable_preemption_timer = 1;
> > +static u64 __ro_after_init preemption_timer_limit;
> > module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
> > #else
> > #define enable_preemption_timer false
> > @@ -8306,6 +8307,26 @@ static inline int u64_shl_div_u64(u64 a, unsigned int shift,
> > return 0;
> > }
> >
> > +/*
> > + * Workaround for a widespread Intel erratum (e.g. EMR158) where the
> > + * VMX-preemption timer may expire earlier than expected when programmed
> > + * with large values. The workaround is to cap the timer value to strictly
> > + * less than 2^25 * CPUID.15H:EBX / CPUID.15H:EAX.
> > + */
> > +static __init u64 calc_preemption_timer_limit(void)
> > +{
> > + u32 eax, ebx, ecx, edx;
> > +
> > + if (cpuid_eax(0) < 0x15)
> > + return 1ULL << 32;
>
> Tracking an exclusive limit is cumbersome because all of KVM's usage deals with
> the max value, i.e. it forces all usage to effectively do "- 1". I also think
> we should go with "max_value" instead of "limit" so that there's less chance for
> confusion around whether the limit is exclusive or inclusive.
>
> And we should provide a local "const u64" for maximum architectural value. The
> other option would be to initialize the global to the max architectural value,
> and then do early returns here, but I like explicitly setting the global before
> checking it against use_timer_freq.
>
> > +
> > + cpuid(0x15, &eax, &ebx, &ecx, &edx);
> > + if (!eax || !ebx)
> > + return 1ULL << 32;
> > +
> > + return min_t(u64, 1ULL << 32, ((u64)ebx << 25) / eax);
>
> While I appreciate Sashiko's paranoia about "((u64)ebx << 25) / eax)" yielding
> zero, I think we should treat that as a WARNable offence. And to avoid a false
> positive due to running as a VM of a misconfigured hypervisor, I think we should
> only apply the erratum workaround on bare metal, i.e. use the maximum value if
> KVM detects X86_FEATURE_HYPERVISOR.
That's fair. We always emulate the VMCS12 VMX preemption timer with an
hr_timer, right?
> So this as fixup? If this looks good to you, I'm happy to post v6 since I've
> already got it locally and tested on CLX, ICX, and EMR.
Go for it!
Note that, like you, I didn't actually look at it. :)
> diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
> index ebe83a641473..dae41842a9ce 100644
> --- a/arch/x86/kvm/vmx/vmx.c
> +++ b/arch/x86/kvm/vmx/vmx.c
> @@ -153,7 +153,7 @@ module_param(dump_invalid_vmcs, bool, 0644);
> #ifdef CONFIG_X86_64
> static int __read_mostly cpu_preemption_timer_multi;
> static bool __read_mostly enable_preemption_timer = 1;
> -static u64 __ro_after_init preemption_timer_limit;
> +static u64 __ro_after_init preemption_timer_max_value;
> module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO);
> #else
> #define enable_preemption_timer false
> @@ -8313,18 +8313,25 @@ static inline int u64_shl_div_u64(u64 a, unsigned int shift,
> * with large values. The workaround is to cap the timer value to strictly
> * less than 2^25 * CPUID.15H:EBX / CPUID.15H:EAX.
> */
> -static __init u64 calc_preemption_timer_limit(void)
> +static __init u64 calc_preemption_timer_max_value(void)
> {
> + const u64 ARCHITECTURAL_MAX_VALUE = UINT_MAX;
> u32 eax, ebx, ecx, edx;
>
> + if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
> + return ARCHITECTURAL_MAX_VALUE;
> +
> if (cpuid_eax(0) < 0x15)
> - return 1ULL << 32;
> + return ARCHITECTURAL_MAX_VALUE;
>
> cpuid(0x15, &eax, &ebx, &ecx, &edx);
> if (!eax || !ebx)
> - return 1ULL << 32;
> + return ARCHITECTURAL_MAX_VALUE;
>
> - return min_t(u64, 1ULL << 32, ((u64)ebx << 25) / eax);
> + if (WARN_ON_ONCE(!(((u64)ebx << 25) / eax)))
> + return ARCHITECTURAL_MAX_VALUE;
> +
> + return (((u64)ebx << 25) / eax) - 1;
> }
>
> static __init void vmx_setup_preemption_timer(void)
> @@ -8338,7 +8345,7 @@ static __init void vmx_setup_preemption_timer(void)
> cpu_preemption_timer_multi =
> vmx_misc_preemption_timer_rate(vmcs_config.misc);
>
> - preemption_timer_limit = calc_preemption_timer_limit();
> + preemption_timer_max_value = calc_preemption_timer_max_value();
>
> if (tsc_khz)
> use_timer_freq = (u64)tsc_khz * 1000;
> @@ -8349,8 +8356,7 @@ static __init void vmx_setup_preemption_timer(void)
> * value. Don't use the timer if it might cause spurious exits
> * at a rate faster than 0.1 Hz (of uninterrupted guest time).
> */
> - if (!preemption_timer_limit ||
> - use_timer_freq > (preemption_timer_limit - 1) / 10)
> + if (use_timer_freq > preemption_timer_max_value / 10)
> enable_preemption_timer = false;
> }
>
> @@ -8392,7 +8398,7 @@ int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
> * It's possible that it fits on later vmentries, but checking
> * on every vmentry is costly so we just use an hrtimer.
> */
> - if ((delta_tsc >> cpu_preemption_timer_multi) >= preemption_timer_limit)
> + if ((delta_tsc >> cpu_preemption_timer_multi) > preemption_timer_max_value)
> return -ERANGE;
>
> vmx->hv_deadline_tsc = tscl + delta_tsc;
> @@ -8426,7 +8432,7 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit
> vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
> vmx->loaded_vmcs->hv_timer_soft_disabled = false;
> } else if (!vmx->loaded_vmcs->hv_timer_soft_disabled) {
> - vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preemption_timer_limit - 1);
> + vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, preemption_timer_max_value);
> vmx->loaded_vmcs->hv_timer_soft_disabled = true;
> }
> }
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum
2026-07-28 15:28 ` Jim Mattson
@ 2026-07-28 15:40 ` Sean Christopherson
0 siblings, 0 replies; 9+ messages in thread
From: Sean Christopherson @ 2026-07-28 15:40 UTC (permalink / raw)
To: Jim Mattson; +Cc: pbonzini, chao.gao, kvm
On Tue, Jul 28, 2026, Jim Mattson wrote:
> On Tue, Jul 28, 2026 at 8:03 AM Sean Christopherson <seanjc@google.com> wrote:
> > > +
> > > + cpuid(0x15, &eax, &ebx, &ecx, &edx);
> > > + if (!eax || !ebx)
> > > + return 1ULL << 32;
> > > +
> > > + return min_t(u64, 1ULL << 32, ((u64)ebx << 25) / eax);
> >
> > While I appreciate Sashiko's paranoia about "((u64)ebx << 25) / eax)" yielding
> > zero, I think we should treat that as a WARNable offence. And to avoid a false
> > positive due to running as a VM of a misconfigured hypervisor, I think we should
> > only apply the erratum workaround on bare metal, i.e. use the maximum value if
> > KVM detects X86_FEATURE_HYPERVISOR.
>
> That's fair. We always emulate the VMCS12 VMX preemption timer with an
> hr_timer, right?
Yep. And even if KVM (or any hypervisor) as L0 used the hardware VMX timer to
emulate vmcs12's timer, it would be L0's responsibility to deal with the erratum.
Checking CPUID when running as a VM isn't obviously flawed right now, because
there's nothing that enumerates the erratum or its fix, but I'm guessing we'll
end up with FMS-based detection of the bug, at which point we'll run into the
typical issues with trying to do FMS matching when running as a VM.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-07-28 15:40 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 23:49 [PATCH v5 0/2] Compute safe bound for VMX preemption timer Jim Mattson
2026-07-24 23:49 ` [PATCH v5 1/2] KVM: VMX: Bury all of the VMX preemption timer code under CONFIG_X86_64=y Jim Mattson
2026-07-28 5:16 ` Chao Gao
2026-07-28 7:13 ` Binbin Wu
2026-07-24 23:49 ` [PATCH v5 2/2] KVM: VMX: Cap VMX preemption timer to work around Intel erratum Jim Mattson
2026-07-28 7:22 ` Binbin Wu
2026-07-28 15:03 ` Sean Christopherson
2026-07-28 15:28 ` Jim Mattson
2026-07-28 15:40 ` Sean Christopherson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox