[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 09/17] vmx: nest: interrupt



This patch adds interrupt handling of nested, mainly includes:
  - L1 interrupt causes L2 to exit,
  - idtv handling in L2.
  - interrupt blocking handling in l2

Signed-off-by: Qing He <qing.he@xxxxxxxxx>

---
 arch/x86/hvm/vmx/intr.c        |   91 +++++++++++++++++++++++++++++++++++++++++
 arch/x86/hvm/vmx/nest.c        |   66 +++++++++++++++++++++++++++++
 arch/x86/hvm/vmx/vmx.c         |   78 +++++++++++++++++++++++------------
 include/asm-x86/hvm/vmx/nest.h |    5 ++
 4 files changed, 214 insertions(+), 26 deletions(-)

diff -r c7e763bbea63 -r a7de30ed250d xen/arch/x86/hvm/vmx/intr.c
--- a/xen/arch/x86/hvm/vmx/intr.c       Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/arch/x86/hvm/vmx/intr.c       Thu Apr 22 22:30:09 2010 +0800
@@ -33,6 +33,7 @@
 #include <asm/hvm/support.h>
 #include <asm/hvm/vmx/vmx.h>
 #include <asm/hvm/vmx/vmcs.h>
+#include <asm/hvm/vmx/vvmcs.h>
 #include <asm/hvm/vpic.h>
 #include <asm/hvm/vlapic.h>
 #include <public/hvm/ioreq.h>
@@ -110,6 +111,93 @@
     }
 }
 
+/*
+ * Nested virtualization interrupt handling:
+ *
+ *   When vcpu runs in nested context (L2), the event delivery from
+ *   L0 to L1 may be blocked by several reasons:
+ *       - virtual VMExit
+ *       - virtual VMEntry
+ *       - IDT vectoring reinjection
+ *
+ *   However, when in nesting, the blocked interrupt should not be
+ *   blocked by normal reasons like RFLAGS.IF (generating a VMExit
+ *   instead), so simply intr window may delay the interrupt from
+ *   delivery in time.
+ *
+ *   To solve this, the algorithm below is used.
+ *   v->arch.hvm_vmx.exec_control.VIRTUAL_INTR_PENDING now denotes
+ *   only L0 control, physical control may be different from it.
+ *       - if in L1, it behaves normally, intr window is written
+ *         to physical control as it is
+ *       - if in L2, replace it to MTF (or NMI window) if possible
+ *       - if MTF/NMI window is not used, intr window can still be
+ *         used but may have negative impact on interrupt performance.
+ */
+static int nest_intr_blocked(struct vcpu *v, struct hvm_intack intack)
+{
+    int r = 0;
+
+    if ( !v->arch.hvm_vcpu.in_nesting &&
+         v->arch.hvm_vmx.nest.vmresume_pending )
+        r = 1;
+
+    if ( v->arch.hvm_vcpu.in_nesting )
+    {
+        if ( v->arch.hvm_vmx.nest.vmexit_pending ||
+             v->arch.hvm_vmx.nest.vmresume_in_progress ||
+             (__vmread(VM_ENTRY_INTR_INFO) & INTR_INFO_VALID_MASK) )
+            r = 1;
+    }
+
+    return r;
+}
+
+static int vmx_nest_intr_intercept(struct vcpu *v, struct hvm_intack intack)
+{
+    u32 exit_ctrl;
+
+    /*
+     * TODO:
+     *   - if L1 intr-window exiting == 0
+     *   - vNMI
+     */
+
+    if ( nest_intr_blocked(v, intack) )
+    {
+        enable_intr_window(v, intack);
+        return 1;
+    }
+
+    if ( v->arch.hvm_vcpu.in_nesting )
+    {
+        if ( intack.source == hvm_intsrc_pic ||
+                 intack.source == hvm_intsrc_lapic )
+        {
+            vmx_inject_extint(intack.vector);
+
+            exit_ctrl = __get_vvmcs(v->arch.hvm_vmx.nest.vvmcs,
+                            VM_EXIT_CONTROLS);
+            if ( exit_ctrl & VM_EXIT_ACK_INTR_ON_EXIT )
+            {
+                /* for now, duplicate the ack path in vmx_intr_assist */
+                hvm_vcpu_ack_pending_irq(v, intack);
+                pt_intr_post(v, intack);
+
+                intack = hvm_vcpu_has_pending_irq(v);
+                if ( unlikely(intack.source != hvm_intsrc_none) )
+                    enable_intr_window(v, intack);
+            }
+            else
+                enable_intr_window(v, intack);
+
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 asmlinkage void vmx_intr_assist(void)
 {
     struct hvm_intack intack;
@@ -133,6 +221,9 @@
         if ( likely(intack.source == hvm_intsrc_none) )
             goto out;
 
+        if ( unlikely(vmx_nest_intr_intercept(v, intack)) )
+            goto out;
+
         intblk = hvm_interrupt_blocked(v, intack);
         if ( intblk == hvm_intblk_tpr )
         {
diff -r c7e763bbea63 -r a7de30ed250d xen/arch/x86/hvm/vmx/nest.c
--- a/xen/arch/x86/hvm/vmx/nest.c       Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/arch/x86/hvm/vmx/nest.c       Thu Apr 22 22:30:09 2010 +0800
@@ -642,6 +642,7 @@
 {
     struct vmx_nest_struct *nest = &v->arch.hvm_vmx.nest;
 
+    /* TODO: change L0 intr window to MTF or NMI window */
     set_shadow_control(nest, CPU_BASED_VM_EXEC_CONTROL, value);
 }
 
@@ -839,6 +840,33 @@
     __set_vvmcs(nest->vvmcs, VM_ENTRY_INTR_INFO, 0);
 }
 
+static void vmx_nest_intr_exit(struct vmx_nest_struct *nest)
+{
+    if ( !(nest->intr_info & INTR_INFO_VALID_MASK) )
+        return;
+
+    switch ( nest->intr_info & INTR_INFO_INTR_TYPE_MASK )
+    {
+    case X86_EVENTTYPE_EXT_INTR:
+        /* rename exit_reason to EXTERNAL_INTERRUPT */
+        __set_vvmcs(nest->vvmcs, VM_EXIT_REASON, 
EXIT_REASON_EXTERNAL_INTERRUPT);
+        __set_vvmcs(nest->vvmcs, EXIT_QUALIFICATION, 0);
+        __set_vvmcs(nest->vvmcs, VM_EXIT_INTR_INFO, nest->intr_info);
+        break;
+
+    case X86_EVENTTYPE_HW_EXCEPTION:
+    case X86_EVENTTYPE_SW_INTERRUPT:
+    case X86_EVENTTYPE_SW_EXCEPTION:
+        /* throw to L1 */
+        __set_vvmcs(nest->vvmcs, VM_EXIT_INTR_INFO, nest->intr_info);
+        __set_vvmcs(nest->vvmcs, VM_EXIT_INTR_ERROR_CODE, nest->error_code);
+        break;
+    case X86_EVENTTYPE_NMI:
+    default:
+        break;
+    }
+}
+
 static void virtual_vmexit(struct cpu_user_regs *regs)
 {
     struct vcpu *v = current;
@@ -848,6 +876,8 @@
 #endif
 
     sync_vvmcs_ro(nest);
+    vmx_nest_intr_exit(nest);
+
     sync_vvmcs_guest_state(nest);
 
     vmx_vmcs_switch_current(v, v->arch.hvm_vmx.vmcs, nest->hvmcs);
@@ -910,3 +940,39 @@
         virtual_vmentry(regs);
     }
 }
+
+void vmx_nest_idtv_handling(void)
+{
+    struct vcpu *v = current;
+    struct vmx_nest_struct *nest = &v->arch.hvm_vmx.nest;
+    unsigned int idtv_info = __vmread(IDT_VECTORING_INFO);
+
+    if ( likely(!(idtv_info & INTR_INFO_VALID_MASK)) )
+        return;
+
+    /*
+     * If L0 can solve the fault that causes idt vectoring, it should
+     * be reinjected, otherwise, pass to L1.
+     */
+    if ( (__vmread(VM_EXIT_REASON) != EXIT_REASON_EPT_VIOLATION &&
+          !(nest->intr_info & INTR_INFO_VALID_MASK)) ||
+         (__vmread(VM_EXIT_REASON) == EXIT_REASON_EPT_VIOLATION &&
+          !nest->vmexit_pending) )
+    {
+        __vmwrite(VM_ENTRY_INTR_INFO, idtv_info & ~INTR_INFO_RESVD_BITS_MASK);
+        if ( idtv_info & INTR_INFO_DELIVER_CODE_MASK )
+            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
+                        __vmread(IDT_VECTORING_ERROR_CODE));
+        /*
+         * SDM 23.2.4, if L1 tries to inject a software interrupt
+         * and the delivery fails, VM_EXIT_INSTRUCTION_LEN receives
+         * the value of previous VM_ENTRY_INSTRUCTION_LEN.
+         *
+         * This means EXIT_INSTRUCTION_LEN is always valid here, for
+         * software interrupts both injected by L1, and generated in L2.
+         */
+        __vmwrite(VM_ENTRY_INSTRUCTION_LEN, __vmread(VM_EXIT_INSTRUCTION_LEN));
+    }
+
+    /* TODO: NMI */
+}
diff -r c7e763bbea63 -r a7de30ed250d xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c        Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/arch/x86/hvm/vmx/vmx.c        Thu Apr 22 22:30:09 2010 +0800
@@ -1274,6 +1274,7 @@
 {
     unsigned long intr_fields;
     struct vcpu *curr = current;
+    struct vmx_nest_struct *nest = &curr->arch.hvm_vmx.nest;
 
     /*
      * NB. Callers do not need to worry about clearing STI/MOV-SS blocking:
@@ -1285,11 +1286,21 @@
 
     intr_fields = (INTR_INFO_VALID_MASK | (type<<8) | trap);
     if ( error_code != HVM_DELIVER_NO_ERROR_CODE ) {
-        __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
         intr_fields |= INTR_INFO_DELIVER_CODE_MASK;
+        if ( curr->arch.hvm_vcpu.in_nesting )
+            nest->error_code = error_code;
+        else
+            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
     }
 
-    __vmwrite(VM_ENTRY_INTR_INFO, intr_fields);
+    if ( curr->arch.hvm_vcpu.in_nesting )
+    {
+        nest->intr_info = intr_fields;
+        nest->vmexit_pending = 1;
+        return;
+    }
+    else
+        __vmwrite(VM_ENTRY_INTR_INFO, intr_fields);
 
     /* Can't inject exceptions in virtual 8086 mode because they would 
      * use the protected-mode IDT.  Emulate at the next vmenter instead. */
@@ -1299,9 +1310,14 @@
 
 void vmx_inject_hw_exception(int trap, int error_code)
 {
-    unsigned long intr_info = __vmread(VM_ENTRY_INTR_INFO);
+    unsigned long intr_info;
     struct vcpu *curr = current;
 
+    if ( curr->arch.hvm_vcpu.in_nesting )
+        intr_info = curr->arch.hvm_vmx.nest.intr_info;
+    else
+        intr_info = __vmread(VM_ENTRY_INTR_INFO);
+
     switch ( trap )
     {
     case TRAP_debug:
@@ -2314,9 +2330,31 @@
     return -1;
 }
 
+static void vmx_idtv_reinject(unsigned long idtv_info)
+{
+    if ( hvm_event_needs_reinjection((idtv_info>>8)&7, idtv_info&0xff) )
+    {
+        /* See SDM 3B 25.7.1.1 and .2 for info about masking resvd bits. */
+        __vmwrite(VM_ENTRY_INTR_INFO,
+                  idtv_info & ~INTR_INFO_RESVD_BITS_MASK);
+        if ( idtv_info & INTR_INFO_DELIVER_CODE_MASK )
+            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
+                      __vmread(IDT_VECTORING_ERROR_CODE));
+    }
+
+    /*
+     * Clear NMI-blocking interruptibility info if an NMI delivery faulted.
+     * Re-delivery will re-set it (see SDM 3B 25.7.1.2).
+     */
+    if ( (idtv_info & INTR_INFO_INTR_TYPE_MASK) == (X86_EVENTTYPE_NMI<<8) )
+        __vmwrite(GUEST_INTERRUPTIBILITY_INFO,
+                  __vmread(GUEST_INTERRUPTIBILITY_INFO) &
+                  ~VMX_INTR_SHADOW_NMI);
+}
+
 asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
 {
-    unsigned int exit_reason, idtv_info, intr_info = 0, vector = 0;
+    unsigned int exit_reason, idtv_info = 0, intr_info = 0, vector = 0;
     unsigned long exit_qualification, inst_len = 0;
     struct vcpu *v = current;
 
@@ -2398,29 +2436,14 @@
 
     hvm_maybe_deassert_evtchn_irq();
 
-    /* Event delivery caused this intercept? Queue for redelivery. */
-    idtv_info = __vmread(IDT_VECTORING_INFO);
-    if ( unlikely(idtv_info & INTR_INFO_VALID_MASK) &&
-         (exit_reason != EXIT_REASON_TASK_SWITCH) )
+    /* TODO: consolidate nested idtv handling with ordinary one */
+    if ( !v->arch.hvm_vcpu.in_nesting )
     {
-        if ( hvm_event_needs_reinjection((idtv_info>>8)&7, idtv_info&0xff) )
-        {
-            /* See SDM 3B 25.7.1.1 and .2 for info about masking resvd bits. */
-            __vmwrite(VM_ENTRY_INTR_INFO,
-                      idtv_info & ~INTR_INFO_RESVD_BITS_MASK);
-            if ( idtv_info & INTR_INFO_DELIVER_CODE_MASK )
-                __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
-                          __vmread(IDT_VECTORING_ERROR_CODE));
-        }
-
-        /*
-         * Clear NMI-blocking interruptibility info if an NMI delivery faulted.
-         * Re-delivery will re-set it (see SDM 3B 25.7.1.2).
-         */
-        if ( (idtv_info & INTR_INFO_INTR_TYPE_MASK) == (X86_EVENTTYPE_NMI<<8) )
-            __vmwrite(GUEST_INTERRUPTIBILITY_INFO,
-                      __vmread(GUEST_INTERRUPTIBILITY_INFO) &
-                      ~VMX_INTR_SHADOW_NMI);
+        /* Event delivery caused this intercept? Queue for redelivery. */
+        idtv_info = __vmread(IDT_VECTORING_INFO);
+        if ( unlikely(idtv_info & INTR_INFO_VALID_MASK) &&
+             (exit_reason != EXIT_REASON_TASK_SWITCH) )
+            vmx_idtv_reinject(idtv_info);
     }
 
     switch ( exit_reason )
@@ -2736,6 +2759,9 @@
         domain_crash(v->domain);
         break;
     }
+
+    if ( v->arch.hvm_vcpu.in_nesting )
+        vmx_nest_idtv_handling();
 }
 
 asmlinkage void vmx_vmenter_helper(void)
diff -r c7e763bbea63 -r a7de30ed250d xen/include/asm-x86/hvm/vmx/nest.h
--- a/xen/include/asm-x86/hvm/vmx/nest.h        Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/include/asm-x86/hvm/vmx/nest.h        Thu Apr 22 22:30:09 2010 +0800
@@ -44,6 +44,9 @@
     int                  vmexit_pending;
     int                  vmresume_pending;
     int                  vmresume_in_progress;
+
+    unsigned long        intr_info;
+    unsigned long        error_code;
 };
 
 asmlinkage void vmx_nest_switch_mode(void);
@@ -66,4 +69,6 @@
                                             unsigned long value);
 void vmx_nest_update_exception_bitmap(struct vcpu *v, unsigned long value);
 
+void vmx_nest_idtv_handling(void);
+
 #endif /* __ASM_X86_HVM_NEST_H__ */

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.