]> git.proxmox.com Git - pve-qemu-kvm.git/blobdiff - debian/patches/0004-introduce-new-vma-archive-format.patch
update to latest backup patches
[pve-qemu-kvm.git] / debian / patches / 0004-introduce-new-vma-archive-format.patch
index 1c9c0128b9c4935d8977c973afb5cb80d6dc9224..905c58761088469754ebde009456f03363192eeb 100644 (file)
@@ -1,7 +1,7 @@
-From ff878b16c010752dd46a474329d523cded78a004 Mon Sep 17 00:00:00 2001
+From 5476ae43806488e74cd293bbaa17f130aa53d402 Mon Sep 17 00:00:00 2001
 From: Dietmar Maurer <dietmar@proxmox.com>
 Date: Tue, 13 Nov 2012 11:11:38 +0100
-Subject: [PATCH v3 4/6] introduce new vma archive format
+Subject: [PATCH v4 4/6] introduce new vma archive format
 
 This is a very simple archive format, see docs/specs/vma_spec.txt
 
@@ -9,13 +9,14 @@ Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
 ---
  Makefile                |    3 +-
  Makefile.objs           |    2 +-
+ backup.h                |    1 +
  blockdev.c              |    6 +-
  docs/specs/vma_spec.txt |   24 ++
- vma-reader.c            |  772 ++++++++++++++++++++++++++++++++++++++++
- vma-writer.c            |  900 +++++++++++++++++++++++++++++++++++++++++++++++
- vma.c                   |  550 +++++++++++++++++++++++++++++
+ vma-reader.c            |  799 ++++++++++++++++++++++++++++++++++++++++
+ vma-writer.c            |  932 +++++++++++++++++++++++++++++++++++++++++++++++
+ vma.c                   |  55++++++++++++++++++++++++++++
  vma.h                   |  145 ++++++++
8 files changed, 2398 insertions(+), 4 deletions(-)
9 files changed, 2467 insertions(+), 4 deletions(-)
  create mode 100644 docs/specs/vma_spec.txt
  create mode 100644 vma-reader.c
  create mode 100644 vma-writer.c
@@ -23,65 +24,77 @@ Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
  create mode 100644 vma.h
 
 diff --git a/Makefile b/Makefile
-index 9ecbcbb..30a9268 100644
+index 0d9099a..16f1c25 100644
 --- a/Makefile
 +++ b/Makefile
-@@ -100,7 +100,7 @@ defconfig:
- -include config-all-devices.mak
+@@ -115,7 +115,7 @@ ifeq ($(CONFIG_SMARTCARD_NSS),y)
+ include $(SRC_PATH)/libcacard/Makefile
+ endif
  
 -all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
 +all: $(DOCS) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all
  
  config-host.h: config-host.h-timestamp
  config-host.h-timestamp: config-host.mak
-@@ -194,6 +194,7 @@ tools-obj-$(CONFIG_POSIX) += compatfd.o
- qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) libqemustub.a
- qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
- qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
-+vma$(EXESUF): vma.o vma-writer.o vma-reader.o $(tools-obj-y) $(block-obj-y) libqemustub.a
+@@ -167,6 +167,7 @@ qemu-img.o: qemu-img-cmds.h
+ qemu-img$(EXESUF): qemu-img.o $(block-obj-y) libqemuutil.a libqemustub.a
+ qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) libqemuutil.a libqemustub.a
+ qemu-io$(EXESUF): qemu-io.o cmd.o $(block-obj-y) libqemuutil.a libqemustub.a
++vma$(EXESUF): vma.o vma-writer.o vma-reader.o $(block-obj-y)  libqemuutil.a libqemustub.a
  
  qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
  
 diff --git a/Makefile.objs b/Makefile.objs
-index cb46be5..b5732e2 100644
+index df64f70..91f133b 100644
 --- a/Makefile.objs
 +++ b/Makefile.objs
-@@ -48,7 +48,7 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
- block-obj-y = iov.o cache-utils.o qemu-option.o module.o async.o
- block-obj-y += nbd.o block.o blockjob.o aes.o qemu-config.o
- block-obj-y += thread-pool.o qemu-progress.o qemu-sockets.o uri.o notify.o
+@@ -13,7 +13,7 @@ block-obj-$(CONFIG_POSIX) += aio-posix.o
+ block-obj-$(CONFIG_WIN32) += aio-win32.o
+ block-obj-y += block/
+ block-obj-y += qapi-types.o qapi-visit.o
 -block-obj-y += backup.o
 +block-obj-y += vma-writer.o backup.o
- block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y)
- block-obj-$(CONFIG_POSIX) += event_notifier-posix.o aio-posix.o
- block-obj-$(CONFIG_WIN32) += event_notifier-win32.o aio-win32.o
+ block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
+ block-obj-y += qemu-coroutine-sleep.o
+diff --git a/backup.h b/backup.h
+index c8ba153..406f011 100644
+--- a/backup.h
++++ b/backup.h
+@@ -15,6 +15,7 @@
+ #define QEMU_BACKUP_H
+ #include <uuid/uuid.h>
++#include "block/block.h"
+ #define BACKUP_CLUSTER_BITS 16
+ #define BACKUP_CLUSTER_SIZE (1<<BACKUP_CLUSTER_BITS)
 diff --git a/blockdev.c b/blockdev.c
-index c635d21..f424933 100644
+index c340fde..1cfc780 100644
 --- a/blockdev.c
 +++ b/blockdev.c
 @@ -21,6 +21,7 @@
  #include "trace.h"
- #include "arch_init.h"
+ #include "sysemu/arch_init.h"
  #include "backup.h"
 +#include "vma.h"
  
  static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
  
-@@ -1483,10 +1484,11 @@ char *qmp_backup(const char *backupfile, bool has_format, const char *format,
+@@ -1530,10 +1531,11 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format,
      /* Todo: try to auto-detect format based on file name */
-     format = has_format ? format : "vma";
+     format = has_format ? format : BACKUP_FORMAT_VMA;
  
 -    /* fixme: find driver for specifued format */
      const BackupDriver *driver = NULL;
  
 -    if (!driver) {
-+    if (strcmp(format, "vma") == 0) {
++    if (format == BACKUP_FORMAT_VMA) {
 +        driver = &backup_vma_driver;
 +    } else {
-         error_set(errp, ERROR_CLASS_GENERIC_ERROR,
-                   "no backup driver for format '%s'", format);
+         error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format");
          return NULL;
+     }
 diff --git a/docs/specs/vma_spec.txt b/docs/specs/vma_spec.txt
 new file mode 100644
 index 0000000..052c629
@@ -114,10 +127,10 @@ index 0000000..052c629
 +
 diff --git a/vma-reader.c b/vma-reader.c
 new file mode 100644
-index 0000000..154c96b
+index 0000000..7e81847
 --- /dev/null
 +++ b/vma-reader.c
-@@ -0,0 +1,772 @@
+@@ -0,0 +1,799 @@
 +/*
 + * VMA: Virtual Machine Archive
 + *
@@ -143,12 +156,10 @@ index 0000000..154c96b
 +#include <uuid/uuid.h>
 +
 +#include "qemu-common.h"
-+#include "qemu_socket.h"
-+#include "qemu-coroutine.h"
-+#include "qemu-aio.h"
++#include "qemu/timer.h"
 +#include "qemu/ratelimit.h"
 +#include "vma.h"
-+#include "block.h"
++#include "block/block.h"
 +
 +#define BITS_PER_LONG  (sizeof(unsigned long) * 8)
 +
@@ -171,6 +182,11 @@ index 0000000..154c96b
 +    GList *cdata_list;
 +    guint8 vmstate_stream;
 +    uint32_t vmstate_clusters;
++    /* to show restore percentage if run with -v */
++    time_t start_time;
++    int64_t cluster_count;
++    int64_t clusters_read;
++    int clusters_read_per;
 +};
 +
 +static guint
@@ -487,7 +503,11 @@ index 0000000..154c96b
 +
 +    VmaReader *vmar = g_new0(VmaReader, 1);
 +
-+    vmar->fd = open(filename, O_RDONLY);
++    if (strcmp(filename, "-") == 0) {
++        vmar->fd = dup(0);
++    } else {
++        vmar->fd = open(filename, O_RDONLY);
++    }
 +
 +    if (vmar->fd < 0) {
 +        error_setg(errp, "can't open file %s - %s\n", filename,
@@ -572,6 +592,8 @@ index 0000000..154c96b
 +    vmar->rstate[dev_id].bitmap_size = bitmap_size;
 +    vmar->rstate[dev_id].bitmap = g_new0(unsigned long, bitmap_size);
 +
++    vmar->cluster_count += size/VMA_CLUSTER_SIZE;
++
 +    return 0;
 +}
 +
@@ -638,7 +660,8 @@ index 0000000..154c96b
 +    return 0;
 +}
 +static int restore_extent(VmaReader *vmar, unsigned char *buf,
-+                          int extent_size, int vmstate_fd, Error **errp)
++                          int extent_size, int vmstate_fd,
++                          bool verbose, Error **errp)
 +{
 +    assert(vmar);
 +    assert(buf);
@@ -649,8 +672,8 @@ index 0000000..154c96b
 +
 +    for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) {
 +        uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]);
-+        uint32_t cluster_num = block_info &  0xffffffff;
-+        uint8_t dev_id = (block_info >> 32) &  0xff;
++        uint64_t cluster_num = block_info & 0xffffffff;
++        uint8_t dev_id = (block_info >> 32) & 0xff;
 +        uint16_t mask = block_info >> (32+16);
 +        int64_t max_sector;
 +
@@ -669,7 +692,7 @@ index 0000000..154c96b
 +            }
 +
 +            if (vma_reader_get_bitmap(rstate, cluster_num)) {
-+                error_setg(errp, "found duplicated cluster %d for stream %s",
++                error_setg(errp, "found duplicated cluster %zd for stream %s",
 +                          cluster_num, vmar->devinfo[dev_id].devname);
 +                return -1;
 +            }
@@ -685,6 +708,20 @@ index 0000000..154c96b
 +            vmar->vmstate_clusters++;
 +        }
 +
++        vmar->clusters_read++;
++
++        if (verbose) {
++            time_t duration = time(NULL) - vmar->start_time;
++            int percent = (vmar->clusters_read*100)/vmar->cluster_count;
++            if (percent != vmar->clusters_read_per) {
++                printf("progress %d%% (read %zd bytes, duration %zd sec)\n",
++                       percent, vmar->clusters_read*VMA_CLUSTER_SIZE,
++                       duration);
++                fflush(stdout);
++                vmar->clusters_read_per = percent;
++            }
++        }
++
 +        /* try to write whole clusters to speedup restore */
 +        if (mask == 0xffff) {
 +            if ((start + VMA_CLUSTER_SIZE) > extent_size) {
@@ -749,7 +786,7 @@ index 0000000..154c96b
 +
 +                } else {
 +
-+                    if (rstate->write_zeroes & (end_sector > sector_num)) {
++                    if (rstate->write_zeroes && (end_sector > sector_num)) {
 +                        /* Todo: use bdrv_co_write_zeroes (but that need to
 +                         * be run inside coroutine?)
 +                         */
@@ -775,7 +812,8 @@ index 0000000..154c96b
 +    return 0;
 +}
 +
-+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, Error **errp)
++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
++                       Error **errp)
 +{
 +    assert(vmar);
 +    assert(vmar->head_data);
@@ -786,6 +824,7 @@ index 0000000..154c96b
 +    unsigned char md5sum[16];
 +    VmaHeader *h = (VmaHeader *)vmar->head_data;
 +
++    vmar->start_time = time(NULL);
 +
 +    while (1) {
 +        int bytes = full_read(vmar->fd, buf + buf_pos, sizeof(buf) - buf_pos);
@@ -840,7 +879,8 @@ index 0000000..154c96b
 +            return -1;
 +        }
 +
-+        if (restore_extent(vmar, buf, extent_size, vmstate_fd, errp) < 0) {
++        if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose,
++                           errp) < 0) {
 +            return -1;
 +        }
 +
@@ -892,10 +932,10 @@ index 0000000..154c96b
 +
 diff --git a/vma-writer.c b/vma-writer.c
 new file mode 100644
-index 0000000..02d4447
+index 0000000..761d7ca
 --- /dev/null
 +++ b/vma-writer.c
-@@ -0,0 +1,900 @@
+@@ -0,0 +1,932 @@
 +/*
 + * VMA: Virtual Machine Archive
 + *
@@ -921,12 +961,9 @@ index 0000000..02d4447
 +#include <uuid/uuid.h>
 +
 +#include "qemu-common.h"
-+#include "qemu_socket.h"
-+#include "qemu-coroutine.h"
-+#include "qemu-aio.h"
-+#include "qemu/ratelimit.h"
 +#include "vma.h"
-+#include "block.h"
++#include "block/block.h"
++#include "monitor/monitor.h"
 +
 +#define DEBUG_VMA 0
 +
@@ -937,8 +974,8 @@ index 0000000..02d4447
 +
 +typedef struct VmaAIOCB VmaAIOCB;
 +struct VmaAIOCB {
-+    VmaWriter *vmaw;
 +    unsigned char buffer[VMA_MAX_EXTENT_SIZE];
++    VmaWriter *vmaw;
 +    size_t bytes;
 +    Coroutine *co;
 +};
@@ -958,15 +995,13 @@ index 0000000..02d4447
 +    int outbuf_count; /* in VMA_BLOCKS */
 +    uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT];
 +
-+    VmaAIOCB aiocbs[WRITE_BUFFERS];
++    VmaAIOCB *aiocbs[WRITE_BUFFERS];
 +    CoQueue wqueue;
 +
 +    GChecksum *md5csum;
 +    CoMutex writer_lock;
 +    CoMutex flush_lock;
 +    Coroutine *co_writer;
-+    RateLimit limit;
-+    uint64_t delay_ns;
 +
 +    /* drive informations */
 +    VmaStreamInfo stream_info[256];
@@ -1117,12 +1152,17 @@ index 0000000..02d4447
 +{
 +    VmaWriter *vmaw = opaque;
 +
-+    qemu_aio_set_fd_handler(vmaw->fd, NULL, NULL, NULL, NULL);
-+
 +    DPRINTF("vma_co_continue_write\n");
 +    qemu_coroutine_enter(vmaw->co_writer, NULL);
 +}
 +
++static int vma_co_write_finished(void *opaque)
++{
++    VmaWriter *vmaw = opaque;
++
++    return (vmaw->co_writer != 0);
++}
++
 +static ssize_t coroutine_fn
 +vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes)
 +{
@@ -1134,6 +1174,17 @@ index 0000000..02d4447
 +
 +    DPRINTF("vma_co_write enter %zd\n", bytes);
 +
++    assert(vmaw->co_writer == NULL);
++
++    vmaw->co_writer = qemu_coroutine_self();
++
++    qemu_aio_set_fd_handler(vmaw->fd, NULL, vma_co_continue_write,
++                            vma_co_write_finished, vmaw);
++
++    DPRINTF("vma_co_write wait until writable\n");
++    qemu_coroutine_yield();
++    DPRINTF("vma_co_write starting %zd\n", bytes);
++
 +    while (done < bytes) {
 +        ret = write(vmaw->fd, buf + done, bytes - done);
 +        if (ret > 0) {
@@ -1142,11 +1193,6 @@ index 0000000..02d4447
 +        } else if (ret < 0) {
 +            if (errno == EAGAIN || errno == EWOULDBLOCK) {
 +                DPRINTF("vma_co_write yield %zd\n", done);
-+
-+                vmaw->co_writer = qemu_coroutine_self();
-+                qemu_aio_set_fd_handler(vmaw->fd, NULL, vma_co_continue_write,
-+                                        NULL, vmaw);
-+
 +                qemu_coroutine_yield();
 +                DPRINTF("vma_co_write restart %zd\n", done);
 +            } else {
@@ -1160,6 +1206,10 @@ index 0000000..02d4447
 +        }
 +    }
 +
++    qemu_aio_set_fd_handler(vmaw->fd, NULL, NULL, NULL, NULL);
++
++    vmaw->co_writer = NULL;
++
 +    qemu_co_mutex_unlock(&vmaw->writer_lock);
 +
 +    DPRINTF("vma_co_write leave %zd\n", done);
@@ -1198,10 +1248,10 @@ index 0000000..02d4447
 +        int i;
 +        VmaAIOCB *cb = NULL;
 +        for (i = 0; i < WRITE_BUFFERS; i++) {
-+            if (vmaw->aiocbs[i].bytes) {
-+                cb = &vmaw->aiocbs[i];
++            if (vmaw->aiocbs[i]->bytes) {
++                cb = vmaw->aiocbs[i];
 +                DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i,
-+                        vmaw->aiocbs[i].bytes);
++                        vmaw->aiocbs[i]->bytes);
 +                break;
 +            }
 +        }
@@ -1231,8 +1281,8 @@ index 0000000..02d4447
 +    while (!cb) {
 +        int i;
 +        for (i = 0; i < WRITE_BUFFERS; i++) {
-+            if (!vmaw->aiocbs[i].bytes) {
-+                cb = &vmaw->aiocbs[i];
++            if (!vmaw->aiocbs[i]->bytes) {
++                cb = vmaw->aiocbs[i];
 +                break;
 +            }
 +        }
@@ -1254,8 +1304,7 @@ index 0000000..02d4447
 +    return bytes;
 +}
 +
-+VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, int64_t speed,
-+                             Error **errp)
++VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp)
 +{
 +    const char *p;
 +
@@ -1279,10 +1328,35 @@ index 0000000..02d4447
 +            goto err;
 +        }
 +        vmaw->fd = fileno(vmaw->cmd);
-+        socket_set_nonblock(vmaw->fd);
++
++        /* try to use O_NONBLOCK and O_DIRECT */
++        fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
++        fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_DIRECT);
 +
 +    } else {
-+        vmaw->fd = open(filename, O_NONBLOCK|O_WRONLY|O_CREAT|O_EXCL, 0644);
++        struct stat st;
++        int oflags;
++        const char *tmp_id_str;
++
++        if ((stat(filename, &st) == 0) && S_ISFIFO(st.st_mode)) {
++            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY;
++            vmaw->fd = qemu_open(filename, oflags, 0644);
++        } else if (strstart(filename, "/dev/fdset/", &tmp_id_str)) {
++            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY;
++            vmaw->fd = qemu_open(filename, oflags, 0644);
++        } else if (strstart(filename, "/dev/fdname/", &tmp_id_str)) {
++            vmaw->fd = monitor_get_fd(cur_mon, tmp_id_str, errp);
++            if (vmaw->fd < 0) {
++                goto err;
++            }
++            /* try to use O_NONBLOCK and O_DIRECT */
++            fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK);
++            fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_DIRECT);
++        } else  {
++            oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_CREAT|O_EXCL;
++            vmaw->fd = qemu_open(filename, oflags, 0644);
++        }
++
 +        if (vmaw->fd < 0) {
 +            error_setg(errp, "can't open file %s - %s\n", filename,
 +                       strerror(errno));
@@ -1290,6 +1364,13 @@ index 0000000..02d4447
 +        }
 +    }
 +
++    /* we use O_DIRECT, so we need to align IO buffers */
++    int i;
++    for (i = 0; i < WRITE_BUFFERS; i++) {
++        vmaw->aiocbs[i] = qemu_memalign(512, sizeof(VmaAIOCB));
++        memset(vmaw->aiocbs[i], 0, sizeof(VmaAIOCB));
++    }
++
 +    vmaw->outbuf_count = 0;
 +    vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE;
 +
@@ -1301,12 +1382,6 @@ index 0000000..02d4447
 +
 +    uuid_copy(vmaw->uuid, uuid);
 +
-+    if (speed <= 0) {
-+        speed = 10*1024*1024*1024LLU; /* default 10GB/s */
-+    }
-+
-+    ratelimit_set_speed(&vmaw->limit, speed, 100000000ULL /* 0.1 sec */);
-+
 +    return vmaw;
 +
 +err:
@@ -1532,22 +1607,22 @@ index 0000000..02d4447
 +
 +static int vma_writer_get_buffer(VmaWriter *vmaw)
 +{
++    int ret = 0;
++
++    qemu_co_mutex_lock(&vmaw->flush_lock);
 +
 +    /* wait until buffer is available */
 +    while (vmaw->outbuf_count >= (VMA_BLOCKS_PER_EXTENT - 1)) {
-+        int res = 0;
-+
-+        qemu_co_mutex_lock(&vmaw->flush_lock);
-+        res = vma_writer_flush(vmaw);
-+        qemu_co_mutex_unlock(&vmaw->flush_lock);
-+
-+        if (res < 0) {
++        ret = vma_writer_flush(vmaw);
++        if (ret < 0) {
 +            vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed");
-+            return -1;
++            break;
 +        }
 +    }
 +
-+    return 0;
++    qemu_co_mutex_unlock(&vmaw->flush_lock);
++
++    return ret;
 +}
 +
 +
@@ -1601,27 +1676,21 @@ index 0000000..02d4447
 +        return -1;
 +    }
 +
-+    DPRINTF("VMA WRITE %zd\n", cluster_num);
++    DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num);
 +
 +    int i;
 +    int bit = 1;
 +    uint16_t mask = 0;
 +    for (i = 0; i < 16; i++) {
 +        unsigned char *vmablock = buf + (i*VMA_BLOCK_SIZE);
-+        if (buffer_is_zero(vmablock, VMA_BLOCK_SIZE)) {
-+            DPRINTF("VMA WRITE %zd ZERO BLOCK %d\n", cluster_num, i);
-+            vmaw->stream_info[dev_id].zero_bytes += VMA_BLOCK_SIZE;
-+            *zero_bytes += VMA_BLOCK_SIZE;
-+        } else {
++        if (!buffer_is_zero(vmablock, VMA_BLOCK_SIZE)) {
 +            mask |= bit;
 +            memcpy(vmaw->outbuf + vmaw->outbuf_pos, vmablock, VMA_BLOCK_SIZE);
 +            vmaw->outbuf_pos += VMA_BLOCK_SIZE;
-+
-+            vmaw->delay_ns = ratelimit_calculate_delay(&vmaw->limit,
-+                                                       VMA_BLOCK_SIZE);
-+            if (vmaw->delay_ns) {
-+                co_sleep_ns(rt_clock, vmaw->delay_ns);
-+            }
++        } else {
++            DPRINTF("VMA WRITE %zd ZERO BLOCK %d\n", cluster_num, i);
++            vmaw->stream_info[dev_id].zero_bytes += VMA_BLOCK_SIZE;
++            *zero_bytes += VMA_BLOCK_SIZE;
 +        }
 +
 +        bit = bit << 1;
@@ -1630,7 +1699,7 @@ index 0000000..02d4447
 +    uint64_t block_info = ((uint64_t)mask) << (32+16);
 +    block_info |= ((uint64_t)dev_id) << 32;
 +    block_info |= (cluster_num & 0xffffffff);
-+    vmaw->outbuf_block_info[vmaw->outbuf_count] =  block_info;
++    vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info;
 +
 +    DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info);
 +
@@ -1730,6 +1799,10 @@ index 0000000..02d4447
 +        g_checksum_free(vmaw->md5csum);
 +    }
 +
++    for (i = 0; i < WRITE_BUFFERS; i++) {
++        free(vmaw->aiocbs[i]);
++    }
++
 +    g_free(vmaw);
 +}
 +
@@ -1780,10 +1853,9 @@ index 0000000..02d4447
 +    return vma_writer_add_config(vmaw, name, data, data_len);
 +}
 +
-+static void *vma_open_cb(const char *filename, uuid_t uuid, int64_t speed,
-+                         Error **errp)
++static void *vma_open_cb(const char *filename, uuid_t uuid, Error **errp)
 +{
-+    return vma_writer_create(filename, uuid, speed, errp);
++    return vma_writer_create(filename, uuid, errp);
 +}
 +
 +const BackupDriver backup_vma_driver = {
@@ -1798,10 +1870,10 @@ index 0000000..02d4447
 +
 diff --git a/vma.c b/vma.c
 new file mode 100644
-index 0000000..69af80c
+index 0000000..b2e276c
 --- /dev/null
 +++ b/vma.c
-@@ -0,0 +1,550 @@
+@@ -0,0 +1,559 @@
 +/*
 + * VMA: Virtual Machine Archive
 + *
@@ -1826,13 +1898,9 @@ index 0000000..69af80c
 +#include <glib.h>
 +
 +#include "qemu-common.h"
-+#include "qemu-option.h"
-+#include "qemu-error.h"
-+#include "osdep.h"
-+#include "sysemu.h"
-+#include "block_int.h"
-+#include <stdio.h>
++#include "qemu/error-report.h"
 +#include "vma.h"
++#include "block/block.h"
 +
 +static void help(void)
 +{
@@ -1874,8 +1942,6 @@ index 0000000..69af80c
 +
 +    VmaHeader *head = vma_reader_get_header(vmar);
 +
-+    printf("CTIME: %s", ctime(&head->ctime));
-+
 +    GList *l = vma_reader_get_config_data(vmar);
 +    while (l && l->data) {
 +        VmaConfigData *cdata = (VmaConfigData *)l->data;
@@ -1896,6 +1962,9 @@ index 0000000..69af80c
 +            }
 +        }
 +    }
++    /* ctime is the last entry we print */
++    printf("CTIME: %s", ctime(&head->ctime));
++    fflush(stdout);
 +}
 +
 +static int list_content(int argc, char **argv)
@@ -1947,12 +2016,13 @@ index 0000000..69af80c
 +static int extract_content(int argc, char **argv)
 +{
 +    int c, ret = 0;
++    int verbose = 0;
 +    const char *filename;
 +    const char *dirname;
-+    int readmap = 0;
++    const char *readmap = NULL;
 +
 +    for (;;) {
-+        c = getopt(argc, argv, "hr");
++        c = getopt(argc, argv, "hvr:");
 +        if (c == -1) {
 +            break;
 +        }
@@ -1962,7 +2032,10 @@ index 0000000..69af80c
 +            help();
 +            break;
 +        case 'r':
-+            readmap = 1;
++            readmap = optarg;
++            break;
++        case 'v':
++            verbose = 1;
 +            break;
 +        default:
 +            help();
@@ -1996,7 +2069,7 @@ index 0000000..69af80c
 +        GError *err = NULL;
 +        if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len,
 +                                 &err)) {
-+            g_error("Unable to write file: %s", err->message);
++            g_error("unable to write file: %s", err->message);
 +        }
 +    }
 +
@@ -2005,9 +2078,14 @@ index 0000000..69af80c
 +    if (readmap) {
 +        print_content(vmar);
 +
++        FILE *map = fopen(readmap, "r");
++        if (!map) {
++            g_error("unable to open fifo %s - %s", readmap, strerror(errno));
++        }
++
 +        while (1) {
 +            char inbuf[8192];
-+            char *line = fgets(inbuf, sizeof(inbuf), stdin);
++            char *line = fgets(inbuf, sizeof(inbuf), map);
 +            if (!line || line[0] == '\0' || !strcmp(line, "done\n")) {
 +                break;
 +            }
@@ -2027,7 +2105,7 @@ index 0000000..69af80c
 +            } else if (line[0] == '1' && line[1] == ':') {
 +                path = inbuf + 2;
 +                write_zero = true;
-+           } else {
++            } else {
 +                g_error("read map failed - parse error ('%s')", inbuf);
 +            }
 +
@@ -2038,8 +2116,6 @@ index 0000000..69af80c
 +                        inbuf);
 +            }
 +
-+            printf("TEST %s %s\n", path, devname);
-+
 +            RestoreMap *map = g_new0(RestoreMap, 1);
 +            map->devname = g_strdup(devname);
 +            map->path = g_strdup(path);
@@ -2083,9 +2159,11 @@ index 0000000..69af80c
 +                                        dirname, di->devname);
 +                printf("DEVINFO %s %zd\n", devfn, di->size);
 +
-+                if (bdrv_img_create(devfn, "raw", NULL, NULL, NULL,
-+                                    di->size, flags)) {
-+                    g_error("can't create file %s", devfn);
++                bdrv_img_create(devfn, "raw", NULL, NULL, NULL, di->size,
++                                flags, &errp);
++                if (error_is_set(&errp)) {
++                    g_error("can't create file %s: %s", devfn,
++                            error_get_pretty(errp));
 +                }
 +
 +                /* Note: we created an empty file above, so there is no
@@ -2094,8 +2172,8 @@ index 0000000..69af80c
 +                write_zero = false;
 +            }
 +
-+            BlockDriverState *bs = NULL;
-+            if (bdrv_file_open(&bs, devfn, flags)) {
++            BlockDriverState *bs = bdrv_new(di->devname);
++            if (bdrv_open(bs, devfn, flags, NULL)) {
 +                g_error("can't open file %s", devfn);
 +            }
 +            if (vma_reader_register_bs(vmar, i, bs, write_zero, &errp) < 0) {
@@ -2108,7 +2186,7 @@ index 0000000..69af80c
 +        }
 +    }
 +
-+    if (vma_reader_restore(vmar, vmstate_fd, &errp) < 0) {
++    if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) {
 +        g_error("restore failed - %s", error_get_pretty(errp));
 +    }
 +
@@ -2209,7 +2287,7 @@ index 0000000..69af80c
 +    uuid_generate(uuid);
 +
 +    Error *local_err = NULL;
-+    VmaWriter *vmaw = vma_writer_create(archivename, uuid, 0, &local_err);
++    VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err);
 +
 +    if (vmaw == NULL) {
 +        g_error("%s", error_get_pretty(local_err));
@@ -2259,9 +2337,12 @@ index 0000000..69af80c
 +        bcb->vmaw = vmaw;
 +        bcb->dev_id = dev_id;
 +
-+        if (backup_job_start(bs, backup_dump_cb, backup_complete_cb, bcb) < 0) {
++        if (backup_job_create(bs, backup_dump_cb, backup_complete_cb,
++                              bcb, 0) < 0) {
 +            unlink(archivename);
 +            g_error("backup_job_start failed");
++        } else {
++            backup_job_start(bs, false);
 +        }
 +    }
 +
@@ -2354,7 +2435,7 @@ index 0000000..69af80c
 +}
 diff --git a/vma.h b/vma.h
 new file mode 100644
-index 0000000..10800a1
+index 0000000..76d0dc8
 --- /dev/null
 +++ b/vma.h
 @@ -0,0 +1,145 @@
@@ -2473,8 +2554,7 @@ index 0000000..10800a1
 +
 +extern const BackupDriver backup_vma_driver;
 +
-+VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, int64_t speed,
-+                             Error **errp);
++VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp);
 +int vma_writer_close(VmaWriter *vmaw, Error **errp);
 +void vma_writer_destroy(VmaWriter *vmaw);
 +int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data,
@@ -2500,7 +2580,8 @@ index 0000000..10800a1
 +int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id,
 +                           BlockDriverState *bs, bool write_zeroes,
 +                           Error **errp);
-+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, Error **errp);
++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose,
++                       Error **errp);
 +
 +#endif /* BACKUP_VMA_H */
 --