Handle a hostdev source specified as a node device name
by looking up the node device, parsing its XML for
PCI/USB address details, using those as the hostdev
source and, finally, dettaching and resetting the
device.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
---
src/qemu_conf.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 102 insertions(+), 13 deletions(-)
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index 6f58ee8..dd35f9a 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -47,6 +47,7 @@
#include "datatypes.h"
#include "xml.h"
#include "nodeinfo.h"
+#include "node_device_conf.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -629,6 +630,69 @@ qemudNetworkIfaceConnect(virConnectPtr conn,
}
+static int qemudResolveHostdev(virConnectPtr conn, virDomainHostdevDefPtr hostdev)
+{
+ virNodeDevicePtr dev = NULL;
+ virNodeDeviceDefPtr def = NULL;
+ virNodeDevCapsDefPtr cap;
+ char *xml = NULL;
+ int ret = 0;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_NODEDEV)
+ goto out;
+
+ ret = -1;
+
+ dev = virNodeDeviceLookupByName(conn, hostdev->source.subsys.u.nodedev_name);
+ if (!dev)
+ goto out;
+
+ xml = virNodeDeviceGetXMLDesc(dev, 0);
+ if (!xml)
+ goto out;
+
+ def = virNodeDeviceDefParseString(dev->conn, xml);
+ if (!def)
+ goto out;
+
+ cap = def->caps;
+ while (cap) {
+ if (cap->type == VIR_NODE_DEV_CAP_USB_DEV) {
+ hostdev->source.subsys.type =
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB;
+ hostdev->source.subsys.u.usb.bus = cap->data.usb_dev.bus;
+ hostdev->source.subsys.u.usb.device = cap->data.usb_dev.device;
+ hostdev->source.subsys.u.usb.vendor = cap->data.usb_dev.vendor;
+ hostdev->source.subsys.u.usb.product = cap->data.usb_dev.product;
+ break;
+ } else if (cap->type == VIR_NODE_DEV_CAP_PCI_DEV) {
+ hostdev->source.subsys.type =
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
+ hostdev->source.subsys.u.pci.domain = cap->data.pci_dev.domain;
+ hostdev->source.subsys.u.pci.bus = cap->data.pci_dev.bus;
+ hostdev->source.subsys.u.pci.slot = cap->data.pci_dev.slot;
+ hostdev->source.subsys.u.pci.function = cap->data.pci_dev.function;
+ break;
+ }
+ cap = cap->next;
+ }
+
+ if (!cap) {
+ qemudReportError(dev->conn, NULL, NULL, VIR_ERR_INVALID_ARG,
+ _("device %s is not a PCI or USB device"),
dev->name);
+ goto out;
+ }
+
+ if (virNodeDeviceDettach(dev) < 0)
+ goto out;
+
+ ret = 0;
+out:
+ virNodeDeviceDefFree(def);
+ VIR_FREE(xml);
+ virNodeDeviceFree(dev);
+ return ret;
+}
+
static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev,
char *buf,
int buflen)
@@ -1357,20 +1421,23 @@ int qemudBuildCommandLine(virConnectPtr conn,
int ret;
char* usbdev;
char* pcidev;
- virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i];
+ virDomainHostdevDef hostdev = *vm->def->hostdevs[i];
+
+ if (qemudResolveHostdev(conn, &hostdev) < 0)
+ goto error;
/* USB */
- if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
- hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
- if(hostdev->source.subsys.u.usb.vendor) {
+ if (hostdev.mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+ hostdev.source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
+ if(hostdev.source.subsys.u.usb.vendor) {
ret = virAsprintf(&usbdev, "host:%.4x:%.4x",
- hostdev->source.subsys.u.usb.vendor,
- hostdev->source.subsys.u.usb.product);
+ hostdev.source.subsys.u.usb.vendor,
+ hostdev.source.subsys.u.usb.product);
} else {
ret = virAsprintf(&usbdev, "host:%.3d.%.3d",
- hostdev->source.subsys.u.usb.bus,
- hostdev->source.subsys.u.usb.device);
+ hostdev.source.subsys.u.usb.bus,
+ hostdev.source.subsys.u.usb.device);
}
if (ret < 0)
goto error;
@@ -1381,12 +1448,12 @@ int qemudBuildCommandLine(virConnectPtr conn,
}
/* PCI */
- if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
- hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
+ if (hostdev.mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+ hostdev.source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
ret = virAsprintf(&pcidev, "host=%.2x:%.2x.%.1x",
- hostdev->source.subsys.u.pci.bus,
- hostdev->source.subsys.u.pci.slot,
- hostdev->source.subsys.u.pci.function);
+ hostdev.source.subsys.u.pci.bus,
+ hostdev.source.subsys.u.pci.slot,
+ hostdev.source.subsys.u.pci.function);
if (ret < 0) {
pcidev = NULL;
goto error;
@@ -1395,7 +1462,29 @@ int qemudBuildCommandLine(virConnectPtr conn,
ADD_ARG_LIT(pcidev);
VIR_FREE(pcidev);
}
+ }
+
+ /* Now try and reset passthrough node devices; we do this
+ * because we want to have dettached *all* devices before
+ * resetting *any* devices. */
+ for (i = 0 ; i < vm->def->nhostdevs ; i++) {
+ virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i];
+ virNodeDevicePtr dev;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_NODEDEV)
+ continue;
+
+ dev = virNodeDeviceLookupByName(conn, hostdev->source.subsys.u.nodedev_name);
+ if (!dev)
+ goto error;
+
+ if (virNodeDeviceReset(dev) < 0) {
+ virNodeDeviceFree(dev);
+ goto error;
+ }
+ virNodeDeviceFree(dev);
}
if (migrateFrom) {
--
1.6.0.6