Skip to content

Commit 92c970c

Browse files
committed
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 b82a0ea commit 92c970c

1 file changed

Lines changed: 129 additions & 119 deletions

File tree

drivers/pci/controller/pci-hyperv.c

Lines changed: 129 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,128 @@ static int hv_msi_prepare(struct irq_domain *domain, struct device *dev,
613613
return ret;
614614
}
615615

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

833961
/**
@@ -1449,127 +1577,9 @@ static void hv_irq_mask(struct irq_data *data)
14491577
irq_chip_mask_parent(data);
14501578
}
14511579

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

15741584
if (data->parent_data->chip->irq_unmask)
15751585
irq_chip_unmask_parent(data);

0 commit comments

Comments
 (0)