Merge tag 'pci-v5.10-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / drivers / pci / controller / pci-hyperv.c
index 4e99240..03ed5cb 100644 (file)
@@ -1276,11 +1276,25 @@ static void hv_irq_unmask(struct irq_data *data)
 exit_unlock:
        spin_unlock_irqrestore(&hbus->retarget_msi_interrupt_lock, flags);
 
-       if (res) {
+       /*
+        * During hibernation, when a CPU is offlined, the kernel tries
+        * to move the interrupt to the remaining CPUs that haven't
+        * been offlined yet. In this case, the below hv_do_hypercall()
+        * always fails since the vmbus channel has been closed:
+        * refer to cpu_disable_common() -> fixup_irqs() ->
+        * irq_migrate_all_off_this_cpu() -> migrate_one_irq().
+        *
+        * Suppress the error message for hibernation because the failure
+        * during hibernation does not matter (at this time all the devices
+        * have been frozen). Note: the correct affinity info is still updated
+        * into the irqdata data structure in migrate_one_irq() ->
+        * irq_do_set_affinity() -> hv_set_affinity(), so later when the VM
+        * resumes, hv_pci_restore_msi_state() is able to correctly restore
+        * the interrupt with the correct affinity.
+        */
+       if (res && hbus->state != hv_pcibus_removing)
                dev_err(&hbus->hdev->device,
                        "%s() failed: %#llx", __func__, res);
-               return;
-       }
 
        pci_msi_unmask_irq(data);
 }
@@ -3367,6 +3381,34 @@ static int hv_pci_suspend(struct hv_device *hdev)
        return 0;
 }
 
+static int hv_pci_restore_msi_msg(struct pci_dev *pdev, void *arg)
+{
+       struct msi_desc *entry;
+       struct irq_data *irq_data;
+
+       for_each_pci_msi_entry(entry, pdev) {
+               irq_data = irq_get_irq_data(entry->irq);
+               if (WARN_ON_ONCE(!irq_data))
+                       return -EINVAL;
+
+               hv_compose_msi_msg(irq_data, &entry->msg);
+       }
+
+       return 0;
+}
+
+/*
+ * Upon resume, pci_restore_msi_state() -> ... ->  __pci_write_msi_msg()
+ * directly writes the MSI/MSI-X registers via MMIO, but since Hyper-V
+ * doesn't trap and emulate the MMIO accesses, here hv_compose_msi_msg()
+ * must be used to ask Hyper-V to re-create the IOMMU Interrupt Remapping
+ * Table entries.
+ */
+static void hv_pci_restore_msi_state(struct hv_pcibus_device *hbus)
+{
+       pci_walk_bus(hbus->pci_bus, hv_pci_restore_msi_msg, NULL);
+}
+
 static int hv_pci_resume(struct hv_device *hdev)
 {
        struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
@@ -3400,6 +3442,8 @@ static int hv_pci_resume(struct hv_device *hdev)
 
        prepopulate_bars(hbus);
 
+       hv_pci_restore_msi_state(hbus);
+
        hbus->state = hv_pcibus_installed;
        return 0;
 out: