[PATCH 2/3] KVM: nSVM: Rename recalc_intercepts() to clarify vmcb02 as the target

Yosry Ahmed posted 3 patches 3 weeks, 5 days ago
[PATCH 2/3] KVM: nSVM: Rename recalc_intercepts() to clarify vmcb02 as the target
Posted by Yosry Ahmed 3 weeks, 5 days ago
recalc_intercepts() updates the intercept bits in vmcb02 based on vmcb01
and (cached) vmcb12. However, the name is too generic to make this
clear, and is especially confusing while searching through the code as
it shares the same name as the recalc_intercepts callback in
kvm_x86_ops.

Rename it to nested_vmcb02_recalc_intercepts() (similar to other
nested_vmcb02_* scoped functions), to make it clear what it is doing.

No functional change intended.

Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev>
---
 arch/x86/kvm/svm/nested.c |  4 ++--
 arch/x86/kvm/svm/sev.c    |  2 +-
 arch/x86/kvm/svm/svm.c    |  4 ++--
 arch/x86/kvm/svm/svm.h    | 10 +++++-----
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 2dda52221fd8..bacb2ac4c59e 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -123,7 +123,7 @@ static bool nested_vmcb_needs_vls_intercept(struct vcpu_svm *svm)
 	return false;
 }
 
-void recalc_intercepts(struct vcpu_svm *svm)
+void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
 {
 	struct vmcb *vmcb01, *vmcb02;
 	unsigned int i;
@@ -918,7 +918,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm,
 	 * Merge guest and host intercepts - must be called with vcpu in
 	 * guest-mode to take effect.
 	 */
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static void nested_svm_copy_common_state(struct vmcb *from_vmcb, struct vmcb *to_vmcb)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index f59c65abe3cf..f50a95aa41bc 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4604,7 +4604,7 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm, bool init_event)
 	if (!sev_vcpu_has_debug_swap(svm)) {
 		vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ);
 		vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE);
-		recalc_intercepts(svm);
+		nested_vmcb02_recalc_intercepts(svm);
 	} else {
 		/*
 		 * Disable #DB intercept iff DebugSwap is enabled.  KVM doesn't
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7041498a8091..485c2710d7a4 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -635,7 +635,7 @@ static void set_dr_intercepts(struct vcpu_svm *svm)
 	vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ);
 	vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE);
 
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static void clr_dr_intercepts(struct vcpu_svm *svm)
@@ -644,7 +644,7 @@ static void clr_dr_intercepts(struct vcpu_svm *svm)
 
 	vmcb->control.intercepts[INTERCEPT_DR] = 0;
 
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr)
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index 7d28a739865f..330633291c57 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -357,7 +357,7 @@ struct svm_cpu_data {
 
 DECLARE_PER_CPU(struct svm_cpu_data, svm_data);
 
-void recalc_intercepts(struct vcpu_svm *svm);
+void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm);
 
 static __always_inline struct kvm_svm *to_kvm_svm(struct kvm *kvm)
 {
@@ -485,7 +485,7 @@ static inline void set_exception_intercept(struct vcpu_svm *svm, u32 bit)
 	WARN_ON_ONCE(bit >= 32);
 	vmcb_set_intercept(&vmcb->control, INTERCEPT_EXCEPTION_OFFSET + bit);
 
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static inline void clr_exception_intercept(struct vcpu_svm *svm, u32 bit)
@@ -495,7 +495,7 @@ static inline void clr_exception_intercept(struct vcpu_svm *svm, u32 bit)
 	WARN_ON_ONCE(bit >= 32);
 	vmcb_clr_intercept(&vmcb->control, INTERCEPT_EXCEPTION_OFFSET + bit);
 
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static inline void svm_set_intercept(struct vcpu_svm *svm, int bit)
@@ -504,7 +504,7 @@ static inline void svm_set_intercept(struct vcpu_svm *svm, int bit)
 
 	vmcb_set_intercept(&vmcb->control, bit);
 
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static inline void svm_clr_intercept(struct vcpu_svm *svm, int bit)
@@ -513,7 +513,7 @@ static inline void svm_clr_intercept(struct vcpu_svm *svm, int bit)
 
 	vmcb_clr_intercept(&vmcb->control, bit);
 
-	recalc_intercepts(svm);
+	nested_vmcb02_recalc_intercepts(svm);
 }
 
 static inline bool svm_is_intercept(struct vcpu_svm *svm, int bit)
-- 
2.52.0.457.g6b5491de43-goog
Re: [PATCH 2/3] KVM: nSVM: Rename recalc_intercepts() to clarify vmcb02 as the target
Posted by Sean Christopherson 3 days, 15 hours ago
On Mon, Jan 12, 2026, Yosry Ahmed wrote:
> recalc_intercepts() updates the intercept bits in vmcb02 based on vmcb01
> and (cached) vmcb12.

Ah, but it does more than that.  More below.

> However, the name is too generic to make this
> clear, and is especially confusing while searching through the code as
> it shares the same name as the recalc_intercepts callback in
> kvm_x86_ops.
> 
> Rename it to nested_vmcb02_recalc_intercepts() (similar to other
> nested_vmcb02_* scoped functions), to make it clear what it is doing.
> 
> No functional change intended.
> 
> Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev>
> ---
>  arch/x86/kvm/svm/nested.c |  4 ++--
>  arch/x86/kvm/svm/sev.c    |  2 +-
>  arch/x86/kvm/svm/svm.c    |  4 ++--
>  arch/x86/kvm/svm/svm.h    | 10 +++++-----
>  4 files changed, 10 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> index 2dda52221fd8..bacb2ac4c59e 100644
> --- a/arch/x86/kvm/svm/nested.c
> +++ b/arch/x86/kvm/svm/nested.c
> @@ -123,7 +123,7 @@ static bool nested_vmcb_needs_vls_intercept(struct vcpu_svm *svm)
>  	return false;
>  }
>  
> -void recalc_intercepts(struct vcpu_svm *svm)
> +void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
>  {
>  	struct vmcb *vmcb01, *vmcb02;
>  	unsigned int i;

Drat, I should have responded to the previous patch.  Lurking out of sight is a
pre-existing bug that effectively invalidates this entire rename.

The existing code is:

  void recalc_intercepts(struct vcpu_svm *svm)
  {
	struct vmcb *vmcb01, *vmcb02;
	unsigned int i;

	vmcb_mark_dirty(svm->vmcb, VMCB_INTERCEPTS);  <======= not vmcb01!!!!!

	if (!is_guest_mode(&svm->vcpu))
		return;

When L2 is active, svm->vmcb is vmcb02.  Which, at first glance, _looks_ right,
but (the *horribly* named) recalc_intercepts() isn't _just_ recalculating
intercepts for L2, it's also responsible for marking the VMCB_INTERCEPTS dirty
(obviously).

But what isn't so obvious is that _all_ callers operate on vmcb01, because the
pattern is to modify vmcb01 intercepts, and then merge the new vmcb01 intercepts
with vmcb12, i.e. the "recalc intercepts" aspect is "part 2" of the overall
function.

Lost in all of this is that KVM forgets to mark vmcb01 dirty, and unless there's
a call buried somewhere deep, nested_svm_vmexit() isn't guaranteed to mark
VMCB_INTERCEPTS dirty, e.g. if PAUSE interception is disabled.

It's probably a benign bug in practice, as AMD CPUs don't appear to do anything
with the clean fields, but easy to fix.

As a bonus, fixing that bug yields for even better naming and code.  After the
dust settles, we can end up with this in svm.h:

  void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm);

  static inline void svm_mark_intercepts_dirty(struct vcpu_svm *svm)
  {
	vmcb_mark_dirty(svm->vmcb01.ptr, VMCB_INTERCEPTS);

	/*
	 * If L2 is active, recalculate the intercepts for vmcb02 to account
	 * for the changes made to vmcb01.  All intercept configuration is done
	 * for vmcb01 and then propagated to vmcb02 to combine KVM's intercepts
	 * with L1's intercepts (from the vmcb12 snapshot).
	 */
	if (is_guest_mode(&svm->vcpu))
		nested_vmcb02_recalc_intercepts(svm);
  }

and this for nested_vmcb02_recalc_intercepts():

  void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
  {
	struct vmcb_ctrl_area_cached *vmcb12_ctrl = &svm->nested.ctl;
	struct vmcb *vmcb02 = svm->nested.vmcb02.ptr;
	struct vmcb *vmcb01 = svm->vmcb01.ptr;
	unsigned int i;

	if (WARN_ON_ONCE(svm->vmcb != vmcb02))
		return;

	...
  }

with the only other caller of nested_vmcb02_recalc_intercepts() being
nested_vmcb02_prepare_control().
Re: [PATCH 2/3] KVM: nSVM: Rename recalc_intercepts() to clarify vmcb02 as the target
Posted by Yosry Ahmed 3 days, 14 hours ago
On Wed, Feb 04, 2026 at 09:45:52AM -0800, Sean Christopherson wrote:
> On Mon, Jan 12, 2026, Yosry Ahmed wrote:
> > recalc_intercepts() updates the intercept bits in vmcb02 based on vmcb01
> > and (cached) vmcb12.
> 
> Ah, but it does more than that.  More below.
> 
> > However, the name is too generic to make this
> > clear, and is especially confusing while searching through the code as
> > it shares the same name as the recalc_intercepts callback in
> > kvm_x86_ops.
> > 
> > Rename it to nested_vmcb02_recalc_intercepts() (similar to other
> > nested_vmcb02_* scoped functions), to make it clear what it is doing.
> > 
> > No functional change intended.
> > 
> > Signed-off-by: Yosry Ahmed <yosry.ahmed@linux.dev>
> > ---
> >  arch/x86/kvm/svm/nested.c |  4 ++--
> >  arch/x86/kvm/svm/sev.c    |  2 +-
> >  arch/x86/kvm/svm/svm.c    |  4 ++--
> >  arch/x86/kvm/svm/svm.h    | 10 +++++-----
> >  4 files changed, 10 insertions(+), 10 deletions(-)
> > 
> > diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
> > index 2dda52221fd8..bacb2ac4c59e 100644
> > --- a/arch/x86/kvm/svm/nested.c
> > +++ b/arch/x86/kvm/svm/nested.c
> > @@ -123,7 +123,7 @@ static bool nested_vmcb_needs_vls_intercept(struct vcpu_svm *svm)
> >  	return false;
> >  }
> >  
> > -void recalc_intercepts(struct vcpu_svm *svm)
> > +void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
> >  {
> >  	struct vmcb *vmcb01, *vmcb02;
> >  	unsigned int i;
> 
> Drat, I should have responded to the previous patch.  Lurking out of sight is a
> pre-existing bug that effectively invalidates this entire rename.
> 
> The existing code is:
> 
>   void recalc_intercepts(struct vcpu_svm *svm)
>   {
> 	struct vmcb *vmcb01, *vmcb02;
> 	unsigned int i;
> 
> 	vmcb_mark_dirty(svm->vmcb, VMCB_INTERCEPTS);  <======= not vmcb01!!!!!
> 
> 	if (!is_guest_mode(&svm->vcpu))
> 		return;
> 
> When L2 is active, svm->vmcb is vmcb02.  Which, at first glance, _looks_ right,
> but (the *horribly* named) recalc_intercepts() isn't _just_ recalculating
> intercepts for L2, it's also responsible for marking the VMCB_INTERCEPTS dirty
> (obviously).
> 
> But what isn't so obvious is that _all_ callers operate on vmcb01, because the
> pattern is to modify vmcb01 intercepts, and then merge the new vmcb01 intercepts
> with vmcb12, i.e. the "recalc intercepts" aspect is "part 2" of the overall
> function.

I think the 4th law of thermodynamics is that any piece of nSVM code has
a bug if you look at it long enough.

> 
> Lost in all of this is that KVM forgets to mark vmcb01 dirty, and unless there's
> a call buried somewhere deep, nested_svm_vmexit() isn't guaranteed to mark
> VMCB_INTERCEPTS dirty, e.g. if PAUSE interception is disabled.
> 
> It's probably a benign bug in practice, as AMD CPUs don't appear to do anything
> with the clean fields, but easy to fix.
> 
> As a bonus, fixing that bug yields for even better naming and code.  After the
> dust settles, we can end up with this in svm.h:
> 
>   void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm);
> 
>   static inline void svm_mark_intercepts_dirty(struct vcpu_svm *svm)
>   {
> 	vmcb_mark_dirty(svm->vmcb01.ptr, VMCB_INTERCEPTS);
> 
> 	/*
> 	 * If L2 is active, recalculate the intercepts for vmcb02 to account
> 	 * for the changes made to vmcb01.  All intercept configuration is done
> 	 * for vmcb01 and then propagated to vmcb02 to combine KVM's intercepts
> 	 * with L1's intercepts (from the vmcb12 snapshot).
> 	 */
> 	if (is_guest_mode(&svm->vcpu))
> 		nested_vmcb02_recalc_intercepts(svm);
>   }
> 
> and this for nested_vmcb02_recalc_intercepts():
> 
>   void nested_vmcb02_recalc_intercepts(struct vcpu_svm *svm)
>   {
> 	struct vmcb_ctrl_area_cached *vmcb12_ctrl = &svm->nested.ctl;
> 	struct vmcb *vmcb02 = svm->nested.vmcb02.ptr;
> 	struct vmcb *vmcb01 = svm->vmcb01.ptr;
> 	unsigned int i;
> 
> 	if (WARN_ON_ONCE(svm->vmcb != vmcb02))
> 		return;
> 
> 	...
>   }
> 
> with the only other caller of nested_vmcb02_recalc_intercepts() being
> nested_vmcb02_prepare_control().

I think this looks good. Definitely an improvement over what we
currently have :)