cryptospore / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone
dc1fe0
From a3a4fd1733e71d029e38136366e73ace8e78298b Mon Sep 17 00:00:00 2001
dc1fe0
From: Igor Mammedov <imammedo@redhat.com>
dc1fe0
Date: Tue, 1 Mar 2022 10:11:59 -0500
dc1fe0
Subject: [PATCH 02/14] acpi: pcihp: pcie: set power on cap on parent slot
dc1fe0
dc1fe0
RH-Author: Igor Mammedov <imammedo@redhat.com>
dc1fe0
RH-MergeRequest: 124: RHEL-9.0 Fix broken PCIe device after migration
dc1fe0
RH-Commit: [2/2] 135602421e5fed803f7d71121380306633d490d8
dc1fe0
RH-Bugzilla: 2053584
dc1fe0
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
dc1fe0
RH-Acked-by: MST <None>
dc1fe0
RH-Acked-by: Gerd Hoffmann <kraxel@redhat.com>
dc1fe0
dc1fe0
on creation a PCIDevice has power turned on at the end of pci_qdev_realize()
dc1fe0
however later on if PCIe slot isn't populated with any children
dc1fe0
it's power is turned off. It's fine if native hotplug is used
dc1fe0
as plug callback will power slot on among other things.
dc1fe0
However when ACPI hotplug is enabled it replaces native PCIe plug
dc1fe0
callbacks with ACPI specific ones (acpi_pcihp_device_*plug_cb) and
dc1fe0
as result slot stays powered off. It works fine as ACPI hotplug
dc1fe0
on guest side takes care of enumerating/initializing hotplugged
dc1fe0
device. But when later guest is migrated, call chain introduced by]
dc1fe0
commit d5daff7d312 (pcie: implement slot power control for pcie root ports)
dc1fe0
dc1fe0
   pcie_cap_slot_post_load()
dc1fe0
       -> pcie_cap_update_power()
dc1fe0
           -> pcie_set_power_device()
dc1fe0
               -> pci_set_power()
dc1fe0
                   -> pci_update_mappings()
dc1fe0
dc1fe0
will disable earlier initialized BARs for the hotplugged device
dc1fe0
in powered off slot due to commit 23786d13441 (pci: implement power state)
dc1fe0
which disables BARs if power is off.
dc1fe0
dc1fe0
Fix it by setting PCI_EXP_SLTCTL_PCC to PCI_EXP_SLTCTL_PWR_ON
dc1fe0
on slot (root port/downstream port) at the time a device
dc1fe0
hotplugged into it. As result PCI_EXP_SLTCTL_PWR_ON is migrated
dc1fe0
to target and above call chain keeps device plugged into it
dc1fe0
powered on.
dc1fe0
dc1fe0
Fixes: d5daff7d312 ("pcie: implement slot power control for pcie root ports")
dc1fe0
Fixes: 23786d13441 ("pci: implement power state")
dc1fe0
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2053584
dc1fe0
Suggested-by: "Michael S. Tsirkin" <mst@redhat.com>
dc1fe0
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
dc1fe0
Message-Id: <20220301151200.3507298-3-imammedo@redhat.com>
dc1fe0
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
dc1fe0
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
dc1fe0
(cherry picked from commit 6b0969f1ec825984cd74619f0730be421b0c46fb)
dc1fe0
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
dc1fe0
---
dc1fe0
 hw/acpi/pcihp.c       | 12 +++++++++++-
dc1fe0
 hw/pci/pcie.c         | 11 +++++++++++
dc1fe0
 include/hw/pci/pcie.h |  1 +
dc1fe0
 3 files changed, 23 insertions(+), 1 deletion(-)
dc1fe0
dc1fe0
diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c
dc1fe0
index 30405b5113..3d5610e02d 100644
dc1fe0
--- a/hw/acpi/pcihp.c
dc1fe0
+++ b/hw/acpi/pcihp.c
dc1fe0
@@ -32,6 +32,7 @@
dc1fe0
 #include "hw/pci/pci_bridge.h"
dc1fe0
 #include "hw/pci/pci_host.h"
dc1fe0
 #include "hw/pci/pcie_port.h"
dc1fe0
+#include "hw/pci-bridge/xio3130_downstream.h"
dc1fe0
 #include "hw/i386/acpi-build.h"
dc1fe0
 #include "hw/acpi/acpi.h"
dc1fe0
 #include "hw/pci/pci_bus.h"
dc1fe0
@@ -341,6 +342,8 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
dc1fe0
 {
dc1fe0
     PCIDevice *pdev = PCI_DEVICE(dev);
dc1fe0
     int slot = PCI_SLOT(pdev->devfn);
dc1fe0
+    PCIDevice *bridge;
dc1fe0
+    PCIBus *bus;
dc1fe0
     int bsel;
dc1fe0
 
dc1fe0
     /* Don't send event when device is enabled during qemu machine creation:
dc1fe0
@@ -370,7 +373,14 @@ void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
dc1fe0
         return;
dc1fe0
     }
dc1fe0
 
dc1fe0
-    bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
dc1fe0
+    bus = pci_get_bus(pdev);
dc1fe0
+    bridge = pci_bridge_get_device(bus);
dc1fe0
+    if (object_dynamic_cast(OBJECT(bridge), TYPE_PCIE_ROOT_PORT) ||
dc1fe0
+        object_dynamic_cast(OBJECT(bridge), TYPE_XIO3130_DOWNSTREAM)) {
dc1fe0
+        pcie_cap_slot_enable_power(bridge);
dc1fe0
+    }
dc1fe0
+
dc1fe0
+    bsel = acpi_pcihp_get_bsel(bus);
dc1fe0
     g_assert(bsel >= 0);
dc1fe0
     s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
dc1fe0
     acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
dc1fe0
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
dc1fe0
index d7d73a31e4..996f0e24fe 100644
dc1fe0
--- a/hw/pci/pcie.c
dc1fe0
+++ b/hw/pci/pcie.c
dc1fe0
@@ -366,6 +366,17 @@ static void hotplug_event_clear(PCIDevice *dev)
dc1fe0
     }
dc1fe0
 }
dc1fe0
 
dc1fe0
+void pcie_cap_slot_enable_power(PCIDevice *dev)
dc1fe0
+{
dc1fe0
+    uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
dc1fe0
+    uint32_t sltcap = pci_get_long(exp_cap + PCI_EXP_SLTCAP);
dc1fe0
+
dc1fe0
+    if (sltcap & PCI_EXP_SLTCAP_PCP) {
dc1fe0
+        pci_set_word_by_mask(exp_cap + PCI_EXP_SLTCTL,
dc1fe0
+                             PCI_EXP_SLTCTL_PCC, PCI_EXP_SLTCTL_PWR_ON);
dc1fe0
+    }
dc1fe0
+}
dc1fe0
+
dc1fe0
 static void pcie_set_power_device(PCIBus *bus, PCIDevice *dev, void *opaque)
dc1fe0
 {
dc1fe0
     bool *power = opaque;
dc1fe0
diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h
dc1fe0
index 6063bee0ec..c27368d077 100644
dc1fe0
--- a/include/hw/pci/pcie.h
dc1fe0
+++ b/include/hw/pci/pcie.h
dc1fe0
@@ -112,6 +112,7 @@ void pcie_cap_slot_write_config(PCIDevice *dev,
dc1fe0
                                 uint32_t addr, uint32_t val, int len);
dc1fe0
 int pcie_cap_slot_post_load(void *opaque, int version_id);
dc1fe0
 void pcie_cap_slot_push_attention_button(PCIDevice *dev);
dc1fe0
+void pcie_cap_slot_enable_power(PCIDevice *dev);
dc1fe0
 
dc1fe0
 void pcie_cap_root_init(PCIDevice *dev);
dc1fe0
 void pcie_cap_root_reset(PCIDevice *dev);
dc1fe0
-- 
dc1fe0
2.31.1
dc1fe0