From: Wolfgang Mauerer <wolfgang.mauerer(a)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