[libvirt] [PATCH] add screendump async to qemu

Alon Levy alevy at redhat.com
Tue Mar 6 11:02:52 UTC 2012


RHBZ: 800338

Adds a new capability to qemu, QEMU_CAPS_SCREENDUMP_ASYNC, available if
the qmp command "screendump-async" exists.

If that cap exists qemuDomainScreenshot uses it. The implementation
consists of a hash from filename to struct holding the stream and
temporary fd. The fd is closed and the stream is written to (in reverse
order) by the completion callback, qemuProcessScreenshotComplete.

Note: in qemuDomainScreenshot I don't check for an existing entry in the
screenshots hash table because we the key is a temporary filename,
produced by mkstemp, and it's only unlinked at
qemuProcessScreenshotComplete.

For testing you need to apply the following patches (they are still
pending review on qemu-devel):
 http://patchwork.ozlabs.org/patch/144706/
 http://patchwork.ozlabs.org/patch/144705/
 http://patchwork.ozlabs.org/patch/144704/

Signed-off-by: Alon Levy <alevy at redhat.com>
---
 src/qemu/qemu_capabilities.c |    1 +
 src/qemu/qemu_capabilities.h |    1 +
 src/qemu/qemu_domain.c       |    6 ++++++
 src/qemu/qemu_domain.h       |   12 ++++++++++++
 src/qemu/qemu_driver.c       |   42 +++++++++++++++++++++++++++++++++++-------
 src/qemu/qemu_monitor.c      |   26 ++++++++++++++++++++++++++
 src/qemu/qemu_monitor.h      |    8 ++++++++
 src/qemu/qemu_monitor_json.c |   39 +++++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_monitor_json.h |    3 +++
 src/qemu/qemu_process.c      |   29 +++++++++++++++++++++++++++++
 10 files changed, 160 insertions(+), 7 deletions(-)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 64a4546..57771ff 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -154,6 +154,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
               "drive-iotune", /* 85 */
               "system_wakeup",
               "scsi-disk.channel",
+              "screendump-async",
     );
 
 struct qemu_feature_flags {
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index db584ce..24d620d 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -122,6 +122,7 @@ enum qemuCapsFlags {
     QEMU_CAPS_DRIVE_IOTUNE       = 85, /* -drive bps= and friends */
     QEMU_CAPS_WAKEUP             = 86, /* system_wakeup monitor command */
     QEMU_CAPS_SCSI_DISK_CHANNEL  = 87, /* Is scsi-disk.channel available? */
+    QEMU_CAPS_SCREENDUMP_ASYNC   = 88, /* screendump-async qmp command */
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 2fed91e..acf56c4 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -181,6 +181,10 @@ qemuDomainObjFreeJob(qemuDomainObjPrivatePtr priv)
     ignore_value(virCondDestroy(&priv->job.asyncCond));
 }
 
+static void
+freeScreenshot(void *payload, const void *name ATTRIBUTE_UNUSED) {
+    VIR_FREE(payload);
+}
 
 static void *qemuDomainObjPrivateAlloc(void)
 {
@@ -196,6 +200,8 @@ static void *qemuDomainObjPrivateAlloc(void)
         goto error;
 
     priv->migMaxBandwidth = QEMU_DOMAIN_DEFAULT_MIG_BANDWIDTH_MAX;
+    priv->screenshots = virHashCreate(QEMU_DOMAIN_SCREENSHOTS_CONCURRENT_MAX,
+                                      freeScreenshot);
 
     return priv;
 
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 1333d8c..15721ec 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -40,6 +40,8 @@
 
 # define QEMU_DOMAIN_DEFAULT_MIG_BANDWIDTH_MAX 32
 
+# define QEMU_DOMAIN_SCREENSHOTS_CONCURRENT_MAX 16
+
 # define JOB_MASK(job)                  (1 << (job - 1))
 # define DEFAULT_JOB_MASK               \
     (JOB_MASK(QEMU_JOB_QUERY) |         \
@@ -91,6 +93,14 @@ struct qemuDomainJobObj {
     virDomainJobInfo info;              /* Async job progress data */
 };
 
+struct _qemuScreenshotAsync {
+    virStreamPtr stream;                /* stream to write results to */
+    const char *filename;               /* temporary file to read results from */
+    int fd;                             /* handle to open temporary file */
+};
+typedef struct _qemuScreenshotAsync qemuScreenshotAsync;
+typedef qemuScreenshotAsync *qemuScreenshotAsyncPtr;
+
 typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet;
 typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr;
 
@@ -130,6 +140,8 @@ struct _qemuDomainObjPrivate {
     char *origname;
 
     virConsolesPtr cons;
+
+    virHashTablePtr screenshots;
 };
 
 struct qemuDomainWatchdogEvent
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 733df0a..e397bc3 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -3135,6 +3135,7 @@ qemuDomainScreenshot(virDomainPtr dom,
     int tmp_fd = -1;
     char *ret = NULL;
     bool unlink_tmp = false;
+    qemuScreenshotAsync *screenshot;
 
     virCheckFlags(0, NULL);
 
@@ -3184,9 +3185,34 @@ qemuDomainScreenshot(virDomainPtr dom,
     virSecurityManagerSetSavedStateLabel(qemu_driver->securityManager, vm->def, tmp);
 
     qemuDomainObjEnterMonitor(driver, vm);
-    if (qemuMonitorScreendump(priv->mon, tmp) < 0) {
-        qemuDomainObjExitMonitor(driver, vm);
-        goto endjob;
+    if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_SCREENDUMP_ASYNC)) {
+        if (virHashSize(priv->screenshots) >=
+                QEMU_DOMAIN_SCREENSHOTS_CONCURRENT_MAX) {
+            qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                            "%s", _("too many ongoing screenshots"));
+            goto endjob;
+        }
+        if (VIR_ALLOC(screenshot) < 0) {
+            qemuReportError(VIR_ERR_NO_MEMORY, "%s", _("out of memory"));
+            goto endjob;
+        }
+        screenshot->fd = tmp_fd;
+        screenshot->filename = tmp;
+        screenshot->stream = st;
+        virHashAddEntry(priv->screenshots, tmp, screenshot);
+        if (qemuMonitorScreendumpAsync(priv->mon, tmp) < 0) {
+            qemuDomainObjExitMonitor(driver, vm);
+            goto endjob;
+        }
+        /* string and fd are freed by qmp event callback */
+        tmp = NULL;
+        tmp_fd = -1;
+        unlink_tmp = false;
+    } else {
+        if (qemuMonitorScreendump(priv->mon, tmp) < 0) {
+            qemuDomainObjExitMonitor(driver, vm);
+            goto endjob;
+        }
     }
     qemuDomainObjExitMonitor(driver, vm);
 
@@ -3195,10 +3221,12 @@ qemuDomainScreenshot(virDomainPtr dom,
         goto endjob;
     }
 
-    if (virFDStreamOpenFile(st, tmp, 0, 0, O_RDONLY) < 0) {
-        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
-                        _("unable to open stream"));
-        goto endjob;
+    if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_SCREENDUMP_ASYNC)) {
+        if (virFDStreamOpenFile(st, tmp, 0, 0, O_RDONLY) < 0) {
+            qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                            _("unable to open stream"));
+            goto endjob;
+        }
     }
 
     ret = strdup("image/x-portable-pixmap");
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 1da73f6..0df63e7 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1054,6 +1054,18 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
 }
 
 
+int qemuMonitorEmitScreenDumpComplete(qemuMonitorPtr mon,
+                                      const char *filename)
+{
+    int ret = -1;
+    VIR_DEBUG("mon=%p", mon);
+
+    QEMU_MONITOR_CALLBACK(mon, ret, domainScreenshotComplete, mon->vm,
+                          filename);
+    return ret;
+}
+
+
 
 int qemuMonitorSetCapabilities(qemuMonitorPtr mon,
                                virBitmapPtr qemuCaps)
@@ -2710,6 +2722,20 @@ int qemuMonitorScreendump(qemuMonitorPtr mon,
     return ret;
 }
 
+int qemuMonitorScreendumpAsync(qemuMonitorPtr mon,
+                               const char *file)
+{
+    VIR_DEBUG("mon=%p, file=%s", mon, file);
+
+    if (!mon) {
+        qemuReportError(VIR_ERR_INVALID_ARG,"%s",
+                        _("monitor must not be NULL"));
+        return -1;
+    }
+
+    return qemuMonitorJSONScreendumpAsync(mon, file);
+}
+
 int qemuMonitorBlockJob(qemuMonitorPtr mon,
                         const char *device,
                         const char *base,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index b1c956c..abe977c 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -124,6 +124,9 @@ struct _qemuMonitorCallbacks {
                           const char *diskAlias,
                           int type,
                           int status);
+    int (*domainScreenshotComplete)(qemuMonitorPtr mon,
+                                    virDomainObjPtr vm,
+                                    const char *filename);
 };
 
 
@@ -195,6 +198,8 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon,
                             const char *diskAlias,
                             int type,
                             int status);
+int qemuMonitorEmitScreenDumpComplete(qemuMonitorPtr mon,
+                                      const char *filename);
 
 
 
@@ -505,6 +510,9 @@ int qemuMonitorInjectNMI(qemuMonitorPtr mon);
 int qemuMonitorScreendump(qemuMonitorPtr mon,
                           const char *file);
 
+int qemuMonitorScreendumpAsync(qemuMonitorPtr mon,
+                               const char *file);
+
 int qemuMonitorSendKey(qemuMonitorPtr mon,
                        unsigned int holdtime,
                        unsigned int *keycodes,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index dc67b4b..79ec6ba 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -59,6 +59,7 @@ static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr
 static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleScreenDumpComplete(qemuMonitorPtr mon, virJSONValuePtr data);
 
 static struct {
     const char *type;
@@ -75,6 +76,7 @@ static struct {
     { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
     { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
     { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, },
+    { "SCREEN_DUMP_COMPLETE", qemuMonitorJSONHandleScreenDumpComplete, },
 };
 
 
@@ -725,6 +727,16 @@ out:
     qemuMonitorEmitBlockJob(mon, device, type, status);
 }
 
+static void qemuMonitorJSONHandleScreenDumpComplete(qemuMonitorPtr mon,
+                                                    virJSONValuePtr data)
+{
+    const char *filename;
+
+    if ((filename = virJSONValueObjectGetString(data, "filename")) == NULL) {
+        VIR_WARN("missing filename in screen dump complete event");
+    }
+    qemuMonitorEmitScreenDumpComplete(mon, filename);
+}
 
 int
 qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
@@ -836,6 +848,9 @@ qemuMonitorJSONCheckCommands(qemuMonitorPtr mon,
 
         if (STREQ(name, "system_wakeup"))
             qemuCapsSet(qemuCaps, QEMU_CAPS_WAKEUP);
+
+        if (STREQ(name, "screendump-async"))
+            qemuCapsSet(qemuCaps, QEMU_CAPS_SCREENDUMP_ASYNC);
     }
 
     ret = 0;
@@ -3135,6 +3150,30 @@ int qemuMonitorJSONScreendump(qemuMonitorPtr mon,
     return ret;
 }
 
+int qemuMonitorJSONScreendumpAsync(qemuMonitorPtr mon,
+                                   const char *file)
+{
+    int ret;
+    virJSONValuePtr cmd, reply = NULL;
+
+    cmd = qemuMonitorJSONMakeCommand("screendump-async",
+                                     "s:filename", file,
+                                     NULL);
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
 static int qemuMonitorJSONGetBlockJobInfoOne(virJSONValuePtr entry,
                                               const char *device,
                                               virDomainBlockJobInfoPtr info)
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 0932a2c..74acce1 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -244,6 +244,9 @@ int qemuMonitorJSONSendKey(qemuMonitorPtr mon,
 int qemuMonitorJSONScreendump(qemuMonitorPtr mon,
                               const char *file);
 
+int qemuMonitorJSONScreendumpAsync(qemuMonitorPtr mon,
+                                   const char *file);
+
 int qemuMonitorJSONBlockJob(qemuMonitorPtr mon,
                             const char *device,
                             const char *base,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 7b99814..7985a37 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -47,6 +47,7 @@
 
 #include "datatypes.h"
 #include "logging.h"
+#include "fdstream.h"
 #include "virterror_internal.h"
 #include "memory.h"
 #include "hooks.h"
@@ -918,6 +919,33 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
 }
 
 static int
+qemuProcessScreenshotComplete(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+                              virDomainObjPtr vm,
+                              const char *filename)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    qemuScreenshotAsyncPtr screenshot;
+    int ret = 0;
+
+    if ((screenshot = virHashLookup(priv->screenshots, filename)) == NULL) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("got screendump completion event for wrong filename"));
+        ret = -1;
+        goto end;
+    }
+    if (virFDStreamOpenFile(screenshot->stream, filename, 0, 0, O_RDONLY) < 0) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+                        _("unable to open stream"));
+        ret = -1;
+    }
+end:
+    VIR_FORCE_CLOSE(screenshot->fd);
+    VIR_FREE(screenshot->filename);
+    virHashRemoveEntry(priv->screenshots, filename);
+    return ret;
+}
+
+static int
 qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
                           virDomainObjPtr vm,
                           int phase,
@@ -1034,6 +1062,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
     .domainIOError = qemuProcessHandleIOError,
     .domainGraphics = qemuProcessHandleGraphics,
     .domainBlockJob = qemuProcessHandleBlockJob,
+    .domainScreenshotComplete = qemuProcessScreenshotComplete,
 };
 
 static int
-- 
1.7.9.1




More information about the libvir-list mailing list