|
|
ae23c9 |
From db9158c9e7905a77c854b178140be79f5295bcdd Mon Sep 17 00:00:00 2001
|
|
|
ae23c9 |
From: Cornelia Huck <cohuck@redhat.com>
|
|
|
ae23c9 |
Date: Wed, 17 Apr 2019 13:57:33 +0100
|
|
|
ae23c9 |
Subject: [PATCH 16/24] s390x/pci: Introduce unplug requests and split unplug
|
|
|
ae23c9 |
handler
|
|
|
ae23c9 |
|
|
|
ae23c9 |
RH-Author: Cornelia Huck <cohuck@redhat.com>
|
|
|
ae23c9 |
Message-id: <20190417135741.25297-17-cohuck@redhat.com>
|
|
|
ae23c9 |
Patchwork-id: 85799
|
|
|
ae23c9 |
O-Subject: [RHEL-8.1.0 qemu-kvm PATCH v2 16/24] s390x/pci: Introduce unplug requests and split unplug handler
|
|
|
ae23c9 |
Bugzilla: 1699070
|
|
|
ae23c9 |
RH-Acked-by: David Hildenbrand <david@redhat.com>
|
|
|
ae23c9 |
RH-Acked-by: Thomas Huth <thuth@redhat.com>
|
|
|
ae23c9 |
RH-Acked-by: Jens Freimann <jfreimann@redhat.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
From: David Hildenbrand <david@redhat.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
PCI on s390x is really weird and how it was modeled in QEMU might not have
|
|
|
ae23c9 |
been the right choice. Anyhow, right now it is the case that:
|
|
|
ae23c9 |
- Hotplugging a PCI device will silently create a zPCI device
|
|
|
ae23c9 |
(if none is provided)
|
|
|
ae23c9 |
- Hotunplugging a zPCI device will unplug the PCI device (if any)
|
|
|
ae23c9 |
- Hotunplugging a PCI device will unplug also the zPCI device
|
|
|
ae23c9 |
As far as I can see, we can no longer change this behavior. But we
|
|
|
ae23c9 |
should fix it.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Both device types are handled via a single hotplug handler call. This
|
|
|
ae23c9 |
is problematic for various reasons:
|
|
|
ae23c9 |
1. Unplugging via the zPCI device allows to unplug devices that are not
|
|
|
ae23c9 |
hot removable. (check performed in qdev_unplug()) - bad.
|
|
|
ae23c9 |
2. Hotplug handler chains are not possible for the unplug case. In the
|
|
|
ae23c9 |
future, the machine might want to override hotplug handlers, to
|
|
|
ae23c9 |
process device specific stuff and to then branch off to the actual
|
|
|
ae23c9 |
hotplug handler. We need separate hotplug handler calls for both the
|
|
|
ae23c9 |
PCI and zPCI device to make this work reliably. All other PCI
|
|
|
ae23c9 |
implementations are already prepared to handle this correctly, only
|
|
|
ae23c9 |
s390x is missing.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Therefore, introduce the unplug_request handler and properly perform
|
|
|
ae23c9 |
unplug checks by redirecting to the separate unplug_request handlers.
|
|
|
ae23c9 |
When finally unplugging, perform two separate hotplug_handler_unplug()
|
|
|
ae23c9 |
calls, first for the PCI device, followed by the zPCI device. This now
|
|
|
ae23c9 |
nicely splits unplugging paths for both devices.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
The redirect part is a little hairy, as the user is allowed to trigger
|
|
|
ae23c9 |
unplug either via the PCI or the zPCI device. So redirect always to the
|
|
|
ae23c9 |
PCI unplug request handler first and remember if that check has been
|
|
|
ae23c9 |
performed in the zPCI device. Redirect then to the zPCI device unplug
|
|
|
ae23c9 |
request handler to perform the magic. Remembering that we already
|
|
|
ae23c9 |
checked the PCI device breaks the redirect loop.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Signed-off-by: David Hildenbrand <david@redhat.com>
|
|
|
ae23c9 |
Message-Id: <20190130155733.32742-5-david@redhat.com>
|
|
|
ae23c9 |
Reviewed-by: Collin Walling <walling@linux.ibm.com>
|
|
|
ae23c9 |
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
|
|
|
ae23c9 |
(cherry picked from commit e0998fe8910435f0e818e5c9ac58d4d2d9144a98)
|
|
|
ae23c9 |
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
|
|
|
ae23c9 |
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Conflicts:
|
|
|
ae23c9 |
hw/s390x/s390-pci-bus.c
|
|
|
ae23c9 |
We don't have 6e92c70c3754 ("s390x/pci: add common function measurement
|
|
|
ae23c9 |
block") downstream.
|
|
|
ae23c9 |
|
|
|
ae23c9 |
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
|
|
|
ae23c9 |
---
|
|
|
ae23c9 |
hw/s390x/s390-pci-bus.c | 165 ++++++++++++++++++++++++++++++++----------------
|
|
|
ae23c9 |
hw/s390x/s390-pci-bus.h | 1 +
|
|
|
ae23c9 |
2 files changed, 113 insertions(+), 53 deletions(-)
|
|
|
ae23c9 |
|
|
|
ae23c9 |
diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c
|
|
|
ae23c9 |
index e3f576a..21419df 100644
|
|
|
ae23c9 |
--- a/hw/s390x/s390-pci-bus.c
|
|
|
ae23c9 |
+++ b/hw/s390x/s390-pci-bus.c
|
|
|
ae23c9 |
@@ -148,6 +148,22 @@ out:
|
|
|
ae23c9 |
psccb->header.response_code = cpu_to_be16(rc);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ HotplugHandler *hotplug_ctrl;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* Unplug the PCI device */
|
|
|
ae23c9 |
+ if (pbdev->pdev) {
|
|
|
ae23c9 |
+ hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev->pdev));
|
|
|
ae23c9 |
+ hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev->pdev),
|
|
|
ae23c9 |
+ &error_abort);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ /* Unplug the zPCI device */
|
|
|
ae23c9 |
+ hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev));
|
|
|
ae23c9 |
+ hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev), &error_abort);
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
void s390_pci_sclp_deconfigure(SCCB *sccb)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
IoaCfgSccb *psccb = (IoaCfgSccb *)sccb;
|
|
|
ae23c9 |
@@ -179,7 +195,7 @@ void s390_pci_sclp_deconfigure(SCCB *sccb)
|
|
|
ae23c9 |
rc = SCLP_RC_NORMAL_COMPLETION;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
if (pbdev->release_timer) {
|
|
|
ae23c9 |
- qdev_unplug(DEVICE(pbdev->pdev), NULL);
|
|
|
ae23c9 |
+ s390_pci_perform_unplug(pbdev);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
out:
|
|
|
ae23c9 |
@@ -217,6 +233,24 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s,
|
|
|
ae23c9 |
return NULL;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+static S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
|
|
|
ae23c9 |
+ PCIDevice *pci_dev)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ S390PCIBusDevice *pbdev;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (!pci_dev) {
|
|
|
ae23c9 |
+ return NULL;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
|
|
|
ae23c9 |
+ if (pbdev->pdev == pci_dev) {
|
|
|
ae23c9 |
+ return pbdev;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ return NULL;
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
return g_hash_table_lookup(s->zpci_table, &idx);
|
|
|
ae23c9 |
@@ -943,76 +977,100 @@ static void s390_pcihost_timer_cb(void *opaque)
|
|
|
ae23c9 |
pbdev->state = ZPCI_FS_STANDBY;
|
|
|
ae23c9 |
s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
|
|
|
ae23c9 |
pbdev->fh, pbdev->fid);
|
|
|
ae23c9 |
- qdev_unplug(DEVICE(pbdev), NULL);
|
|
|
ae23c9 |
+ s390_pci_perform_unplug(pbdev);
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
|
|
|
ae23c9 |
Error **errp)
|
|
|
ae23c9 |
{
|
|
|
ae23c9 |
S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
|
|
|
ae23c9 |
- PCIDevice *pci_dev = NULL;
|
|
|
ae23c9 |
- PCIBus *bus;
|
|
|
ae23c9 |
- int32_t devfn;
|
|
|
ae23c9 |
S390PCIBusDevice *pbdev = NULL;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
|
|
ae23c9 |
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
|
|
|
ae23c9 |
+ PCIBus *bus;
|
|
|
ae23c9 |
+ int32_t devfn;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
|
|
|
ae23c9 |
+ g_assert(pbdev);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
|
|
|
ae23c9 |
+ pbdev->fh, pbdev->fid);
|
|
|
ae23c9 |
+ bus = pci_get_bus(pci_dev);
|
|
|
ae23c9 |
+ devfn = pci_dev->devfn;
|
|
|
ae23c9 |
+ object_unparent(OBJECT(pci_dev));
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ s390_pci_msix_free(pbdev);
|
|
|
ae23c9 |
+ s390_pci_iommu_free(s, bus, devfn);
|
|
|
ae23c9 |
+ pbdev->pdev = NULL;
|
|
|
ae23c9 |
+ pbdev->state = ZPCI_FS_RESERVED;
|
|
|
ae23c9 |
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
|
|
|
ae23c9 |
+ pbdev = S390_PCI_DEVICE(dev);
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+ if (pbdev->release_timer) {
|
|
|
ae23c9 |
+ timer_del(pbdev->release_timer);
|
|
|
ae23c9 |
+ timer_free(pbdev->release_timer);
|
|
|
ae23c9 |
+ pbdev->release_timer = NULL;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ pbdev->fid = 0;
|
|
|
ae23c9 |
+ QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
|
|
|
ae23c9 |
+ g_hash_table_remove(s->zpci_table, &pbdev->idx);
|
|
|
ae23c9 |
+ object_unparent(OBJECT(pbdev));
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+}
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
+static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev,
|
|
|
ae23c9 |
+ DeviceState *dev,
|
|
|
ae23c9 |
+ Error **errp)
|
|
|
ae23c9 |
+{
|
|
|
ae23c9 |
+ S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
|
|
|
ae23c9 |
+ S390PCIBusDevice *pbdev;
|
|
|
ae23c9 |
+
|
|
|
ae23c9 |
if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
|
|
|
ae23c9 |
error_setg(errp, "PCI bridge hot unplug currently not supported");
|
|
|
ae23c9 |
- return;
|
|
|
ae23c9 |
} else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
|
|
|
ae23c9 |
- pci_dev = PCI_DEVICE(dev);
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
|
|
|
ae23c9 |
- if (pbdev->pdev == pci_dev) {
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
- assert(pbdev != NULL);
|
|
|
ae23c9 |
+ /*
|
|
|
ae23c9 |
+ * Redirect the unplug request to the zPCI device and remember that
|
|
|
ae23c9 |
+ * we've checked the PCI device already (to prevent endless recursion).
|
|
|
ae23c9 |
+ */
|
|
|
ae23c9 |
+ pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
|
|
|
ae23c9 |
+ g_assert(pbdev);
|
|
|
ae23c9 |
+ pbdev->pci_unplug_request_processed = true;
|
|
|
ae23c9 |
+ qdev_unplug(DEVICE(pbdev), errp);
|
|
|
ae23c9 |
} else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
|
|
|
ae23c9 |
pbdev = S390_PCI_DEVICE(dev);
|
|
|
ae23c9 |
- pci_dev = pbdev->pdev;
|
|
|
ae23c9 |
- } else {
|
|
|
ae23c9 |
- g_assert_not_reached();
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- switch (pbdev->state) {
|
|
|
ae23c9 |
- case ZPCI_FS_RESERVED:
|
|
|
ae23c9 |
- goto out;
|
|
|
ae23c9 |
- case ZPCI_FS_STANDBY:
|
|
|
ae23c9 |
- break;
|
|
|
ae23c9 |
- default:
|
|
|
ae23c9 |
- if (pbdev->release_timer) {
|
|
|
ae23c9 |
+ /*
|
|
|
ae23c9 |
+ * If unplug was initially requested for the zPCI device, we
|
|
|
ae23c9 |
+ * first have to redirect to the PCI device, which will in return
|
|
|
ae23c9 |
+ * redirect back to us after performing its checks (if the request
|
|
|
ae23c9 |
+ * is not blocked, e.g. because it's a PCI bridge).
|
|
|
ae23c9 |
+ */
|
|
|
ae23c9 |
+ if (pbdev->pdev && !pbdev->pci_unplug_request_processed) {
|
|
|
ae23c9 |
+ qdev_unplug(DEVICE(pbdev->pdev), errp);
|
|
|
ae23c9 |
return;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
- s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
|
|
|
ae23c9 |
- pbdev->fh, pbdev->fid);
|
|
|
ae23c9 |
- pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
|
|
ae23c9 |
- s390_pcihost_timer_cb,
|
|
|
ae23c9 |
- pbdev);
|
|
|
ae23c9 |
- timer_mod(pbdev->release_timer,
|
|
|
ae23c9 |
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT);
|
|
|
ae23c9 |
- return;
|
|
|
ae23c9 |
- }
|
|
|
ae23c9 |
+ pbdev->pci_unplug_request_processed = false;
|
|
|
ae23c9 |
|
|
|
ae23c9 |
- if (pbdev->release_timer) {
|
|
|
ae23c9 |
- timer_del(pbdev->release_timer);
|
|
|
ae23c9 |
- timer_free(pbdev->release_timer);
|
|
|
ae23c9 |
- pbdev->release_timer = NULL;
|
|
|
ae23c9 |
+ switch (pbdev->state) {
|
|
|
ae23c9 |
+ case ZPCI_FS_STANDBY:
|
|
|
ae23c9 |
+ case ZPCI_FS_RESERVED:
|
|
|
ae23c9 |
+ s390_pci_perform_unplug(pbdev);
|
|
|
ae23c9 |
+ break;
|
|
|
ae23c9 |
+ default:
|
|
|
ae23c9 |
+ if (pbdev->release_timer) {
|
|
|
ae23c9 |
+ return;
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
|
|
|
ae23c9 |
+ pbdev->fh, pbdev->fid);
|
|
|
ae23c9 |
+ pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
|
|
ae23c9 |
+ s390_pcihost_timer_cb, pbdev);
|
|
|
ae23c9 |
+ timer_mod(pbdev->release_timer,
|
|
|
ae23c9 |
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT);
|
|
|
ae23c9 |
+ }
|
|
|
ae23c9 |
+ } else {
|
|
|
ae23c9 |
+ g_assert_not_reached();
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
-
|
|
|
ae23c9 |
- s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
|
|
|
ae23c9 |
- pbdev->fh, pbdev->fid);
|
|
|
ae23c9 |
- bus = pci_get_bus(pci_dev);
|
|
|
ae23c9 |
- devfn = pci_dev->devfn;
|
|
|
ae23c9 |
- object_unparent(OBJECT(pci_dev));
|
|
|
ae23c9 |
- s390_pci_msix_free(pbdev);
|
|
|
ae23c9 |
- s390_pci_iommu_free(s, bus, devfn);
|
|
|
ae23c9 |
- pbdev->pdev = NULL;
|
|
|
ae23c9 |
- pbdev->state = ZPCI_FS_RESERVED;
|
|
|
ae23c9 |
-out:
|
|
|
ae23c9 |
- pbdev->fid = 0;
|
|
|
ae23c9 |
- QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
|
|
|
ae23c9 |
- g_hash_table_remove(s->zpci_table, &pbdev->idx);
|
|
|
ae23c9 |
- object_unparent(OBJECT(pbdev));
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
|
|
|
ae23c9 |
static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
|
|
|
ae23c9 |
@@ -1062,6 +1120,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)
|
|
|
ae23c9 |
dc->realize = s390_pcihost_realize;
|
|
|
ae23c9 |
hc->pre_plug = s390_pcihost_pre_plug;
|
|
|
ae23c9 |
hc->plug = s390_pcihost_plug;
|
|
|
ae23c9 |
+ hc->unplug_request = s390_pcihost_unplug_request;
|
|
|
ae23c9 |
hc->unplug = s390_pcihost_unplug;
|
|
|
ae23c9 |
msi_nonbroken = true;
|
|
|
ae23c9 |
}
|
|
|
ae23c9 |
diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h
|
|
|
ae23c9 |
index 1f7f9b5..7684658 100644
|
|
|
ae23c9 |
--- a/hw/s390x/s390-pci-bus.h
|
|
|
ae23c9 |
+++ b/hw/s390x/s390-pci-bus.h
|
|
|
ae23c9 |
@@ -308,6 +308,7 @@ struct S390PCIBusDevice {
|
|
|
ae23c9 |
IndAddr *summary_ind;
|
|
|
ae23c9 |
IndAddr *indicator;
|
|
|
ae23c9 |
QEMUTimer *release_timer;
|
|
|
ae23c9 |
+ bool pci_unplug_request_processed;
|
|
|
ae23c9 |
QTAILQ_ENTRY(S390PCIBusDevice) link;
|
|
|
ae23c9 |
};
|
|
|
ae23c9 |
|
|
|
ae23c9 |
--
|
|
|
ae23c9 |
1.8.3.1
|
|
|
ae23c9 |
|