]> Git Repo - linux.git/blobdiff - drivers/pci/controller/pci-hyperv.c
KVM: x86: fix CPUID entries returned by KVM_GET_CPUID2 ioctl
[linux.git] / drivers / pci / controller / pci-hyperv.c
index fc4c3a15e5707d6e548b02335bede06313de5ee9..6db8d96a78ebcae34c16d9cc8a039ad68882555e 100644 (file)
@@ -1226,7 +1226,7 @@ static void hv_irq_unmask(struct irq_data *data)
        params->int_target.vector = cfg->vector;
 
        /*
-        * Honoring apic->irq_delivery_mode set to dest_Fixed by
+        * Honoring apic->delivery_mode set to APIC_DELIVERY_MODE_FIXED by
         * setting the HV_DEVICE_INTERRUPT_TARGET_MULTICAST flag results in a
         * spurious interrupt storm. Not doing so does not seem to have a
         * negative effect (yet?).
@@ -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);
 }
@@ -1310,7 +1324,7 @@ static u32 hv_compose_msi_req_v1(
        int_pkt->wslot.slot = slot;
        int_pkt->int_desc.vector = vector;
        int_pkt->int_desc.vector_count = 1;
-       int_pkt->int_desc.delivery_mode = dest_Fixed;
+       int_pkt->int_desc.delivery_mode = APIC_DELIVERY_MODE_FIXED;
 
        /*
         * Create MSI w/ dummy vCPU set, overwritten by subsequent retarget in
@@ -1331,7 +1345,7 @@ static u32 hv_compose_msi_req_v2(
        int_pkt->wslot.slot = slot;
        int_pkt->int_desc.vector = vector;
        int_pkt->int_desc.vector_count = 1;
-       int_pkt->int_desc.delivery_mode = dest_Fixed;
+       int_pkt->int_desc.delivery_mode = APIC_DELIVERY_MODE_FIXED;
 
        /*
         * Create MSI w/ dummy vCPU set targeting just one vCPU, overwritten
@@ -1531,16 +1545,8 @@ static struct irq_chip hv_msi_irq_chip = {
        .irq_unmask             = hv_irq_unmask,
 };
 
-static irq_hw_number_t hv_msi_domain_ops_get_hwirq(struct msi_domain_info *info,
-                                                  msi_alloc_info_t *arg)
-{
-       return arg->msi_hwirq;
-}
-
 static struct msi_domain_ops hv_msi_ops = {
-       .get_hwirq      = hv_msi_domain_ops_get_hwirq,
        .msi_prepare    = pci_msi_prepare,
-       .set_desc       = pci_msi_set_desc,
        .msi_free       = hv_msi_free,
 };
 
@@ -2515,7 +2521,10 @@ static void hv_pci_onchannelcallback(void *context)
 
 /**
  * hv_pci_protocol_negotiation() - Set up protocol
- * @hdev:      VMBus's tracking struct for this root PCI bus
+ * @hdev:              VMBus's tracking struct for this root PCI bus.
+ * @version:           Array of supported channel protocol versions in
+ *                     the order of probing - highest go first.
+ * @num_version:       Number of elements in the version array.
  *
  * This driver is intended to support running on Windows 10
  * (server) and later versions. It will not run on earlier
@@ -3372,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);
@@ -3405,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:
This page took 0.048565 seconds and 4 git commands to generate.