Skip to content

Commit 8a43a74

Browse files
committed
Merge branch 'remotes/lorenzo/pci/hv'
- Avoid retarget interrupt hypercall in irq_unmask() on ARM64 (Boqun Feng) * remotes/lorenzo/pci/hv: PCI: hv: Avoid the retarget interrupt hypercall in irq_unmask() on ARM64
2 parents d93fefa + d06957d commit 8a43a74

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)