From: Sean Christopherson <seanjc@google.com>
To: Maxim Levitsky <mlevitsk@redhat.com>
Cc: kvm@vger.kernel.org, Paolo Bonzini <pbonzini@redhat.com>,
Ingo Molnar <mingo@redhat.com>, "H. Peter Anvin" <hpa@zytor.com>,
Dave Hansen <dave.hansen@linux.intel.com>,
linux-kernel@vger.kernel.org,
Peter Zijlstra <peterz@infradead.org>,
Thomas Gleixner <tglx@linutronix.de>,
Sandipan Das <sandipan.das@amd.com>,
Daniel Sneddon <daniel.sneddon@linux.intel.com>,
Jing Liu <jing2.liu@intel.com>,
Josh Poimboeuf <jpoimboe@kernel.org>,
Wyes Karny <wyes.karny@amd.com>, Borislav Petkov <bp@alien8.de>,
Babu Moger <babu.moger@amd.com>,
Pawan Gupta <pawan.kumar.gupta@linux.intel.com>,
Jim Mattson <jmattson@google.com>,
x86@kernel.org
Subject: Re: [PATCH 09/13] KVM: SVM: allow NMI window with vNMI
Date: Thu, 17 Nov 2022 18:21:38 +0000 [thread overview]
Message-ID: <Y3Z7sq42Ao/qRn0u@google.com> (raw)
In-Reply-To: <20221117143242.102721-10-mlevitsk@redhat.com>
On Thu, Nov 17, 2022, Maxim Levitsky wrote:
> When the vNMI is enabled, the only case when the KVM will use an NMI
> window is when the vNMI injection is pending.
>
> In this case on next IRET/RSM/STGI, the injection has to be complete
> and a new NMI can be injected.
>
> Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
> ---
> arch/x86/kvm/svm/svm.c | 19 ++++++++++++-------
> 1 file changed, 12 insertions(+), 7 deletions(-)
>
> diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> index cfec4c98bb589b..eaa30f8ace518d 100644
> --- a/arch/x86/kvm/svm/svm.c
> +++ b/arch/x86/kvm/svm/svm.c
> @@ -2477,7 +2477,10 @@ static int iret_interception(struct kvm_vcpu *vcpu)
> struct vcpu_svm *svm = to_svm(vcpu);
>
> ++vcpu->stat.nmi_window_exits;
> - vcpu->arch.hflags |= HF_IRET_MASK;
> +
> + if (!is_vnmi_enabled(svm))
> + vcpu->arch.hflags |= HF_IRET_MASK;
Ugh, HF_IRET_MASK is such a terrible name/flag. Given that it lives with GIF
and NMI, one would naturally think that it means "IRET is intercepted", but it
really means "KVM just intercepted an IRET and is waiting for NMIs to become
unblocked".
And on a related topic, why on earth are GIF, NMI, and IRET tracked in hflags?
They are 100% SVM concepts. IMO, this code would be much easier to follow if
by making them bools in vcpu_svm with more descriptive names.
> +
> if (!sev_es_guest(vcpu->kvm)) {
> svm_clr_intercept(svm, INTERCEPT_IRET);
> svm->nmi_iret_rip = kvm_rip_read(vcpu);
The vNMI interaction with this logic is confusing, as nmi_iret_rip doesn't need
to be captured for the vNMI case. SEV-ES actually has unrelated reasons for not
reading RIP vs. not intercepting IRET, they just got bundled together here for
convenience.
This is also an opportunity to clean up the SEV-ES interaction with IRET interception,
which is splattered all over the place and isn't documented anywhere.
E.g. (with an HF_IRET_MASK => awaiting_iret_completion change)
/*
* For SEV-ES guests, KVM must not rely on IRET to detect NMI unblocking as
* #VC->IRET in the guest will result in KVM thinking NMIs are unblocked before
* the guest is ready for a new NMI. Architecturally, KVM is 100% correct to
* treat NMIs as unblocked on IRET, but the guest-host ABI for SEV-ES guests is
* that KVM must wait for an explicit "NMI Complete" from the guest.
*/
static void svm_disable_iret_interception(struct vcpu_svm *svm)
{
if (!sev_es_guest(svm->vcpu.kvm))
svm_clr_intercept(svm, INTERCEPT_IRET);
}
static void svm_enable_iret_interception(struct vcpu_svm *svm)
{
if (!sev_es_guest(svm->vcpu.kvm))
svm_set_intercept(svm, INTERCEPT_IRET);
}
static int iret_interception(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
++vcpu->stat.nmi_window_exits;
/*
* No need to wait for the IRET to complete if vNMIs are enabled as
* hardware will automatically process the pending NMI when NMIs are
* unblocked from the guest's perspective.
*/
if (!is_vnmi_enabled(svm)) {
svm->awaiting_iret_completion = true;
/*
* The guest's RIP is inaccessible for SEV-ES guests, just
* assume forward progress was made on the next VM-Exit.
*/
if (!sev_es_guest(vcpu->kvm))
svm->nmi_iret_rip = kvm_rip_read(vcpu);
}
svm_disable_iret_interception(svm);
kvm_make_request(KVM_REQ_EVENT, vcpu);
return 1;
}
> @@ -3735,9 +3738,6 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu)
> {
> struct vcpu_svm *svm = to_svm(vcpu);
>
> - if (is_vnmi_enabled(svm))
> - return;
> -
> if ((vcpu->arch.hflags & (HF_NMI_MASK | HF_IRET_MASK)) == HF_NMI_MASK)
> return; /* IRET will cause a vm exit */
As much as I like incremental patches, in this case I'm having a hell of a time
reviewing the code as the vNMI logic ends up being split across four patches.
E.g. in this particular case, the above requires knowing that svm_inject_nmi()
never sets HF_NMI_MASK when vNMI is enabled.
In the next version, any objection to squashing patches 7-10 into a single "Add
non-nested vNMI support" patch?
As for this code, IMO some pre-work to change the flow would help with the vNMI
case. The GIF=0 logic overrides legacy NMI blocking, and so can be handled first.
And I vote to explicitly set INTERCEPT_IRET in the above case instead of relying
on INTERCEPT_IRET to already be set by svm_inject_nmi().
That would yield this as a final result:
static void svm_enable_nmi_window(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
/*
* GIF=0 blocks NMIs irrespective of legacy NMI blocking. No need to
* intercept or single-step IRET if GIF=0, just intercept STGI.
*/
if (!gif_set(svm)) {
if (vgif)
svm_set_intercept(svm, INTERCEPT_STGI);
return;
}
/*
* NMI is blocked, either because an NMI is in service or because KVM
* just injected an NMI. If KVM is waiting for an intercepted IRET to
* complete, single-step the IRET to wait for NMIs to become unblocked.
* Otherwise, intercept the guest's next IRET.
*/
if (svm->awaiting_iret_completion) {
svm->nmi_singlestep_guest_rflags = svm_get_rflags(vcpu);
svm->nmi_singlestep = true;
svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF);
} else {
svm_set_intercept(svm, INTERCEPT_IRET);
}
}
>
> @@ -3751,9 +3751,14 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu)
> * Something prevents NMI from been injected. Single step over possible
> * problem (IRET or exception injection or interrupt shadow)
> */
> - svm->nmi_singlestep_guest_rflags = svm_get_rflags(vcpu);
> - svm->nmi_singlestep = true;
> - svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF);
> +
> + if (is_vnmi_enabled(svm)) {
> + svm_set_intercept(svm, INTERCEPT_IRET);
This will break SEV-ES. Per commit 4444dfe4050b ("KVM: SVM: Add NMI support for
an SEV-ES guest"), the hypervisor must not rely on IRET interception to detect
NMI unblocking for SEV-ES guests. As above, I think we should provide helpers to
toggle NMI interception to reduce the probability of breaking SEV-ES.
> + } else {
> + svm->nmi_singlestep_guest_rflags = svm_get_rflags(vcpu);
> + svm->nmi_singlestep = true;
> + svm->vmcb->save.rflags |= (X86_EFLAGS_TF | X86_EFLAGS_RF);
> + }
> }
>
> static void svm_flush_tlb_current(struct kvm_vcpu *vcpu)
> --
> 2.34.3
>
next prev parent reply other threads:[~2022-11-17 18:21 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-17 14:32 [PATCH 00/13] SVM: vNMI (with my fixes) Maxim Levitsky
2022-11-17 14:32 ` [PATCH 01/13] KVM: nSVM: don't sync back tlb_ctl on nested VM exit Maxim Levitsky
2022-11-17 14:32 ` [PATCH 02/13] KVM: nSVM: don't call nested_sync_control_from_vmcb02 on each " Maxim Levitsky
2022-11-17 20:04 ` Sean Christopherson
2022-11-21 11:07 ` Maxim Levitsky
2022-11-21 17:51 ` Sean Christopherson
2022-11-17 14:32 ` [PATCH 03/13] KVM: nSVM: rename nested_sync_control_from_vmcb02 to nested_sync_int_ctl_from_vmcb02 Maxim Levitsky
2022-11-17 14:32 ` [PATCH 04/13] KVM: nSVM: clean up copying of int_ctl fields back to vmcb01/vmcb12 Maxim Levitsky
2022-11-17 20:15 ` Sean Christopherson
2022-11-21 11:10 ` Maxim Levitsky
2022-11-17 14:32 ` [PATCH 05/13] x86/cpu: Add CPUID feature bit for VNMI Maxim Levitsky
2022-11-17 14:32 ` [PATCH 06/13] KVM: SVM: Add VNMI bit definition Maxim Levitsky
2022-11-17 14:37 ` Borislav Petkov
2022-11-17 16:42 ` Sean Christopherson
2022-11-17 17:07 ` Borislav Petkov
2022-11-17 20:33 ` Sean Christopherson
2022-11-17 20:27 ` Sean Christopherson
2022-11-17 14:32 ` [PATCH 07/13] KVM: SVM: Add VNMI support in get/set_nmi_mask Maxim Levitsky
2022-11-17 18:54 ` Sean Christopherson
2022-11-21 12:36 ` Maxim Levitsky
2022-11-21 17:18 ` Sean Christopherson
2022-12-04 18:42 ` Maxim Levitsky
2022-12-06 18:27 ` Sean Christopherson
2022-11-17 14:32 ` [PATCH 08/13] KVM: SVM: Report NMI not allowed when Guest busy handling VNMI Maxim Levitsky
2022-11-17 14:32 ` [PATCH 09/13] KVM: SVM: allow NMI window with vNMI Maxim Levitsky
2022-11-17 18:21 ` Sean Christopherson [this message]
2022-11-21 13:40 ` Maxim Levitsky
2022-11-17 14:32 ` [PATCH 10/13] KVM: SVM: Add VNMI support in inject_nmi Maxim Levitsky
2022-11-21 17:12 ` Sean Christopherson
2022-11-17 14:32 ` [PATCH 11/13] KVM: nSVM: implement nested VNMI Maxim Levitsky
2022-11-17 14:32 ` [PATCH 12/13] KVM: nSVM: emulate VMEXIT_INVALID case for " Maxim Levitsky
2022-11-17 20:18 ` Sean Christopherson
2022-11-17 14:32 ` [PATCH 13/13] KVM: SVM: Enable VNMI feature Maxim Levitsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Y3Z7sq42Ao/qRn0u@google.com \
--to=seanjc@google.com \
--cc=babu.moger@amd.com \
--cc=bp@alien8.de \
--cc=daniel.sneddon@linux.intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=jing2.liu@intel.com \
--cc=jmattson@google.com \
--cc=jpoimboe@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=mlevitsk@redhat.com \
--cc=pawan.kumar.gupta@linux.intel.com \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=sandipan.das@amd.com \
--cc=tglx@linutronix.de \
--cc=wyes.karny@amd.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox