mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 11:22:23 +00:00
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:
parent
1867004ed1
commit
da9d937b94
@ -183,6 +183,8 @@ virDomainDeviceAddressIsValid;
|
||||
virDomainDevicePCIAddressIsValid;
|
||||
virDomainDeviceInfoIsSet;
|
||||
virDomainDeviceAddressClear;
|
||||
virDomainControllerTypeToString;
|
||||
virDomainControllerDefFree;
|
||||
|
||||
|
||||
# domain_event.h
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user