mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
Fix the "hda lost interrupt" issue when creating a VMX guest on a PAE
host. Occasionally when injecting an IDE DMA interrupt into the guest, a page fault occurs (e.g., because the IDT mapping is not present in shadow pagetables). This causes an immediate vmexit and, because it occurred during event delivery, the original VM_ENTRY_INTR_INFO_FIELD is kept in IDT_VECTORING_INFO_FIELD. The current code copies IDT_VECTORING_INFO_FIELD back to VM_ENTRY_INTR_INFO_FIELD, intending that the interrupt will be injected again on next vmresume. However, there is a corner case: if, before the next vmresume, a timer interrupt happened then vmx_intr_assist may overwrite the information on VM_ENTRY_INTR_INFO_FIELD, and the IDE DMA interrupt is effectively lost. This patch checks the IDT_VECTORING_INFO_FIELD in vmx_intr_assist and, if it is set, copies it to VM_ENTRY_INTR_INFO_FIELD and returns. Signed-off-by: Yunhong Jiang <Yunhong.jiang@intel.com> Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
1 parent
c10f3d80c9
commit
7d67a7ab23
2 files changed
+26
-19
No files matched your search
@@ -153,6 +153,9 @@ asmlinkage void vmx_intr_assist(void)
|
||||
struct hvm_domain *plat=&v->domain->arch.hvm_domain;
|
||||
struct hvm_time_info *time_info = &plat->vpit.time_info;
|
||||
struct hvm_virpic *pic= &plat->vpic;
|
||||
unsigned int idtv_info_field;
|
||||
unsigned long inst_len;
|
||||
int has_ext_irq;
|
||||
|
||||
if ( v->vcpu_id == 0 )
|
||||
hvm_pic_assist(v);
|
||||
@@ -162,8 +165,29 @@ asmlinkage void vmx_intr_assist(void)
|
||||
pic_set_irq(pic, 0, 1);
|
||||
}
|
||||
|
||||
if ( !cpu_has_pending_irq(v) ) return;
|
||||
has_ext_irq = cpu_has_pending_irq(v);
|
||||
__vmread(IDT_VECTORING_INFO_FIELD, &idtv_info_field);
|
||||
if (idtv_info_field & INTR_INFO_VALID_MASK) {
|
||||
__vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
|
||||
|
||||
__vmread(VM_EXIT_INSTRUCTION_LEN, &inst_len);
|
||||
if (inst_len >= 1 && inst_len <= 15)
|
||||
__vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
|
||||
|
||||
if (idtv_info_field & 0x800) { /* valid error code */
|
||||
unsigned long error_code;
|
||||
__vmread(IDT_VECTORING_ERROR_CODE, &error_code);
|
||||
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
|
||||
}
|
||||
if ( has_ext_irq )
|
||||
enable_irq_window(v);
|
||||
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !has_ext_irq ) return;
|
||||
if ( is_interruptibility_state() ) { /* pre-cleared for emulated instruction */
|
||||
enable_irq_window(v);
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "interruptibility");
|
||||
|
||||
@@ -2046,7 +2046,7 @@ void restore_cpu_user_regs(struct cpu_user_regs *regs)
|
||||
|
||||
asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs)
|
||||
{
|
||||
unsigned int exit_reason, idtv_info_field;
|
||||
unsigned int exit_reason;
|
||||
unsigned long exit_qualification, eip, inst_len = 0;
|
||||
struct vcpu *v = current;
|
||||
int error;
|
||||
@@ -2056,23 +2056,6 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs)
|
||||
|
||||
perfc_incra(vmexits, exit_reason);
|
||||
|
||||
__vmread(IDT_VECTORING_INFO_FIELD, &idtv_info_field);
|
||||
if (idtv_info_field & INTR_INFO_VALID_MASK) {
|
||||
__vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
|
||||
|
||||
__vmread(VM_EXIT_INSTRUCTION_LEN, &inst_len);
|
||||
if (inst_len >= 1 && inst_len <= 15)
|
||||
__vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
|
||||
|
||||
if (idtv_info_field & 0x800) { /* valid error code */
|
||||
unsigned long error_code;
|
||||
__vmread(IDT_VECTORING_ERROR_CODE, &error_code);
|
||||
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
|
||||
}
|
||||
|
||||
HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
|
||||
}
|
||||
|
||||
/* don't bother H/W interrutps */
|
||||
if (exit_reason != EXIT_REASON_EXTERNAL_INTERRUPT &&
|
||||
exit_reason != EXIT_REASON_VMCALL &&
|
||||
|
||||