[PATCH v5 3/5] HV: Use NMI-window exiting to address req missing issue


Kaige Fu
 

There is a window where we may miss the current request in the
notification period when the work flow is as the following:

CPUx + + CPUr
| |
| +--+
| | | Handle pending req
| <--+
+--+ |
| | Set req flag |
<--+ |
+------------------>---+
| Send NMI | | Handle NMI
| <--+
| |
| |
| +--> vCPU enter
| |
+ +

So, this patch enables the NMI-window exiting to trigger the next vmexit
once there is no "virtual-NMI blocking" after vCPU enter into VMX non-root
mode. Then we can process the pending request on time.

Signed-off-by: Kaige Fu <kaige.fu@...>
---
hypervisor/arch/x86/guest/virq.c | 17 ++++++++++++++++
hypervisor/arch/x86/guest/vmcs.c | 2 +-
hypervisor/arch/x86/guest/vmexit.c | 2 +-
hypervisor/arch/x86/irq.c | 31 ++++++++++++++++++++++++++++--
hypervisor/include/arch/x86/irq.h | 1 +
5 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/hypervisor/arch/x86/guest/virq.c b/hypervisor/arch/x86/guest/virq.c
index 8e4658e4..79210717 100644
--- a/hypervisor/arch/x86/guest/virq.c
+++ b/hypervisor/arch/x86/guest/virq.c
@@ -524,3 +524,20 @@ int32_t exception_vmexit_handler(struct acrn_vcpu *vcpu)

return status;
}
+
+int nmi_window_vmexit_handler(__unused struct acrn_vcpu *vcpu)
+{
+ uint32_t value32;
+
+ /*
+ * Disable NMI-window exiting here. We will process
+ * the pending request in acrn_handle_pending_request later
+ */
+ value32 = exec_vmread32(VMX_PROC_VM_EXEC_CONTROLS);
+ value32 &= ~VMX_PROCBASED_CTLS_NMI_WINEXIT;
+ exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS, value32);
+
+ vcpu_retain_rip(vcpu);
+
+ return 0;
+}
diff --git a/hypervisor/arch/x86/guest/vmcs.c b/hypervisor/arch/x86/guest/vmcs.c
index 1efa37db..7dd2d806 100644
--- a/hypervisor/arch/x86/guest/vmcs.c
+++ b/hypervisor/arch/x86/guest/vmcs.c
@@ -582,7 +582,7 @@ void switch_apicv_mode_x2apic(struct acrn_vcpu *vcpu)
* directly without vmexit. So, here we enable NMI-exiting and use NMI
* as notification signal after passthroughing the lapic to vCPU.
*/
- value32 |= VMX_PINBASED_CTLS_NMI_EXIT;
+ value32 |= VMX_PINBASED_CTLS_NMI_EXIT | VMX_PINBASED_CTLS_VIRT_NMI;
exec_vmwrite32(VMX_PIN_VM_EXEC_CONTROLS, value32);

value32 = exec_vmread32(VMX_EXIT_CONTROLS);
diff --git a/hypervisor/arch/x86/guest/vmexit.c b/hypervisor/arch/x86/guest/vmexit.c
index 23528c2f..0f09741c 100644
--- a/hypervisor/arch/x86/guest/vmexit.c
+++ b/hypervisor/arch/x86/guest/vmexit.c
@@ -51,7 +51,7 @@ static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = {
[VMX_EXIT_REASON_INTERRUPT_WINDOW] = {
.handler = interrupt_window_vmexit_handler},
[VMX_EXIT_REASON_NMI_WINDOW] = {
- .handler = unhandled_vmexit_handler},
+ .handler = nmi_window_vmexit_handler},
[VMX_EXIT_REASON_TASK_SWITCH] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_CPUID] = {
diff --git a/hypervisor/arch/x86/irq.c b/hypervisor/arch/x86/irq.c
index 158165d6..b0ba0351 100644
--- a/hypervisor/arch/x86/irq.c
+++ b/hypervisor/arch/x86/irq.c
@@ -18,6 +18,7 @@
#include <vboot.h>
#include <dump.h>
#include <logmsg.h>
+#include <vmx.h>

static spinlock_t exception_spinlock = { .head = 0U, .tail = 0U, };
static spinlock_t irq_alloc_spinlock = { .head = 0U, .tail = 0U, };
@@ -384,10 +385,36 @@ void dispatch_exception(struct intr_excp_ctx *ctx)

void handle_nmi(__unused struct intr_excp_ctx *ctx)
{
+ uint32_t value32;
+
/*
- * Just ignore the NMI here for now.
- * TODO: implement specific NMI handling function.
+ * There is a window where we may miss the current request in this
+ * notification period when the work flow is as the following:
+ *
+ * CPUx + + CPUr
+ * | |
+ * | +--+
+ * | | | Handle pending req
+ * | <--+
+ * +--+ |
+ * | | Set req flag |
+ * <--+ |
+ * +------------------>---+
+ * | Send NMI | | Handle NMI
+ * | <--+
+ * | |
+ * | |
+ * | +--> vCPU enter
+ * | |
+ * + +
+ *
+ * So, here we enable the NMI-window exiting to trigger the next vmexit
+ * once there is no "virtual-NMI blocking" after vCPU enter into VMX non-root
+ * mode. Then we can process the pending request on time.
*/
+ value32 = exec_vmread32(VMX_PROC_VM_EXEC_CONTROLS);
+ value32 |= VMX_PROCBASED_CTLS_NMI_WINEXIT;
+ exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS, value32);
}

static void init_irq_descs(void)
diff --git a/hypervisor/include/arch/x86/irq.h b/hypervisor/include/arch/x86/irq.h
index 5673d910..42a1055f 100644
--- a/hypervisor/include/arch/x86/irq.h
+++ b/hypervisor/include/arch/x86/irq.h
@@ -209,6 +209,7 @@ void vcpu_make_request(struct acrn_vcpu *vcpu, uint16_t eventid);
* @pre vcpu != NULL
*/
int32_t exception_vmexit_handler(struct acrn_vcpu *vcpu);
+int32_t nmi_window_vmexit_handler(struct acrn_vcpu *vcpu);
int32_t interrupt_window_vmexit_handler(struct acrn_vcpu *vcpu);
int32_t external_interrupt_vmexit_handler(struct acrn_vcpu *vcpu);
int32_t acrn_handle_pending_request(struct acrn_vcpu *vcpu);
--
2.20.0

Join acrn-dev@lists.projectacrn.org to automatically receive all group messages.