On Fri, Jan 08, 2010 at 05:23:06PM +0000, Daniel P. Berrange wrote: > 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 */ ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@xxxxxxxxxxxx | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list