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 <wu.zhou@intel.com>
Reviewed-by: Junjie Mao <junjie.mao@intel.com>
This commit is contained in:
Wu Zhou 2023-07-02 20:08:26 -07:00 committed by acrnsi-robot
parent ac6dba53a7
commit 89d11d91e2
2 changed files with 21 additions and 3 deletions

View File

@ -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);
}
/**

View File

@ -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