From 38d5df723d9358ebbcd2d7982fcbc787be2f9c98 Mon Sep 17 00:00:00 2001 From: Yonghua Huang Date: Tue, 9 Oct 2018 19:50:58 +0800 Subject: [PATCH] hv:enable APICv-Posted Interrupt to enable APICv Posted interrupt supported, following the specifications defined in Intel SDM Section #29.6, Volume3. Posted-interrupt processing is a feature by which a processor processes the virtual interrupts by recording them as pending on the virtual-APIC page. Injecting interrupts to VCPU from remote CPU without causing VM exit on the destination, following steps in SDM Section 29.6,volume3: Tracked-On: #1447 Signed-off-by: Yonghua Huang Acked-by: Eddie Dong --- hypervisor/arch/x86/cpu.c | 5 +++ hypervisor/arch/x86/guest/vlapic.c | 38 ++++++++++++++++++++-- hypervisor/arch/x86/irq.c | 1 + hypervisor/arch/x86/notify.c | 19 +++++++++++ hypervisor/arch/x86/vmx.c | 10 ++++++ hypervisor/include/arch/x86/cpu.h | 1 + hypervisor/include/arch/x86/guest/vlapic.h | 2 ++ hypervisor/include/arch/x86/irq.h | 5 ++- hypervisor/include/arch/x86/vmx.h | 3 ++ 9 files changed, 80 insertions(+), 4 deletions(-) diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index f25262786..7eaa897d2 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -483,6 +483,7 @@ static void bsp_boot_post(void) timer_init(); setup_notification(); + setup_posted_intr_notification(); ptdev_init(); init_scheduler(); @@ -860,6 +861,10 @@ bool is_apicv_intr_delivery_supported(void) return ((cpu_caps.apicv_features & VAPIC_FEATURE_INTR_DELIVERY) != 0U); } +bool is_apicv_posted_intr_supported(void) +{ + return ((cpu_caps.apicv_features & VAPIC_FEATURE_POST_INTR) != 0U); +} static void cpu_xsave_init(void) { diff --git a/hypervisor/arch/x86/guest/vlapic.c b/hypervisor/arch/x86/guest/vlapic.c index 60051979e..b78ac37cd 100644 --- a/hypervisor/arch/x86/guest/vlapic.c +++ b/hypervisor/arch/x86/guest/vlapic.c @@ -460,6 +460,7 @@ vlapic_set_intr_ready(struct acrn_vlapic *vlapic, uint32_t vector, bool level) struct lapic_reg *irrptr, *tmrptr; uint32_t mask; uint32_t idx; + int pending_intr; ASSERT(vector <= NR_MAX_VECTOR, "invalid vector %u", vector); @@ -480,7 +481,26 @@ vlapic_set_intr_ready(struct acrn_vlapic *vlapic, uint32_t vector, bool level) } if (is_apicv_intr_delivery_supported()) { - return apicv_set_intr_ready(vlapic, vector); + pending_intr = apicv_set_intr_ready(vlapic, vector); + if ((pending_intr != 0) + && is_apicv_posted_intr_supported() + && (get_cpu_id() != vlapic->vcpu->pcpu_id)) { + /* + * Send interrupt to vCPU via posted interrupt way: + * 1. If target vCPU is in non-root mode(running), + * send PI notification to vCPU and hardware will + * sync PIR to vIRR automatically. + * 2. If target vCPU is in root mode(isn't running), + * record this request as ACRN_REQUEST_EVENT,then + * will pick up the interrupt from PIR and inject + * it to vCPU in next vmentry. + */ + bitmap_set_lock(ACRN_REQUEST_EVENT, + &vlapic->vcpu->arch_vcpu.pending_req); + vlapic_post_intr(vlapic->vcpu->pcpu_id); + return 0; + } + return pending_intr; } idx = vector >> 5U; @@ -508,6 +528,20 @@ vlapic_set_intr_ready(struct acrn_vlapic *vlapic, uint32_t vector, bool level) return 1; } +/* Post an interrupt to the vcpu running on 'hostcpu'. */ +void vlapic_post_intr(uint16_t dest_pcpu_id) +{ + send_single_ipi(dest_pcpu_id, VECTOR_POSTED_INTR); +} + +uint64_t apicv_get_pir_desc_paddr(struct vcpu *vcpu) +{ + struct acrn_vlapic *vlapic; + + vlapic = &vcpu->arch_vcpu.vlapic; + return hva2hpa(&(vlapic->pir_desc)); +} + /** * @pre offset value shall be one of the folllowing values: * APIC_OFFSET_CMCI_LVT @@ -1817,8 +1851,6 @@ vlapic_set_intr(struct vcpu *vcpu, uint32_t vector, bool level) vlapic = vcpu_vlapic(vcpu); if (vlapic_set_intr_ready(vlapic, vector, level) != 0) { vcpu_make_request(vcpu, ACRN_REQUEST_EVENT); - } else { - ret = -ENODEV; } return ret; diff --git a/hypervisor/arch/x86/irq.c b/hypervisor/arch/x86/irq.c index b59c32c04..fa105fce2 100644 --- a/hypervisor/arch/x86/irq.c +++ b/hypervisor/arch/x86/irq.c @@ -25,6 +25,7 @@ struct static_mapping_table { static struct static_mapping_table irq_static_mappings[NR_STATIC_MAPPINGS] = { {TIMER_IRQ, VECTOR_TIMER}, {NOTIFY_IRQ, VECTOR_NOTIFY_VCPU}, + {POSTED_INTR_NOTIFY_IRQ, VECTOR_POSTED_INTR}, }; /* diff --git a/hypervisor/arch/x86/notify.c b/hypervisor/arch/x86/notify.c index adb17b408..7ca82e3b0 100644 --- a/hypervisor/arch/x86/notify.c +++ b/hypervisor/arch/x86/notify.c @@ -96,3 +96,22 @@ void setup_notification(void) dev_dbg(ACRN_DBG_PTIRQ, "NOTIFY: irq[%d] setup vector %x", notification_irq, irq_to_vector(notification_irq)); } + +static void posted_intr_notification(__unused uint32_t irq, __unused void *data) +{ + /* Dummy IRQ handler for case that Posted-Interrupt Notification + * is sent to vCPU in root mode(isn't running),interrupt will be + * picked up in next vmentry,do nothine here. + */ +} + +/*pre-conditon: be called only by BSP initialization proccess*/ +void setup_posted_intr_notification(void) +{ + if (request_irq(POSTED_INTR_NOTIFY_IRQ, + posted_intr_notification, + NULL, IRQF_NONE) < 0) { + pr_err("Failed to setup posted-intr notification"); + return; + } +} diff --git a/hypervisor/arch/x86/vmx.c b/hypervisor/arch/x86/vmx.c index 04562ded6..c57af6de4 100644 --- a/hypervisor/arch/x86/vmx.c +++ b/hypervisor/arch/x86/vmx.c @@ -773,6 +773,10 @@ static void init_exec_ctrl(struct vcpu *vcpu) value32 = check_vmx_ctrl(MSR_IA32_VMX_PINBASED_CTLS, VMX_PINBASED_CTLS_IRQ_EXIT); + if (is_apicv_posted_intr_supported()) { + value32 |= VMX_PINBASED_CTLS_POST_IRQ; + } + exec_vmwrite32(VMX_PIN_VM_EXEC_CONTROLS, value32); pr_dbg("VMX_PIN_VM_EXEC_CONTROLS: 0x%x ", value32); @@ -864,6 +868,12 @@ static void init_exec_ctrl(struct vcpu *vcpu) exec_vmwrite64(VMX_EOI_EXIT3_FULL, 0UL); exec_vmwrite16(VMX_GUEST_INTR_STATUS, 0U); + if (is_apicv_posted_intr_supported()) { + exec_vmwrite16(VMX_POSTED_INTR_VECTOR, + VECTOR_POSTED_INTR); + exec_vmwrite64(VMX_PIR_DESC_ADDR_FULL, + apicv_get_pir_desc_paddr(vcpu)); + } } /* Load EPTP execution control diff --git a/hypervisor/include/arch/x86/cpu.h b/hypervisor/include/arch/x86/cpu.h index feccb69be..89ae2ca3f 100644 --- a/hypervisor/include/arch/x86/cpu.h +++ b/hypervisor/include/arch/x86/cpu.h @@ -318,6 +318,7 @@ void cpu_do_idle(void); void cpu_dead(uint16_t pcpu_id); void trampoline_start16(void); bool is_apicv_intr_delivery_supported(void); +bool is_apicv_posted_intr_supported(void); bool is_ept_supported(void); bool cpu_has_cap(uint32_t bit); void load_cpu_state_data(void); diff --git a/hypervisor/include/arch/x86/guest/vlapic.h b/hypervisor/include/arch/x86/guest/vlapic.h index b861b2904..a5186f984 100644 --- a/hypervisor/include/arch/x86/guest/vlapic.h +++ b/hypervisor/include/arch/x86/guest/vlapic.h @@ -122,6 +122,8 @@ int vlapic_pending_intr(struct acrn_vlapic *vlapic, uint32_t *vecptr); * block interrupt delivery). */ void vlapic_intr_accepted(struct acrn_vlapic *vlapic, uint32_t vector); +void vlapic_post_intr(uint16_t dest_pcpu_id); +uint64_t apicv_get_pir_desc_paddr(struct vcpu *vcpu); struct acrn_vlapic *vm_lapic_from_pcpuid(struct vm *vm, uint16_t pcpu_id); int vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval); diff --git a/hypervisor/include/arch/x86/irq.h b/hypervisor/include/arch/x86/irq.h index be2df1fa9..f3aaff735 100644 --- a/hypervisor/include/arch/x86/irq.h +++ b/hypervisor/include/arch/x86/irq.h @@ -19,6 +19,7 @@ #define VECTOR_TIMER 0xEFU #define VECTOR_NOTIFY_VCPU 0xF0U +#define VECTOR_POSTED_INTR 0xF2U #define VECTOR_VIRT_IRQ_VHM 0xF7U #define VECTOR_SPURIOUS 0xFFU #define VECTOR_HYPERVISOR_CALLBACK_VHM 0xF3U @@ -33,9 +34,10 @@ #define NR_IRQS 256U #define IRQ_INVALID 0xffffffffU -#define NR_STATIC_MAPPINGS (2U) +#define NR_STATIC_MAPPINGS (3U) #define TIMER_IRQ (NR_IRQS - 1U) #define NOTIFY_IRQ (NR_IRQS - 2U) +#define POSTED_INTR_NOTIFY_IRQ (NR_IRQS - 3U) #define DEFAULT_DEST_MODE IOAPIC_RTE_DESTLOG #define DEFAULT_DELIVERY_MODE IOAPIC_RTE_DELLOPRI @@ -77,6 +79,7 @@ void partition_mode_dispatch_interrupt(struct intr_excp_ctx *ctx); #endif void setup_notification(void); +void setup_posted_intr_notification(void); typedef void (*spurious_handler_t)(uint32_t vector); extern spurious_handler_t spurious_handler; diff --git a/hypervisor/include/arch/x86/vmx.h b/hypervisor/include/arch/x86/vmx.h index 36e2daad0..c1d2d2859 100644 --- a/hypervisor/include/arch/x86/vmx.h +++ b/hypervisor/include/arch/x86/vmx.h @@ -9,6 +9,7 @@ /* 16-bit control fields */ #define VMX_VPID 0x00000000U +#define VMX_POSTED_INTR_VECTOR 0x00000002U /* 16-bit guest-state fields */ #define VMX_GUEST_ES_SEL 0x00000800U #define VMX_GUEST_CS_SEL 0x00000802U @@ -48,6 +49,8 @@ #define VMX_VIRTUAL_APIC_PAGE_ADDR_HIGH 0x00002013U #define VMX_APIC_ACCESS_ADDR_FULL 0x00002014U #define VMX_APIC_ACCESS_ADDR_HIGH 0x00002015U +#define VMX_PIR_DESC_ADDR_FULL 0x00002016U +#define VMX_PIR_DESC_ADDR_HIGH 0x00002017U #define VMX_EPT_POINTER_FULL 0x0000201AU #define VMX_EPT_POINTER_HIGH 0x0000201BU #define VMX_EOI_EXIT0_FULL 0x0000201CU