hv: Offload syncing PIR to vIRR to processor hardware

ACRN syncs PIR to vIRR in the software in cases the Posted
Interrupt notification happens while the pCPU is in root mode.

Sync can be achieved by processor hardware by sending a
posted interrupt notiification vector.

This patch sends a self-IPI, if there are interrupts pending in PIR,
which is serviced by the logical processor at the next
VMEnter

Tracked-On: #4777
Signed-off-by: Sainath Grandhi <sainath.grandhi@intel.com>
This commit is contained in:
Sainath Grandhi 2020-04-29 20:37:25 -07:00 committed by wenlingz
parent 869ccb7ba8
commit bf1ff8c98f
1 changed files with 15 additions and 67 deletions

View File

@ -2228,75 +2228,21 @@ static void apicv_basic_inject_intr(struct acrn_vlapic *vlapic,
} }
/* /*
* Transfer the pending interrupts in the PIR descriptor to the IRR * @brief Send a Posted Interrupt to itself.
* in the virtual APIC page. *
* @pre get_pi_desc(vlapic2vcpu(vlapic)) != NULL * Interrupts are disabled on pCPU at this point of time.
* Upon the next VMEnter the self-IPI is serviced by the logical processor.
* Since the IPI vector is Posted Interrupt vector, logical processor syncs
* PIR to vIRR and updates RVI.
*
* @pre get_pi_desc(vlapic->vcpu) != NULL
*/ */
static void vlapic_apicv_inject_pir(struct acrn_vlapic *vlapic)
{
struct pi_desc *pid;
struct lapic_regs *lapic;
uint64_t val, pirval;
uint16_t rvi, pirbase = 0U, i;
uint16_t intr_status_old, intr_status_new;
struct lapic_reg *irr = NULL;
pid = get_pi_desc(vlapic2vcpu(vlapic));
if (bitmap_test_and_clear_lock(POSTED_INTR_ON, &pid->control.value)) {
pirval = 0UL;
lapic = &(vlapic->apic_page);
irr = &lapic->irr[0];
for (i = 0U; i < 4U; i++) {
val = atomic_readandclear64(&pid->pir[i]);
if (val != 0UL) {
irr[i * 2U].v |= (uint32_t)val;
irr[(i * 2U) + 1U].v |= (uint32_t)(val >> 32U);
pirbase = 64U * i;
pirval = val;
}
}
/*
* Update RVI so the processor can evaluate pending virtual
* interrupts on VM-entry.
*
* It is possible for pirval to be 0 here, even though the
* pending bit has been set. The scenario is:
* CPU-Y is sending a posted interrupt to CPU-X, which
* is running a guest and processing posted interrupts in h/w.
* CPU-X will eventually exit and the state seen in s/w is
* the pending bit set, but no PIR bits set.
*
* CPU-X CPU-Y
* (vm running) (host running)
* rx posted interrupt
* CLEAR pending bit
* SET PIR bit
* READ/CLEAR PIR bits
* SET pending bit
* (vm exit)
* pending bit set, PIR 0
*/
if (pirval != 0UL) {
rvi = pirbase + fls64(pirval);
intr_status_old = 0xFFFFU &
exec_vmread16(VMX_GUEST_INTR_STATUS);
intr_status_new = (intr_status_old & 0xFF00U) | rvi;
if (intr_status_new > intr_status_old) {
exec_vmwrite16(VMX_GUEST_INTR_STATUS,
intr_status_new);
}
}
}
}
static void apicv_advanced_inject_intr(struct acrn_vlapic *vlapic, static void apicv_advanced_inject_intr(struct acrn_vlapic *vlapic,
__unused bool guest_irq_enabled, __unused bool injected) __unused bool guest_irq_enabled, __unused bool injected)
{ {
struct acrn_vcpu *vcpu = vlapic2vcpu(vlapic);
struct pi_desc *pid = get_pi_desc(vcpu);
/* /*
* From SDM Vol3 26.3.2.5: * From SDM Vol3 26.3.2.5:
* Once the virtual interrupt is recognized, it will be delivered * Once the virtual interrupt is recognized, it will be delivered
@ -2307,10 +2253,12 @@ static void apicv_advanced_inject_intr(struct acrn_vlapic *vlapic,
* evaluation/delivery of apicv virtual interrupts in one time * evaluation/delivery of apicv virtual interrupts in one time
* vm-entry. * vm-entry.
* *
* Here to sync the pending interrupts to irr and update rvi if * Here to sync the pending interrupts to irr and update rvi
* needed. And then try to handle vmcs event injection. * self-IPI with Posted Interrupt Notification Vector is sent.
*/ */
vlapic_apicv_inject_pir(vlapic); if (bitmap_test(POSTED_INTR_ON, &(pid->control.value))) {
apicv_trigger_pi_anv(pcpuid_from_vcpu(vcpu), (uint32_t)(vcpu->arch.pid.control.bits.nv));
}
} }
void vlapic_inject_intr(struct acrn_vlapic *vlapic, bool guest_irq_enabled, bool injected) void vlapic_inject_intr(struct acrn_vlapic *vlapic, bool guest_irq_enabled, bool injected)