[libvirt] [PATCH 10/12] Implement SCSI controller hotplug/unplug for QEMU

Daniel P. Berrange berrange at redhat.com
Thu Dec 10 22:22:30 UTC 2009


From: Wolfgang Mauerer <wolfgang.mauerer at siemens.com>

This patch allows for explicit hotplug/unplug of SCSI controllers.
Ordinarily this is not required, since QEMU/libvirt will attach
a new SCSI controller whenever one is required. Allowing explicit
hotplug of controllers though, enables the caller to specify a
static PCI address, instead of auto-assigning the next available
PCI slot. Or it will when we have static PCI addressing.

This patch is derived from Wolfgang Mauerer's disk controller
patch series.

* src/qemu/qemu_driver.c: Support hotplug & unplug of SCSI
  controllers
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
  src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
  new API for attaching PCI SCSI controllers
---
 src/libvirt_private.syms     |    2 +
 src/qemu/qemu_driver.c       |  117 ++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_monitor.c      |   16 ++++++
 src/qemu/qemu_monitor.h      |    3 +
 src/qemu/qemu_monitor_json.c |   40 ++++++++++++++
 src/qemu/qemu_monitor_json.h |    4 ++
 src/qemu/qemu_monitor_text.c |   43 +++++++++++++++
 src/qemu/qemu_monitor_text.h |    5 ++
 8 files changed, 230 insertions(+), 0 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 8622e5b..3e98800 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -165,6 +165,8 @@ virDomainDeviceUSBAddressIsValid;
 virDomainDeviceAddressClear;
 virDomainDeviceAddressTypeToString;
 virDomainDefClearDynamicValues;
+virDomainControllerTypeToString;
+virDomainControllerDefFree;
 
 
 # domain_event.h
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 321cd4e..0a91c2a 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4961,6 +4961,44 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn,
     return ret;
 }
 
+static int qemudDomainAttachPciControllerDevice(virConnectPtr conn,
+                                                struct qemud_driver *driver,
+                                                virDomainObjPtr vm,
+                                                virDomainDeviceDefPtr dev)
+{
+    int i, ret;
+    const char* type = virDomainControllerTypeToString(dev->data.controller->type);
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+        if ((vm->def->controllers[i]->type == dev->data.controller->type) &&
+            (vm->def->controllers[i]->idx == dev->data.controller->idx)) {
+            qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                             _("target %s:%d already exists"),
+                             type, dev->data.controller->idx);
+            return -1;
+        }
+    }
+
+    if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers+1) < 0) {
+        virReportOOMError(conn);
+        return -1;
+    }
+
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+    ret = qemuMonitorAttachPCIDiskController(priv->mon,
+                                             type,
+                                             &dev->data.controller->addr.data.pci);
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+    if (ret == 0) {
+        dev->data.controller->addr.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+        virDomainControllerInsertPreAlloced(vm->def, dev->data.controller);
+    }
+
+    return ret;
+}
+
 static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn,
                                                  struct qemud_driver *driver,
                                                  virDomainObjPtr vm,
@@ -5345,6 +5383,15 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
             virCgroupDenyDevicePath(cgroup,
                                     dev->data.disk->src);
         }
+    } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) {
+        if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
+            ret = qemudDomainAttachPciControllerDevice(dom->conn, driver, vm, dev);
+        } else {
+            qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                             _("disk controller bus '%s' cannot be hotplugged."),
+                             virDomainControllerTypeToString(dev->data.controller->type));
+            /* fallthrough */
+        }
     } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
         ret = qemudDomainAttachNetDevice(dom->conn, driver, vm, dev, qemuCmdFlags);
     } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
@@ -5436,6 +5483,67 @@ cleanup:
     return ret;
 }
 
+static int qemudDomainDetachPciControllerDevice(virConnectPtr conn,
+                                                struct qemud_driver *driver,
+                                                virDomainObjPtr vm,
+                                                virDomainDeviceDefPtr dev)
+{
+    int i, ret = -1;
+    virDomainControllerDefPtr detach = NULL;
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+        if ((vm->def->controllers[i]->type == dev->data.controller->type) &&
+            (vm->def->controllers[i]->idx == dev->data.controller->idx)) {
+            detach = vm->def->controllers[i];
+            break;
+        }
+    }
+
+    if (!detach) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         _("disk controller %s:%d not found"),
+                         virDomainControllerTypeToString(dev->data.controller->type),
+                         dev->data.controller->idx);
+        goto cleanup;
+    }
+
+    if (!virDomainDeviceAddressIsValid(&detach->addr,
+                                       VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s",
+                         _("device cannot be detached without a PCI address"));
+        goto cleanup;
+    }
+
+    qemuDomainObjEnterMonitorWithDriver(driver, vm);
+    if (qemuMonitorRemovePCIDevice(priv->mon,
+                                   &detach->addr.data.pci) < 0) {
+        qemuDomainObjExitMonitor(vm);
+        goto cleanup;
+    }
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+    if (vm->def->ncontrollers > 1) {
+        memmove(vm->def->controllers + i,
+                vm->def->controllers + i + 1,
+                sizeof(*vm->def->controllers) *
+                (vm->def->ncontrollers - (i + 1)));
+        vm->def->ncontrollers--;
+        if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers) < 0) {
+            /* ignore, harmless */
+        }
+    } else {
+        VIR_FREE(vm->def->controllers);
+        vm->def->ncontrollers = 0;
+    }
+    virDomainControllerDefFree(detach);
+
+    ret = 0;
+
+cleanup:
+    return ret;
+}
+
 static int
 qemudDomainDetachNetDevice(virConnectPtr conn,
                            struct qemud_driver *driver,
@@ -5687,6 +5795,15 @@ static int qemudDomainDetachDevice(virDomainPtr dom,
             VIR_WARN0("Fail to restore disk device ownership");
     } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
         ret = qemudDomainDetachNetDevice(dom->conn, driver, vm, dev);
+    } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) {
+        if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
+            ret = qemudDomainDetachPciControllerDevice(dom->conn, driver, vm, dev);
+        } else {
+            qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                             _("disk controller bus '%s' cannot be hotunplugged."),
+                             virDomainControllerTypeToString(dev->data.controller->type));
+            /* fallthrough */
+        }
     } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
         ret = qemudDomainDetachHostDevice(dom->conn, driver, vm, dev);
     } else
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index ded1622..4ecbcba 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1240,3 +1240,19 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
         ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname);
     return ret;
 }
+
+
+int qemuMonitorAttachPCIDiskController(qemuMonitorPtr mon,
+                                       const char *bus,
+                                       virDomainDevicePCIAddress *guestAddr)
+{
+    DEBUG("mon=%p, fd=%d type=%s", mon, mon->fd, bus);
+    int ret;
+
+    if (mon->json)
+        ret = qemuMonitorJSONAttachPCIDiskController(mon, bus, guestAddr);
+    else
+        ret = qemuMonitorTextAttachPCIDiskController(mon, bus, guestAddr);
+
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 46f412e..70827c7 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -260,5 +260,8 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
                                  int vlan,
                                  const char *netname);
 
+int qemuMonitorAttachPCIDiskController(qemuMonitorPtr mon,
+                                       const char *bus,
+                                       virDomainDevicePCIAddress *guestAddr);
 
 #endif /* QEMU_MONITOR_H */
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 48eca13..2e605ac 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1481,3 +1481,43 @@ int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
     virJSONValueFree(reply);
     return ret;
 }
+
+
+int qemuMonitorJSONAttachPCIDiskController(qemuMonitorPtr mon,
+                                           const char *bus,
+                                           virDomainDevicePCIAddress *guestAddr)
+{
+    int ret;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    char *dev;
+
+    memset(guestAddr, 0, sizeof(*guestAddr));
+
+    if (virAsprintf(&dev, "if=%s", bus) < 0) {
+        virReportOOMError(NULL);
+        return -1;
+    }
+
+    cmd = qemuMonitorJSONMakeCommand("pci_add",
+                                     "s:pci_addr", "auto",
+                                     "s:type", "storage",
+                                     "s:opts", dev,
+                                     NULL);
+    VIR_FREE(dev);
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    if (ret == 0 &&
+        qemuMonitorJSONGetGuestAddress(reply, guestAddr) < 0)
+        ret = -1;
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 9df96f5..221dfa7 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -140,4 +140,8 @@ int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
                                      int vlan,
                                      const char *netname);
 
+int qemuMonitorJSONAttachPCIDiskController(qemuMonitorPtr mon,
+                                           const char *bus,
+                                           virDomainDevicePCIAddress *guestAddr);
+
 #endif /* QEMU_MONITOR_JSON_H */
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 27c213d..a9d8a4c 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1577,3 +1577,46 @@ cleanup:
     VIR_FREE(reply);
     return ret;
 }
+
+int qemuMonitorTextAttachPCIDiskController(qemuMonitorPtr mon,
+                                           const char *bus,
+                                           virDomainDevicePCIAddress *guestAddr)
+{
+    char *cmd = NULL;
+    char *reply = NULL;
+    int tryOldSyntax = 0;
+    int ret = -1;
+
+try_command:
+    if (virAsprintf(&cmd, "pci_add %s storage if=%s",
+                    (tryOldSyntax ? "0": "pci_addr=auto"), bus) < 0) {
+        virReportOOMError(NULL);
+        goto cleanup;
+    }
+
+    if (qemuMonitorCommand(mon, cmd, &reply) < 0) {
+        qemudReportError(NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         _("cannot attach %s disk controller"), bus);
+        goto cleanup;
+    }
+
+    if (qemuMonitorTextParsePciAddReply(mon, reply, guestAddr) < 0) {
+        if (!tryOldSyntax && strstr(reply, "invalid char in expression")) {
+            VIR_FREE(reply);
+            VIR_FREE(cmd);
+            tryOldSyntax = 1;
+            goto try_command;
+        }
+
+        qemudReportError (NULL, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("adding %s disk controller failed: %s"), bus, reply);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(cmd);
+    VIR_FREE(reply);
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index f304795..0e131ea 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -140,4 +140,9 @@ int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon,
                                      int vlan,
                                      const char *netname);
 
+int qemuMonitorTextAttachPCIDiskController(qemuMonitorPtr mon,
+                                           const char *bus,
+                                           virDomainDevicePCIAddress *guestAddr);
+
+
 #endif /* QEMU_MONITOR_TEXT_H */
-- 
1.6.5.2




More information about the libvir-list mailing list