On Fri, Jan 08, 2010 at 05:23:06PM +0000, Daniel P. Berrange wrote:
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 | 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 */
ACK,
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit
http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine
http://rpmfind.net/
http://veillard.com/ | virtualization library
http://libvirt.org/