Skip to content

Commit d06957d

Browse files
fbqLorenzo Pieralisi
authored andcommitted
PCI: hv: Avoid the retarget interrupt hypercall in irq_unmask() on ARM64
On ARM64 Hyper-V guests, SPIs are used for the interrupts of virtual PCI devices, and SPIs can be managed directly via GICD registers. Therefore the retarget interrupt hypercall is not needed on ARM64. An arch-specific interface hv_arch_irq_unmask() is introduced to handle the architecture level differences on this. For x86, the behavior remains unchanged, while for ARM64 no hypercall is invoked when unmasking an irq for virtual PCI devices. Link: https://lore.kernel.org/r/20220217034525.1687678-1-boqun.feng@gmail.com Signed-off-by: Boqun Feng <boqun.feng@gmail.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com>
1 parent e783362 commit d06957d

1 file changed

Lines changed: 122 additions & 111 deletions

File tree

drivers/pci/controller/pci-hyperv.c

Lines changed: 122 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,121 @@ static int hv_msi_prepare(struct irq_domain *domain, struct device *dev,
616616
{
617617
return pci_msi_prepare(domain, dev, nvec, info);
618618
}
619+
620+
/**
621+
* hv_arch_irq_unmask() - "Unmask" the IRQ by setting its current
622+
* affinity.
623+
* @data: Describes the IRQ
624+
*
625+
* Build new a destination for the MSI and make a hypercall to
626+
* update the Interrupt Redirection Table. "Device Logical ID"
627+
* is built out of this PCI bus's instance GUID and the function
628+
* number of the device.
629+
*/
630+
static void hv_arch_irq_unmask(struct irq_data *data)
631+
{
632+
struct msi_desc *msi_desc = irq_data_get_msi_desc(data);
633+
struct hv_retarget_device_interrupt *params;
634+
struct hv_pcibus_device *hbus;
635+
struct cpumask *dest;
636+
cpumask_var_t tmp;
637+
struct pci_bus *pbus;
638+
struct pci_dev *pdev;
639+
unsigned long flags;
640+
u32 var_size = 0;
641+
int cpu, nr_bank;
642+
u64 res;
643+
644+
dest = irq_data_get_effective_affinity_mask(data);
645+
pdev = msi_desc_to_pci_dev(msi_desc);
646+
pbus = pdev->bus;
647+
hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
648+
649+
spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags);
650+
651+
params = &hbus->retarget_msi_interrupt_params;
652+
memset(params, 0, sizeof(*params));
653+
params->partition_id = HV_PARTITION_ID_SELF;
654+
params->int_entry.source = HV_INTERRUPT_SOURCE_MSI;
655+
hv_set_msi_entry_from_desc(&params->int_entry.msi_entry, msi_desc);
656+
params->device_id = (hbus->hdev->dev_instance.b[5] << 24) |
657+
(hbus->hdev->dev_instance.b[4] << 16) |
658+
(hbus->hdev->dev_instance.b[7] << 8) |
659+
(hbus->hdev->dev_instance.b[6] & 0xf8) |
660+
PCI_FUNC(pdev->devfn);
661+
params->int_target.vector = hv_msi_get_int_vector(data);
662+
663+
/*
664+
* Honoring apic->delivery_mode set to APIC_DELIVERY_MODE_FIXED by
665+
* setting the HV_DEVICE_INTERRUPT_TARGET_MULTICAST flag results in a
666+
* spurious interrupt storm. Not doing so does not seem to have a
667+
* negative effect (yet?).
668+
*/
669+
670+
if (hbus->protocol_version >= PCI_PROTOCOL_VERSION_1_2) {
671+
/*
672+
* PCI_PROTOCOL_VERSION_1_2 supports the VP_SET version of the
673+
* HVCALL_RETARGET_INTERRUPT hypercall, which also coincides
674+
* with >64 VP support.
675+
* ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED
676+
* is not sufficient for this hypercall.
677+
*/
678+
params->int_target.flags |=
679+
HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;
680+
681+
if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) {
682+
res = 1;
683+
goto exit_unlock;
684+
}
685+
686+
cpumask_and(tmp, dest, cpu_online_mask);
687+
nr_bank = cpumask_to_vpset(&params->int_target.vp_set, tmp);
688+
free_cpumask_var(tmp);
689+
690+
if (nr_bank <= 0) {
691+
res = 1;
692+
goto exit_unlock;
693+
}
694+
695+
/*
696+
* var-sized hypercall, var-size starts after vp_mask (thus
697+
* vp_set.format does not count, but vp_set.valid_bank_mask
698+
* does).
699+
*/
700+
var_size = 1 + nr_bank;
701+
} else {
702+
for_each_cpu_and(cpu, dest, cpu_online_mask) {
703+
params->int_target.vp_mask |=
704+
(1ULL << hv_cpu_number_to_vp_number(cpu));
705+
}
706+
}
707+
708+
res = hv_do_hypercall(HVCALL_RETARGET_INTERRUPT | (var_size << 17),
709+
params, NULL);
710+
711+
exit_unlock:
712+
spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags);
713+
714+
/*
715+
* During hibernation, when a CPU is offlined, the kernel tries
716+
* to move the interrupt to the remaining CPUs that haven't
717+
* been offlined yet. In this case, the below hv_do_hypercall()
718+
* always fails since the vmbus channel has been closed:
719+
* refer to cpu_disable_common() -> fixup_irqs() ->
720+
* irq_migrate_all_off_this_cpu() -> migrate_one_irq().
721+
*
722+
* Suppress the error message for hibernation because the failure
723+
* during hibernation does not matter (at this time all the devices
724+
* have been frozen). Note: the correct affinity info is still updated
725+
* into the irqdata data structure in migrate_one_irq() ->
726+
* irq_do_set_affinity() -> hv_set_affinity(), so later when the VM
727+
* resumes, hv_pci_restore_msi_state() is able to correctly restore
728+
* the interrupt with the correct affinity.
729+
*/
730+
if (!hv_result_success(res) && hbus->state != hv_pcibus_removing)
731+
dev_err(&hbus->hdev->device,
732+
"%s() failed: %#llx", __func__, res);
733+
}
619734
#elif defined(CONFIG_ARM64)
620735
/*
621736
* SPI vectors to use for vPCI; arch SPIs range is [32, 1019], but leaving a bit
@@ -839,6 +954,12 @@ static struct irq_domain *hv_pci_get_root_domain(void)
839954
{
840955
return hv_msi_gic_irq_domain;
841956
}
957+
958+
/*
959+
* SPIs are used for interrupts of PCI devices and SPIs is managed via GICD
960+
* registers which Hyper-V already supports, so no hypercall needed.
961+
*/
962+
static void hv_arch_irq_unmask(struct irq_data *data) { }
842963
#endif /* CONFIG_ARM64 */
843964

844965
/**
@@ -1456,119 +1577,9 @@ static void hv_irq_mask(struct irq_data *data)
14561577
irq_chip_mask_parent(data);
14571578
}
14581579

1459-
/**
1460-
* hv_irq_unmask() - "Unmask" the IRQ by setting its current
1461-
* affinity.
1462-
* @data: Describes the IRQ
1463-
*
1464-
* Build new a destination for the MSI and make a hypercall to
1465-
* update the Interrupt Redirection Table. "Device Logical ID"
1466-
* is built out of this PCI bus's instance GUID and the function
1467-
* number of the device.
1468-
*/
14691580
static void hv_irq_unmask(struct irq_data *data)
14701581
{
1471-
struct msi_desc *msi_desc = irq_data_get_msi_desc(data);
1472-
struct hv_retarget_device_interrupt *params;
1473-
struct hv_pcibus_device *hbus;
1474-
struct cpumask *dest;
1475-
cpumask_var_t tmp;
1476-
struct pci_bus *pbus;
1477-
struct pci_dev *pdev;
1478-
unsigned long flags;
1479-
u32 var_size = 0;
1480-
int cpu, nr_bank;
1481-
u64 res;
1482-
1483-
dest = irq_data_get_effective_affinity_mask(data);
1484-
pdev = msi_desc_to_pci_dev(msi_desc);
1485-
pbus = pdev->bus;
1486-
hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
1487-
1488-
spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags);
1489-
1490-
params = &hbus->retarget_msi_interrupt_params;
1491-
memset(params, 0, sizeof(*params));
1492-
params->partition_id = HV_PARTITION_ID_SELF;
1493-
params->int_entry.source = HV_INTERRUPT_SOURCE_MSI;
1494-
hv_set_msi_entry_from_desc(&params->int_entry.msi_entry, msi_desc);
1495-
params->device_id = (hbus->hdev->dev_instance.b[5] << 24) |
1496-
(hbus->hdev->dev_instance.b[4] << 16) |
1497-
(hbus->hdev->dev_instance.b[7] << 8) |
1498-
(hbus->hdev->dev_instance.b[6] & 0xf8) |
1499-
PCI_FUNC(pdev->devfn);
1500-
params->int_target.vector = hv_msi_get_int_vector(data);
1501-
1502-
/*
1503-
* Honoring apic->delivery_mode set to APIC_DELIVERY_MODE_FIXED by
1504-
* setting the HV_DEVICE_INTERRUPT_TARGET_MULTICAST flag results in a
1505-
* spurious interrupt storm. Not doing so does not seem to have a
1506-
* negative effect (yet?).
1507-
*/
1508-
1509-
if (hbus->protocol_version >= PCI_PROTOCOL_VERSION_1_2) {
1510-
/*
1511-
* PCI_PROTOCOL_VERSION_1_2 supports the VP_SET version of the
1512-
* HVCALL_RETARGET_INTERRUPT hypercall, which also coincides
1513-
* with >64 VP support.
1514-
* ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED
1515-
* is not sufficient for this hypercall.
1516-
*/
1517-
params->int_target.flags |=
1518-
HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET;
1519-
1520-
if (!alloc_cpumask_var(&tmp, GFP_ATOMIC)) {
1521-
res = 1;
1522-
goto exit_unlock;
1523-
}
1524-
1525-
cpumask_and(tmp, dest, cpu_online_mask);
1526-
nr_bank = cpumask_to_vpset(&params->int_target.vp_set, tmp);
1527-
free_cpumask_var(tmp);
1528-
1529-
if (nr_bank <= 0) {
1530-
res = 1;
1531-
goto exit_unlock;
1532-
}
1533-
1534-
/*
1535-
* var-sized hypercall, var-size starts after vp_mask (thus
1536-
* vp_set.format does not count, but vp_set.valid_bank_mask
1537-
* does).
1538-
*/
1539-
var_size = 1 + nr_bank;
1540-
} else {
1541-
for_each_cpu_and(cpu, dest, cpu_online_mask) {
1542-
params->int_target.vp_mask |=
1543-
(1ULL << hv_cpu_number_to_vp_number(cpu));
1544-
}
1545-
}
1546-
1547-
res = hv_do_hypercall(HVCALL_RETARGET_INTERRUPT | (var_size << 17),
1548-
params, NULL);
1549-
1550-
exit_unlock:
1551-
spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags);
1552-
1553-
/*
1554-
* During hibernation, when a CPU is offlined, the kernel tries
1555-
* to move the interrupt to the remaining CPUs that haven't
1556-
* been offlined yet. In this case, the below hv_do_hypercall()
1557-
* always fails since the vmbus channel has been closed:
1558-
* refer to cpu_disable_common() -> fixup_irqs() ->
1559-
* irq_migrate_all_off_this_cpu() -> migrate_one_irq().
1560-
*
1561-
* Suppress the error message for hibernation because the failure
1562-
* during hibernation does not matter (at this time all the devices
1563-
* have been frozen). Note: the correct affinity info is still updated
1564-
* into the irqdata data structure in migrate_one_irq() ->
1565-
* irq_do_set_affinity() -> hv_set_affinity(), so later when the VM
1566-
* resumes, hv_pci_restore_msi_state() is able to correctly restore
1567-
* the interrupt with the correct affinity.
1568-
*/
1569-
if (!hv_result_success(res) && hbus->state != hv_pcibus_removing)
1570-
dev_err(&hbus->hdev->device,
1571-
"%s() failed: %#llx", __func__, res);
1582+
hv_arch_irq_unmask(data);
15721583

15731584
if (data->parent_data->chip->irq_unmask)
15741585
irq_chip_unmask_parent(data);

0 commit comments

Comments
 (0)