mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-28 11:31:16 +00:00
qemu: Wire up <memory/> live update
As advertised in one of previous commits, we want to be able to change 'requested-size' attribute of virtio-mem on the fly. This commit does exactly that. Changing anything else is checked for and forbidden. Once guest has changed the allocation, QEMU emits an event which we will use to track the allocation. In the next commit. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
parent
363866a1e2
commit
3ec559661a
@ -16577,6 +16577,50 @@ virDomainMemoryFindInactiveByDef(virDomainDef *def,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virDomainMemoryFindByDeviceInfo:
|
||||
* @def: domain defintion
|
||||
* @info: device info to match
|
||||
* @pos: store position within array
|
||||
*
|
||||
* For given domain definition @def find <memory/> device with
|
||||
* matching address and matching device alias (if set in @info,
|
||||
* otherwise ignored).
|
||||
*
|
||||
* If @pos is not NULL then the position of the matched device
|
||||
* within the array is stored there.
|
||||
*
|
||||
* Returns: device if found,
|
||||
* NULL otherwise.
|
||||
*/
|
||||
virDomainMemoryDef *
|
||||
virDomainMemoryFindByDeviceInfo(virDomainDef *def,
|
||||
virDomainDeviceInfo *info,
|
||||
int *pos)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < def->nmems; i++) {
|
||||
virDomainMemoryDef *tmp = def->mems[i];
|
||||
|
||||
if (!virDomainDeviceInfoAddressIsEqual(&tmp->info, info))
|
||||
continue;
|
||||
|
||||
/* alias, if present */
|
||||
if (info->alias &&
|
||||
STRNEQ_NULLABLE(tmp->info.alias, info->alias))
|
||||
continue;
|
||||
|
||||
if (pos)
|
||||
*pos = i;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virDomainMemoryInsert:
|
||||
*
|
||||
@ -28590,7 +28634,8 @@ virDomainDefCompatibleDevice(virDomainDef *def,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((virDomainDefGetMemoryTotal(def) + sz) > def->mem.max_memory) {
|
||||
if (action == VIR_DOMAIN_DEVICE_ACTION_ATTACH &&
|
||||
(virDomainDefGetMemoryTotal(def) + sz) > def->mem.max_memory) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("Attaching memory device with size '%llu' would "
|
||||
"exceed domain's maxMemory config size '%llu'"),
|
||||
|
@ -3807,6 +3807,11 @@ int virDomainMemoryFindByDef(virDomainDef *def, virDomainMemoryDef *mem)
|
||||
int virDomainMemoryFindInactiveByDef(virDomainDef *def,
|
||||
virDomainMemoryDef *mem)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
|
||||
virDomainMemoryDef *
|
||||
virDomainMemoryFindByDeviceInfo(virDomainDef *dev,
|
||||
virDomainDeviceInfo *info,
|
||||
int *pos)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
int virDomainShmemDefInsert(virDomainDef *def, virDomainShmemDef *shmem)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
@ -508,6 +508,7 @@ virDomainMemballoonModelTypeFromString;
|
||||
virDomainMemballoonModelTypeToString;
|
||||
virDomainMemoryDefFree;
|
||||
virDomainMemoryFindByDef;
|
||||
virDomainMemoryFindByDeviceInfo;
|
||||
virDomainMemoryFindInactiveByDef;
|
||||
virDomainMemoryInsert;
|
||||
virDomainMemoryModelTypeToString;
|
||||
|
@ -6976,6 +6976,175 @@ qemuDomainChangeDiskLive(virDomainObj *vm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
qemuDomainChangeMemoryLiveValidateChange(const virDomainMemoryDef *oldDef,
|
||||
const virDomainMemoryDef *newDef)
|
||||
{
|
||||
/* The only thing that is allowed to change is 'requestedsize' for
|
||||
* virtio-mem model. Check if user isn't trying to sneak in change for
|
||||
* something else. */
|
||||
|
||||
switch (oldDef->model) {
|
||||
case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM:
|
||||
break;
|
||||
|
||||
case VIR_DOMAIN_MEMORY_MODEL_NONE:
|
||||
case VIR_DOMAIN_MEMORY_MODEL_DIMM:
|
||||
case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
|
||||
case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
|
||||
case VIR_DOMAIN_MEMORY_MODEL_LAST:
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory of model '%s'"),
|
||||
virDomainMemoryModelTypeToString(oldDef->model));
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldDef->model != newDef->model) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory model from '%s' to '%s'"),
|
||||
virDomainMemoryModelTypeToString(oldDef->model),
|
||||
virDomainMemoryModelTypeToString(newDef->model));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->access != newDef->access) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory access from '%s' to '%s'"),
|
||||
virDomainMemoryAccessTypeToString(oldDef->access),
|
||||
virDomainMemoryAccessTypeToString(newDef->access));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->discard != newDef->discard) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory discard from '%s' to '%s'"),
|
||||
virTristateBoolTypeToString(oldDef->discard),
|
||||
virTristateBoolTypeToString(newDef->discard));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!virBitmapEqual(oldDef->sourceNodes,
|
||||
newDef->sourceNodes)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("cannot modify memory source nodes"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->pagesize != newDef->pagesize) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory pagesize from '%llu' to '%llu'"),
|
||||
oldDef->pagesize,
|
||||
newDef->pagesize);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (STRNEQ_NULLABLE(oldDef->nvdimmPath, newDef->nvdimmPath)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory path from '%s' to '%s'"),
|
||||
NULLSTR(oldDef->nvdimmPath),
|
||||
NULLSTR(newDef->nvdimmPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->alignsize != newDef->alignsize) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory align size from '%llu' to '%llu'"),
|
||||
oldDef->alignsize, newDef->alignsize);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->nvdimmPmem != newDef->nvdimmPmem) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory pmem from '%d' to '%d'"),
|
||||
oldDef->nvdimmPmem, newDef->nvdimmPmem);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->targetNode != newDef->targetNode) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory targetNode from '%d' to '%d'"),
|
||||
oldDef->targetNode, newDef->targetNode);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->size != newDef->size) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory size from '%llu' to '%llu'"),
|
||||
oldDef->size, newDef->size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldDef->labelsize != newDef->labelsize) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory label size from '%llu' to '%llu'"),
|
||||
oldDef->labelsize, newDef->labelsize);
|
||||
return false;
|
||||
}
|
||||
if (oldDef->blocksize != newDef->blocksize) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("cannot modify memory block size from '%llu' to '%llu'"),
|
||||
oldDef->blocksize, newDef->blocksize);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* requestedsize can change */
|
||||
|
||||
if (oldDef->readonly != newDef->readonly) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("cannot modify memory pmem flag"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((oldDef->uuid || newDef->uuid) &&
|
||||
!(oldDef->uuid && newDef->uuid &&
|
||||
memcmp(oldDef->uuid, newDef->uuid, VIR_UUID_BUFLEN) == 0)) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("cannot modify memory UUID"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qemuDomainChangeMemoryLive(virQEMUDriver *driver G_GNUC_UNUSED,
|
||||
virDomainObj *vm,
|
||||
virDomainDeviceDef *dev)
|
||||
{
|
||||
virDomainDeviceDef oldDev = { .type = VIR_DOMAIN_DEVICE_MEMORY };
|
||||
virDomainMemoryDef *newDef = dev->data.memory;
|
||||
virDomainMemoryDef *oldDef = NULL;
|
||||
|
||||
oldDef = virDomainMemoryFindByDeviceInfo(vm->def, &dev->data.memory->info, NULL);
|
||||
if (!oldDef) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("memory '%s' not found"), dev->data.memory->info.alias);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oldDev.data.memory = oldDef;
|
||||
|
||||
if (virDomainDefCompatibleDevice(vm->def, dev, &oldDev,
|
||||
VIR_DOMAIN_DEVICE_ACTION_UPDATE,
|
||||
true) < 0)
|
||||
return -1;
|
||||
|
||||
if (!qemuDomainChangeMemoryLiveValidateChange(oldDef, newDef))
|
||||
return -1;
|
||||
|
||||
if (qemuDomainChangeMemoryRequestedSize(driver, vm,
|
||||
newDef->info.alias,
|
||||
newDef->requestedsize) < 0)
|
||||
return -1;
|
||||
|
||||
oldDef->requestedsize = newDef->requestedsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qemuDomainUpdateDeviceLive(virDomainObj *vm,
|
||||
virDomainDeviceDef *dev,
|
||||
@ -7017,6 +7186,10 @@ qemuDomainUpdateDeviceLive(virDomainObj *vm,
|
||||
ret = qemuDomainChangeNet(driver, vm, dev);
|
||||
break;
|
||||
|
||||
case VIR_DOMAIN_DEVICE_MEMORY:
|
||||
ret = qemuDomainChangeMemoryLive(driver, vm, dev);
|
||||
break;
|
||||
|
||||
case VIR_DOMAIN_DEVICE_FS:
|
||||
case VIR_DOMAIN_DEVICE_INPUT:
|
||||
case VIR_DOMAIN_DEVICE_SOUND:
|
||||
@ -7032,7 +7205,6 @@ qemuDomainUpdateDeviceLive(virDomainObj *vm,
|
||||
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
||||
case VIR_DOMAIN_DEVICE_CONTROLLER:
|
||||
case VIR_DOMAIN_DEVICE_REDIRDEV:
|
||||
case VIR_DOMAIN_DEVICE_MEMORY:
|
||||
case VIR_DOMAIN_DEVICE_CHR:
|
||||
case VIR_DOMAIN_DEVICE_NONE:
|
||||
case VIR_DOMAIN_DEVICE_TPM:
|
||||
|
@ -6797,3 +6797,21 @@ qemuDomainSetVcpuInternal(virQEMUDriver *driver,
|
||||
virBitmapFree(livevcpus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuDomainChangeMemoryRequestedSize(virQEMUDriver *driver,
|
||||
virDomainObj *vm,
|
||||
const char *alias,
|
||||
unsigned long long requestedsize)
|
||||
{
|
||||
qemuDomainObjPrivate *priv = vm->privateData;
|
||||
int rc;
|
||||
|
||||
qemuDomainObjEnterMonitor(driver, vm);
|
||||
rc = qemuMonitorChangeMemoryRequestedSize(priv->mon, alias, requestedsize);
|
||||
if (qemuDomainObjExitMonitor(driver, vm) < 0)
|
||||
return -1;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -165,3 +165,8 @@ int qemuHotplugAttachDBusVMState(virQEMUDriver *driver,
|
||||
int qemuHotplugRemoveDBusVMState(virQEMUDriver *driver,
|
||||
virDomainObj *vm,
|
||||
qemuDomainAsyncJob asyncJob);
|
||||
|
||||
int qemuDomainChangeMemoryRequestedSize(virQEMUDriver *driver,
|
||||
virDomainObj *vm,
|
||||
const char *alias,
|
||||
unsigned long long requestedsize);
|
||||
|
@ -4625,3 +4625,16 @@ qemuMonitorSetAction(qemuMonitor *mon,
|
||||
|
||||
return qemuMonitorJSONSetAction(mon, shutdown, reboot, watchdog, panic);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuMonitorChangeMemoryRequestedSize(qemuMonitor *mon,
|
||||
const char *alias,
|
||||
unsigned long long requestedsize)
|
||||
{
|
||||
VIR_DEBUG("alias=%s requestedsize=%llu", alias, requestedsize);
|
||||
|
||||
QEMU_CHECK_MONITOR(mon);
|
||||
|
||||
return qemuMonitorJSONChangeMemoryRequestedSize(mon, alias, requestedsize);
|
||||
}
|
||||
|
@ -1537,3 +1537,8 @@ qemuMonitorSetAction(qemuMonitor *mon,
|
||||
qemuMonitorActionReboot reboot,
|
||||
qemuMonitorActionWatchdog watchdog,
|
||||
qemuMonitorActionPanic panic);
|
||||
|
||||
int
|
||||
qemuMonitorChangeMemoryRequestedSize(qemuMonitor *mon,
|
||||
const char *alias,
|
||||
unsigned long long requestedsize);
|
||||
|
@ -9398,3 +9398,18 @@ qemuMonitorJSONSetAction(qemuMonitor *mon,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon,
|
||||
const char *alias,
|
||||
unsigned long long requestedsize)
|
||||
{
|
||||
g_autofree char *path = g_strdup_printf("/machine/peripheral/%s", alias);
|
||||
qemuMonitorJSONObjectProperty prop = {
|
||||
.type = QEMU_MONITOR_OBJECT_PROPERTY_ULONG,
|
||||
.val.ul = requestedsize * 1024, /* monitor needs bytes */
|
||||
};
|
||||
|
||||
return qemuMonitorJSONSetObjectProperty(mon, path, "requested-size", &prop);
|
||||
}
|
||||
|
@ -710,3 +710,8 @@ qemuMonitorJSONSetAction(qemuMonitor *mon,
|
||||
qemuMonitorActionReboot reboot,
|
||||
qemuMonitorActionWatchdog watchdog,
|
||||
qemuMonitorActionPanic panic);
|
||||
|
||||
int
|
||||
qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon,
|
||||
const char *alias,
|
||||
unsigned long long requestedsize);
|
||||
|
Loading…
Reference in New Issue
Block a user