[libvirt] [PATCH] Compressed save image format for Qemu.
Daniel P. Berrange
berrange at redhat.com
Thu Aug 13 18:35:09 UTC 2009
On Thu, Aug 13, 2009 at 10:47:18AM +0200, Chris Lalancette wrote:
> Implement a compressed save image format for qemu. While ideally
> we would have the choice between compressed/non-compressed
> available to the libvirt API, unfortunately there is no "flags"
> parameter to the virDomainSave() API. Therefore, implement this
> as a qemu.conf option. Both gzip and bzip2 are implemented, and
> it should be very easy to implement additional compression
> methods.
>
> One open question is if/how we should detect the gzip and bzip2
> binaries. One way to do it is to do compile-time setting of the
> paths (via configure.in), but that doesn't seem like a great thing
> to do. Another solution (my preferred solution) is not to detect
> at all; when we go to run the commands that need them, if they
> aren't available, or aren't available in one of the standard paths,
> then we'll fail. Maybe somebody else has another option or
> opinion, though.
>
> In the future, we'll have a more robust (managed) save/restore API,
> at which time we can expose this functionality properly in the API.
>
> V2: get rid of redundant dd command and just use >> to append data.
> V3: Add back the missing pieces for the enum and bumping the save version.
> V4: Make the compressed field in the save_header an int.
> Implement LZMA compression.
>
> Signed-off-by: Chris Lalancette <clalance at redhat.com>
ACK, this looks good now. Bonus points if you do a follow-up
patch to add the new param to libvirtd_qemu.aug and
test_libvirtd_qemu.aug.
> ---
> src/qemu.conf | 10 ++++++
> src/qemu_conf.c | 11 ++++++
> src/qemu_conf.h | 2 +
> src/qemu_driver.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++----
> 4 files changed, 109 insertions(+), 7 deletions(-)
>
> diff --git a/src/qemu.conf b/src/qemu.conf
> index 653f487..1f10b43 100644
> --- a/src/qemu.conf
> +++ b/src/qemu.conf
> @@ -129,3 +129,13 @@
> # "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
> # "/dev/rtc", "/dev/hpet", "/dev/net/tun",
> #]
> +
> +# The default format for Qemu/KVM guest save images is raw; that is, the
> +# memory from the domain is dumped out directly to a file. If you have
> +# guests with a large amount of memory, however, this can take up quite
> +# a bit of space. If you would like to compress the images while they
> +# are being saved to disk, you can also set "gzip", "bzip2", or "lzma"
> +# for save_image_format. Note that this means you slow down the
> +# process of saving a domain in order to save disk space.
> +#
> +# save_image_format = "raw"
> diff --git a/src/qemu_conf.c b/src/qemu_conf.c
> index 7ca5a15..ed87e13 100644
> --- a/src/qemu_conf.c
> +++ b/src/qemu_conf.c
> @@ -280,6 +280,17 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
> driver->cgroupDeviceACL[i] = NULL;
> }
>
> + p = virConfGetValue (conf, "save_image_format");
> + CHECK_TYPE ("save_image_format", VIR_CONF_STRING);
> + if (p && p->str) {
> + VIR_FREE(driver->saveImageFormat);
> + if (!(driver->saveImageFormat = strdup(p->str))) {
> + virReportOOMError(NULL);
> + virConfFree(conf);
> + return -1;
> + }
> + }
> +
> virConfFree (conf);
> return 0;
> }
> diff --git a/src/qemu_conf.h b/src/qemu_conf.h
> index 8f4ef6a..e34baab 100644
> --- a/src/qemu_conf.h
> +++ b/src/qemu_conf.h
> @@ -111,6 +111,8 @@ struct qemud_driver {
>
> char *securityDriverName;
> virSecurityDriverPtr securityDriver;
> +
> + char *saveImageFormat;
> };
>
>
> diff --git a/src/qemu_driver.c b/src/qemu_driver.c
> index 20906ef..3fd153d 100644
> --- a/src/qemu_driver.c
> +++ b/src/qemu_driver.c
> @@ -3411,18 +3411,27 @@ static char *qemudEscapeShellArg(const char *in)
> }
>
> #define QEMUD_SAVE_MAGIC "LibvirtQemudSave"
> -#define QEMUD_SAVE_VERSION 1
> +#define QEMUD_SAVE_VERSION 2
> +
> +enum qemud_save_formats {
> + QEMUD_SAVE_FORMAT_RAW,
> + QEMUD_SAVE_FORMAT_GZIP,
> + QEMUD_SAVE_FORMAT_BZIP2,
> + QEMUD_SAVE_FORMAT_LZMA,
> +};
>
> struct qemud_save_header {
> char magic[sizeof(QEMUD_SAVE_MAGIC)-1];
> int version;
> int xml_len;
> int was_running;
> - int unused[16];
> + int compressed;
> + int unused[15];
> };
>
> static int qemudDomainSave(virDomainPtr dom,
> - const char *path) {
> + const char *path)
> +{
> struct qemud_driver *driver = dom->conn->privateData;
> virDomainObjPtr vm;
> char *command = NULL;
> @@ -3433,11 +3442,28 @@ static int qemudDomainSave(virDomainPtr dom,
> struct qemud_save_header header;
> int ret = -1;
> virDomainEventPtr event = NULL;
> + int internalret;
>
> memset(&header, 0, sizeof(header));
> memcpy(header.magic, QEMUD_SAVE_MAGIC, sizeof(header.magic));
> header.version = QEMUD_SAVE_VERSION;
>
> + if (driver->saveImageFormat == NULL)
> + header.compressed = QEMUD_SAVE_FORMAT_RAW;
> + else if (STREQ(driver->saveImageFormat, "raw"))
> + header.compressed = QEMUD_SAVE_FORMAT_RAW;
> + else if (STREQ(driver->saveImageFormat, "gzip"))
> + header.compressed = QEMUD_SAVE_FORMAT_GZIP;
> + else if (STREQ(driver->saveImageFormat, "bzip2"))
> + header.compressed = QEMUD_SAVE_FORMAT_BZIP2;
> + else if (STREQ(driver->saveImageFormat, "lzma"))
> + header.compressed = QEMUD_SAVE_FORMAT_LZMA;
> + else {
> + qemudReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
> + "%s", _("Invalid save image format specified in configuration file"));
> + return -1;
> + }
> +
> qemuDriverLock(driver);
> vm = virDomainFindByUUID(&driver->domains, dom->uuid);
>
> @@ -3510,11 +3536,28 @@ static int qemudDomainSave(virDomainPtr dom,
> virReportOOMError(dom->conn);
> goto cleanup;
> }
> - if (virAsprintf(&command, "migrate \"exec:"
> - "dd of='%s' oflag=append conv=notrunc 2>/dev/null"
> - "\"", safe_path) == -1) {
> +
> + if (header.compressed == QEMUD_SAVE_FORMAT_RAW)
> + internalret = virAsprintf(&command, "migrate \"exec:"
> + "dd of='%s' oflag=append conv=notrunc 2>/dev/null"
> + "\"", safe_path);
> + else if (header.compressed == QEMUD_SAVE_FORMAT_GZIP)
> + internalret = virAsprintf(&command, "migrate \"exec:"
> + "gzip -c >> '%s' 2>/dev/null\"", safe_path);
> + else if (header.compressed == QEMUD_SAVE_FORMAT_BZIP2)
> + internalret = virAsprintf(&command, "migrate \"exec:"
> + "bzip2 -c >> '%s' 2>/dev/null\"", safe_path);
> + else if (header.compressed == QEMUD_SAVE_FORMAT_LZMA)
> + internalret = virAsprintf(&command, "migrate \"exec:"
> + "lzma -c >> '%s' 2>/dev/null\"", safe_path);
> + else {
> + qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
> + _("Invalid compress format %d"),
> + header.compressed);
> + goto cleanup;
> + }
> + if (internalret < 0) {
> virReportOOMError(dom->conn);
> - command = NULL;
> goto cleanup;
> }
>
> @@ -4035,6 +4078,9 @@ static int qemudDomainRestore(virConnectPtr conn,
> char *xml = NULL;
> struct qemud_save_header header;
> virDomainEventPtr event = NULL;
> + int intermediatefd = -1;
> + pid_t intermediate_pid = -1;
> + int childstat;
>
> qemuDriverLock(driver);
> /* Verify the header and read the XML */
> @@ -4124,8 +4170,41 @@ static int qemudDomainRestore(virConnectPtr conn,
> }
> def = NULL;
>
> + if (header.version == 2) {
> + const char *intermediate_argv[3] = { NULL, "-dc", NULL };
> + if (header.compressed == QEMUD_SAVE_FORMAT_GZIP)
> + intermediate_argv[0] = "gzip";
> + else if (header.compressed == QEMUD_SAVE_FORMAT_BZIP2)
> + intermediate_argv[0] = "bzip2";
> + else if (header.compressed == QEMUD_SAVE_FORMAT_LZMA)
> + intermediate_argv[0] = "lzma";
> + else if (header.compressed != QEMUD_SAVE_FORMAT_RAW) {
> + qemudReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
> + _("Unknown compressed save format %d"),
> + header.compressed);
> + goto cleanup;
> + }
> + if (intermediate_argv[0] != NULL) {
> + intermediatefd = fd;
> + fd = -1;
> + if (virExec(conn, intermediate_argv, NULL, NULL,
> + &intermediate_pid, intermediatefd, &fd, NULL, 0) < 0) {
> + qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
> + _("Failed to start decompression binary %s"),
> + intermediate_argv[0]);
> + goto cleanup;
> + }
> + }
> + }
> /* Set the migration source and start it up. */
> ret = qemudStartVMDaemon(conn, driver, vm, "stdio", fd);
> + if (intermediate_pid != -1) {
> + /* Wait for intermediate process to exit */
> + while (waitpid(intermediate_pid, &childstat, 0) == -1 &&
> + errno == EINTR);
> + }
> + if (intermediatefd != -1)
> + close(intermediatefd);
> close(fd);
> fd = -1;
> if (ret < 0) {
Oh actually need a VIR_FREE(driver->save_image_format) in qemudShutdown()
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
More information about the libvir-list
mailing list