Skip to content

Commit d19bb68

Browse files
fbqallenpais
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/[email protected] Signed-off-by: Boqun Feng <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Reviewed-by: Michael Kelley <[email protected]>
1 parent b6ba1d2 commit d19bb68

File tree

1 file changed

+124
-114
lines changed

1 file changed

+124
-114
lines changed

drivers/pci/controller/pci-hyperv.c

Lines changed: 124 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,123 @@ static int hv_msi_prepare(struct irq_domain *domain, struct device *dev,
619619
return ret;
620620
}
621621

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

839962
/**
@@ -1455,122 +1578,9 @@ static void hv_irq_mask(struct irq_data *data)
14551578
irq_chip_mask_parent(data);
14561579
}
14571580

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

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

0 commit comments

Comments
 (0)