-From de76a5c6306d782733217668f3f55c92aa2be36a Mon Sep 17 00:00:00 2001
+From aa580be154923056a52fdeec429f7e80f04b456e Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
-Date: Wed, 20 Feb 2013 10:26:23 +0100
-Subject: [PATCH v4 0/6] Efficient VM backup for qemu
+Date: Thu, 21 Feb 2013 12:22:41 +0100
+Subject: [PATCH v5 0/6] Efficient VM backup for qemu
This series provides a way to efficiently backup VMs.
* add documentation for 'devlist' parameter.
* rename backup_cancel to backup-cancel
+Changes since v4:
+
+* vma create: write verbose output to stderr (not stdout)
+* backup.c: use rwlock instead of sleep, remove backup_in_progress_count
+* BackupDriver: remove '_cb' suffix
+* include cleanups suggested by Stefan
+* use CHAR_BIT instead of magic number '8'
+* implemented bdrv_co_is_allocated_above() - disabled for now because
+ it actually slows down backup speed by 15%
+* extend regression tests to test unallocated regions
Dietmar Maurer (6):
add documenation for new backup framework
Makefile | 3 +-
Makefile.objs | 1 +
- backup.c | 338 +++++++++++++++++
+ backup.c | 355 +++++++++++++++++
backup.h | 45 +++
block.c | 71 ++++-
blockdev.c | 617 ++++++++++++++++++++++++++++++
qapi-schema.json | 98 +++++
qmp-commands.hx | 27 ++
tests/Makefile | 11 +-
- tests/backup-test.c | 517 +++++++++++++++++++++++++
+ tests/backup-test.c | 529 ++++++++++++++++++++++++++
vma-reader.c | 799 +++++++++++++++++++++++++++++++++++++++
- vma-writer.c | 932 ++++++++++++++++++++++++++++++++++++++++++++++
- vma.c | 559 +++++++++++++++++++++++++++
+ vma-writer.c | 940 ++++++++++++++++++++++++++++++++++++++++++++++
+ vma.c | 561 +++++++++++++++++++++++++++
vma.h | 145 +++++++
- 22 files changed, 4411 insertions(+), 9 deletions(-)
+ 22 files changed, 4450 insertions(+), 9 deletions(-)
create mode 100644 backup.c
create mode 100644 backup.h
create mode 100644 docs/backup.txt
From 2f0dcd89a0de8b656d33ce6997c09879bd287af7 Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Tue, 13 Nov 2012 09:24:50 +0100
-Subject: [PATCH v4 1/6] add documenation for new backup framework
+Subject: [PATCH v5 1/6] add documenation for new backup framework
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
-From 940afda26b17f3d5776e4809e6dfce5cee44c102 Mon Sep 17 00:00:00 2001
+From 1d0c6dfc9616c0dd17986cab6744c10cb748de1e Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Tue, 13 Nov 2012 10:03:52 +0100
-Subject: [PATCH v4 2/6] add basic backup support to block driver
+Subject: [PATCH v5 2/6] add basic backup support to block driver
Function backup_job_create() creates a block job to backup a block device.
The coroutine is started with backup_job_start().
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
---
Makefile.objs | 1 +
- backup.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++
- backup.h | 32 +++++
+ backup.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++
+ backup.h | 30 ++++
block.c | 71 +++++++++-
include/block/block.h | 2 +
include/block/blockjob.h | 10 ++
- 6 files changed, 448 insertions(+), 6 deletions(-)
+ 6 files changed, 463 insertions(+), 6 deletions(-)
create mode 100644 backup.c
create mode 100644 backup.h
block-obj-y += qemu-coroutine-sleep.o
diff --git a/backup.c b/backup.c
new file mode 100644
-index 0000000..c9576d5
+index 0000000..8955e1a
--- /dev/null
+++ b/backup.c
-@@ -0,0 +1,338 @@
+@@ -0,0 +1,355 @@
+/*
+ * QEMU backup
+ *
+
+#define DEBUG_BACKUP 0
+
++#define USE_ALLOCATION_CHECK 0
++
+#define DPRINTF(fmt, ...) \
+ do { if (DEBUG_BACKUP) { printf("backup: " fmt, ## __VA_ARGS__); } } \
+ while (0)
+typedef struct BackupBlockJob {
+ BlockJob common;
+ RateLimit limit;
++ CoRwlock rwlock;
+ uint64_t sectors_read;
+ unsigned long *bitmap;
+ int bitmap_size;
+ void *opaque;
+} BackupBlockJob;
+
-+static int backup_get_bitmap(BackupBlockJob *job, int64_t cluster_num)
++static bool backup_get_bitmap(BackupBlockJob *job, int64_t cluster_num)
+{
+ assert(job);
+ assert(job->bitmap);
+}
+
+static void backup_set_bitmap(BackupBlockJob *job, int64_t cluster_num,
-+ int dirty)
++ bool dirty)
+{
+ assert(job);
+ assert(job->bitmap);
+ bit = cluster_num % BITS_PER_LONG;
+ val = job->bitmap[idx];
+ if (dirty) {
-+ if (!(val & (1UL << bit))) {
-+ val |= 1UL << bit;
-+ }
++ val |= 1UL << bit;
+ } else {
-+ if (val & (1UL << bit)) {
-+ val &= ~(1UL << bit);
-+ }
++ val &= ~(1UL << bit);
+ }
+ job->bitmap[idx] = val;
+}
+
-+static int backup_in_progress_count;
-+
+static int coroutine_fn backup_do_cow(BlockDriverState *bs,
+ int64_t sector_num, int nb_sectors)
+{
+ void *bounce_buffer = NULL;
+ int ret = 0;
+
-+ backup_in_progress_count++;
++ qemu_co_rwlock_rdlock(&job->rwlock);
+
+ int64_t start, end;
+
+ end = (sector_num + nb_sectors + BACKUP_BLOCKS_PER_CLUSTER - 1) /
+ BACKUP_BLOCKS_PER_CLUSTER;
+
-+ DPRINTF("brdv_co_backup_cow enter %s C%zd %zd %d\n",
++ DPRINTF("brdv_co_backup_cow enter %s C%" PRId64 " %" PRId64 " %d\n",
+ bdrv_get_device_name(bs), start, sector_num, nb_sectors);
+
+ for (; start < end; start++) {
++ bool zero = 0;
++
+ if (backup_get_bitmap(job, start)) {
-+ DPRINTF("brdv_co_backup_cow skip C%zd\n", start);
++ DPRINTF("brdv_co_backup_cow skip C%" PRId64 "\n", start);
+ continue; /* already copied */
+ }
+
+ /* immediately set bitmap (avoid coroutine race) */
+ backup_set_bitmap(job, start, 1);
+
-+ DPRINTF("brdv_co_backup_cow C%zd\n", start);
++ DPRINTF("brdv_co_backup_cow C%" PRId64 "\n", start);
+
+ if (!bounce_buffer) {
+ iov.iov_len = BACKUP_CLUSTER_SIZE;
+ qemu_iovec_init_external(&bounce_qiov, &iov, 1);
+ }
+
-+ ret = drv->bdrv_co_readv(bs, start * BACKUP_BLOCKS_PER_CLUSTER,
-+ BACKUP_BLOCKS_PER_CLUSTER,
-+ &bounce_qiov);
-+
-+ job->sectors_read += BACKUP_BLOCKS_PER_CLUSTER;
-+
++#if USE_ALLOCATION_CHECK
++ int n = 0;
++ ret = bdrv_co_is_allocated_above(bs, NULL,
++ start * BACKUP_BLOCKS_PER_CLUSTER,
++ BACKUP_BLOCKS_PER_CLUSTER, &n);
+ if (ret < 0) {
-+ DPRINTF("brdv_co_backup_cow bdrv_read C%zd failed\n", start);
++ DPRINTF("brdv_co_backup_cow is_allocated C%" PRId64 " failed\n",
++ start);
+ goto out;
+ }
+
-+ ret = job->backup_dump_cb(job->opaque, bs, start, bounce_buffer);
++ zero = (ret == 0) && (n == BACKUP_BLOCKS_PER_CLUSTER);
++
++ if (!zero) {
++#endif
++ ret = drv->bdrv_co_readv(bs, start * BACKUP_BLOCKS_PER_CLUSTER,
++ BACKUP_BLOCKS_PER_CLUSTER,
++ &bounce_qiov);
++ if (ret < 0) {
++ DPRINTF("brdv_co_backup_cow bdrv_read C%" PRId64 " failed\n",
++ start);
++ goto out;
++ }
++#if USE_ALLOCATION_CHECK
++ }
++#endif
++ job->sectors_read += BACKUP_BLOCKS_PER_CLUSTER;
++
++ ret = job->backup_dump_cb(job->opaque, bs, start,
++ zero ? NULL : bounce_buffer);
+ if (ret < 0) {
-+ DPRINTF("brdv_co_backup_cow dump_cluster_cb C%zd failed\n", start);
++ DPRINTF("brdv_co_backup_cow dump_cluster_cb C%" PRId64 " failed\n",
++ start);
+ goto out;
+ }
+
-+ DPRINTF("brdv_co_backup_cow done C%zd\n", start);
++ DPRINTF("brdv_co_backup_cow done C%" PRId64 "\n", start);
+ }
+
+out:
+ qemu_vfree(bounce_buffer);
+ }
+
-+ backup_in_progress_count--;
++ qemu_co_rwlock_unlock(&job->rwlock);
+
+ return ret;
+}
+ end = (bs->total_sectors + BACKUP_BLOCKS_PER_CLUSTER - 1) /
+ BACKUP_BLOCKS_PER_CLUSTER;
+
-+ DPRINTF("backup_run start %s %zd %zd\n", bdrv_get_device_name(bs),
-+ start, end);
++ DPRINTF("backup_run start %s %" PRId64 " %" PRId64 "\n",
++ bdrv_get_device_name(bs), start, end);
+
+ int ret = 0;
+
+
+ /* we need to yield so that qemu_aio_flush() returns.
+ * (without, VM does not reboot)
-+ * Note: use 1000 instead of 0 (0 prioritize this task too much)
++ * Note: use 1000 instead of 0 (0 prioritize this task too much)
+ */
+ if (job->common.speed) {
+ uint64_t delay_ns = ratelimit_calculate_delay(
+ continue; /* already copied */
+ }
+
-+ DPRINTF("backup_run loop C%zd\n", start);
++ DPRINTF("backup_run loop C%" PRId64 "\n", start);
+
+ /**
+ * This triggers a cluster copy
+ job->common.offset += BACKUP_CLUSTER_SIZE;
+ }
+
-+ while (backup_in_progress_count > 0) {
-+ DPRINTF("backup_run backup_in_progress_count != 0 (%d)",
-+ backup_in_progress_count);
-+ block_job_sleep_ns(&job->common, rt_clock, 10000);
-+
-+ }
++ /* wait until pending backup_do_cow()calls have completed */
++ qemu_co_rwlock_wrlock(&job->rwlock);
++ qemu_co_rwlock_unlock(&job->rwlock);
+
+ DPRINTF("backup_run complete %d\n", ret);
+ block_job_completed(&job->common, ret);
+ BackupBlockJob *job = block_job_create(&backup_job_type, bs, speed,
+ backup_job_cleanup_cb, bs, &errp);
+
++ qemu_co_rwlock_init(&job->rwlock);
++
+ job->common.cluster_size = BACKUP_CLUSTER_SIZE;
+
+ bitmap_size = bs->total_sectors +
+}
diff --git a/backup.h b/backup.h
new file mode 100644
-index 0000000..d9395bc
+index 0000000..9b1ea1c
--- /dev/null
+++ b/backup.h
-@@ -0,0 +1,32 @@
+@@ -0,0 +1,30 @@
+/*
+ * QEMU backup related definitions
+ *
+#ifndef QEMU_BACKUP_H
+#define QEMU_BACKUP_H
+
-+#include <uuid/uuid.h>
-+
+#define BACKUP_CLUSTER_BITS 16
+#define BACKUP_CLUSTER_SIZE (1<<BACKUP_CLUSTER_BITS)
+#define BACKUP_BLOCKS_PER_CLUSTER (BACKUP_CLUSTER_SIZE/BDRV_SECTOR_SIZE)
-From 982a8ac63f778110ad89b4dc415011166c9dcd8e Mon Sep 17 00:00:00 2001
+From cf9cc8878d3246069da3fdf7ec865b7b983487fe Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Tue, 13 Nov 2012 11:27:56 +0100
-Subject: [PATCH v4 3/6] add backup related monitor commands
+Subject: [PATCH v5 3/6] add backup related monitor commands
We use a generic BackupDriver struct to encapsulate all archive format
related function.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
---
- backup.h | 12 ++
+ backup.h | 15 ++
blockdev.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
hmp-commands.hx | 31 ++++
hmp.c | 63 ++++++++
monitor.c | 7 +
qapi-schema.json | 95 ++++++++++++
qmp-commands.hx | 27 ++++
- 8 files changed, 661 insertions(+), 0 deletions(-)
+ 8 files changed, 664 insertions(+), 0 deletions(-)
diff --git a/backup.h b/backup.h
-index d9395bc..c8ba153 100644
+index 9b1ea1c..22598a6 100644
--- a/backup.h
+++ b/backup.h
-@@ -29,4 +29,16 @@ int backup_job_create(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb,
+@@ -14,6 +14,9 @@
+ #ifndef QEMU_BACKUP_H
+ #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)
+ #define BACKUP_BLOCKS_PER_CLUSTER (BACKUP_CLUSTER_SIZE/BDRV_SECTOR_SIZE)
+@@ -27,4 +30,16 @@ int backup_job_create(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb,
BlockDriverCompletionFunc *backup_complete_cb,
void *opaque, int64_t speed);
+typedef struct BackupDriver {
+ const char *format;
-+ void *(*open_cb)(const char *filename, uuid_t uuid, Error **errp);
-+ int (*close_cb)(void *opaque, Error **errp);
-+ int (*register_config_cb)(void *opaque, const char *name, gpointer data,
++ void *(*open)(const char *filename, uuid_t uuid, Error **errp);
++ int (*close)(void *opaque, Error **errp);
++ int (*register_config)(void *opaque, const char *name, gpointer data,
+ size_t data_len);
-+ int (*register_stream_cb)(void *opaque, const char *devname, size_t size);
-+ int (*dump_cb)(void *opaque, uint8_t dev_id, int64_t cluster_num,
++ int (*register_stream)(void *opaque, const char *devname, size_t size);
++ int (*dump)(void *opaque, uint8_t dev_id, int64_t cluster_num,
+ unsigned char *buf, size_t *zero_bytes);
-+ int (*complete_cb)(void *opaque, uint8_t dev_id, int ret);
++ int (*complete)(void *opaque, uint8_t dev_id, int ret);
+} BackupDriver;
+
#endif /* QEMU_BACKUP_H */
diff --git a/blockdev.c b/blockdev.c
-index 63e6f1e..c340fde 100644
+index 63e6f1e..84f598d 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -20,6 +20,7 @@
+
+ assert(backup_state.driver);
+ assert(backup_state.writer);
-+ assert(backup_state.driver->dump_cb);
++ assert(backup_state.driver->dump);
+
+ size_t zero_bytes = 0;
-+ int bytes = backup_state.driver->dump_cb(backup_state.writer,
-+ bcb->dev_id, cluster_num,
-+ buf, &zero_bytes);
++ int bytes = backup_state.driver->dump(backup_state.writer,
++ bcb->dev_id, cluster_num,
++ buf, &zero_bytes);
+
+ if (bytes > 0) {
+ bcb->transferred += bytes;
+ if (backup_state.writer && backup_state.driver) {
+ backup_state.end_time = time(NULL);
+ Error *local_err = NULL;
-+ backup_state.driver->close_cb(backup_state.writer, &local_err);
++ backup_state.driver->close(backup_state.writer, &local_err);
+ error_propagate(&backup_state.error, local_err);
+ backup_state.writer = NULL;
+ }
+
+ assert(backup_state.driver);
+ assert(backup_state.writer);
-+ assert(backup_state.driver->complete_cb);
-+ assert(backup_state.driver->close_cb);
++ assert(backup_state.driver->complete);
++ assert(backup_state.driver->close);
+
+ bcb->completed = true;
+
-+ backup_state.driver->complete_cb(backup_state.writer, bcb->dev_id, ret);
++ backup_state.driver->complete(backup_state.writer, bcb->dev_id, ret);
+
+ if (!backup_state.cancel) {
+ backup_run_next_job();
+
+ uuid_generate(uuid);
+
-+ writer = driver->open_cb(backup_file, uuid, &local_err);
++ writer = driver->open(backup_file, uuid, &local_err);
+ if (!writer) {
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+
+ int64_t size = bdrv_getlength(bcb->bs);
+ const char *devname = bdrv_get_device_name(bcb->bs);
-+ bcb->dev_id = driver->register_stream_cb(writer, devname, size);
++ bcb->dev_id = driver->register_stream(writer, devname, size);
+ if (bcb->dev_id <= 0) {
+ error_set(errp, ERROR_CLASS_GENERIC_ERROR,
+ "register_stream failed");
+ }
+
+ const char *basename = g_path_get_basename(config_file);
-+ if (driver->register_config_cb(writer, basename, cdata, clen) < 0) {
++ if (driver->register_config(writer, basename, cdata, clen) < 0) {
+ error_setg(errp, "register_config failed");
+ g_free(cdata);
+ goto err;
+ unlink(backup_file);
+ if (driver) {
+ Error *err = NULL;
-+ driver->close_cb(writer, &err);
++ driver->close(writer, &err);
+ }
+ }
+
-From 5476ae43806488e74cd293bbaa17f130aa53d402 Mon Sep 17 00:00:00 2001
+From ec5c25c57d35ea662f70382a074620ef2abd0627 Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Tue, 13 Nov 2012 11:11:38 +0100
-Subject: [PATCH v4 4/6] introduce new vma archive format
+Subject: [PATCH v5 4/6] introduce new vma archive format
This is a very simple archive format, see docs/specs/vma_spec.txt
---
Makefile | 3 +-
Makefile.objs | 2 +-
- backup.h | 1 +
blockdev.c | 6 +-
docs/specs/vma_spec.txt | 24 ++
vma-reader.c | 799 ++++++++++++++++++++++++++++++++++++++++
- vma-writer.c | 932 +++++++++++++++++++++++++++++++++++++++++++++++
- vma.c | 559 ++++++++++++++++++++++++++++
+ vma-writer.c | 940 +++++++++++++++++++++++++++++++++++++++++++++++
+ vma.c | 561 ++++++++++++++++++++++++++++
vma.h | 145 ++++++++
- 9 files changed, 2467 insertions(+), 4 deletions(-)
+ 8 files changed, 2476 insertions(+), 4 deletions(-)
create mode 100644 docs/specs/vma_spec.txt
create mode 100644 vma-reader.c
create mode 100644 vma-writer.c
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 c340fde..1cfc780 100644
+index 84f598d..683f7da 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -21,6 +21,7 @@
}
diff --git a/docs/specs/vma_spec.txt b/docs/specs/vma_spec.txt
new file mode 100644
-index 0000000..052c629
+index 0000000..9b715f2
--- /dev/null
+++ b/docs/specs/vma_spec.txt
@@ -0,0 +1,24 @@
+cluster in the header to store the following information:
+
+* 1 byte dev_id (to identity the drive)
++* 1 byte not used (reserved)
+* 2 bytes zero indicator (mark zero regions (16x4096))
+* 4 bytes cluster number
-+* 1 byte not used (reserved)
+
+We only store non-zero blocks (such block is 4096 bytes).
+
+
diff --git a/vma-reader.c b/vma-reader.c
new file mode 100644
-index 0000000..7e81847
+index 0000000..6d5a9ba
--- /dev/null
+++ b/vma-reader.c
@@ -0,0 +1,799 @@
+#include "vma.h"
+#include "block/block.h"
+
-+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
++#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT)
+
+static unsigned char zero_vma_block[VMA_BLOCK_SIZE];
+
+
diff --git a/vma-writer.c b/vma-writer.c
new file mode 100644
-index 0000000..761d7ca
+index 0000000..c292851
--- /dev/null
+++ b/vma-writer.c
-@@ -0,0 +1,932 @@
+@@ -0,0 +1,940 @@
+/*
+ * VMA: Virtual Machine Archive
+ *
+
+ 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)) {
-+ mask |= bit;
-+ memcpy(vmaw->outbuf + vmaw->outbuf_pos, vmablock, VMA_BLOCK_SIZE);
-+ vmaw->outbuf_pos += VMA_BLOCK_SIZE;
-+ } 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;
++ if (buf) {
++ int i;
++ int bit = 1;
++ for (i = 0; i < 16; i++) {
++ unsigned char *vmablock = buf + (i*VMA_BLOCK_SIZE);
++ 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;
++ } 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;
++ }
++ } else {
++ DPRINTF("VMA WRITE %zd ZERO CLUSTER\n", cluster_num);
++ vmaw->stream_info[dev_id].zero_bytes += VMA_CLUSTER_SIZE;
++ *zero_bytes += VMA_CLUSTER_SIZE;
+ }
+
+ uint64_t block_info = ((uint64_t)mask) << (32+16);
+
+const BackupDriver backup_vma_driver = {
+ .format = "vma",
-+ .open_cb = vma_open_cb,
-+ .close_cb = vma_close_cb,
-+ .register_config_cb = vma_register_config_cb,
-+ .register_stream_cb = vma_register_stream_cb,
-+ .dump_cb = vma_dump_cb,
-+ .complete_cb = vma_complete_cb,
++ .open = vma_open_cb,
++ .close = vma_close_cb,
++ .register_config = vma_register_config_cb,
++ .register_stream = vma_register_stream_cb,
++ .dump = vma_dump_cb,
++ .complete = vma_complete_cb,
+};
+
diff --git a/vma.c b/vma.c
new file mode 100644
-index 0000000..b2e276c
+index 0000000..d3fe39c
--- /dev/null
+++ b/vma.c
-@@ -0,0 +1,559 @@
+@@ -0,0 +1,561 @@
+/*
+ * VMA: Virtual Machine Archive
+ *
+ }
+ percent = (transferred*100)/total;
+ if (percent != last_percent) {
-+ printf("progress %d%% %zd/%zd %zd\n", percent,
-+ transferred, total, zero_bytes);
++ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent,
++ transferred, total, zero_bytes);
++ fflush(stderr);
+
+ last_percent = percent;
+ }
+ for (i = 0; i < 256; i++) {
+ VmaStreamInfo *si = &vmastat.stream_info[i];
+ if (si->size) {
-+ printf("image %s: size=%zd zeros=%zd saved=%zd\n", si->devname,
-+ si->size, si->zero_bytes, si->size - si->zero_bytes);
++ fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n",
++ si->devname, si->size, si->zero_bytes,
++ si->size - si->zero_bytes);
+ }
+ }
+ }
-From 4fb44b8d03b764db04e7751a14055cbb06a7791d Mon Sep 17 00:00:00 2001
+From 0c613da24e8de22960e5a9f0f1636d9e2b0bd18d Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Wed, 14 Nov 2012 09:57:04 +0100
-Subject: [PATCH v4 5/6] add regression tests for backup
+Subject: [PATCH v5 5/6] add regression tests for backup
Simple regression tests using vma-reader and vma-writer.
-Note: the call to g_thread_init() solves problems with g_slice_alloc() - without that call we get arbitrary crashes.
-
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
---
tests/Makefile | 11 +-
- tests/backup-test.c | 517 +++++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 526 insertions(+), 2 deletions(-)
+ tests/backup-test.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 538 insertions(+), 2 deletions(-)
create mode 100644 tests/backup-test.c
diff --git a/tests/Makefile b/tests/Makefile
-include $(wildcard tests/*.d)
diff --git a/tests/backup-test.c b/tests/backup-test.c
new file mode 100644
-index 0000000..5ff6f1d
+index 0000000..039ac1d
--- /dev/null
+++ b/tests/backup-test.c
-@@ -0,0 +1,517 @@
+@@ -0,0 +1,529 @@
+/*
+ * QEMU backup test suit
+ *
-+ * Copyright (C) Proxmox Server Solutions
++ * Copyright (C) 2013 Proxmox Server Solutions
+ *
+ * Authors:
+ * Dietmar Maurer (dietmar@proxmox.com)
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
-+ * Fixme: running 'backup-test -l' trigger a bug in g_slice_alloc()
-+ * Note: 'G_SLICE=always-malloc ./tests/backup-test -l' works
-+ *
+ */
+
+#include <sys/time.h>
+{
+ BackupCB *bcb = opaque;
+
-+ DPRINTF("backup_dump_cb C%zd %d\n", cluster_num, bcb->dev_id);
++ DPRINTF("backup_dump_cb C%" PRId64 " %d\n", cluster_num, bcb->dev_id);
+
+ size_t zb = 0;
+ if (vma_writer_write(bcb->vmaw, bcb->dev_id, cluster_num, buf, &zb) < 0) {
+{
+ int ret;
+
-+ DPRINTF("write_sec_pattern_cd %zd\n", offset);
++ DPRINTF("write_sec_pattern_cd %" PRId64 "\n", offset);
+
+ if (offset & 0x1ff) {
-+ g_error("write_sec_pattern_cd offset %zd is not sector aligned\n",
-+ offset);
++ g_error("write_sec_pattern_cd offset %" PRId64
++ " is not sector aligned\n", offset);
+ }
+
+ ret = bdrv_write(bs, offset >> 9, buf_sec_pattern_cd, 1);
+ if (ret < 0) {
-+ g_error("write_sec_pattern_cd %zd failed", offset);
++ g_error("write_sec_pattern_cd %" PRId64 " failed", offset);
+ }
+
+}
+
+static void read_sec(BlockDriverState *bs, int64_t offset, unsigned char *buf)
+{
-+ DPRINTF("read_sec C%zd start %zd\n", offset>>VMA_CLUSTER_BITS, offset);
++ DPRINTF("read_sec C%" PRId64 " start %" PRId64 "\n",
++ offset>>VMA_CLUSTER_BITS, offset);
+
+ if (offset & 0x1ff) {
-+ g_error("read_sec offset %zd is not sector aligned\n", offset);
++ g_error("read_sec offset %" PRId64 " is not sector aligned\n", offset);
+ }
+
+ if (bdrv_read(bs, offset >> 9, buf, 1) < 0) {
+ data = 0; /* add zero region for testing */
+ }
+
++
++ if (sector_num >= 20*BACKUP_BLOCKS_PER_CLUSTER &&
++ sector_num <= 23*BACKUP_BLOCKS_PER_CLUSTER) {
++ data = 0; /* another zero region for testing unallocated regions */
++ }
++
+ for (i = 0; i < (512/sizeof(int64_t)); i++) {
+ i64buf[i] = data;
+ }
+ }
+ fill_test_sector(buf2, i);
+ if (bcmp(buf, buf2, sizeof(buf))) {
-+ g_error("data is different at sector %zd", i);
++ g_error("data is different at sector %" PRId64, i);
+ }
+ }
+
+ int64_t buf[512/sizeof(int64_t)];
+
+ for (i = 0; i < sectors; i++) {
++ if (i >= 20*BACKUP_BLOCKS_PER_CLUSTER &&
++ i <= 23*BACKUP_BLOCKS_PER_CLUSTER) {
++ continue; /* create a hole */
++ }
++
+ fill_test_sector(buf, i);
+
-+ int res = 0;
++ int res = 0;
+ while (1) {
-+ res = write(fd, buf, sizeof(buf));
++ res = pwrite(fd, buf, sizeof(buf), i*512);
+ if (!(res < 0 && errno == EINTR)) {
+ break;
+ }
+
+ VmaStatus vmastat;
+ vma_writer_get_status(vmaw, &vmastat);
-+ DPRINTF("statistic %zd %zd\n", vmastat.stream_info[1].size,
++ DPRINTF("statistic %" PRId64 " %" PRId64 "\n", vmastat.stream_info[1].size,
+ vmastat.stream_info[1].transferred);
+ assert(vmastat.stream_info[1].size == vmastat.stream_info[1].transferred);
+
+{
+ int c;
+
++ /* Note: GLib needs to be running in multithreaded mode in order
++ * for the GSlice allocator to be thread-safe
++ */
+ g_thread_init(NULL);
+
+ for (;;) {
-From de76a5c6306d782733217668f3f55c92aa2be36a Mon Sep 17 00:00:00 2001
+From aa580be154923056a52fdeec429f7e80f04b456e Mon Sep 17 00:00:00 2001
From: Dietmar Maurer <dietmar@proxmox.com>
Date: Thu, 29 Nov 2012 10:46:49 +0100
-Subject: [PATCH v4 6/6] add vm state to backups
+Subject: [PATCH v5 6/6] add vm state to backups
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
3 files changed, 200 insertions(+), 4 deletions(-)
diff --git a/blockdev.c b/blockdev.c
-index 1cfc780..1b81824 100644
+index 683f7da..dd20631 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -22,6 +22,8 @@
+ memset(backup_state.buf + backup_state.buf_index, 0,
+ BACKUP_CLUSTER_SIZE - backup_state.buf_index);
+ }
-+ int bytes = backup_state.driver->dump_cb(
++ int bytes = backup_state.driver->dump(
+ backup_state.writer, backup_state.vmstate_dev_id,
+ backup_state.buf_cluster_num,
+ backup_state.buf, &zero_bytes);
+{
+ assert(backup_state.driver);
+ assert(backup_state.writer);
-+ assert(backup_state.driver->dump_cb);
++ assert(backup_state.driver->dump);
+
+ /* Note: our backup driver expects to get whole clusters (64KB) */
+
+ size -= l;
+ if (backup_state.buf_index == BACKUP_CLUSTER_SIZE) {
+ size_t zero_bytes = 0;
-+ int bytes = backup_state.driver->dump_cb(
++ int bytes = backup_state.driver->dump(
+ backup_state.writer, backup_state.vmstate_dev_id,
+ backup_state.buf_cluster_num++,
+ backup_state.buf, &zero_bytes);
+ "backup_start_savevm: qemu_fclose failed");
+ goto abort;
+ }
-+ if (backup_state.driver->complete_cb(backup_state.writer,
++ if (backup_state.driver->complete(backup_state.writer,
+ backup_state.vmstate_dev_id, 0) < 0) {
-+ err = g_strdup("backup_start_savevm: complete_cb failed");
++ err = g_strdup("backup_start_savevm: complete failed");
+ goto abort;
+ }
+ backup_run_next_job();
+ backup_state.end_time = time(NULL);
+
+ Error *local_err = NULL;
-+ backup_state.driver->close_cb(backup_state.writer, &local_err);
++ backup_state.driver->close(backup_state.writer, &local_err);
+ backup_state.writer = NULL;
+
+ error_propagate(&backup_state.error, local_err);
+ * not know that size in advance).
+ */
+ size_t ramsize = ram_bytes_total();
-+ vmstate_dev_id = driver->register_stream_cb(writer, "vmstate", ramsize);
++ vmstate_dev_id = driver->register_stream(writer, "vmstate", ramsize);
+ if (vmstate_dev_id <= 0) {
+ error_setg(errp, "register vmstate stream failed");
+ goto err;