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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Wolfgang Mauerer <wolfgang.mauerer@xxxxxxxxxxx>

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       |  118 ++++++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_monitor.c      |   16 ++++++
 src/qemu/qemu_monitor.h      |    4 ++
 src/qemu/qemu_monitor_json.c |   40 ++++++++++++++
 src/qemu/qemu_monitor_json.h |    4 ++
 src/qemu/qemu_monitor_text.c |   45 ++++++++++++++++
 src/qemu/qemu_monitor_text.h |    5 ++
 8 files changed, 234 insertions(+), 0 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index a346aa5..fa8825f 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -183,6 +183,8 @@ virDomainDeviceAddressIsValid;
 virDomainDevicePCIAddressIsValid;
 virDomainDeviceInfoIsSet;
 virDomainDeviceAddressClear;
+virDomainControllerTypeToString;
+virDomainControllerDefFree;
 
 
 # domain_event.h
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 8c2e6d6..9dcbe25 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5111,6 +5111,45 @@ static int qemudDomainAttachPciDiskDevice(virConnectPtr conn,
     return ret;
 }
 
+static int qemudDomainAttachPciControllerDevice(virConnectPtr conn,
+                                                struct qemud_driver *driver,
+                                                virDomainObjPtr vm,
+                                                virDomainControllerDefPtr def)
+{
+    int i, ret;
+    const char* type = virDomainControllerTypeToString(def->type);
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+        if ((vm->def->controllers[i]->type == def->type) &&
+            (vm->def->controllers[i]->idx == def->idx)) {
+            qemudReportError(conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                             _("target %s:%d already exists"),
+                             type, def->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,
+                                             &def->info.addr.pci);
+    qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+    if (ret == 0) {
+        def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+        virDomainControllerInsertPreAlloced(vm->def, def);
+    }
+
+    return ret;
+}
+
+
 static int qemudDomainAttachUsbMassstorageDevice(virConnectPtr conn,
                                                  struct qemud_driver *driver,
                                                  virDomainObjPtr vm,
@@ -5496,6 +5535,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->data.controller);
+        } 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) {
@@ -5587,6 +5635,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->info,
+                                       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->info.addr.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,
@@ -5838,6 +5947,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 caf2d35..dca8906 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1250,3 +1250,19 @@ int qemuMonitorGetPtyPaths(qemuMonitorPtr mon,
 
     return qemuMonitorTextGetPtyPaths(mon, paths);
 }
+
+
+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 e0a0552..1096106 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -265,4 +265,8 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
 int qemuMonitorGetPtyPaths(qemuMonitorPtr mon,
                            virHashTablePtr paths);
 
+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 e0a3225..14b8680 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -1482,3 +1482,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 74d88b2..96eb68d 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -141,4 +141,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 7c8b0a2..db3912e 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1747,6 +1747,51 @@ int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon,
     ret = 0;
 
 cleanup:
+    VIR_FREE(cmd);
+    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 f1f2f77..ca2538a 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -148,4 +148,9 @@ int qemuMonitorTextRemoveHostNetwork(qemuMonitorPtr mon,
 int qemuMonitorTextGetPtyPaths(qemuMonitorPtr mon,
                                virHashTablePtr paths);
 
+int qemuMonitorTextAttachPCIDiskController(qemuMonitorPtr mon,
+                                           const char *bus,
+                                           virDomainDevicePCIAddress *guestAddr);
+
+
 #endif /* QEMU_MONITOR_TEXT_H */
-- 
1.6.5.2

--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]