QEMU internally updates the size of video memory if you specify too low
memory size or there are some dependencies like for QXL device and its
'vgamem' and 'ram' size. We need to know about the changes and store
them into the status XML to not break migration or managedsave trough
different libvirt versions.
Signed-off-by: Pavel Hrdina <phrdina(a)redhat.com>
---
src/qemu/qemu_monitor.c | 34 +++++++++++++++
src/qemu/qemu_monitor.h | 10 +++++
src/qemu/qemu_process.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 152 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index ee4460f..e061e02 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1153,6 +1153,40 @@ qemuMonitorFindBalloonObjectPath(qemuMonitorPtr mon,
return ret;
}
+
+/**
+ * For device finds its QOM Object path and after that reads and return
+ * specified property. If the path cannot be found or the QOM Object doesn't
+ * have that property it will fail and report appropriate error message.
+ *
+ * Returns 0 on success, otherwise -1.
+ */
+int
+qemuMonitorGetDeviceParam(qemuMonitorPtr mon,
+ const char *device,
+ const char *name,
+ qemuMonitorObjectPropertyPtr prop)
+{
+ char *path = NULL;
+
+ if (qemuMonitorFindObjectPath(mon, "/", device, &path) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to find QOM Object path for device
'%s'"),
+ device);
+ return -1;
+ }
+
+ if (qemuMonitorJSONGetObjectProperty(mon, path, name, prop)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("QOM Object '%s' has no property
'%s'"), device, name);
+ VIR_FREE(path);
+ return -1;
+ }
+
+ return 0;
+}
+
+
int qemuMonitorHMPCommandWithFd(qemuMonitorPtr mon,
const char *cmd,
int scm_fd,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 4918588..dc74964 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -271,6 +271,12 @@ virJSONValuePtr qemuMonitorGetOptions(qemuMonitorPtr mon)
ATTRIBUTE_NONNULL(1);
void qemuMonitorSetOptions(qemuMonitorPtr mon, virJSONValuePtr options)
ATTRIBUTE_NONNULL(1);
+int qemuMonitorGetDeviceParam(qemuMonitorPtr mon,
+ const char *device,
+ const char *name,
+ qemuMonitorObjectPropertyPtr prop)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
+ ATTRIBUTE_NONNULL(4);
int qemuMonitorHMPCommandWithFd(qemuMonitorPtr mon,
const char *cmd,
int scm_fd,
@@ -367,6 +373,10 @@ int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
int **pids);
int qemuMonitorGetVirtType(qemuMonitorPtr mon,
int *virtType);
+int qemuMonitorGetVideoDeviceParam(qemuMonitorPtr mon,
+ const char *device,
+ const char *name,
+ qemuMonitorObjectPropertyPtr prop);
int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
unsigned long long *currmem);
int qemuMonitorGetMemoryStats(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index ab4df9b..551d48c 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3037,6 +3037,110 @@ qemuProcessCleanupChardevDevice(virDomainDefPtr def
ATTRIBUTE_UNUSED,
}
+/**
+ * Loads and update video memory size for video devices according to QEMU
+ * process as the QEMU will silently update the values that we pass to QEMU
+ * through command line. We need to load these updated values and store them
+ * into the status XML.
+ *
+ * We will fail if for some reason the values cannot be loaded from QEMU because
+ * its mandatory to get the correct video memory size to status XML to not break
+ * migration.
+ */
+static int
+qemuProcessUpdateVideoRamSize(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ int asyncJob)
+{
+ ssize_t i;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainVideoDefPtr video = NULL;
+ virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+ qemuMonitorObjectProperty prop = {
+ QEMU_MONITOR_OBJECT_PROPERTY_ULONG,
+ {0}
+ };
+
+ if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
+ return -1;
+
+ for (i = 0; i < vm->def->nvideos; i++) {
+ video = vm->def->videos[i];
+ switch (video->type) {
+ case VIR_DOMAIN_VIDEO_TYPE_VGA:
+ if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VGA_VGAMEM)) {
+ if (qemuMonitorGetDeviceParam(priv->mon,
+ "VGA",
+ "vgamem_mb",
+ &prop) < 0) {
+ goto error;
+ }
+
+ video->vram = prop.val.ul * 1024;
+ }
+ break;
+ case VIR_DOMAIN_VIDEO_TYPE_QXL:
+ if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_QXL_VGA_VGAMEM)) {
+ if (qemuMonitorGetDeviceParam(priv->mon,
+ video->primary ? "qxl-vga" :
"qxl",
+ "vram_size",
+ &prop) < 0) {
+ goto error;
+ }
+
+ video->vram = prop.val.ul / 1024;
+ if (qemuMonitorGetDeviceParam(priv->mon,
+ video->primary ? "qxl-vga" :
"qxl",
+ "ram_size",
+ &prop) < 0) {
+ goto error;
+ }
+
+ video->ram = prop.val.ul / 1024;
+ if (qemuMonitorGetDeviceParam(priv->mon,
+ video->primary ? "qxl-vga" :
"qxl",
+ "vgamem_mb",
+ &prop) < 0) {
+ goto error;
+ }
+
+ video->vgamem = prop.val.ul * 1024;
+ }
+ break;
+ case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
+ if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VMWARE_SVGA_VGAMEM)) {
+
+ if (qemuMonitorGetDeviceParam(priv->mon,
+ "vmware-svga",
+ "vgamem_mb",
+ &prop) < 0) {
+ goto error;
+ }
+
+ video->vram = prop.val.ul * 1024;
+ }
+ break;
+ case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
+ case VIR_DOMAIN_VIDEO_TYPE_XEN:
+ case VIR_DOMAIN_VIDEO_TYPE_VBOX:
+ case VIR_DOMAIN_VIDEO_TYPE_LAST:
+ break;
+ }
+ }
+
+ qemuDomainObjExitMonitor(driver, vm);
+
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ return -1;
+
+ return 0;
+
+ error:
+ qemuDomainObjExitMonitor(driver, vm);
+ return -1;
+}
+
+
struct qemuProcessHookData {
virConnectPtr conn;
virDomainObjPtr vm;
@@ -4787,6 +4891,10 @@ int qemuProcessStart(virConnectPtr conn,
}
qemuDomainObjExitMonitor(driver, vm);
+ VIR_DEBUG("Detecting actual memory size for video device");
+ if (qemuProcessUpdateVideoRamSize(driver, vm, asyncJob) < 0)
+ goto cleanup;
+
if (!(flags & VIR_QEMU_PROCESS_START_PAUSED)) {
VIR_DEBUG("Starting domain CPUs");
/* Allow the CPUS to start executing */
--
2.0.4