pci-failover allows to create a device capable of failover without
relying on DeviceListener::hide_device(), which intrudes the
pci-device implementation from outside.

Signed-off-by: Akihiko Odaki <akihiko.od...@daynix.com>
---
 include/hw/pci/pci_device.h | 14 ++++++++++++++
 hw/pci/pci.c                | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)

diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index 8e287c5414..a7bfb192e8 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -9,6 +9,11 @@ typedef struct PCIDeviceClass PCIDeviceClass;
 DECLARE_OBJ_CHECKERS(PCIDevice, PCIDeviceClass,
                      PCI_DEVICE, TYPE_PCI_DEVICE)
 
+#define TYPE_PCI_FAILOVER "pci-failover"
+typedef struct PCIFailoverClass PCIFailoverClass;
+DECLARE_CLASS_CHECKERS(PCIFailoverClass, PCI_FAILOVER, TYPE_PCI_FAILOVER)
+#define PCI_FAILOVER(obj) INTERFACE_CHECK(PciFailover, (obj), 
TYPE_PCI_FAILOVER)
+
 /*
  * Implemented by devices that can be plugged on CXL buses. In the spec, this 
is
  * actually a "CXL Component, but we name it device to match the PCI naming.
@@ -162,6 +167,15 @@ struct PCIDevice {
     uint32_t acpi_index;
 };
 
+struct PCIFailoverClass {
+    /* private */
+    InterfaceClass parent_class;
+
+    /* public */
+    bool (* set_primary)(DeviceState *dev, const QDict *device_opts,
+                         bool from_json, Error **errp);
+};
+
 static inline int pci_intx(PCIDevice *pci_dev)
 {
     return pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1;
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 53c59a5b9f..3d07246f8e 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -46,6 +46,7 @@
 #include "hw/pci/msix.h"
 #include "hw/hotplug.h"
 #include "hw/boards.h"
+#include "qapi/qmp/qdict.h"
 #include "qapi/error.h"
 #include "qemu/cutils.h"
 #include "pci-internal.h"
@@ -2050,6 +2051,40 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, 
uint8_t devfn)
     return bus->devices[devfn];
 }
 
+static bool pci_qdev_hide(DeviceClass *dc, const QDict *device_opts,
+                          bool from_json, Error **errp)
+{
+    const char *standby_id;
+    DeviceState *dev;
+    ObjectClass *class;
+    ObjectClass *interface;
+
+    if (!device_opts) {
+        return false;
+    }
+
+    if (!qdict_haskey(device_opts, "failover_pair_id")) {
+        return false;
+    }
+
+    standby_id = qdict_get_str(device_opts, "failover_pair_id");
+    dev = qdev_find_recursive(sysbus_get_default(), standby_id);
+    if (!dev) {
+        error_setg(errp, "failover pair not found");
+        return false;
+    }
+
+    class = object_get_class(OBJECT(dev));
+    interface = object_class_dynamic_cast(class, TYPE_PCI_FAILOVER);
+    if (!interface) {
+        error_setg(errp, "failover pair does not support failover");
+        return false;
+    }
+
+    return ((PCIFailoverClass *)interface)->set_primary(dev, device_opts,
+                                                        from_json, errp);
+}
+
 #define ONBOARD_INDEX_MAX (16 * 1024 - 1)
 
 static void pci_qdev_realize(DeviceState *qdev, Error **errp)
@@ -2653,6 +2688,7 @@ static void pci_device_class_init(ObjectClass *klass, 
void *data)
 {
     DeviceClass *k = DEVICE_CLASS(klass);
 
+    k->hide = pci_qdev_hide;
     k->realize = pci_qdev_realize;
     k->unrealize = pci_qdev_unrealize;
     k->bus_type = TYPE_PCI_BUS;
@@ -2861,6 +2897,12 @@ static const TypeInfo pci_device_type_info = {
     .class_base_init = pci_device_class_base_init,
 };
 
+static const TypeInfo pci_failover_type_info = {
+    .name = TYPE_PCI_FAILOVER,
+    .parent = TYPE_INTERFACE,
+    .class_size = sizeof(PCIFailoverClass),
+};
+
 static void pci_register_types(void)
 {
     type_register_static(&pci_bus_info);
@@ -2870,6 +2912,7 @@ static void pci_register_types(void)
     type_register_static(&cxl_interface_info);
     type_register_static(&pcie_interface_info);
     type_register_static(&pci_device_type_info);
+    type_register_static(&pci_failover_type_info);
 }
 
 type_init(pci_register_types)

-- 
2.43.0


Reply via email to