HV: Use NMI-window exiting to address req missing issue

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.

Tracked-On: #3886
Acked-by: Eddie Dong <eddie.dong@intel.com>
Signed-off-by: Kaige Fu <kaige.fu@intel.com>
This commit is contained in:
Kaige Fu 2019-12-06 09:34:07 +00:00 committed by wenlingz
parent 40ba7e8686
commit a13909cedc
5 changed files with 49 additions and 4 deletions

View File

@ -531,3 +531,20 @@ int32_t exception_vmexit_handler(struct acrn_vcpu *vcpu)
return status;
}
int32_t nmi_window_vmexit_handler(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;
}

View File

@ -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);

View File

@ -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] = {

View File

@ -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)

View File

@ -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);