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 | 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 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list