From 89d11d91e2bdb7e328a6db479c031001f559013d Mon Sep 17 00:00:00 2001 From: Wu Zhou Date: Sun, 2 Jul 2023 20:08:26 -0700 Subject: [PATCH] hv: bugfix: fix the ptdev irq destination issue According to SDM Vol3 11.12.10, in x2APIC mode, Logical Destination has two parts: - Cluster ID (LDR[31:16]) - Logical ID (LDR[15:0]) Cluster ID is a numerical address, while Logical ID is a 16bit mask. We can only use Logical ID to address multi destinations within a Cluster. So we can't just 'or' all the Logical Destination in LDR registers to get one mask for all target pCPUs. This would get a wrong destination mask if the target Destinations are from different Clusters. For example in ADL/RPL x2APIC LDRs for core 2-5 are 0x10001 0x10100 0x20001 0x20100. If we 'or' them together, we would get a Logical Destination of 0x30101, which points to core 6 and another core. If core 6 is running a RTVM, then the irq is unable to get to core 2-5, causing the guest on core 2-5 driver fail. Guests working in xAPIC mode may use 'Flat Model' to select an arbitrary list of CPUs as its irq destination. HV may not be able to include them all when transfering to physical destinations, because the HW is working in x2APIC mode and can only use 'Cluster Model'. There would be no perfect fix for this issue. This patch is a simple fix, by just keep the first Cluster of all target Logical Destinations. Tracked-On: #8435 Signed-off-by: Wu Zhou Reviewed-by: Junjie Mao --- hypervisor/arch/x86/guest/assign.c | 22 +++++++++++++++++++--- hypervisor/include/arch/x86/asm/apicreg.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/hypervisor/arch/x86/guest/assign.c b/hypervisor/arch/x86/guest/assign.c index c7c074413..4f6067b16 100644 --- a/hypervisor/arch/x86/guest/assign.c +++ b/hypervisor/arch/x86/guest/assign.c @@ -53,17 +53,33 @@ static struct acrn_vcpu *is_single_destination(struct acrn_vm *vm, const struct static uint32_t calculate_logical_dest_mask(uint64_t pdmask) { - uint32_t dest_mask = 0UL; + uint32_t dest_cluster_id, cluster_id, logical_id_mask = 0U; uint64_t pcpu_mask = pdmask; uint16_t pcpu_id; + /* Guest using Guests working in xAPIC mode may use 'Flat Model' to select an + * arbitrary list of CPUs. But as the HW is woring in x2APIC mode and can only + * use 'Cluster Model', destination mask can only be assigned to pCPUs within + * one Cluster. So some pCPUs may not be included. + * Here we use the first Cluster of all the requested pCPUs. + */ pcpu_id = ffs64(pcpu_mask); + dest_cluster_id = per_cpu(lapic_ldr, pcpu_id) & X2APIC_LDR_CLUSTER_ID_MASK; while (pcpu_id < MAX_PCPU_NUM) { bitmap_clear_nolock(pcpu_id, &pcpu_mask); - dest_mask |= per_cpu(lapic_ldr, pcpu_id); + cluster_id = per_cpu(lapic_ldr, pcpu_id) & X2APIC_LDR_CLUSTER_ID_MASK; + if (cluster_id == dest_cluster_id) { + logical_id_mask |= (per_cpu(lapic_ldr, pcpu_id) & X2APIC_LDR_LOGICAL_ID_MASK); + } else { + pr_warn("The cluster ID of pCPU %d is %d which differs from that (%d) of " + "the previous cores in the guest logical destination.\n" + "Ignore that pCPU in the logical destination for physical interrupts.", + pcpu_id, cluster_id >> 16U, dest_cluster_id >> 16U); + } pcpu_id = ffs64(pcpu_mask); } - return dest_mask; + + return (dest_cluster_id | logical_id_mask); } /** diff --git a/hypervisor/include/arch/x86/asm/apicreg.h b/hypervisor/include/arch/x86/asm/apicreg.h index 5eb4e2269..2a73c2af9 100644 --- a/hypervisor/include/arch/x86/asm/apicreg.h +++ b/hypervisor/include/arch/x86/asm/apicreg.h @@ -253,6 +253,8 @@ union ioapic_rte { /* fields in LDR */ #define APIC_LDR_RESERVED 0x00ffffffU +#define X2APIC_LDR_LOGICAL_ID_MASK 0x0000ffffU +#define X2APIC_LDR_CLUSTER_ID_MASK 0xffff0000U /* fields in DFR */ #define APIC_DFR_RESERVED 0x0fffffffU