diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1cf310965d..54769611a4 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 783d9fc9ca..3d5e5e0897 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5132,6 +5132,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, @@ -5517,6 +5556,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) { @@ -5608,6 +5656,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, @@ -5859,6 +5968,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 caf2d3583e..dca89068a7 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 e0a0552b28..1096106b93 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 ca3a159888..e2bd1b1b9c 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 74d88b2859..96eb68dc3b 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 7c8b0a2c9b..db3912e86d 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 f1f2f770fe..ca2538ad76 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 */