Mark McLoughlin c034c1
From 332979bb680d833529ab9cecac6828c6ce54d731 Mon Sep 17 00:00:00 2001
Mark McLoughlin c034c1
From: Mark McLoughlin <markmc@redhat.com>
Mark McLoughlin c034c1
Date: Fri, 14 Aug 2009 08:31:10 +0100
Mark McLoughlin c034c1
Subject: [PATCH] Add host PCI device hotplug support
Mark McLoughlin c034c1
Mark McLoughlin c034c1
Attaching a host PCI device to a qemu guest is done with a
Mark McLoughlin c034c1
straightforward 'pci_add pci_addr auto host host=XX:XX.X' command.
Mark McLoughlin c034c1
Mark McLoughlin c034c1
Like with NIC and disk hotplug, we need to retain the guest PCI address
Mark McLoughlin c034c1
assigned by qemu so that we can use it for hot-unplug.
Mark McLoughlin c034c1
Mark McLoughlin c034c1
Identifying a device for detach is done using the host PCI address.
Mark McLoughlin c034c1
Mark McLoughlin c034c1
Managed mode is handled by detaching/resetting the device before
Mark McLoughlin c034c1
attaching it to the guest and re-attaching it after detaching it from
Mark McLoughlin c034c1
the guest.
Mark McLoughlin c034c1
Mark McLoughlin c034c1
(cherry picked from commit 7636ef4630fc15c3d559eceb5b5c4fb1524b7c5a)
Mark McLoughlin c034c1
(cherry picked from commit 0c5b7b93a3cdb197c55d79c2605e9e19e3af43f5)
Mark McLoughlin c034c1
(cherry picked from commit 60ff07585ca8f7e639fed477e2e2cf79ce1c5c21)
Mark McLoughlin c034c1
(cherry picked from commit 4e12af5623e4a962a6bb911af06fa29aa85befba)
Mark McLoughlin c034c1
(cherry picked from commit 4dbecff9fbd5b5d1154bc7a41a5d4dd00533b359)
Mark McLoughlin c034c1
(cherry picked from commit 12edef9a6aca5bd9a2ea18b73ca862f615684d84)
Mark McLoughlin c034c1
(cherry picked from commit 457e05062863a35c7efb35470886b9b83a49d04d)
Mark McLoughlin c034c1
(cherry picked from commit e8ad33931296c67de0538e78d12e21706a826d37)
Mark McLoughlin c034c1
Mark McLoughlin c034c1
Fedora-patch: libvirt-add-pci-hostdev-hotplug-support.patch
Mark McLoughlin c034c1
---
Mark McLoughlin c034c1
 src/domain_conf.c        |   33 +++++-
Mark McLoughlin c034c1
 src/domain_conf.h        |   13 +++
Mark McLoughlin c034c1
 src/libvirt_private.syms |    2 +
Mark McLoughlin c034c1
 src/qemu_driver.c        |  266 ++++++++++++++++++++++++++++++++++++++++++++--
Mark McLoughlin c034c1
 4 files changed, 300 insertions(+), 14 deletions(-)
Mark McLoughlin c034c1
Mark McLoughlin c034c1
diff --git a/src/domain_conf.c b/src/domain_conf.c
Mark McLoughlin c034c1
index 2301a96..bad53f7 100644
Mark McLoughlin c034c1
--- a/src/domain_conf.c
Mark McLoughlin c034c1
+++ b/src/domain_conf.c
Mark McLoughlin c034c1
@@ -1977,7 +1977,8 @@ out:
Mark McLoughlin c034c1
 static int
Mark McLoughlin c034c1
 virDomainHostdevSubsysPciDefParseXML(virConnectPtr conn,
Mark McLoughlin c034c1
                                      const xmlNodePtr node,
Mark McLoughlin c034c1
-                                     virDomainHostdevDefPtr def) {
Mark McLoughlin c034c1
+                                     virDomainHostdevDefPtr def,
Mark McLoughlin c034c1
+                                     int flags) {
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
     int ret = -1;
Mark McLoughlin c034c1
     xmlNodePtr cur;
Mark McLoughlin c034c1
@@ -2049,6 +2050,20 @@ virDomainHostdevSubsysPciDefParseXML(virConnectPtr conn,
Mark McLoughlin c034c1
                                          _("pci address needs function id"));
Mark McLoughlin c034c1
                     goto out;
Mark McLoughlin c034c1
                 }
Mark McLoughlin c034c1
+            } else if ((flags & VIR_DOMAIN_XML_INTERNAL_STATUS) &&
Mark McLoughlin c034c1
+                       xmlStrEqual(cur->name, BAD_CAST "state")) {
Mark McLoughlin c034c1
+                char *devaddr = virXMLPropString(cur, "devaddr");
Mark McLoughlin c034c1
+                if (devaddr &&
Mark McLoughlin c034c1
+                    sscanf(devaddr, "%x:%x:%x",
Mark McLoughlin c034c1
+                           &def->source.subsys.u.pci.guest_addr.domain,
Mark McLoughlin c034c1
+                           &def->source.subsys.u.pci.guest_addr.bus,
Mark McLoughlin c034c1
+                           &def->source.subsys.u.pci.guest_addr.slot) < 3) {
Mark McLoughlin c034c1
+                    virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
Mark McLoughlin c034c1
+                                         _("Unable to parse devaddr parameter '%s'"),
Mark McLoughlin c034c1
+                                         devaddr);
Mark McLoughlin c034c1
+                    VIR_FREE(devaddr);
Mark McLoughlin c034c1
+                    goto out;
Mark McLoughlin c034c1
+                }
Mark McLoughlin c034c1
             } else {
Mark McLoughlin c034c1
                 virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
Mark McLoughlin c034c1
                                      _("unknown pci source type '%s'"),
Mark McLoughlin c034c1
@@ -2123,7 +2138,7 @@ virDomainHostdevDefParseXML(virConnectPtr conn,
Mark McLoughlin c034c1
                 }
Mark McLoughlin c034c1
                 if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
Mark McLoughlin c034c1
                     def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
Mark McLoughlin c034c1
-                        if (virDomainHostdevSubsysPciDefParseXML(conn, cur, def) < 0)
Mark McLoughlin c034c1
+                        if (virDomainHostdevSubsysPciDefParseXML(conn, cur, def, flags) < 0)
Mark McLoughlin c034c1
                             goto error;
Mark McLoughlin c034c1
                 }
Mark McLoughlin c034c1
             } else {
Mark McLoughlin c034c1
@@ -3937,7 +3952,8 @@ virDomainGraphicsDefFormat(virConnectPtr conn,
Mark McLoughlin c034c1
 static int
Mark McLoughlin c034c1
 virDomainHostdevDefFormat(virConnectPtr conn,
Mark McLoughlin c034c1
                           virBufferPtr buf,
Mark McLoughlin c034c1
-                          virDomainHostdevDefPtr def)
Mark McLoughlin c034c1
+                          virDomainHostdevDefPtr def,
Mark McLoughlin c034c1
+                          int flags)
Mark McLoughlin c034c1
 {
Mark McLoughlin c034c1
     const char *mode = virDomainHostdevModeTypeToString(def->mode);
Mark McLoughlin c034c1
     const char *type;
Mark McLoughlin c034c1
@@ -3978,6 +3994,15 @@ virDomainHostdevDefFormat(virConnectPtr conn,
Mark McLoughlin c034c1
                           def->source.subsys.u.pci.bus,
Mark McLoughlin c034c1
                           def->source.subsys.u.pci.slot,
Mark McLoughlin c034c1
                           def->source.subsys.u.pci.function);
Mark McLoughlin c034c1
+        if (flags & VIR_DOMAIN_XML_INTERNAL_STATUS) {
Mark McLoughlin c034c1
+            virBufferAddLit(buf, "      
Mark McLoughlin c034c1
+            if (virHostdevHasValidGuestAddr(def))
Mark McLoughlin c034c1
+                virBufferVSprintf(buf, " devaddr='%.4x:%.2x:%.2x'",
Mark McLoughlin c034c1
+                                  def->source.subsys.u.pci.guest_addr.domain,
Mark McLoughlin c034c1
+                                  def->source.subsys.u.pci.guest_addr.bus,
Mark McLoughlin c034c1
+                                  def->source.subsys.u.pci.guest_addr.slot);
Mark McLoughlin c034c1
+            virBufferAddLit(buf, "/>\n");
Mark McLoughlin c034c1
+        }
Mark McLoughlin c034c1
     }
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
     virBufferAddLit(buf, "      </source>\n");
Mark McLoughlin c034c1
@@ -4192,7 +4217,7 @@ char *virDomainDefFormat(virConnectPtr conn,
Mark McLoughlin c034c1
             goto cleanup;
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
     for (n = 0 ; n < def->nhostdevs ; n++)
Mark McLoughlin c034c1
-        if (virDomainHostdevDefFormat(conn, &buf, def->hostdevs[n]) < 0)
Mark McLoughlin c034c1
+        if (virDomainHostdevDefFormat(conn, &buf, def->hostdevs[n], flags) < 0)
Mark McLoughlin c034c1
             goto cleanup;
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
     virBufferAddLit(&buf, "  </devices>\n");
Mark McLoughlin c034c1
diff --git a/src/domain_conf.h b/src/domain_conf.h
Mark McLoughlin c034c1
index 63fca76..44302be 100644
Mark McLoughlin c034c1
--- a/src/domain_conf.h
Mark McLoughlin c034c1
+++ b/src/domain_conf.h
Mark McLoughlin c034c1
@@ -391,6 +391,11 @@ struct _virDomainHostdevDef {
Mark McLoughlin c034c1
                      unsigned bus;
Mark McLoughlin c034c1
                      unsigned slot;
Mark McLoughlin c034c1
                      unsigned function;
Mark McLoughlin c034c1
+                    struct {
Mark McLoughlin c034c1
+                        unsigned domain;
Mark McLoughlin c034c1
+                        unsigned bus;
Mark McLoughlin c034c1
+                        unsigned slot;
Mark McLoughlin c034c1
+                    } guest_addr;
Mark McLoughlin c034c1
                 } pci;
Mark McLoughlin c034c1
             } u;
Mark McLoughlin c034c1
         } subsys;
Mark McLoughlin c034c1
@@ -404,6 +409,14 @@ struct _virDomainHostdevDef {
Mark McLoughlin c034c1
     char* target;
Mark McLoughlin c034c1
 };
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
+static inline int
Mark McLoughlin c034c1
+virHostdevHasValidGuestAddr(virDomainHostdevDefPtr def)
Mark McLoughlin c034c1
+{
Mark McLoughlin c034c1
+    return def->source.subsys.u.pci.guest_addr.domain ||
Mark McLoughlin c034c1
+           def->source.subsys.u.pci.guest_addr.bus ||
Mark McLoughlin c034c1
+           def->source.subsys.u.pci.guest_addr.slot;
Mark McLoughlin c034c1
+}
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
 /* Flags for the 'type' field in next struct */
Mark McLoughlin c034c1
 enum virDomainDeviceType {
Mark McLoughlin c034c1
     VIR_DOMAIN_DEVICE_DISK,
Mark McLoughlin c034c1
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
Mark McLoughlin c034c1
index 4f1b01f..22131c4 100644
Mark McLoughlin c034c1
--- a/src/libvirt_private.syms
Mark McLoughlin c034c1
+++ b/src/libvirt_private.syms
Mark McLoughlin c034c1
@@ -88,6 +88,8 @@ virDomainGetRootFilesystem;
Mark McLoughlin c034c1
 virDomainGraphicsTypeFromString;
Mark McLoughlin c034c1
 virDomainGraphicsDefFree;
Mark McLoughlin c034c1
 virDomainHostdevDefFree;
Mark McLoughlin c034c1
+virDomainHostdevModeTypeToString;
Mark McLoughlin c034c1
+virDomainHostdevSubsysTypeToString;
Mark McLoughlin c034c1
 virDomainInputDefFree;
Mark McLoughlin c034c1
 virDomainLifecycleTypeFromString;
Mark McLoughlin c034c1
 virDomainLifecycleTypeToString;
Mark McLoughlin c034c1
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
Mark McLoughlin c034c1
index cbc27c4..99dac52 100644
Mark McLoughlin c034c1
--- a/src/qemu_driver.c
Mark McLoughlin c034c1
+++ b/src/qemu_driver.c
Mark McLoughlin c034c1
@@ -5258,9 +5258,91 @@ cleanup:
Mark McLoughlin c034c1
     return -1;
Mark McLoughlin c034c1
 }
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
-static int qemudDomainAttachHostDevice(virConnectPtr conn,
Mark McLoughlin c034c1
-                                       virDomainObjPtr vm,
Mark McLoughlin c034c1
-                                       virDomainDeviceDefPtr dev)
Mark McLoughlin c034c1
+static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
Mark McLoughlin c034c1
+                                          struct qemud_driver *driver,
Mark McLoughlin c034c1
+                                          virDomainObjPtr vm,
Mark McLoughlin c034c1
+                                          virDomainDeviceDefPtr dev)
Mark McLoughlin c034c1
+{
Mark McLoughlin c034c1
+    virDomainHostdevDefPtr hostdev = dev->data.hostdev;
Mark McLoughlin c034c1
+    char *cmd, *reply;
Mark McLoughlin c034c1
+    unsigned domain, bus, slot;
Mark McLoughlin c034c1
+    pciDevice *pci;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
Mark McLoughlin c034c1
+        virReportOOMError(conn);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    pci = pciGetDevice(conn,
Mark McLoughlin c034c1
+                       hostdev->source.subsys.u.pci.domain,
Mark McLoughlin c034c1
+                       hostdev->source.subsys.u.pci.bus,
Mark McLoughlin c034c1
+                       hostdev->source.subsys.u.pci.slot,
Mark McLoughlin c034c1
+                       hostdev->source.subsys.u.pci.function);
Mark McLoughlin c034c1
+    if (!dev)
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if ((hostdev->managed && pciDettachDevice(conn, pci) < 0) ||
Mark McLoughlin c034c1
+        pciResetDevice(conn, pci, driver->activePciHostdevs) < 0) {
Mark McLoughlin c034c1
+        pciFreeDevice(conn, pci);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (pciDeviceListAdd(conn, driver->activePciHostdevs, pci) < 0) {
Mark McLoughlin c034c1
+        pciFreeDevice(conn, pci);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    cmd = reply = NULL;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (virAsprintf(&cmd, "pci_add pci_addr=auto host host=%.2x:%.2x.%.1x",
Mark McLoughlin c034c1
+                    hostdev->source.subsys.u.pci.bus,
Mark McLoughlin c034c1
+                    hostdev->source.subsys.u.pci.slot,
Mark McLoughlin c034c1
+                    hostdev->source.subsys.u.pci.function) < 0) {
Mark McLoughlin c034c1
+        virReportOOMError(conn);
Mark McLoughlin c034c1
+        goto error;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (qemudMonitorCommand(vm, cmd, &reply) < 0) {
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
Mark McLoughlin c034c1
+                         "%s", _("cannot attach host pci device"));
Mark McLoughlin c034c1
+        goto error;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (strstr(reply, "invalid type: host")) {
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT, "%s",
Mark McLoughlin c034c1
+                         _("PCI device assignment is not supported by this version of qemu"));
Mark McLoughlin c034c1
+        goto error;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (qemudParsePciAddReply(vm, reply, &domain, &bus, &slot) < 0) {
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
Mark McLoughlin c034c1
+                         _("parsing pci_add reply failed: %s"), reply);
Mark McLoughlin c034c1
+        goto error;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    hostdev->source.subsys.u.pci.guest_addr.domain = domain;
Mark McLoughlin c034c1
+    hostdev->source.subsys.u.pci.guest_addr.bus    = bus;
Mark McLoughlin c034c1
+    hostdev->source.subsys.u.pci.guest_addr.slot   = slot;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    VIR_FREE(reply);
Mark McLoughlin c034c1
+    VIR_FREE(cmd);
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    return 0;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+error:
Mark McLoughlin c034c1
+    pciDeviceListDel(conn, driver->activePciHostdevs, pci);
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    VIR_FREE(reply);
Mark McLoughlin c034c1
+    VIR_FREE(cmd);
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    return -1;
Mark McLoughlin c034c1
+}
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+static int qemudDomainAttachHostUsbDevice(virConnectPtr conn,
Mark McLoughlin c034c1
+                                          virDomainObjPtr vm,
Mark McLoughlin c034c1
+                                          virDomainDeviceDefPtr dev)
Mark McLoughlin c034c1
 {
Mark McLoughlin c034c1
     int ret;
Mark McLoughlin c034c1
     char *cmd, *reply;
Mark McLoughlin c034c1
@@ -5310,6 +5392,36 @@ static int qemudDomainAttachHostDevice(virConnectPtr conn,
Mark McLoughlin c034c1
     return 0;
Mark McLoughlin c034c1
 }
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
+static int qemudDomainAttachHostDevice(virConnectPtr conn,
Mark McLoughlin c034c1
+                                       struct qemud_driver *driver,
Mark McLoughlin c034c1
+                                       virDomainObjPtr vm,
Mark McLoughlin c034c1
+                                       virDomainDeviceDefPtr dev)
Mark McLoughlin c034c1
+{
Mark McLoughlin c034c1
+    virDomainHostdevDefPtr hostdev = dev->data.hostdev;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
Mark McLoughlin c034c1
+                         _("hostdev mode '%s' not supported"),
Mark McLoughlin c034c1
+                         virDomainHostdevModeTypeToString(hostdev->mode));
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (qemuDomainSetDeviceOwnership(conn, driver, dev, 0) < 0)
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    switch (hostdev->source.subsys.type) {
Mark McLoughlin c034c1
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
Mark McLoughlin c034c1
+        return qemudDomainAttachHostPciDevice(conn, driver, vm, dev);
Mark McLoughlin c034c1
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
Mark McLoughlin c034c1
+        return qemudDomainAttachHostUsbDevice(conn, vm, dev);
Mark McLoughlin c034c1
+    default:
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
Mark McLoughlin c034c1
+                         _("hostdev subsys type '%s' not supported"),
Mark McLoughlin c034c1
+                         virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+}
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
 static int qemudDomainAttachDevice(virDomainPtr dom,
Mark McLoughlin c034c1
                                    const char *xml) {
Mark McLoughlin c034c1
     struct qemud_driver *driver = dom->conn->privateData;
Mark McLoughlin c034c1
@@ -5411,13 +5523,8 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
Mark McLoughlin c034c1
         }
Mark McLoughlin c034c1
     } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
Mark McLoughlin c034c1
         ret = qemudDomainAttachNetDevice(dom->conn, driver, vm, dev, qemuCmdFlags);
Mark McLoughlin c034c1
-    } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV &&
Mark McLoughlin c034c1
-               dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
Mark McLoughlin c034c1
-               dev->data.hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
Mark McLoughlin c034c1
-        if (qemuDomainSetDeviceOwnership(dom->conn, driver, dev, 0) < 0)
Mark McLoughlin c034c1
-            goto cleanup;
Mark McLoughlin c034c1
-
Mark McLoughlin c034c1
-        ret = qemudDomainAttachHostDevice(dom->conn, vm, dev);
Mark McLoughlin c034c1
+    } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
Mark McLoughlin c034c1
+        ret = qemudDomainAttachHostDevice(dom->conn, driver, vm, dev);
Mark McLoughlin c034c1
     } else {
Mark McLoughlin c034c1
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
Mark McLoughlin c034c1
                          _("device type '%s' cannot be attached"),
Mark McLoughlin c034c1
@@ -5630,6 +5737,143 @@ cleanup:
Mark McLoughlin c034c1
     return ret;
Mark McLoughlin c034c1
 }
Mark McLoughlin c034c1
 
Mark McLoughlin c034c1
+static int qemudDomainDetachHostPciDevice(virConnectPtr conn,
Mark McLoughlin c034c1
+                                          struct qemud_driver *driver,
Mark McLoughlin c034c1
+                                          virDomainObjPtr vm,
Mark McLoughlin c034c1
+                                          virDomainDeviceDefPtr dev)
Mark McLoughlin c034c1
+{
Mark McLoughlin c034c1
+    virDomainHostdevDefPtr detach;
Mark McLoughlin c034c1
+    char *cmd, *reply;
Mark McLoughlin c034c1
+    int i, ret;
Mark McLoughlin c034c1
+    pciDevice *pci;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    for (i = 0 ; i < vm->def->nhostdevs ; i++) {
Mark McLoughlin c034c1
+        unsigned domain   = vm->def->hostdevs[i]->source.subsys.u.pci.domain;
Mark McLoughlin c034c1
+        unsigned bus      = vm->def->hostdevs[i]->source.subsys.u.pci.bus;
Mark McLoughlin c034c1
+        unsigned slot     = vm->def->hostdevs[i]->source.subsys.u.pci.slot;
Mark McLoughlin c034c1
+        unsigned function = vm->def->hostdevs[i]->source.subsys.u.pci.function;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+        if (dev->data.hostdev->source.subsys.u.pci.domain   == domain &&
Mark McLoughlin c034c1
+            dev->data.hostdev->source.subsys.u.pci.bus      == bus &&
Mark McLoughlin c034c1
+            dev->data.hostdev->source.subsys.u.pci.slot     == slot &&
Mark McLoughlin c034c1
+            dev->data.hostdev->source.subsys.u.pci.function == function) {
Mark McLoughlin c034c1
+            detach = vm->def->hostdevs[i];
Mark McLoughlin c034c1
+            break;
Mark McLoughlin c034c1
+        }
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (!detach) {
Mark McLoughlin c034c1
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
Mark McLoughlin c034c1
+                         _("host pci device %.4x:%.2x:%.2x.%.1x not found"),
Mark McLoughlin c034c1
+                         dev->data.hostdev->source.subsys.u.pci.domain,
Mark McLoughlin c034c1
+                         dev->data.hostdev->source.subsys.u.pci.bus,
Mark McLoughlin c034c1
+                         dev->data.hostdev->source.subsys.u.pci.slot,
Mark McLoughlin c034c1
+                         dev->data.hostdev->source.subsys.u.pci.function);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (!virHostdevHasValidGuestAddr(detach)) {
Mark McLoughlin c034c1
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
Mark McLoughlin c034c1
+                         "%s", _("hostdev cannot be detached - device state missing"));
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (virAsprintf(&cmd, "pci_del pci_addr=%.4x:%.2x:%.2x",
Mark McLoughlin c034c1
+                    detach->source.subsys.u.pci.guest_addr.domain,
Mark McLoughlin c034c1
+                    detach->source.subsys.u.pci.guest_addr.bus,
Mark McLoughlin c034c1
+                    detach->source.subsys.u.pci.guest_addr.slot) < 0) {
Mark McLoughlin c034c1
+        virReportOOMError(conn);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (qemudMonitorCommand(vm, cmd, &reply) < 0) {
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
Mark McLoughlin c034c1
+                         "%s", _("cannot detach host pci device"));
Mark McLoughlin c034c1
+        VIR_FREE(cmd);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    DEBUG("%s: pci_del reply: %s", vm->def->name,  reply);
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    /* If the command fails due to a wrong PCI address qemu prints
Mark McLoughlin c034c1
+     * 'invalid pci address'; nothing is printed on success */
Mark McLoughlin c034c1
+    if (strstr(reply, "Invalid pci address")) {
Mark McLoughlin c034c1
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
Mark McLoughlin c034c1
+                         _("failed to detach host pci device: invalid PCI address %.4x:%.2x:%.2x: %s"),
Mark McLoughlin c034c1
+                         detach->source.subsys.u.pci.guest_addr.domain,
Mark McLoughlin c034c1
+                         detach->source.subsys.u.pci.guest_addr.bus,
Mark McLoughlin c034c1
+                         detach->source.subsys.u.pci.guest_addr.slot,
Mark McLoughlin c034c1
+                         reply);
Mark McLoughlin c034c1
+        VIR_FREE(reply);
Mark McLoughlin c034c1
+        VIR_FREE(cmd);
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    VIR_FREE(reply);
Mark McLoughlin c034c1
+    VIR_FREE(cmd);
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    ret = 0;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    pci = pciGetDevice(conn,
Mark McLoughlin c034c1
+                       detach->source.subsys.u.pci.domain,
Mark McLoughlin c034c1
+                       detach->source.subsys.u.pci.bus,
Mark McLoughlin c034c1
+                       detach->source.subsys.u.pci.slot,
Mark McLoughlin c034c1
+                       detach->source.subsys.u.pci.function);
Mark McLoughlin c034c1
+    if (!pci)
Mark McLoughlin c034c1
+        ret = -1;
Mark McLoughlin c034c1
+    else {
Mark McLoughlin c034c1
+        pciDeviceListDel(conn, driver->activePciHostdevs, pci);
Mark McLoughlin c034c1
+        if (pciResetDevice(conn, pci, driver->activePciHostdevs) < 0)
Mark McLoughlin c034c1
+            ret = -1;
Mark McLoughlin c034c1
+        if (detach->managed && pciReAttachDevice(conn, pci) < 0)
Mark McLoughlin c034c1
+            ret = -1;
Mark McLoughlin c034c1
+        pciFreeDevice(conn, pci);
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (i != --vm->def->nhostdevs)
Mark McLoughlin c034c1
+        memmove(&vm->def->hostdevs[i],
Mark McLoughlin c034c1
+                &vm->def->hostdevs[i+1],
Mark McLoughlin c034c1
+                sizeof(*vm->def->hostdevs) * (vm->def->nhostdevs-i));
Mark McLoughlin c034c1
+    if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) {
Mark McLoughlin c034c1
+        virReportOOMError(conn);
Mark McLoughlin c034c1
+        ret = -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    return ret;
Mark McLoughlin c034c1
+}
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+static int qemudDomainDetachHostDevice(virConnectPtr conn,
Mark McLoughlin c034c1
+                                       struct qemud_driver *driver,
Mark McLoughlin c034c1
+                                       virDomainObjPtr vm,
Mark McLoughlin c034c1
+                                       virDomainDeviceDefPtr dev)
Mark McLoughlin c034c1
+{
Mark McLoughlin c034c1
+    virDomainHostdevDefPtr hostdev = dev->data.hostdev;
Mark McLoughlin c034c1
+    int ret;
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
Mark McLoughlin c034c1
+                         _("hostdev mode '%s' not supported"),
Mark McLoughlin c034c1
+                         virDomainHostdevModeTypeToString(hostdev->mode));
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    switch (hostdev->source.subsys.type) {
Mark McLoughlin c034c1
+    case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
Mark McLoughlin c034c1
+        ret = qemudDomainDetachHostPciDevice(conn, driver, vm, dev);
Mark McLoughlin c034c1
+        break;
Mark McLoughlin c034c1
+    default:
Mark McLoughlin c034c1
+        qemudReportError(conn, dom, NULL, VIR_ERR_NO_SUPPORT,
Mark McLoughlin c034c1
+                         _("hostdev subsys type '%s' not supported"),
Mark McLoughlin c034c1
+                         virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
Mark McLoughlin c034c1
+        return -1;
Mark McLoughlin c034c1
+    }
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    if (qemuDomainSetDeviceOwnership(conn, driver, dev, 1) < 0)
Mark McLoughlin c034c1
+        VIR_WARN0("Fail to restore disk device ownership");
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
+    return ret;
Mark McLoughlin c034c1
+}
Mark McLoughlin c034c1
+
Mark McLoughlin c034c1
 static int qemudDomainDetachDevice(virDomainPtr dom,
Mark McLoughlin c034c1
                                    const char *xml) {
Mark McLoughlin c034c1
     struct qemud_driver *driver = dom->conn->privateData;
Mark McLoughlin c034c1
@@ -5670,6 +5914,8 @@ static int qemudDomainDetachDevice(virDomainPtr dom,
Mark McLoughlin c034c1
             VIR_WARN0("Fail to restore disk device ownership");
Mark McLoughlin c034c1
     } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
Mark McLoughlin c034c1
         ret = qemudDomainDetachNetDevice(dom->conn, vm, dev);
Mark McLoughlin c034c1
+    } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
Mark McLoughlin c034c1
+        ret = qemudDomainDetachHostDevice(dom->conn, driver, vm, dev);
Mark McLoughlin c034c1
     } else
Mark McLoughlin c034c1
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
Mark McLoughlin c034c1
                          "%s", _("only SCSI or virtio disk device can be detached dynamically"));
Mark McLoughlin c034c1
-- 
Mark McLoughlin c034c1
1.6.2.5
Mark McLoughlin c034c1