Implement SCSI controller hotplug/unplug for QEMU

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
This commit is contained in:
Wolfgang Mauerer 2009-12-07 19:28:05 +00:00 committed by Daniel P. Berrange
parent 1867004ed1
commit da9d937b94
8 changed files with 234 additions and 0 deletions

View File

@ -183,6 +183,8 @@ virDomainDeviceAddressIsValid;
virDomainDevicePCIAddressIsValid;
virDomainDeviceInfoIsSet;
virDomainDeviceAddressClear;
virDomainControllerTypeToString;
virDomainControllerDefFree;
# domain_event.h

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */