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:
Michal Privoznik 2020-11-23 17:29:00 +01:00
parent 363866a1e2
commit 3ec559661a
10 changed files with 286 additions and 2 deletions

View File

@ -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'"),

View File

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

View File

@ -508,6 +508,7 @@ virDomainMemballoonModelTypeFromString;
virDomainMemballoonModelTypeToString;
virDomainMemoryDefFree;
virDomainMemoryFindByDef;
virDomainMemoryFindByDeviceInfo;
virDomainMemoryFindInactiveByDef;
virDomainMemoryInsert;
virDomainMemoryModelTypeToString;

View File

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

View File

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

View File

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

View File

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

View File

@ -1537,3 +1537,8 @@ qemuMonitorSetAction(qemuMonitor *mon,
qemuMonitorActionReboot reboot,
qemuMonitorActionWatchdog watchdog,
qemuMonitorActionPanic panic);
int
qemuMonitorChangeMemoryRequestedSize(qemuMonitor *mon,
const char *alias,
unsigned long long requestedsize);

View File

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

View File

@ -710,3 +710,8 @@ qemuMonitorJSONSetAction(qemuMonitor *mon,
qemuMonitorActionReboot reboot,
qemuMonitorActionWatchdog watchdog,
qemuMonitorActionPanic panic);
int
qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon,
const char *alias,
unsigned long long requestedsize);