hv: send IPI instead of irq injection to notify vcpu with lapic pt

For VM with local apic pt for realtime scenatios, we support virtio device with PMD backend.
But we still need to inject MSI to notify the front-end, to avoid changing the front-end drivers.

Since the lapic is passed through, irq injection to vlapic won't work.
This commit fix it by sending IPI with vector need to inject.

Tracked-On: #2351
Signed-off-by: Yan, Like <like.yan@intel.com>
Acked-by: Anthony Xu <anthony.xu@intel.com>
This commit is contained in:
Yan, Like 2018-12-22 20:12:53 +08:00 committed by wenlingz
parent 16df57aa44
commit e2cecfb560
6 changed files with 131 additions and 28 deletions

View File

@ -101,7 +101,7 @@ vm_lapic_from_vcpu_id(struct acrn_vm *vm, uint16_t vcpu_id)
return vcpu_vlapic(vcpu);
}
static uint16_t vm_apicid2vcpu_id(struct acrn_vm *vm, uint8_t lapicid)
static uint16_t vm_apicid2vcpu_id(struct acrn_vm *vm, uint32_t lapicid)
{
uint16_t i;
struct acrn_vcpu *vcpu;
@ -117,7 +117,7 @@ static uint16_t vm_apicid2vcpu_id(struct acrn_vm *vm, uint8_t lapicid)
if (cpu_id == INVALID_CPU_ID) {
cpu_id = get_pcpu_nums();
pr_err("%s: bad lapicid %hhu", __func__, lapicid);
pr_err("%s: bad lapicid %lu", __func__, lapicid);
}
return cpu_id;
@ -1045,7 +1045,7 @@ vlapic_calcdest(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys, b
* Physical mode: destination is LAPIC ID.
*/
*dmask = 0UL;
vcpu_id = vm_apicid2vcpu_id(vm, (uint8_t)dest);
vcpu_id = vm_apicid2vcpu_id(vm, dest);
if (vcpu_id < vm->hw.created_vcpus) {
bitmap_set_lock(vcpu_id, dmask);
}
@ -1135,6 +1135,57 @@ vlapic_calcdest(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys, b
}
}
/*
* This function populates 'dmask' with the set of vcpus that is the possible destination
* when lapic is passthru.
*/
void
vlapic_calcdest_lapic_pt(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys)
{
struct acrn_vlapic *vlapic;
struct acrn_vcpu *vcpu;
uint32_t ldr, logical_id, dest_logical_id, dest_cluster_id;
uint16_t vcpu_id;
if (dest == 0xffU) {
/*
* Broadcast in both logical and physical modes.
*/
*dmask = vm_active_cpus(vm);
} else if (phys) {
/*
* Physical mode: destination is LAPIC ID.
*/
*dmask = 0UL;
vcpu_id = vm_apicid2vcpu_id(vm, dest);
if (vcpu_id < vm->hw.created_vcpus) {
bitmap_set_lock(vcpu_id, dmask);
}
dev_dbg(ACRN_DBG_LAPICPT, "%s: phys destmod, dmask: 0x%016llx", __func__, *dmask);
} else {
/*
* Logical mode: match each APIC that has a bit set
* in its LDR that matches a bit in the ldest.
*/
foreach_vcpu(vcpu_id, vm, vcpu) {
vlapic = vm_lapic_from_vcpu_id(vm, vcpu_id);
ldr = vlapic->apic_page.ldr.v;
logical_id = ldr & 0xFFFFU;
dest_cluster_id = (dest >> 16U) & 0xFFFFU;
dest_logical_id = dest & 0xFFFFU;
if (dest_cluster_id != ((ldr >> 16U) & 0xFFFFU)) {
continue;
}
if ((dest_logical_id & logical_id) != 0U) {
bitmap_set_lock(vcpu_id, dmask);
}
}
dev_dbg(ACRN_DBG_LAPICPT, "%s: logical destmod, dmask: 0x%016llx", __func__, *dmask);
}
}
static void
vlapic_set_tpr(struct acrn_vlapic *vlapic, uint32_t val)
{

View File

@ -6,27 +6,6 @@
#include <hypervisor.h>
/* x2APIC Interrupt Command Register (ICR) structure */
union apic_icr {
uint64_t value;
struct {
uint32_t lo_32;
uint32_t hi_32;
} value_32;
struct {
uint32_t vector:8;
uint32_t delivery_mode:3;
uint32_t destination_mode:1;
uint32_t rsvd_1:2;
uint32_t level:1;
uint32_t trigger_mode:1;
uint32_t rsvd_2:2;
uint32_t shorthand:2;
uint32_t rsvd_3:12;
uint32_t dest_field:32;
} bits;
};
union lapic_base_msr {
uint64_t value;
struct {

View File

@ -375,6 +375,53 @@ int32_t hcall_set_irqline(const struct acrn_vm *vm, uint16_t vmid,
return ret;
}
static void inject_msi_lapic_pt(struct acrn_vm *vm, const struct acrn_msi_entry *vmsi)
{
union apic_icr icr;
struct acrn_vcpu *vcpu;
union msi_addr_reg vmsi_addr;
union msi_data_reg vmsi_data;
uint64_t vdmask = 0UL;
uint32_t vdest, dest = 0U;
uint16_t vcpu_id;
bool phys;
vmsi_addr.full = vmsi->msi_addr;
vmsi_data.full = (uint32_t)vmsi->msi_data;
dev_dbg(ACRN_DBG_LAPICPT, "%s: msi_addr 0x%016llx, msi_data 0x%016llx",
__func__, vmsi->msi_addr, vmsi->msi_data);
if (vmsi_addr.bits.addr_base == MSI_ADDR_BASE) {
vdest = vmsi_addr.bits.dest_field;
phys = (vmsi_addr.bits.dest_mode == MSI_ADDR_DESTMODE_PHYS);
/*
* calculate all reachable destination vcpu.
* the delivery mode of vmsi will be forwarded to ICR delievry field
* and handled by hardware.
*/
vlapic_calcdest_lapic_pt(vm, &vdmask, vdest, phys);
dev_dbg(ACRN_DBG_LAPICPT, "%s: vcpu destination mask 0x%016llx", __func__, vdmask);
vcpu_id = ffs64(vdmask);
while (vcpu_id != INVALID_BIT_INDEX) {
bitmap_clear_nolock(vcpu_id, &vdmask);
vcpu = vcpu_from_vid(vm, vcpu_id);
dest |= per_cpu(lapic_ldr, vcpu->pcpu_id);
vcpu_id = ffs64(vdmask);
}
icr.value = 0UL;
icr.bits.dest_field = dest;
icr.bits.vector = vmsi_data.bits.vector;
icr.bits.delivery_mode = vmsi_data.bits.delivery_mode;
icr.bits.destination_mode = MSI_ADDR_DESTMODE_LOGICAL;
msr_write(MSR_IA32_EXT_APIC_ICR, icr.value);
dev_dbg(ACRN_DBG_LAPICPT, "%s: icr.value 0x%016llx", __func__, icr.value);
}
}
/**
* @brief inject MSI interrupt
*
@ -390,7 +437,7 @@ int32_t hcall_set_irqline(const struct acrn_vm *vm, uint16_t vmid,
*/
int32_t hcall_inject_msi(struct acrn_vm *vm, uint16_t vmid, uint64_t param)
{
int32_t ret;
int32_t ret = -1;
struct acrn_msi_entry msi;
struct acrn_vm *target_vm = get_vm_from_vmid(vmid);
@ -400,10 +447,14 @@ int32_t hcall_inject_msi(struct acrn_vm *vm, uint16_t vmid, uint64_t param)
pr_err("%s: Unable copy param to vm\n", __func__);
ret = -1;
} else {
ret = vlapic_intr_msi(target_vm, msi.msi_addr, msi.msi_data);
/* For target cpu with lapic pt, send ipi instead of injection via vlapic */
if (is_lapic_pt(target_vm)) {
inject_msi_lapic_pt(target_vm, &msi);
ret = 0;
} else {
ret = vlapic_intr_msi(target_vm, msi.msi_addr, msi.msi_data);
}
}
} else {
ret = -1;
}
return ret;

View File

@ -249,6 +249,7 @@ int32_t apic_write_vmexit_handler(struct acrn_vcpu *vcpu);
int32_t veoi_vmexit_handler(struct acrn_vcpu *vcpu);
int32_t tpr_below_threshold_vmexit_handler(__unused struct acrn_vcpu *vcpu);
void vlapic_calcdest(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys, bool lowprio);
void vlapic_calcdest_lapic_pt(struct acrn_vm *vm, uint64_t *dmask, uint32_t dest, bool phys);
/**
* @}

View File

@ -51,6 +51,26 @@ enum intr_cpu_startup_shorthand {
INTR_CPU_STARTUP_UNKNOWN,
};
/* x2APIC Interrupt Command Register (ICR) structure */
union apic_icr {
uint64_t value;
struct {
uint32_t lo_32;
uint32_t hi_32;
} value_32;
struct {
uint32_t vector:8;
uint32_t delivery_mode:3;
uint32_t destination_mode:1;
uint32_t rsvd_1:2;
uint32_t level:1;
uint32_t trigger_mode:1;
uint32_t rsvd_2:2;
uint32_t shorthand:2;
uint32_t rsvd_3:12;
uint32_t dest_field:32;
} bits;
};
/**
* @brief Save context of lapic

View File

@ -27,6 +27,7 @@
*/
#define LOG_MESSAGE_MAX_SIZE (4U * LOG_ENTRY_SIZE)
#define ACRN_DBG_LAPICPT 5U
#if defined(HV_DEBUG)
extern uint16_t console_loglevel;