qemu: Add support for hot/cold-(un)plug of shmem devices

This is needed in order to migrate a domain with shmem devices as that
is not allowed to migrate.

Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
This commit is contained in:
Martin Kletzander 2016-09-12 15:40:48 +02:00
parent 06524fd52c
commit fb2d0cc633
12 changed files with 445 additions and 5 deletions

View File

@ -7612,6 +7612,15 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
dev->data.memory = NULL;
break;
case VIR_DOMAIN_DEVICE_SHMEM:
ret = qemuDomainAttachShmemDevice(driver, vm,
dev->data.shmem);
if (ret < 0) {
alias = dev->data.shmem->info.alias;
dev->data.shmem = NULL;
}
break;
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_FS:
case VIR_DOMAIN_DEVICE_INPUT:
@ -7623,7 +7632,6 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_TPM:
case VIR_DOMAIN_DEVICE_PANIC:
case VIR_DOMAIN_DEVICE_IOMMU:
@ -7701,6 +7709,9 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_MEMORY:
ret = qemuDomainDetachMemoryDevice(driver, vm, dev->data.memory);
break;
case VIR_DOMAIN_DEVICE_SHMEM:
ret = qemuDomainDetachShmemDevice(driver, vm, dev->data.shmem);
break;
case VIR_DOMAIN_DEVICE_FS:
case VIR_DOMAIN_DEVICE_INPUT:
@ -7712,7 +7723,6 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_REDIRDEV:
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_TPM:
@ -7859,6 +7869,7 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef,
virDomainControllerDefPtr controller;
virDomainFSDefPtr fs;
virDomainRedirdevDefPtr redirdev;
virDomainShmemDefPtr shmem;
switch ((virDomainDeviceType) dev->type) {
case VIR_DOMAIN_DEVICE_DISK:
@ -7983,6 +7994,18 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef,
dev->data.redirdev = NULL;
break;
case VIR_DOMAIN_DEVICE_SHMEM:
shmem = dev->data.shmem;
if (virDomainShmemDefFind(vmdef, shmem) >= 0) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("device is already in the domain configuration"));
return -1;
}
if (virDomainShmemDefInsert(vmdef, shmem) < 0)
return -1;
dev->data.shmem = NULL;
break;
case VIR_DOMAIN_DEVICE_INPUT:
case VIR_DOMAIN_DEVICE_SOUND:
case VIR_DOMAIN_DEVICE_VIDEO:
@ -7992,7 +8015,6 @@ qemuDomainAttachDeviceConfig(virDomainDefPtr vmdef,
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_TPM:
case VIR_DOMAIN_DEVICE_PANIC:
@ -8139,6 +8161,16 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef,
virDomainRedirdevDefFree(virDomainRedirdevDefRemove(vmdef, idx));
break;
case VIR_DOMAIN_DEVICE_SHMEM:
if ((idx = virDomainShmemDefFind(vmdef, dev->data.shmem)) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
_("matching shmem device was not found"));
return -1;
}
virDomainShmemDefFree(virDomainShmemDefRemove(vmdef, idx));
break;
case VIR_DOMAIN_DEVICE_INPUT:
case VIR_DOMAIN_DEVICE_SOUND:
@ -8149,7 +8181,6 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef,
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_TPM:
case VIR_DOMAIN_DEVICE_PANIC:

View File

@ -2471,6 +2471,131 @@ qemuDomainAttachHostDevice(virConnectPtr conn,
return -1;
}
int
qemuDomainAttachShmemDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainShmemDefPtr shmem)
{
int ret = -1;
char *shmstr = NULL;
char *charAlias = NULL;
char *memAlias = NULL;
bool release_backing = false;
bool release_address = true;
virErrorPtr orig_err = NULL;
virJSONValuePtr props = NULL;
qemuDomainObjPrivatePtr priv = vm->privateData;
switch ((virDomainShmemModel)shmem->model) {
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL:
break;
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM:
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("live attach of shmem model '%s' is not supported"),
virDomainShmemModelTypeToString(shmem->model));
/* fall-through */
case VIR_DOMAIN_SHMEM_MODEL_LAST:
return -1;
}
if (qemuAssignDeviceShmemAlias(vm->def, shmem, -1) < 0)
return -1;
if (qemuDomainPrepareShmemChardev(shmem) < 0)
return -1;
if (VIR_REALLOC_N(vm->def->shmems, vm->def->nshmems + 1) < 0)
return -1;
if ((shmem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE ||
shmem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
(virDomainPCIAddressEnsureAddr(priv->pciaddrs, &shmem->info) < 0))
return -1;
if (!(shmstr = qemuBuildShmemDevStr(vm->def, shmem, priv->qemuCaps)))
goto cleanup;
if (shmem->server.enabled) {
if (virAsprintf(&charAlias, "char%s", shmem->info.alias) < 0)
goto cleanup;
} else {
if (!(props = qemuBuildShmemBackendMemProps(shmem)))
goto cleanup;
if (virAsprintf(&memAlias, "shmmem-%s", shmem->info.alias) < 0)
goto cleanup;
}
qemuDomainObjEnterMonitor(driver, vm);
if (shmem->server.enabled) {
if (qemuMonitorAttachCharDev(priv->mon, charAlias,
&shmem->server.chr) < 0)
goto exit_monitor;
} else {
if (qemuMonitorAddObject(priv->mon, "memory-backend-file",
memAlias, props) < 0) {
props = NULL;
goto exit_monitor;
}
props = NULL;
}
release_backing = true;
if (qemuMonitorAddDevice(priv->mon, shmstr) < 0)
goto exit_monitor;
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
release_address = false;
goto cleanup;
}
/* Doing a copy here just so the pointer doesn't get nullified
* because we need it in the audit function */
VIR_APPEND_ELEMENT_COPY_INPLACE(vm->def->shmems, vm->def->nshmems, shmem);
ret = 0;
release_address = false;
audit:
virDomainAuditShmem(vm, shmem, "attach", ret == 0);
cleanup:
if (release_address)
qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL);
virJSONValueFree(props);
VIR_FREE(memAlias);
VIR_FREE(charAlias);
VIR_FREE(shmstr);
return ret;
exit_monitor:
orig_err = virSaveLastError();
if (release_backing) {
if (shmem->server.enabled)
ignore_value(qemuMonitorDetachCharDev(priv->mon, charAlias));
else
ignore_value(qemuMonitorDelObject(priv->mon, memAlias));
}
if (orig_err) {
virSetError(orig_err);
virFreeError(orig_err);
}
if (qemuDomainObjExitMonitor(driver, vm) < 0)
release_address = false;
goto audit;
}
static int
qemuDomainChangeNetBridge(virDomainObjPtr vm,
virDomainNetDefPtr olddev,
@ -3787,6 +3912,62 @@ qemuDomainRemoveRNGDevice(virQEMUDriverPtr driver,
}
static int
qemuDomainRemoveShmemDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainShmemDefPtr shmem)
{
int rc;
int ret = -1;
ssize_t idx = -1;
char *charAlias = NULL;
char *memAlias = NULL;
qemuDomainObjPrivatePtr priv = vm->privateData;
virObjectEventPtr event = NULL;
VIR_DEBUG("Removing shmem device %s from domain %p %s",
shmem->info.alias, vm, vm->def->name);
if (shmem->server.enabled) {
if (virAsprintf(&charAlias, "char%s", shmem->info.alias) < 0)
return -1;
} else {
if (virAsprintf(&memAlias, "shmmem-%s", shmem->info.alias) < 0)
return -1;
}
qemuDomainObjEnterMonitor(driver, vm);
if (shmem->server.enabled)
rc = qemuMonitorDetachCharDev(priv->mon, charAlias);
else
rc = qemuMonitorDelObject(priv->mon, memAlias);
if (qemuDomainObjExitMonitor(driver, vm) < 0)
goto cleanup;
virDomainAuditShmem(vm, shmem, "detach", rc == 0);
if (rc < 0)
goto cleanup;
event = virDomainEventDeviceRemovedNewFromObj(vm, shmem->info.alias);
qemuDomainEventQueue(driver, event);
if ((idx = virDomainShmemDefFind(vm->def, shmem)) >= 0)
virDomainShmemDefRemove(vm->def, idx);
qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL);
virDomainShmemDefFree(shmem);
ret = 0;
cleanup:
VIR_FREE(charAlias);
VIR_FREE(memAlias);
return ret;
}
int
qemuDomainRemoveDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
@ -3818,6 +3999,10 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
ret = qemuDomainRemoveMemoryDevice(driver, vm, dev->data.memory);
break;
case VIR_DOMAIN_DEVICE_SHMEM:
ret = qemuDomainRemoveShmemDevice(driver, vm, dev->data.shmem);
break;
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_LEASE:
case VIR_DOMAIN_DEVICE_FS:
@ -3831,7 +4016,6 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver,
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_TPM:
case VIR_DOMAIN_DEVICE_PANIC:
case VIR_DOMAIN_DEVICE_IOMMU:
@ -4406,6 +4590,59 @@ int qemuDomainDetachHostDevice(virQEMUDriverPtr driver,
return qemuDomainDetachThisHostDevice(driver, vm, detach);
}
int
qemuDomainDetachShmemDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainShmemDefPtr dev)
{
int ret = -1;
ssize_t idx = -1;
virDomainShmemDefPtr shmem = NULL;
qemuDomainObjPrivatePtr priv = vm->privateData;
if ((idx = virDomainShmemDefFind(vm->def, dev)) < 0) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("device not present in domain configuration"));
return -1;
}
shmem = vm->def->shmems[idx];
switch ((virDomainShmemModel)shmem->model) {
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL:
break;
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM:
virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
_("live detach of shmem model '%s' is not supported"),
virDomainShmemModelTypeToString(shmem->model));
/* fall-through */
case VIR_DOMAIN_SHMEM_MODEL_LAST:
return -1;
}
qemuDomainMarkDeviceForRemoval(vm, &shmem->info);
qemuDomainObjEnterMonitor(driver, vm);
ret = qemuMonitorDelDevice(priv->mon, shmem->info.alias);
if (qemuDomainObjExitMonitor(driver, vm) < 0)
ret = -1;
if (ret == 0) {
if ((ret = qemuDomainWaitForDeviceRemoval(vm)) == 1) {
qemuDomainReleaseDeviceAddress(vm, &shmem->info, NULL);
ret = qemuDomainRemoveShmemDevice(driver, vm, shmem);
}
}
qemuDomainResetDeviceRemoval(vm);
return ret;
}
int
qemuDomainDetachNetDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,

View File

@ -51,6 +51,9 @@ int qemuDomainAttachHostDevice(virConnectPtr conn,
virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainHostdevDefPtr hostdev);
int qemuDomainAttachShmemDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainShmemDefPtr shmem);
int qemuDomainFindGraphicsIndex(virDomainDefPtr def,
virDomainGraphicsDefPtr dev);
int qemuDomainAttachMemory(virQEMUDriverPtr driver,
@ -87,6 +90,9 @@ int qemuDomainDetachNetDevice(virQEMUDriverPtr driver,
int qemuDomainDetachHostDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainDeviceDefPtr dev);
int qemuDomainDetachShmemDevice(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainShmemDefPtr dev);
int qemuDomainAttachLease(virQEMUDriverPtr driver,
virDomainObjPtr vm,
virDomainLeaseDefPtr lease);

View File

@ -74,6 +74,8 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VIRTIO_SCSI);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VIRTIO_CCW);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_IVSHMEM_PLAIN);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_IVSHMEM_DOORBELL);
if (event)
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_DEL_EVENT);
@ -120,6 +122,9 @@ testQemuHotplugAttach(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_CHR:
ret = qemuDomainAttachChrDevice(NULL, &driver, vm, dev->data.chr);
break;
case VIR_DOMAIN_DEVICE_SHMEM:
ret = qemuDomainAttachShmemDevice(&driver, vm, dev->data.shmem);
break;
default:
VIR_TEST_VERBOSE("device type '%s' cannot be attached\n",
virDomainDeviceTypeToString(dev->type));
@ -142,6 +147,9 @@ testQemuHotplugDetach(virDomainObjPtr vm,
case VIR_DOMAIN_DEVICE_CHR:
ret = qemuDomainDetachChrDevice(&driver, vm, dev->data.chr);
break;
case VIR_DOMAIN_DEVICE_SHMEM:
ret = qemuDomainDetachShmemDevice(&driver, vm, dev->data.shmem);
break;
default:
VIR_TEST_VERBOSE("device type '%s' cannot be detached\n",
virDomainDeviceTypeToString(dev->type));
@ -561,6 +569,19 @@ mymain(void)
"human-monitor-command", HMP("OK\\r\\n"),
"device_add", QMP_OK);
DO_TEST_ATTACH("base-live", "ivshmem-plain", false, true,
"object-add", QMP_OK,
"device_add", QMP_OK);
DO_TEST_ATTACH("base-live", "ivshmem-doorbell", false, true,
"chardev-add", QMP_OK,
"device_add", QMP_OK);
DO_TEST_DETACH("base-live+ivshmem-plain", "ivshmem-doorbell-detach", false, true,
"device_del", QMP_OK,
"chardev-remove", QMP_OK);
DO_TEST_DETACH("base-live", "ivshmem-plain-detach", false, false,
"device_del", QMP_OK,
"object-del", QMP_OK);
qemuTestDriverFree(&driver);
return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,7 @@
<shmem name='shmem1'>
<model type='ivshmem-doorbell'/>
<server path='/var/lib/libvirt/shmem-shmem1-sock'/>
<msi ioeventfd='on'/>
<alias name='shmem1'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</shmem>

View File

@ -0,0 +1,4 @@
<shmem name='shmem1'>
<model type='ivshmem-doorbell'/>
<server/>
</shmem>

View File

@ -0,0 +1,6 @@
<shmem name='shmem0'>
<model type='ivshmem-plain'/>
<size unit='M'>4</size>
<alias name='shmem0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</shmem>

View File

@ -0,0 +1,3 @@
<shmem name='shmem0'>
<model type='ivshmem-plain'/>
</shmem>

View File

@ -0,0 +1 @@
qemuhotplug-base-live+ivshmem-plain.xml

View File

@ -0,0 +1,65 @@
<domain type='kvm' id='7'>
<name>hotplug</name>
<uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
<memory unit='KiB'>4194304</memory>
<currentMemory unit='KiB'>4194304</currentMemory>
<vcpu placement='static'>4</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/libexec/qemu-kvm</emulator>
<controller type='usb' index='0'>
<alias name='usb'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='ide' index='0'>
<alias name='ide'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
</controller>
<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</controller>
<controller type='pci' index='0' model='pci-root'>
<alias name='pci'/>
</controller>
<controller type='virtio-serial' index='0'>
<alias name='virtio-serial0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</controller>
<input type='mouse' bus='ps2'>
<alias name='input0'/>
</input>
<input type='keyboard' bus='ps2'>
<alias name='input1'/>
</input>
<memballoon model='none'>
<alias name='balloon0'/>
</memballoon>
<shmem name='shmem0'>
<model type='ivshmem-plain'/>
<size unit='M'>4</size>
<alias name='shmem0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</shmem>
<shmem name='shmem1'>
<model type='ivshmem-doorbell'/>
<server path='/var/lib/libvirt/shmem-shmem1-sock'/>
<msi ioeventfd='on'/>
<alias name='shmem1'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</shmem>
</devices>
<seclabel type='none' model='none'/>
</domain>

View File

@ -0,0 +1 @@
qemuhotplug-base-live.xml

View File

@ -0,0 +1,58 @@
<domain type='kvm' id='7'>
<name>hotplug</name>
<uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
<memory unit='KiB'>4194304</memory>
<currentMemory unit='KiB'>4194304</currentMemory>
<vcpu placement='static'>4</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
<pae/>
</features>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>
<emulator>/usr/libexec/qemu-kvm</emulator>
<controller type='usb' index='0'>
<alias name='usb'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
<controller type='ide' index='0'>
<alias name='ide'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
</controller>
<controller type='scsi' index='0' model='virtio-scsi'>
<alias name='scsi0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
</controller>
<controller type='pci' index='0' model='pci-root'>
<alias name='pci'/>
</controller>
<controller type='virtio-serial' index='0'>
<alias name='virtio-serial0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
</controller>
<input type='mouse' bus='ps2'>
<alias name='input0'/>
</input>
<input type='keyboard' bus='ps2'>
<alias name='input1'/>
</input>
<memballoon model='none'>
<alias name='balloon0'/>
</memballoon>
<shmem name='shmem0'>
<model type='ivshmem-plain'/>
<size unit='M'>4</size>
<alias name='shmem0'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</shmem>
</devices>
<seclabel type='none' model='none'/>
</domain>