@@ -176,6 +176,25 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo
}
}
+static void acpi_pcihp_cleanup_failover_primary(AcpiPciHpState *s, int bsel)
+{
+ BusChild *kid, *next;
+ PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
+
+ if (!bus) {
+ return;
+ }
+ QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
+ DeviceState *qdev = kid->child;
+ PCIDevice *pdev = PCI_DEVICE(qdev);
+ int slot = PCI_SLOT(pdev->devfn);
+
+ if (pdev->failover_primary) {
+ s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
+ }
+ }
+}
+
static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
{
BusChild *kid, *next;
@@ -207,6 +226,14 @@ static void acpi_pcihp_update(AcpiPciHpState *s)
int i;
for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) {
+ /*
+ * Set the acpi_pcihp_pci_status[].down bits of all the
+ * failover_primary devices so that the devices are ejected
+ * from the guest. We can't use the qdev_unplug() as well as the
+ * hotplug_handler to unplug the devices, because the guest may
+ * not be in a state to cooperate.
+ */
+ acpi_pcihp_cleanup_failover_primary(s, i);
acpi_pcihp_update_hotplug_bus(s, i);
}
}
@@ -248,6 +248,29 @@ static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq)
}
}
+static void virtio_net_failover_notify_event(VirtIONet *n, uint8_t status)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(n);
+
+ if (virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_STANDBY)) {
+ const char *ncn = n->netclient_name;
+ gchar *path = object_get_canonical_path(OBJECT(n->qdev));
+ /*
+ * Emit FAILOVER_STANDBY_CHANGED event with enabled=true
+ * when the status transitions from 0 to VIRTIO_CONFIG_S_DRIVER_OK
+ * Emit FAILOVER_STANDBY_CHANGED event with enabled=false
+ * when the status transitions from VIRTIO_CONFIG_S_DRIVER_OK to 0
+ */
+ if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK))) {
+ qapi_event_send_failover_standby_changed(!!ncn, ncn, path, true);
+ } else if ((!(status & VIRTIO_CONFIG_S_DRIVER_OK)) &&
+ (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+ qapi_event_send_failover_standby_changed(!!ncn, ncn, path, false);
+ }
+ }
+}
+
static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
{
VirtIONet *n = VIRTIO_NET(vdev);
@@ -256,6 +279,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
uint8_t queue_status;
virtio_net_vnet_endian_status(n, status);
+ virtio_net_failover_notify_event(n, status);
virtio_net_vhost_status(n, status);
for (i = 0; i < n->max_queues; i++) {
@@ -3077,6 +3077,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
vfio_register_err_notifier(vdev);
vfio_register_req_notifier(vdev);
vfio_setup_resetfn_quirk(vdev);
+ pdev->failover_primary = vdev->failover_primary;
return;
@@ -3219,6 +3220,8 @@ static Property vfio_pci_dev_properties[] = {
qdev_prop_nv_gpudirect_clique, uint8_t),
DEFINE_PROP_OFF_AUTO_PCIBAR("x-msix-relocation", VFIOPCIDevice, msix_relo,
OFF_AUTOPCIBAR_OFF),
+ DEFINE_PROP_BOOL("failover-primary", VFIOPCIDevice, failover_primary,
+ false),
/*
* TODO - support passed fds... is this necessary?
* DEFINE_PROP_STRING("vfiofd", VFIOPCIDevice, vfiofd_name),
@@ -167,6 +167,7 @@ typedef struct VFIOPCIDevice {
bool no_vfio_ioeventfd;
bool enable_ramfb;
VFIODisplay *dpy;
+ bool failover_primary;
} VFIOPCIDevice;
uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len);
@@ -351,6 +351,7 @@ struct PCIDevice {
MSIVectorUseNotifier msix_vector_use_notifier;
MSIVectorReleaseNotifier msix_vector_release_notifier;
MSIVectorPollNotifier msix_vector_poll_notifier;
+ bool failover_primary;
};
void pci_register_bar(PCIDevice *pci_dev, int region_num,
@@ -683,3 +683,31 @@
##
{ 'event': 'NIC_RX_FILTER_CHANGED',
'data': { '*name': 'str', 'path': 'str' } }
+
+##
+# @FAILOVER_STANDBY_CHANGED:
+#
+# Emitted whenever the virtio_net driver status changes (either the guest
+# successfully loads the driver after the F_STANDBY feature bit is negotiated,
+# or the guest unloads the driver or reboots).
+#
+# @device: Indicates the virtio_net device.
+#
+# @path: Indicates the device path.
+#
+# @enabled: true if the virtio_net driver is loaded.
+# false if the virtio_net driver is unloaded or the guest reboots.
+#
+# Since: 4.0
+#
+# Example:
+#
+# <- { "event": "FAILOVER_STANDBY_CHANGED",
+# "data": { "device": "net0",
+# "path": "/machine/peripheral/net0/virtio-backend",
+# "enabled": "true" },
+# "timestamp": { "seconds": 1432121972, "microseconds": 744001 } },
+#
+##
+{ 'event': 'FAILOVER_STANDBY_CHANGED',
+ 'data': {'*device': 'str', 'path': 'str', 'enabled': 'bool'} }
Added a new event, FAILOVER_STANDBY_CHANGED, which is emitted whenever the status of the virtio_net driver in the guest changes (either the guest successfully loads the driver after the F_STANDBY feature bit is negotiated, or the guest unloads the driver or reboots). Management stack can use this event to determine when to plug/unplug the VF device to/from the guest. Also, the Virtual Functions will be automatically removed from the guest if the guest is rebooted. To properly identify the VFIO devices that must be removed, a new property named "failover-primary" is added to the vfio-pci devices. Only the vfio-pci devices that have this property enabled are removed from the guest upon reboot. Signed-off-by: Venu Busireddy <venu.busireddy@oracle.com> --- hw/acpi/pcihp.c | 27 +++++++++++++++++++++++++++ hw/net/virtio-net.c | 24 ++++++++++++++++++++++++ hw/vfio/pci.c | 3 +++ hw/vfio/pci.h | 1 + include/hw/pci/pci.h | 1 + qapi/net.json | 28 ++++++++++++++++++++++++++++ 6 files changed, 84 insertions(+)