From f3a8505656935cde32e28c1c6317f725084da1e0 Mon Sep 17 00:00:00 2001 From: Jens Freimann Date: Tue, 29 Oct 2019 12:48:55 +0100 Subject: qdev/qbus: add hidden device support This adds support for hiding a device to the qbus and qdev APIs. The first user of this will be the virtio-net failover feature but the API introduced with this patch could be used to implement other features as well, for example hiding pci devices when a pci bus is powered off. qdev_device_add() is modified to check for a failover_pair_id argument in the option string. A DeviceListener callback should_be_hidden() is added. It can be used by a standby device to inform qdev that this device should not be added now. The standby device handler can store the device options to plug the device in at a later point in time. One reason for hiding the device is that we don't want to expose both devices to the guest kernel until the respective virtio feature bit VIRTIO_NET_F_STANDBY was negotiated and we know that the devices will be handled correctly by the guest. More information on the kernel feature this is using: https://www.kernel.org/doc/html/latest/networking/net_failover.html An example where the primary device is a vfio-pci device and the standby device is a virtio-net device: A device is hidden when it has an "failover_pair_id" option, e.g. -device virtio-net-pci,...,failover=on,... -device vfio-pci,...,failover_pair_id=net1,... Signed-off-by: Jens Freimann Message-Id: <20191029114905.6856-2-jfreimann@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/qdev-core.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'include/hw') diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index aa123f8..710981a 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -78,6 +78,19 @@ typedef void (*BusUnrealize)(BusState *bus, Error **errp); * respective parent types. * * + * + * # Hiding a device # + * To hide a device, a DeviceListener function should_be_hidden() needs to + * be registered. + * It can be used to defer adding a device and therefore hide it from the + * guest. The handler registering to this DeviceListener can save the QOpts + * passed to it for re-using it later and must return that it wants the device + * to be/remain hidden or not. When the handler function decides the device + * shall not be hidden it will be added in qdev_device_add() and + * realized as any other device. Otherwise qdev_device_add() will return early + * without adding the device. The guest will not see a "hidden" device + * until it was marked don't hide and qdev_device_add called again. + * */ typedef struct DeviceClass { /*< private >*/ @@ -154,6 +167,12 @@ struct DeviceState { struct DeviceListener { void (*realize)(DeviceListener *listener, DeviceState *dev); void (*unrealize)(DeviceListener *listener, DeviceState *dev); + /* + * This callback is called upon init of the DeviceState and allows to + * inform qdev that a device should be hidden, depending on the device + * opts, for example, to hide a standby device. + */ + int (*should_be_hidden)(DeviceListener *listener, QemuOpts *device_opts); QTAILQ_ENTRY(DeviceListener) link; }; @@ -451,4 +470,14 @@ static inline bool qbus_is_hotpluggable(BusState *bus) void device_listener_register(DeviceListener *listener); void device_listener_unregister(DeviceListener *listener); +/** + * @qdev_should_hide_device: + * @opts: QemuOpts as passed on cmdline. + * + * Check if a device should be added. + * When a device is added via qdev_device_add() this will be called, + * and return if the device should be added now or not. + */ +bool qdev_should_hide_device(QemuOpts *opts); + #endif -- cgit v1.1 From 4f5b6a05a4e76892d8d03838a36aca735e9bcefb Mon Sep 17 00:00:00 2001 From: Jens Freimann Date: Tue, 29 Oct 2019 12:48:56 +0100 Subject: pci: add option for net failover This patch adds a failover_pair_id property to PCIDev which is used to link the primary device in a failover pair (the PCI dev) to a standby (a virtio-net-pci) device. It only supports ethernet devices. Also currently it only supports PCIe devices. The requirement for PCIe is because it doesn't support other hotplug controllers at the moment. The failover functionality can be added to other hotplug controllers like ACPI, SHCP,... later on. Signed-off-by: Jens Freimann Message-Id: <20191029114905.6856-3-jfreimann@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/pci/pci.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/hw') diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index f3f0ffd..69d1f02 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -352,6 +352,9 @@ struct PCIDevice { MSIVectorUseNotifier msix_vector_use_notifier; MSIVectorReleaseNotifier msix_vector_release_notifier; MSIVectorPollNotifier msix_vector_poll_notifier; + + /* ID of standby device in net_failover pair */ + char *failover_pair_id; }; void pci_register_bar(PCIDevice *pci_dev, int region_num, -- cgit v1.1 From a99c4da9fc2a39847cfebbc0c6802ee122dcca39 Mon Sep 17 00:00:00 2001 From: Jens Freimann Date: Tue, 29 Oct 2019 12:48:57 +0100 Subject: pci: mark devices partially unplugged Only the guest unplug request was triggered. This is needed for the failover feature. In case of a failed migration we need to plug the device back to the guest. Signed-off-by: Jens Freimann Message-Id: <20191029114905.6856-4-jfreimann@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/pci/pci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/hw') diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 69d1f02..db75c6d 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -265,6 +265,7 @@ typedef struct PCIReqIDCache PCIReqIDCache; struct PCIDevice { DeviceState qdev; + bool partially_hotplugged; /* PCI config space */ uint8_t *config; -- cgit v1.1 From a1190ab628c0e2816eae42786cd7396d6638aa48 Mon Sep 17 00:00:00 2001 From: Jens Freimann Date: Tue, 29 Oct 2019 12:49:01 +0100 Subject: migration: allow unplug during migration for failover devices In "b06424de62 migration: Disable hotplug/unplug during migration" we added a check to disable unplug for all devices until we have figured out what works. For failover primary devices qdev_unplug() is called from the migration handler, i.e. during migration. This patch adds a flag to DeviceState which is set to false for all devices and makes an exception for PCI devices that are also primary devices in a failover pair. Signed-off-by: Jens Freimann Message-Id: <20191029114905.6856-8-jfreimann@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/qdev-core.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/hw') diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 710981a..1518495 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -156,6 +156,7 @@ struct DeviceState { bool pending_deleted_event; QemuOpts *opts; int hotplugged; + bool allow_unplug_during_migration; BusState *parent_bus; QLIST_HEAD(, NamedGPIOList) gpios; QLIST_HEAD(, BusState) child_bus; -- cgit v1.1 From 9711cd0dfc3fa414f7f64935713c07134ae67971 Mon Sep 17 00:00:00 2001 From: Jens Freimann Date: Tue, 29 Oct 2019 12:49:04 +0100 Subject: net/virtio: add failover support This patch adds support to handle failover device pairs of a virtio-net device and a (vfio-)pci device, where the virtio-net acts as the standby device and the (vfio-)pci device as the primary. The general idea is that we have a pair of devices, a (vfio-)pci and a emulated (virtio-net) device. Before migration the vfio device is unplugged and data flows to the emulated device, on the target side another (vfio-)pci device is plugged in to take over the data-path. In the guest the net_failover module will pair net devices with the same MAC address. To achieve this we need: 1. Provide a callback function for the should_be_hidden DeviceListener. It is called when the primary device is plugged in. Evaluate the QOpt passed in to check if it is the matching primary device. It returns if the device should be hidden or not. When it should be hidden it stores the device options in the VirtioNet struct and the device is added once the VIRTIO_NET_F_STANDBY feature is negotiated during virtio feature negotiation. If the virtio-net devices are not realized at the time the (vfio-)pci devices are realized, we need to connect the devices later. This way we make sure primary and standby devices can be specified in any order. 2. Register a callback for migration status notifier. When called it will unplug its primary device before the migration happens. 3. Register a callback for the migration code that checks if a device needs to be unplugged from the guest. Signed-off-by: Jens Freimann Message-Id: <20191029114905.6856-11-jfreimann@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/virtio-net.h | 12 ++++++++++++ include/hw/virtio/virtio.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include/hw') diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 07a9319..96c68d4 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -18,6 +18,7 @@ #include "standard-headers/linux/virtio_net.h" #include "hw/virtio/virtio.h" #include "net/announce.h" +#include "qemu/option_int.h" #define TYPE_VIRTIO_NET "virtio-net-device" #define VIRTIO_NET(obj) \ @@ -43,6 +44,7 @@ typedef struct virtio_net_conf int32_t speed; char *duplex_str; uint8_t duplex; + char *primary_id_str; } virtio_net_conf; /* Coalesced packets type & status */ @@ -187,6 +189,16 @@ struct VirtIONet { AnnounceTimer announce_timer; bool needs_vnet_hdr_swap; bool mtu_bypass_backend; + QemuOpts *primary_device_opts; + QDict *primary_device_dict; + DeviceState *primary_dev; + BusState *primary_bus; + char *primary_device_id; + char *standby_id; + bool primary_should_be_hidden; + bool failover; + DeviceListener primary_listener; + Notifier migration_state; }; void virtio_net_set_netclient_name(VirtIONet *n, const char *name, diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 60bbc0e..3448d67 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -160,6 +160,7 @@ typedef struct VirtioDeviceClass { */ int (*post_load)(VirtIODevice *vdev); const VMStateDescription *vmsd; + bool (*primary_unplug_pending)(void *opaque); } VirtioDeviceClass; void virtio_instance_init_common(Object *proxy_obj, void *data, -- cgit v1.1