From: Wolfgang Bumiller Date: Mon, 18 Jan 2016 10:21:26 +0000 (+0100) Subject: moving all old patches to the old/ directory X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;ds=sidebyside;h=8e7c96604a7bd795fd445e772ab77f74155af301;hp=259e9b41bf9ac008252f3b46bb597e662d3fc966;p=pve-qemu-kvm.git moving all old patches to the old/ directory --- diff --git a/debian/patches/0000-cover-letter.patch b/debian/patches/0000-cover-letter.patch deleted file mode 100644 index 2c2f006..0000000 --- a/debian/patches/0000-cover-letter.patch +++ /dev/null @@ -1,98 +0,0 @@ -From aa580be154923056a52fdeec429f7e80f04b456e Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -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. - -* Backup to a single archive file -* Backup contain all data to restore VM (full backup) -* Do not depend on storage type or image format -* Avoid use of temporary storage -* store sparse images efficiently - -The file docs/backup.txt contains more details. - -Changes since v1: - -* fix spelling errors -* move BackupInfo from BDS to BackupBlockJob -* introduce BackupDriver to allow more than one backup format -* vma: add suport to store vmstate (size is not known in advance) -* add ability to store VM state - -Changes since v2: - -* BackupDriver: remove cancel_cb -* use enum for BackupFormat -* vma: use bdrv_open instead of bdrv_file_open -* vma: fix aio, use O_DIRECT -* backup one drive after another (try to avoid high load) - -Changes since v3: - -* move reviewer info from commit messages to cover-letter -* remove 'RFC' from log headers and file names -* removed comment about slow qcow2 snapshot bug -* fix spelling errors -* fixed copyright -* change 'backupfile' parameter name to 'backup-file' -* change 'config-filename' parameter name to 'config-file' -* 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 - add basic backup support to block driver - add backup related monitor commands - introduce new vma archive format - add regression tests for backup - add vm state to backups - - Makefile | 3 +- - Makefile.objs | 1 + - backup.c | 355 +++++++++++++++++ - backup.h | 45 +++ - block.c | 71 ++++- - blockdev.c | 617 ++++++++++++++++++++++++++++++ - docs/backup.txt | 116 ++++++ - docs/specs/vma_spec.txt | 24 ++ - hmp-commands.hx | 31 ++ - hmp.c | 64 ++++ - hmp.h | 3 + - include/block/block.h | 2 + - include/block/blockjob.h | 10 + - monitor.c | 7 + - qapi-schema.json | 98 +++++ - qmp-commands.hx | 27 ++ - tests/Makefile | 11 +- - tests/backup-test.c | 529 ++++++++++++++++++++++++++ - vma-reader.c | 799 +++++++++++++++++++++++++++++++++++++++ - vma-writer.c | 940 ++++++++++++++++++++++++++++++++++++++++++++++ - vma.c | 561 +++++++++++++++++++++++++++ - vma.h | 145 +++++++ - 22 files changed, 4450 insertions(+), 9 deletions(-) - create mode 100644 backup.c - create mode 100644 backup.h - create mode 100644 docs/backup.txt - create mode 100644 docs/specs/vma_spec.txt - create mode 100644 tests/backup-test.c - create mode 100644 vma-reader.c - create mode 100644 vma-writer.c - create mode 100644 vma.c - create mode 100644 vma.h - --- -1.7.2.5 - diff --git a/debian/patches/0001-add-documenation-for-new-backup-framework.patch b/debian/patches/0001-add-documenation-for-new-backup-framework.patch deleted file mode 100644 index bb3b9bf..0000000 --- a/debian/patches/0001-add-documenation-for-new-backup-framework.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 2f0dcd89a0de8b656d33ce6997c09879bd287af7 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 13 Nov 2012 09:24:50 +0100 -Subject: [PATCH v5 1/6] add documenation for new backup framework - - -Signed-off-by: Dietmar Maurer ---- - docs/backup.txt | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 files changed, 116 insertions(+), 0 deletions(-) - create mode 100644 docs/backup.txt - -diff --git a/docs/backup.txt b/docs/backup.txt -new file mode 100644 -index 0000000..927d787 ---- /dev/null -+++ b/docs/backup.txt -@@ -0,0 +1,116 @@ -+Efficient VM backup for qemu -+ -+=Requirements= -+ -+* Backup to a single archive file -+* Backup needs to contain all data to restore VM (full backup) -+* Do not depend on storage type or image format -+* Avoid use of temporary storage -+* store sparse images efficiently -+ -+=Introduction= -+ -+Most VM backup solutions use some kind of snapshot to get a consistent -+VM view at a specific point in time. For example, we previously used -+LVM to create a snapshot of all used VM images, which are then copied -+into a tar file. -+ -+That basically means that any data written during backup involve -+considerable overhead. For LVM we get the following steps: -+ -+1.) read original data (VM write) -+2.) write original data into snapshot (VM write) -+3.) write new data (VM write) -+4.) read data from snapshot (backup) -+5.) write data from snapshot into tar file (backup) -+ -+Another approach to backup VM images is to create a new qcow2 image -+which use the old image as base. During backup, writes are redirected -+to the new image, so the old image represents a 'snapshot'. After -+backup, data need to be copied back from new image into the old -+one (commit). So a simple write during backup triggers the following -+steps: -+ -+1.) write new data to new image (VM write) -+2.) read data from old image (backup) -+3.) write data from old image into tar file (backup) -+ -+4.) read data from new image (commit) -+5.) write data to old image (commit) -+ -+This is in fact the same overhead as before. Other tools like qemu -+livebackup produces similar overhead (2 reads, 3 writes). -+ -+Some storage types/formats supports internal snapshots using some kind -+of reference counting (rados, sheepdog, dm-thin, qcow2). It would be possible -+to use that for backups, but for now we want to be storage-independent. -+ -+=Make it more efficient= -+ -+The be more efficient, we simply need to avoid unnecessary steps. The -+following steps are always required: -+ -+1.) read old data before it gets overwritten -+2.) write that data into the backup archive -+3.) write new data (VM write) -+ -+As you can see, this involves only one read, and two writes. -+ -+To make that work, our backup archive need to be able to store image -+data 'out of order'. It is important to notice that this will not work -+with traditional archive formats like tar. -+ -+During backup we simply intercept writes, then read existing data and -+store that directly into the archive. After that we can continue the -+write. -+ -+==Advantages== -+ -+* very good performance (1 read, 2 writes) -+* works on any storage type and image format. -+* avoid usage of temporary storage -+* we can define a new and simple archive format, which is able to -+ store sparse files efficiently. -+ -+Note: Storing sparse files is a mess with existing archive -+formats. For example, tar requires information about holes at the -+beginning of the archive. -+ -+==Disadvantages== -+ -+* we need to define a new archive format -+ -+Note: Most existing archive formats are optimized to store small files -+including file attributes. We simply do not need that for VM archives. -+ -+* archive contains data 'out of order' -+ -+If you want to access image data in sequential order, you need to -+re-order archive data. It would be possible to to that on the fly, -+using temporary files. -+ -+Fortunately, a normal restore/extract works perfectly with 'out of -+order' data, because the target files are seekable. -+ -+* slow backup storage can slow down VM during backup -+ -+It is important to note that we only do sequential writes to the -+backup storage. Furthermore one can compress the backup stream. IMHO, -+it is better to slow down the VM a bit. All other solutions creates -+large amounts of temporary data during backup. -+ -+=Archive format requirements= -+ -+The basic requirement for such new format is that we can store image -+date 'out of order'. It is also very likely that we have less than 256 -+drives/images per VM, and we want to be able to store VM configuration -+files. -+ -+We have defined a very simply format with those properties, see: -+ -+docs/specs/vma_spec.txt -+ -+Please let us know if you know an existing format which provides the -+same functionality. -+ -+ --- -1.7.2.5 - diff --git a/debian/patches/0001-aio-Fix-use-after-free-in-cancellation-path.patch b/debian/patches/0001-aio-Fix-use-after-free-in-cancellation-path.patch deleted file mode 100644 index df88f44..0000000 --- a/debian/patches/0001-aio-Fix-use-after-free-in-cancellation-path.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 271c0f68b4eae72691721243a1c37f46a3232d61 Mon Sep 17 00:00:00 2001 -From: Fam Zheng -Date: Wed, 21 May 2014 10:42:13 +0800 -Subject: [PATCH] aio: Fix use-after-free in cancellation path - -The current flow of canceling a thread from THREAD_ACTIVE state is: - - 1) Caller wants to cancel a request, so it calls thread_pool_cancel. - - 2) thread_pool_cancel waits on the conditional variable - elem->check_cancel. - - 3) The worker thread changes state to THREAD_DONE once the task is - done, and notifies elem->check_cancel to allow thread_pool_cancel - to continue execution, and signals the notifier (pool->notifier) to - allow callback function to be called later. But because of the - global mutex, the notifier won't get processed until step 4) and 5) - are done. - - 4) thread_pool_cancel continues, leaving the notifier signaled, it - just returns to caller. - - 5) Caller thinks the request is already canceled successfully, so it - releases any related data, such as freeing elem->common.opaque. - - 6) In the next main loop iteration, the notifier handler, - event_notifier_ready, is called. It finds the canceled thread in - THREAD_DONE state, so calls elem->common.cb, with an (likely) - dangling opaque pointer. This is a use-after-free. - -Fix it by calling event_notifier_ready before leaving -thread_pool_cancel. - -Test case update: This change will let cancel complete earlier than -test-thread-pool.c expects, so update the code to check this case: if -it's already done, done_cb sets .aiocb to NULL, skip calling -bdrv_aio_cancel on them. - -Reported-by: Ulrich Obergfell -Suggested-by: Paolo Bonzini -Signed-off-by: Fam Zheng -Signed-off-by: Stefan Hajnoczi ---- - tests/test-thread-pool.c | 2 +- - thread-pool.c | 1 + - 2 files changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c -index c1f8e13..aa156bc 100644 ---- a/tests/test-thread-pool.c -+++ b/tests/test-thread-pool.c -@@ -180,7 +180,7 @@ static void test_cancel(void) - - /* Canceling the others will be a blocking operation. */ - for (i = 0; i < 100; i++) { -- if (data[i].n != 3) { -+ if (data[i].aiocb && data[i].n != 3) { - bdrv_aio_cancel(data[i].aiocb); - } - } -diff --git a/thread-pool.c b/thread-pool.c -index fbdd3ff..dfb699d 100644 ---- a/thread-pool.c -+++ b/thread-pool.c -@@ -224,6 +224,7 @@ static void thread_pool_cancel(BlockDriverAIOCB *acb) - pool->pending_cancellations--; - } - qemu_mutex_unlock(&pool->lock); -+ event_notifier_ready(&pool->notifier); - } - - static const AIOCBInfo thread_pool_aiocb_info = { --- -1.7.10.4 - diff --git a/debian/patches/0001-aio-fix-qemu_bh_schedule-bh-ctx-race-condition.patch b/debian/patches/0001-aio-fix-qemu_bh_schedule-bh-ctx-race-condition.patch deleted file mode 100644 index a01765b..0000000 --- a/debian/patches/0001-aio-fix-qemu_bh_schedule-bh-ctx-race-condition.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 4535f739edfdea392e381811963823bf05649e42 Mon Sep 17 00:00:00 2001 -From: Stefan Hajnoczi -Date: Tue, 3 Jun 2014 11:21:01 +0200 -Subject: [PATCH] aio: fix qemu_bh_schedule() bh->ctx race condition - -qemu_bh_schedule() is supposed to be thread-safe at least the first time -it is called. Unfortunately this is not quite true: - - bh->scheduled = 1; - aio_notify(bh->ctx); - -Since another thread may run the BH callback once it has been scheduled, -there is a race condition if the callback frees the BH before -aio_notify(bh->ctx) has a chance to run. - -Reported-by: Stefan Priebe -Signed-off-by: Stefan Hajnoczi -Signed-off-by: Stefan Priebe ---- - async.c | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/async.c b/async.c -index 6930185..5b6fe6b 100644 ---- a/async.c -+++ b/async.c -@@ -117,15 +117,21 @@ void qemu_bh_schedule_idle(QEMUBH *bh) - - void qemu_bh_schedule(QEMUBH *bh) - { -+ AioContext *ctx; -+ - if (bh->scheduled) - return; -+ ctx = bh->ctx; - bh->idle = 0; -- /* Make sure that idle & any writes needed by the callback are done -- * before the locations are read in the aio_bh_poll. -+ /* Make sure that: -+ * 1. idle & any writes needed by the callback are done before the -+ * locations are read in the aio_bh_poll. -+ * 2. ctx is loaded before scheduled is set and the callback has a chance -+ * to execute. - */ -- smp_wmb(); -+ smp_mb(); - bh->scheduled = 1; -- aio_notify(bh->ctx); -+ aio_notify(ctx); - } - - --- -1.7.10.4 - diff --git a/debian/patches/0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch b/debian/patches/0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch deleted file mode 100644 index 39eceba..0000000 --- a/debian/patches/0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 6314c83ae14ee32835668e38bb55f4b93b800736 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Mon, 30 Nov 2015 15:38:22 +0800 -Subject: [PATCH 1/2] net: pcnet: add check to validate receive data - size(CVE-2015-7504) - -In loopback mode, pcnet_receive routine appends CRC code to the -receive buffer. If the data size given is same as the buffer size, -the appended CRC code overwrites 4 bytes after s->buffer. Added a -check to avoid that. - -Reported by: Qinghao Tang -Cc: qemu-stable@nongnu.org -Signed-off-by: Prasad J Pandit -Signed-off-by: Jason Wang ---- - hw/net/pcnet.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c -index 0eb3cc4..309c40b 100644 ---- a/hw/net/pcnet.c -+++ b/hw/net/pcnet.c -@@ -1084,7 +1084,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) - uint32_t fcs = ~0; - uint8_t *p = src; - -- while (p != &src[size-4]) -+ while (p != &src[size]) - CRC(fcs, *p++); - crc_err = (*(uint32_t *)p != htonl(fcs)); - } -@@ -1233,8 +1233,10 @@ static void pcnet_transmit(PCNetState *s) - bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); - - /* if multi-tmd packet outsizes s->buffer then skip it silently. -- Note: this is not what real hw does */ -- if (s->xmit_pos + bcnt > sizeof(s->buffer)) { -+ * Note: this is not what real hw does. -+ * Last four bytes of s->buffer are used to store CRC FCS code. -+ */ -+ if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) { - s->xmit_pos = -1; - goto txdone; - } --- -2.1.4 - diff --git a/debian/patches/0001-smm_available-false.patch b/debian/patches/0001-smm_available-false.patch deleted file mode 100644 index 8e4d6de..0000000 --- a/debian/patches/0001-smm_available-false.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 4a1e7790e45da3b0dae61bd483b37556bae338ee Mon Sep 17 00:00:00 2001 -From: Alexandre Derumier -Date: Tue, 29 Sep 2015 15:37:44 +0200 -Subject: [PATCH] smm_available = false - -Signed-off-by: Alexandre Derumier ---- - hw/i386/pc.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/i386/pc.c b/hw/i386/pc.c -index 7661ea9..5529dc8 100644 ---- a/hw/i386/pc.c -+++ b/hw/i386/pc.c -@@ -1842,7 +1842,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) - if (tcg_enabled() || qtest_enabled()) { - smm_available = true; - } else if (kvm_enabled()) { -- smm_available = kvm_has_smm(); -+ smm_available = false; - } - - if (smm_available) { --- -2.1.4 - diff --git a/debian/patches/0001-ui-vnc-avoid-floating-point-exception.patch b/debian/patches/0001-ui-vnc-avoid-floating-point-exception.patch deleted file mode 100644 index f8adea7..0000000 --- a/debian/patches/0001-ui-vnc-avoid-floating-point-exception.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 4c65fed8bdf96780735dbdb92a8bd0d6b6526cc3 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Thu, 3 Dec 2015 18:54:17 +0530 -Subject: [PATCH] ui: vnc: avoid floating point exception - -While sending 'SetPixelFormat' messages to a VNC server, -the client could set the 'red-max', 'green-max' and 'blue-max' -values to be zero. This leads to a floating point exception in -write_png_palette while doing frame buffer updates. - -Reported-by: Lian Yihan -Signed-off-by: Prasad J Pandit -Reviewed-by: Gerd Hoffmann -Signed-off-by: Peter Maydell ---- - ui/vnc.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/ui/vnc.c b/ui/vnc.c -index 7538405..cbe4d33 100644 ---- a/ui/vnc.c -+++ b/ui/vnc.c -@@ -2198,15 +2198,15 @@ static void set_pixel_format(VncState *vs, - return; - } - -- vs->client_pf.rmax = red_max; -+ vs->client_pf.rmax = red_max ? red_max : 0xFF; - vs->client_pf.rbits = hweight_long(red_max); - vs->client_pf.rshift = red_shift; - vs->client_pf.rmask = red_max << red_shift; -- vs->client_pf.gmax = green_max; -+ vs->client_pf.gmax = green_max ? green_max : 0xFF; - vs->client_pf.gbits = hweight_long(green_max); - vs->client_pf.gshift = green_shift; - vs->client_pf.gmask = green_max << green_shift; -- vs->client_pf.bmax = blue_max; -+ vs->client_pf.bmax = blue_max ? blue_max : 0xFF; - vs->client_pf.bbits = hweight_long(blue_max); - vs->client_pf.bshift = blue_shift; - vs->client_pf.bmask = blue_max << blue_shift; --- -2.1.4 - diff --git a/debian/patches/0002-add-basic-backup-support-to-block-driver.patch b/debian/patches/0002-add-basic-backup-support-to-block-driver.patch deleted file mode 100644 index a178c59..0000000 --- a/debian/patches/0002-add-basic-backup-support-to-block-driver.patch +++ /dev/null @@ -1,626 +0,0 @@ -From 1d0c6dfc9616c0dd17986cab6744c10cb748de1e Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 13 Nov 2012 10:03:52 +0100 -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(). - -We call backup_do_cow() for each write during backup. That function -reads the original data and pass it to backup_dump_cb(). - -The tracked_request infrastructure is used to serialize access. - -Currently backup cluster size is hardcoded to 65536 bytes. - -Signed-off-by: Dietmar Maurer ---- - Makefile.objs | 1 + - backup.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++ - backup.h | 30 ++++ - block.c | 71 +++++++++- - include/block/block.h | 2 + - include/block/blockjob.h | 10 ++ - 6 files changed, 463 insertions(+), 6 deletions(-) - create mode 100644 backup.c - create mode 100644 backup.h - -diff --git a/Makefile.objs b/Makefile.objs -index a68cdac..df64f70 100644 ---- a/Makefile.objs -+++ b/Makefile.objs -@@ -13,6 +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 += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o - block-obj-y += qemu-coroutine-sleep.o -diff --git a/backup.c b/backup.c -new file mode 100644 -index 0000000..8955e1a ---- /dev/null -+++ b/backup.c -@@ -0,0 +1,355 @@ -+/* -+ * QEMU backup -+ * -+ * 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 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+ -+#include "block/block.h" -+#include "block/block_int.h" -+#include "block/blockjob.h" -+#include "qemu/ratelimit.h" -+#include "backup.h" -+ -+#define DEBUG_BACKUP 0 -+ -+#define USE_ALLOCATION_CHECK 0 -+ -+#define DPRINTF(fmt, ...) \ -+ do { if (DEBUG_BACKUP) { printf("backup: " fmt, ## __VA_ARGS__); } } \ -+ while (0) -+ -+ -+#define SLICE_TIME 100000000ULL /* ns */ -+ -+typedef struct BackupBlockJob { -+ BlockJob common; -+ RateLimit limit; -+ CoRwlock rwlock; -+ uint64_t sectors_read; -+ unsigned long *bitmap; -+ int bitmap_size; -+ BackupDumpFunc *backup_dump_cb; -+ BlockDriverCompletionFunc *backup_complete_cb; -+ void *opaque; -+} BackupBlockJob; -+ -+static bool backup_get_bitmap(BackupBlockJob *job, int64_t cluster_num) -+{ -+ assert(job); -+ assert(job->bitmap); -+ -+ unsigned long val, idx, bit; -+ -+ idx = cluster_num / BITS_PER_LONG; -+ -+ assert(job->bitmap_size > idx); -+ -+ bit = cluster_num % BITS_PER_LONG; -+ val = job->bitmap[idx]; -+ -+ return !!(val & (1UL << bit)); -+} -+ -+static void backup_set_bitmap(BackupBlockJob *job, int64_t cluster_num, -+ bool dirty) -+{ -+ assert(job); -+ assert(job->bitmap); -+ -+ unsigned long val, idx, bit; -+ -+ idx = cluster_num / BITS_PER_LONG; -+ -+ assert(job->bitmap_size > idx); -+ -+ bit = cluster_num % BITS_PER_LONG; -+ val = job->bitmap[idx]; -+ if (dirty) { -+ val |= 1UL << bit; -+ } else { -+ val &= ~(1UL << bit); -+ } -+ job->bitmap[idx] = val; -+} -+ -+static int coroutine_fn backup_do_cow(BlockDriverState *bs, -+ int64_t sector_num, int nb_sectors) -+{ -+ assert(bs); -+ BackupBlockJob *job = (BackupBlockJob *)bs->job; -+ assert(job); -+ -+ BlockDriver *drv = bs->drv; -+ struct iovec iov; -+ QEMUIOVector bounce_qiov; -+ void *bounce_buffer = NULL; -+ int ret = 0; -+ -+ qemu_co_rwlock_rdlock(&job->rwlock); -+ -+ int64_t start, end; -+ -+ start = sector_num / BACKUP_BLOCKS_PER_CLUSTER; -+ end = (sector_num + nb_sectors + BACKUP_BLOCKS_PER_CLUSTER - 1) / -+ BACKUP_BLOCKS_PER_CLUSTER; -+ -+ 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%" PRId64 "\n", start); -+ continue; /* already copied */ -+ } -+ -+ /* immediately set bitmap (avoid coroutine race) */ -+ backup_set_bitmap(job, start, 1); -+ -+ DPRINTF("brdv_co_backup_cow C%" PRId64 "\n", start); -+ -+ if (!bounce_buffer) { -+ iov.iov_len = BACKUP_CLUSTER_SIZE; -+ iov.iov_base = bounce_buffer = qemu_blockalign(bs, iov.iov_len); -+ qemu_iovec_init_external(&bounce_qiov, &iov, 1); -+ } -+ -+#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 is_allocated C%" PRId64 " failed\n", -+ start); -+ goto out; -+ } -+ -+ 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%" PRId64 " failed\n", -+ start); -+ goto out; -+ } -+ -+ DPRINTF("brdv_co_backup_cow done C%" PRId64 "\n", start); -+ } -+ -+out: -+ if (bounce_buffer) { -+ qemu_vfree(bounce_buffer); -+ } -+ -+ qemu_co_rwlock_unlock(&job->rwlock); -+ -+ return ret; -+} -+ -+static int coroutine_fn backup_before_read(BlockDriverState *bs, -+ int64_t sector_num, -+ int nb_sectors, QEMUIOVector *qiov) -+{ -+ return backup_do_cow(bs, sector_num, nb_sectors); -+} -+ -+static int coroutine_fn backup_before_write(BlockDriverState *bs, -+ int64_t sector_num, -+ int nb_sectors, QEMUIOVector *qiov) -+{ -+ return backup_do_cow(bs, sector_num, nb_sectors); -+} -+ -+static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) -+{ -+ BackupBlockJob *s = container_of(job, BackupBlockJob, common); -+ -+ if (speed < 0) { -+ error_set(errp, QERR_INVALID_PARAMETER, "speed"); -+ return; -+ } -+ ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); -+} -+ -+static BlockJobType backup_job_type = { -+ .instance_size = sizeof(BackupBlockJob), -+ .before_read = backup_before_read, -+ .before_write = backup_before_write, -+ .job_type = "backup", -+ .set_speed = backup_set_speed, -+}; -+ -+static void coroutine_fn backup_run(void *opaque) -+{ -+ BackupBlockJob *job = opaque; -+ BlockDriverState *bs = job->common.bs; -+ assert(bs); -+ -+ int64_t start, end; -+ -+ start = 0; -+ end = (bs->total_sectors + BACKUP_BLOCKS_PER_CLUSTER - 1) / -+ BACKUP_BLOCKS_PER_CLUSTER; -+ -+ DPRINTF("backup_run start %s %" PRId64 " %" PRId64 "\n", -+ bdrv_get_device_name(bs), start, end); -+ -+ int ret = 0; -+ -+ for (; start < end; start++) { -+ if (block_job_is_cancelled(&job->common)) { -+ ret = -1; -+ break; -+ } -+ -+ /* 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) -+ */ -+ if (job->common.speed) { -+ uint64_t delay_ns = ratelimit_calculate_delay( -+ &job->limit, job->sectors_read); -+ job->sectors_read = 0; -+ block_job_sleep_ns(&job->common, rt_clock, delay_ns); -+ } else { -+ block_job_sleep_ns(&job->common, rt_clock, 1000); -+ } -+ -+ if (block_job_is_cancelled(&job->common)) { -+ ret = -1; -+ break; -+ } -+ -+ if (backup_get_bitmap(job, start)) { -+ continue; /* already copied */ -+ } -+ -+ DPRINTF("backup_run loop C%" PRId64 "\n", start); -+ -+ /** -+ * This triggers a cluster copy -+ * Note: avoid direct call to brdv_co_backup_cow, because -+ * this does not call tracked_request_begin() -+ */ -+ ret = bdrv_co_backup(bs, start*BACKUP_BLOCKS_PER_CLUSTER, 1); -+ if (ret < 0) { -+ break; -+ } -+ /* Publish progress */ -+ job->common.offset += BACKUP_CLUSTER_SIZE; -+ } -+ -+ /* 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); -+} -+ -+static void backup_job_cleanup_cb(void *opaque, int ret) -+{ -+ BlockDriverState *bs = opaque; -+ assert(bs); -+ BackupBlockJob *job = (BackupBlockJob *)bs->job; -+ assert(job); -+ -+ DPRINTF("backup_job_cleanup_cb start %d\n", ret); -+ -+ job->backup_complete_cb(job->opaque, ret); -+ -+ DPRINTF("backup_job_cleanup_cb end\n"); -+ -+ g_free(job->bitmap); -+} -+ -+void -+backup_job_start(BlockDriverState *bs, bool cancel) -+{ -+ assert(bs); -+ assert(bs->job); -+ assert(bs->job->co == NULL); -+ -+ if (cancel) { -+ block_job_cancel(bs->job); /* set cancel flag */ -+ } -+ -+ bs->job->co = qemu_coroutine_create(backup_run); -+ qemu_coroutine_enter(bs->job->co, bs->job); -+} -+ -+int -+backup_job_create(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb, -+ BlockDriverCompletionFunc *backup_complete_cb, -+ void *opaque, int64_t speed) -+{ -+ assert(bs); -+ assert(backup_dump_cb); -+ assert(backup_complete_cb); -+ -+ if (bs->job) { -+ DPRINTF("bdrv_backup_init failed - running job on %s\n", -+ bdrv_get_device_name(bs)); -+ return -1; -+ } -+ -+ int64_t bitmap_size; -+ const char *devname = bdrv_get_device_name(bs); -+ -+ if (!devname || !devname[0]) { -+ return -1; -+ } -+ -+ DPRINTF("bdrv_backup_init %s\n", bdrv_get_device_name(bs)); -+ -+ Error *errp; -+ 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 + -+ BACKUP_BLOCKS_PER_CLUSTER * BITS_PER_LONG - 1; -+ bitmap_size /= BACKUP_BLOCKS_PER_CLUSTER * BITS_PER_LONG; -+ -+ job->backup_dump_cb = backup_dump_cb; -+ job->backup_complete_cb = backup_complete_cb; -+ job->opaque = opaque; -+ job->bitmap_size = bitmap_size; -+ job->bitmap = g_new0(unsigned long, bitmap_size); -+ -+ job->common.len = bs->total_sectors*BDRV_SECTOR_SIZE; -+ -+ return 0; -+} -diff --git a/backup.h b/backup.h -new file mode 100644 -index 0000000..9b1ea1c ---- /dev/null -+++ b/backup.h -@@ -0,0 +1,30 @@ -+/* -+ * QEMU backup related definitions -+ * -+ * 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 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#ifndef QEMU_BACKUP_H -+#define QEMU_BACKUP_H -+ -+#define BACKUP_CLUSTER_BITS 16 -+#define BACKUP_CLUSTER_SIZE (1<backing_hd) { - return -ENOTSUP; - } -@@ -1691,6 +1692,22 @@ void bdrv_round_to_clusters(BlockDriverState *bs, - } - } - -+/** -+ * Round a region to job cluster boundaries -+ */ -+static void round_to_job_clusters(BlockDriverState *bs, -+ int64_t sector_num, int nb_sectors, -+ int job_cluster_size, -+ int64_t *cluster_sector_num, -+ int *cluster_nb_sectors) -+{ -+ int64_t c = job_cluster_size/BDRV_SECTOR_SIZE; -+ -+ *cluster_sector_num = QEMU_ALIGN_DOWN(sector_num, c); -+ *cluster_nb_sectors = QEMU_ALIGN_UP(sector_num - *cluster_sector_num + -+ nb_sectors, c); -+} -+ - static bool tracked_request_overlaps(BdrvTrackedRequest *req, - int64_t sector_num, int nb_sectors) { - /* aaaa bbbb */ -@@ -1705,7 +1722,9 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req, - } - - static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs, -- int64_t sector_num, int nb_sectors) -+ int64_t sector_num, -+ int nb_sectors, -+ int job_cluster_size) - { - BdrvTrackedRequest *req; - int64_t cluster_sector_num; -@@ -1721,6 +1740,11 @@ static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs, - bdrv_round_to_clusters(bs, sector_num, nb_sectors, - &cluster_sector_num, &cluster_nb_sectors); - -+ if (job_cluster_size) { -+ round_to_job_clusters(bs, sector_num, nb_sectors, job_cluster_size, -+ &cluster_sector_num, &cluster_nb_sectors); -+ } -+ - do { - retry = false; - QLIST_FOREACH(req, &bs->tracked_requests, list) { -@@ -2260,12 +2284,24 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, - bs->copy_on_read_in_flight++; - } - -- if (bs->copy_on_read_in_flight) { -- wait_for_overlapping_requests(bs, sector_num, nb_sectors); -+ int job_cluster_size = bs->job && bs->job->cluster_size ? -+ bs->job->cluster_size : 0; -+ -+ if (bs->copy_on_read_in_flight || job_cluster_size) { -+ wait_for_overlapping_requests(bs, sector_num, nb_sectors, -+ job_cluster_size); - } - - tracked_request_begin(&req, bs, sector_num, nb_sectors, false); - -+ if (bs->job && bs->job->job_type->before_read) { -+ ret = bs->job->job_type->before_read(bs, sector_num, nb_sectors, qiov); -+ if ((ret < 0) || (flags & BDRV_REQ_BACKUP_ONLY)) { -+ /* Note: We do not return any data to the caller */ -+ goto out; -+ } -+ } -+ - if (flags & BDRV_REQ_COPY_ON_READ) { - int pnum; - -@@ -2309,6 +2345,17 @@ int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, - BDRV_REQ_COPY_ON_READ); - } - -+int coroutine_fn bdrv_co_backup(BlockDriverState *bs, -+ int64_t sector_num, int nb_sectors) -+{ -+ if (!bs->job) { -+ return -ENOTSUP; -+ } -+ -+ return bdrv_co_do_readv(bs, sector_num, nb_sectors, NULL, -+ BDRV_REQ_BACKUP_ONLY); -+} -+ - static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, - int64_t sector_num, int nb_sectors) - { -@@ -2366,12 +2413,23 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, - bdrv_io_limits_intercept(bs, true, nb_sectors); - } - -- if (bs->copy_on_read_in_flight) { -- wait_for_overlapping_requests(bs, sector_num, nb_sectors); -+ int job_cluster_size = bs->job && bs->job->cluster_size ? -+ bs->job->cluster_size : 0; -+ -+ if (bs->copy_on_read_in_flight || job_cluster_size) { -+ wait_for_overlapping_requests(bs, sector_num, nb_sectors, -+ job_cluster_size); - } - - tracked_request_begin(&req, bs, sector_num, nb_sectors, true); - -+ if (bs->job && bs->job->job_type->before_write) { -+ ret = bs->job->job_type->before_write(bs, sector_num, nb_sectors, qiov); -+ if (ret < 0) { -+ goto out; -+ } -+ } -+ - if (flags & BDRV_REQ_ZERO_WRITE) { - ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors); - } else { -@@ -2390,6 +2448,7 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, - bs->wr_highest_sector = sector_num + nb_sectors - 1; - } - -+out: - tracked_request_end(&req); - - return ret; -diff --git a/include/block/block.h b/include/block/block.h -index 5c3b911..b6144be 100644 ---- a/include/block/block.h -+++ b/include/block/block.h -@@ -172,6 +172,8 @@ int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); - int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); -+int coroutine_fn bdrv_co_backup(BlockDriverState *bs, -+ int64_t sector_num, int nb_sectors); - int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); - /* -diff --git a/include/block/blockjob.h b/include/block/blockjob.h -index c290d07..6f42495 100644 ---- a/include/block/blockjob.h -+++ b/include/block/blockjob.h -@@ -50,6 +50,13 @@ typedef struct BlockJobType { - * manually. - */ - void (*complete)(BlockJob *job, Error **errp); -+ -+ /** tracked requests */ -+ int coroutine_fn (*before_read)(BlockDriverState *bs, int64_t sector_num, -+ int nb_sectors, QEMUIOVector *qiov); -+ int coroutine_fn (*before_write)(BlockDriverState *bs, int64_t sector_num, -+ int nb_sectors, QEMUIOVector *qiov); -+ - } BlockJobType; - - /** -@@ -103,6 +110,9 @@ struct BlockJob { - /** Speed that was set with @block_job_set_speed. */ - int64_t speed; - -+ /** tracked requests */ -+ int cluster_size; -+ - /** The completion function that will be called when the job completes. */ - BlockDriverCompletionFunc *cb; - --- -1.7.2.5 - diff --git a/debian/patches/0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch b/debian/patches/0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch deleted file mode 100644 index 72c8d25..0000000 --- a/debian/patches/0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 59fb70f22143eccdf74639871e862df0c2f570fc Mon Sep 17 00:00:00 2001 -From: Jason Wang -Date: Mon, 30 Nov 2015 15:38:23 +0800 -Subject: [PATCH 2/2] pcnet: fix rx buffer overflow(CVE-2015-7512) - -Backends could provide a packet whose length is greater than buffer -size. Check for this and truncate the packet to avoid rx buffer -overflow in this case. - -Cc: Prasad J Pandit -Cc: qemu-stable@nongnu.org -Signed-off-by: Jason Wang ---- - hw/net/pcnet.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c -index 309c40b..1f4a3db 100644 ---- a/hw/net/pcnet.c -+++ b/hw/net/pcnet.c -@@ -1064,6 +1064,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) - int pktcount = 0; - - if (!s->looptest) { -+ if (size > 4092) { -+#ifdef PCNET_DEBUG_RMD -+ fprintf(stderr, "pcnet: truncates rx packet.\n"); -+#endif -+ size = 4092; -+ } - memcpy(src, buf, size); - /* no need to compute the CRC */ - src[size] = 0; --- -2.1.4 - diff --git a/debian/patches/0003-add-backup-related-monitor-commands.patch b/debian/patches/0003-add-backup-related-monitor-commands.patch deleted file mode 100644 index 0a23a45..0000000 --- a/debian/patches/0003-add-backup-related-monitor-commands.patch +++ /dev/null @@ -1,826 +0,0 @@ -From cf9cc8878d3246069da3fdf7ec865b7b983487fe Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 13 Nov 2012 11:27:56 +0100 -Subject: [PATCH v5 3/6] add backup related monitor commands - -We use a generic BackupDriver struct to encapsulate all archive format -related function. - -Another option would be to simply dump to -the output fh (pipe), and an external binary saves the data. That way we -could move the whole archive format related code out of qemu. - -Signed-off-by: Dietmar Maurer ---- - backup.h | 15 ++ - blockdev.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ - hmp-commands.hx | 31 ++++ - hmp.c | 63 ++++++++ - hmp.h | 3 + - monitor.c | 7 + - qapi-schema.json | 95 ++++++++++++ - qmp-commands.hx | 27 ++++ - 8 files changed, 664 insertions(+), 0 deletions(-) - -diff --git a/backup.h b/backup.h -index 9b1ea1c..22598a6 100644 ---- a/backup.h -+++ b/backup.h -@@ -14,6 +14,9 @@ - #ifndef QEMU_BACKUP_H - #define QEMU_BACKUP_H - -+#include -+#include "block/block.h" -+ - #define BACKUP_CLUSTER_BITS 16 - #define BACKUP_CLUSTER_SIZE (1<dump); -+ -+ size_t zero_bytes = 0; -+ int bytes = backup_state.driver->dump(backup_state.writer, -+ bcb->dev_id, cluster_num, -+ buf, &zero_bytes); -+ -+ if (bytes > 0) { -+ bcb->transferred += bytes; -+ backup_state.transferred += bytes; -+ if (zero_bytes) { -+ bcb->zero_bytes += bytes; -+ backup_state.zero_bytes += zero_bytes; -+ } -+ } -+ -+ return bytes; -+} -+ -+static void backup_cleanup(void) -+{ -+ if (backup_state.writer && backup_state.driver) { -+ backup_state.end_time = time(NULL); -+ Error *local_err = NULL; -+ backup_state.driver->close(backup_state.writer, &local_err); -+ error_propagate(&backup_state.error, local_err); -+ backup_state.writer = NULL; -+ } -+ -+ if (backup_state.bcb_list) { -+ GList *l = backup_state.bcb_list; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ drive_put_ref_bh_schedule(drive_get_by_blockdev(bcb->bs)); -+ g_free(bcb); -+ } -+ g_list_free(backup_state.bcb_list); -+ backup_state.bcb_list = NULL; -+ } -+} -+ -+static void backup_complete_cb(void *opaque, int ret) -+{ -+ BackupCB *bcb = opaque; -+ -+ assert(backup_state.driver); -+ assert(backup_state.writer); -+ assert(backup_state.driver->complete); -+ assert(backup_state.driver->close); -+ -+ bcb->completed = true; -+ -+ backup_state.driver->complete(backup_state.writer, bcb->dev_id, ret); -+ -+ if (!backup_state.cancel) { -+ backup_run_next_job(); -+ } -+} -+ -+static void backup_cancel(void) -+{ -+ backup_state.cancel = true; -+ -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, "backup cancelled"); -+ } -+ -+ /* drain all i/o (awake jobs waiting for aio) */ -+ bdrv_drain_all(); -+ -+ int job_count = 0; -+ GList *l = backup_state.bcb_list; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ BlockJob *job = bcb->bs->job; -+ if (job) { -+ job_count++; -+ if (!bcb->started) { -+ bcb->started = true; -+ backup_job_start(bcb->bs, true); -+ } -+ if (!bcb->completed) { -+ block_job_cancel_sync(job); -+ } -+ } -+ } -+ -+ backup_cleanup(); -+} -+ -+void qmp_backup_cancel(Error **errp) -+{ -+ backup_cancel(); -+} -+ -+static void backup_run_next_job(void) -+{ -+ GList *l = backup_state.bcb_list; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ -+ if (bcb->bs && bcb->bs->job && !bcb->completed) { -+ if (!bcb->started) { -+ bcb->started = true; -+ bool cancel = backup_state.error || backup_state.cancel; -+ backup_job_start(bcb->bs, cancel); -+ } -+ return; -+ } -+ } -+ -+ backup_cleanup(); -+} -+ -+static void backup_start_jobs(void) -+{ -+ /* create all jobs (one for each device), start first one */ -+ GList *l = backup_state.bcb_list; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ -+ if (backup_job_create(bcb->bs, backup_dump_cb, backup_complete_cb, -+ bcb, backup_state.speed) != 0) { -+ error_setg(&backup_state.error, "backup_job_create failed"); -+ backup_cancel(); -+ return; -+ } -+ } -+ -+ backup_run_next_job(); -+} -+ -+char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, -+ bool has_config_file, const char *config_file, -+ bool has_devlist, const char *devlist, -+ bool has_speed, int64_t speed, Error **errp) -+{ -+ BlockDriverState *bs; -+ Error *local_err = NULL; -+ uuid_t uuid; -+ void *writer = NULL; -+ gchar **devs = NULL; -+ GList *bcblist = NULL; -+ -+ if (backup_state.bcb_list) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "previous backup not finished"); -+ return NULL; -+ } -+ -+ /* Todo: try to auto-detect format based on file name */ -+ format = has_format ? format : BACKUP_FORMAT_VMA; -+ -+ /* fixme: find driver for specifued format */ -+ const BackupDriver *driver = NULL; -+ -+ if (!driver) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); -+ return NULL; -+ } -+ -+ if (has_devlist) { -+ devs = g_strsplit_set(devlist, ",;:", -1); -+ -+ gchar **d = devs; -+ while (d && *d) { -+ bs = bdrv_find(*d); -+ if (bs) { -+ if (bdrv_is_read_only(bs)) { -+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, *d); -+ goto err; -+ } -+ if (!bdrv_is_inserted(bs)) { -+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d); -+ goto err; -+ } -+ BackupCB *bcb = g_new0(BackupCB, 1); -+ bcb->bs = bs; -+ bcblist = g_list_append(bcblist, bcb); -+ } else { -+ error_set(errp, QERR_DEVICE_NOT_FOUND, *d); -+ goto err; -+ } -+ d++; -+ } -+ -+ } else { -+ -+ bs = NULL; -+ while ((bs = bdrv_next(bs))) { -+ -+ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { -+ continue; -+ } -+ -+ BackupCB *bcb = g_new0(BackupCB, 1); -+ bcb->bs = bs; -+ bcblist = g_list_append(bcblist, bcb); -+ } -+ } -+ -+ if (!bcblist) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list"); -+ goto err; -+ } -+ -+ GList *l = bcblist; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ if (bcb->bs->job) { -+ error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bcb->bs)); -+ goto err; -+ } -+ } -+ -+ uuid_generate(uuid); -+ -+ writer = driver->open(backup_file, uuid, &local_err); -+ if (!writer) { -+ if (error_is_set(&local_err)) { -+ error_propagate(errp, local_err); -+ } -+ goto err; -+ } -+ -+ size_t total = 0; -+ -+ /* register all devices for vma writer */ -+ l = bcblist; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ -+ int64_t size = bdrv_getlength(bcb->bs); -+ const char *devname = bdrv_get_device_name(bcb->bs); -+ bcb->dev_id = driver->register_stream(writer, devname, size); -+ if (bcb->dev_id <= 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "register_stream failed"); -+ goto err; -+ } -+ bcb->size = size; -+ total += size; -+ } -+ -+ /* add configuration file to archive */ -+ if (has_config_file) { -+ char *cdata = NULL; -+ gsize clen = 0; -+ GError *err = NULL; -+ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) { -+ error_setg(errp, "unable to read file '%s'", config_file); -+ goto err; -+ } -+ -+ const char *basename = g_path_get_basename(config_file); -+ if (driver->register_config(writer, basename, cdata, clen) < 0) { -+ error_setg(errp, "register_config failed"); -+ g_free(cdata); -+ goto err; -+ } -+ g_free(cdata); -+ } -+ -+ /* initialize global backup_state now */ -+ -+ backup_state.cancel = false; -+ -+ if (backup_state.error) { -+ error_free(backup_state.error); -+ backup_state.error = NULL; -+ } -+ -+ backup_state.driver = driver; -+ -+ backup_state.speed = (has_speed && speed > 0) ? speed : 0; -+ -+ backup_state.start_time = time(NULL); -+ backup_state.end_time = 0; -+ -+ if (backup_state.backup_file) { -+ g_free(backup_state.backup_file); -+ } -+ backup_state.backup_file = g_strdup(backup_file); -+ -+ backup_state.writer = writer; -+ -+ uuid_copy(backup_state.uuid, uuid); -+ uuid_unparse_lower(uuid, backup_state.uuid_str); -+ -+ backup_state.bcb_list = bcblist; -+ -+ backup_state.total = total; -+ backup_state.transferred = 0; -+ backup_state.zero_bytes = 0; -+ -+ /* Grab a reference so hotplug does not delete the -+ * BlockDriverState from underneath us. -+ */ -+ l = bcblist; -+ while (l) { -+ BackupCB *bcb = l->data; -+ l = g_list_next(l); -+ drive_get_ref(drive_get_by_blockdev(bcb->bs)); -+ } -+ -+ backup_start_jobs(); -+ -+ return g_strdup(backup_state.uuid_str); -+ -+err: -+ -+ l = bcblist; -+ while (l) { -+ g_free(l->data); -+ l = g_list_next(l); -+ } -+ g_list_free(bcblist); -+ -+ if (devs) { -+ g_strfreev(devs); -+ } -+ -+ if (writer) { -+ unlink(backup_file); -+ if (driver) { -+ Error *err = NULL; -+ driver->close(writer, &err); -+ } -+ } -+ -+ return NULL; -+} -+ -+BackupStatus *qmp_query_backup(Error **errp) -+{ -+ BackupStatus *info = g_malloc0(sizeof(*info)); -+ -+ if (!backup_state.start_time) { -+ /* not started, return {} */ -+ return info; -+ } -+ -+ info->has_status = true; -+ info->has_start_time = true; -+ info->start_time = backup_state.start_time; -+ -+ if (backup_state.backup_file) { -+ info->has_backup_file = true; -+ info->backup_file = g_strdup(backup_state.backup_file); -+ } -+ -+ info->has_uuid = true; -+ info->uuid = g_strdup(backup_state.uuid_str); -+ -+ if (backup_state.end_time) { -+ if (backup_state.error) { -+ info->status = g_strdup("error"); -+ info->has_errmsg = true; -+ info->errmsg = g_strdup(error_get_pretty(backup_state.error)); -+ } else { -+ info->status = g_strdup("done"); -+ } -+ info->has_end_time = true; -+ info->end_time = backup_state.end_time; -+ } else { -+ info->status = g_strdup("active"); -+ } -+ -+ info->has_total = true; -+ info->total = backup_state.total; -+ info->has_zero_bytes = true; -+ info->zero_bytes = backup_state.zero_bytes; -+ info->has_transferred = true; -+ info->transferred = backup_state.transferred; -+ -+ return info; -+} -+ - static BlockJob *find_block_job(const char *device) - { - BlockDriverState *bs; -diff --git a/hmp-commands.hx b/hmp-commands.hx -index 64008a9..0f178d8 100644 ---- a/hmp-commands.hx -+++ b/hmp-commands.hx -@@ -83,6 +83,35 @@ STEXI - Copy data from a backing file into a block device. - ETEXI - -+ { -+ .name = "backup", -+ .args_type = "backupfile:s,speed:o?,devlist:s?", -+ .params = "backupfile [speed [devlist]]", -+ .help = "create a VM Backup.", -+ .mhandler.cmd = hmp_backup, -+ }, -+ -+STEXI -+@item backup -+@findex backup -+Create a VM backup. -+ETEXI -+ -+ { -+ .name = "backup_cancel", -+ .args_type = "", -+ .params = "", -+ .help = "cancel the current VM backup", -+ .mhandler.cmd = hmp_backup_cancel, -+ }, -+ -+STEXI -+@item backup_cancel -+@findex backup_cancel -+Cancel the current VM backup. -+ -+ETEXI -+ - { - .name = "block_job_set_speed", - .args_type = "device:B,speed:o", -@@ -1630,6 +1659,8 @@ show CPU statistics - show user network stack connection states - @item info migrate - show migration status -+@item info backup -+show backup status - @item info migrate_capabilities - show current migration capabilities - @item info migrate_cache_size -diff --git a/hmp.c b/hmp.c -index 2f47a8a..b2c1f23 100644 ---- a/hmp.c -+++ b/hmp.c -@@ -131,6 +131,38 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict) - qapi_free_MouseInfoList(mice_list); - } - -+void hmp_info_backup(Monitor *mon, const QDict *qdict) -+{ -+ BackupStatus *info; -+ -+ info = qmp_query_backup(NULL); -+ if (info->has_status) { -+ if (info->has_errmsg) { -+ monitor_printf(mon, "Backup status: %s - %s\n", -+ info->status, info->errmsg); -+ } else { -+ monitor_printf(mon, "Backup status: %s\n", info->status); -+ } -+ } -+ if (info->has_backup_file) { -+ int per = (info->has_total && info->total && -+ info->has_transferred && info->transferred) ? -+ (info->transferred * 100)/info->total : 0; -+ int zero_per = (info->has_total && info->total && -+ info->has_zero_bytes && info->zero_bytes) ? -+ (info->zero_bytes * 100)/info->total : 0; -+ monitor_printf(mon, "Backup file: %s\n", info->backup_file); -+ monitor_printf(mon, "Backup uuid: %s\n", info->uuid); -+ monitor_printf(mon, "Total size: %zd\n", info->total); -+ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n", -+ info->transferred, per); -+ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n", -+ info->zero_bytes, zero_per); -+ } -+ -+ qapi_free_BackupStatus(info); -+} -+ - void hmp_info_migrate(Monitor *mon, const QDict *qdict) - { - MigrationInfo *info; -@@ -998,6 +1030,37 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) - hmp_handle_error(mon, &error); - } - -+void hmp_backup_cancel(Monitor *mon, const QDict *qdict) -+{ -+ Error *errp = NULL; -+ -+ qmp_backup_cancel(&errp); -+ -+ if (error_is_set(&errp)) { -+ monitor_printf(mon, "%s\n", error_get_pretty(errp)); -+ error_free(errp); -+ return; -+ } -+} -+ -+void hmp_backup(Monitor *mon, const QDict *qdict) -+{ -+ const char *backup_file = qdict_get_str(qdict, "backup-file"); -+ const char *devlist = qdict_get_try_str(qdict, "devlist"); -+ int64_t speed = qdict_get_try_int(qdict, "speed", 0); -+ -+ Error *errp = NULL; -+ -+ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, -+ devlist, qdict_haskey(qdict, "speed"), speed, &errp); -+ -+ if (error_is_set(&errp)) { -+ monitor_printf(mon, "%s\n", error_get_pretty(errp)); -+ error_free(errp); -+ return; -+ } -+} -+ - void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) - { - Error *error = NULL; -diff --git a/hmp.h b/hmp.h -index 30b3c20..ad4cf80 100644 ---- a/hmp.h -+++ b/hmp.h -@@ -28,6 +28,7 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict); - void hmp_info_migrate(Monitor *mon, const QDict *qdict); - void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); - void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict); -+void hmp_info_backup(Monitor *mon, const QDict *qdict); - void hmp_info_cpus(Monitor *mon, const QDict *qdict); - void hmp_info_block(Monitor *mon, const QDict *qdict); - void hmp_info_blockstats(Monitor *mon, const QDict *qdict); -@@ -65,6 +66,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict); - void hmp_change(Monitor *mon, const QDict *qdict); - void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); - void hmp_block_stream(Monitor *mon, const QDict *qdict); -+void hmp_backup(Monitor *mon, const QDict *qdict); -+void hmp_backup_cancel(Monitor *mon, const QDict *qdict); - void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); - void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); - void hmp_block_job_pause(Monitor *mon, const QDict *qdict); -diff --git a/monitor.c b/monitor.c -index 6a0f257..e4a810c 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -2666,6 +2666,13 @@ static mon_cmd_t info_cmds[] = { - }, - #endif - { -+ .name = "backup", -+ .args_type = "", -+ .params = "", -+ .help = "show backup status", -+ .mhandler.cmd = hmp_info_backup, -+ }, -+ { - .name = "migrate", - .args_type = "", - .params = "", -diff --git a/qapi-schema.json b/qapi-schema.json -index 7275b5d..09ca8ef 100644 ---- a/qapi-schema.json -+++ b/qapi-schema.json -@@ -425,6 +425,39 @@ - { 'type': 'EventInfo', 'data': {'name': 'str'} } - - ## -+# @BackupStatus: -+# -+# Detailed backup status. -+# -+# @status: #optional string describing the current backup status. -+# This can be 'active', 'done', 'error'. If this field is not -+# returned, no backup process has been initiated -+# -+# @errmsg: #optional error message (only returned if status is 'error') -+# -+# @total: #optional total amount of bytes involved in the backup process -+# -+# @transferred: #optional amount of bytes already backed up. -+# -+# @zero-bytes: #optional amount of 'zero' bytes detected. -+# -+# @start-time: #optional time (epoch) when backup job started. -+# -+# @end-time: #optional time (epoch) when backup job finished. -+# -+# @backupfile: #optional backup file name -+# -+# @uuid: #optional uuid for this backup job -+# -+# Since: 1.5.0 -+## -+{ 'type': 'BackupStatus', -+ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', -+ '*transferred': 'int', '*zero-bytes': 'int', -+ '*start-time': 'int', '*end-time': 'int', -+ '*backup-file': 'str', '*uuid': 'str' } } -+ -+## - # @query-events: - # - # Return a list of supported QMP events by this server -@@ -1824,6 +1857,68 @@ - 'data': { 'path': 'str' }, - 'returns': [ 'ObjectPropertyInfo' ] } - -+ -+## -+# @BackupFormat -+# -+# An enumeration of supported backup formats. -+# -+# @vma: Proxmox vma backup format -+## -+{ 'enum': 'BackupFormat', -+ 'data': [ 'vma' ] } -+ -+## -+# @backup: -+# -+# Starts a VM backup. -+# -+# @backup-file: the backup file name -+# -+# @format: format of the backup file -+# -+# @config-filename: #optional name of a configuration file to include into -+# the backup archive. -+# -+# @speed: #optional the maximum speed, in bytes per second -+# -+# @devlist: #optional list of block device names (separated by ',', ';' -+# or ':'). By default the backup includes all writable block devices. -+# -+# Returns: the uuid of the backup job -+# -+# Since: 1.5.0 -+## -+{ 'command': 'backup', 'data': { 'backup-file': 'str', -+ '*format': 'BackupFormat', -+ '*config-file': 'str', -+ '*devlist': 'str', '*speed': 'int' }, -+ 'returns': 'str' } -+ -+## -+# @query-backup -+# -+# Returns information about current/last backup task. -+# -+# Returns: @BackupStatus -+# -+# Since: 1.5.0 -+## -+{ 'command': 'query-backup', 'returns': 'BackupStatus' } -+ -+## -+# @backup-cancel -+# -+# Cancel the current executing backup process. -+# -+# Returns: nothing on success -+# -+# Notes: This command succeeds even if there is no backup process running. -+# -+# Since: 1.5.0 -+## -+{ 'command': 'backup-cancel' } -+ - ## - # @qom-get: - # -diff --git a/qmp-commands.hx b/qmp-commands.hx -index 799adea..17e381b 100644 ---- a/qmp-commands.hx -+++ b/qmp-commands.hx -@@ -889,6 +889,18 @@ EQMP - }, - - { -+ .name = "backup", -+ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?", -+ .mhandler.cmd_new = qmp_marshal_input_backup, -+ }, -+ -+ { -+ .name = "backup-cancel", -+ .args_type = "", -+ .mhandler.cmd_new = qmp_marshal_input_backup_cancel, -+ }, -+ -+ { - .name = "block-job-set-speed", - .args_type = "device:B,speed:o", - .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed, -@@ -2566,6 +2578,21 @@ EQMP - }, - - SQMP -+ -+query-backup -+------------- -+ -+Backup status. -+ -+EQMP -+ -+ { -+ .name = "query-backup", -+ .args_type = "", -+ .mhandler.cmd_new = qmp_marshal_input_query_backup, -+ }, -+ -+SQMP - migrate-set-capabilities - ------- - --- -1.7.2.5 - diff --git a/debian/patches/0004-introduce-new-vma-archive-format.patch b/debian/patches/0004-introduce-new-vma-archive-format.patch deleted file mode 100644 index 4af6d62..0000000 --- a/debian/patches/0004-introduce-new-vma-archive-format.patch +++ /dev/null @@ -1,2586 +0,0 @@ -From 6289a43696ca6f713a5d3bb9f95a5adb608a5e13 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 13 Nov 2012 11:11:38 +0100 -Subject: [PATCH v5 4/6] introduce new vma archive format - -This is a very simple archive format, see docs/specs/vma_spec.txt - -Signed-off-by: Dietmar Maurer ---- - Makefile | 3 +- - Makefile.objs | 2 +- - blockdev.c | 6 +- - docs/specs/vma_spec.txt | 24 ++ - vma-reader.c | 799 ++++++++++++++++++++++++++++++++++++++++ - vma-writer.c | 940 +++++++++++++++++++++++++++++++++++++++++++++++ - vma.c | 561 ++++++++++++++++++++++++++++ - vma.h | 145 ++++++++ - 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 - create mode 100644 vma.c - create mode 100644 vma.h - -diff --git a/Makefile b/Makefile -index 0d9099a..16f1c25 100644 ---- a/Makefile -+++ b/Makefile -@@ -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 -@@ -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 df64f70..91f133b 100644 ---- a/Makefile.objs -+++ b/Makefile.objs -@@ -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 += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o - block-obj-y += qemu-coroutine-sleep.o -diff --git a/blockdev.c b/blockdev.c -index 84f598d..683f7da 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -21,6 +21,7 @@ - #include "trace.h" - #include "sysemu/arch_init.h" - #include "backup.h" -+#include "vma.h" - - static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); - -@@ -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 : BACKUP_FORMAT_VMA; - -- /* fixme: find driver for specifued format */ - const BackupDriver *driver = NULL; - -- if (!driver) { -+ if (format == BACKUP_FORMAT_VMA) { -+ driver = &backup_vma_driver; -+ } else { - 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..9b715f2 ---- /dev/null -+++ b/docs/specs/vma_spec.txt -@@ -0,0 +1,24 @@ -+=Virtual Machine Archive format (VMA)= -+ -+This format contains a header which includes the VM configuration as -+binary blobs, and a list of devices (dev_id, name). -+ -+The actual VM image data is stored inside extents. An extent contains -+up to 64 clusters, and start with a 512 byte header containing -+additional information for those clusters. -+ -+We use a cluster size of 65536, and use 8 bytes for each -+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 -+ -+We only store non-zero blocks (such block is 4096 bytes). -+ -+Each archive is marked with a uuid. The archive header and all -+extent headers includes that uuid and a MD5 checksum (over header -+data). -+ -+ -diff --git a/vma-reader.c b/vma-reader.c -new file mode 100644 -index 0000000..bc36cba ---- /dev/null -+++ b/vma-reader.c -@@ -0,0 +1,799 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) 2012 Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qemu-common.h" -+#include "qemu/timer.h" -+#include "qemu/ratelimit.h" -+#include "vma.h" -+#include "block/block.h" -+ -+#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT) -+ -+static unsigned char zero_vma_block[VMA_BLOCK_SIZE]; -+ -+typedef struct VmaRestoreState { -+ BlockDriverState *bs; -+ bool write_zeroes; -+ unsigned long *bitmap; -+ int bitmap_size; -+} VmaRestoreState; -+ -+struct VmaReader { -+ int fd; -+ GChecksum *md5csum; -+ GHashTable *blob_hash; -+ unsigned char *head_data; -+ VmaDeviceInfo devinfo[256]; -+ VmaRestoreState rstate[256]; -+ 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 -+g_int32_hash(gconstpointer v) -+{ -+ return *(const uint32_t *)v; -+} -+ -+static gboolean -+g_int32_equal(gconstpointer v1, gconstpointer v2) -+{ -+ return *((const uint32_t *)v1) == *((const uint32_t *)v2); -+} -+ -+static int vma_reader_get_bitmap(VmaRestoreState *rstate, int64_t cluster_num) -+{ -+ assert(rstate); -+ assert(rstate->bitmap); -+ -+ unsigned long val, idx, bit; -+ -+ idx = cluster_num / BITS_PER_LONG; -+ -+ assert(rstate->bitmap_size > idx); -+ -+ bit = cluster_num % BITS_PER_LONG; -+ val = rstate->bitmap[idx]; -+ -+ return !!(val & (1UL << bit)); -+} -+ -+static void vma_reader_set_bitmap(VmaRestoreState *rstate, int64_t cluster_num, -+ int dirty) -+{ -+ assert(rstate); -+ assert(rstate->bitmap); -+ -+ unsigned long val, idx, bit; -+ -+ idx = cluster_num / BITS_PER_LONG; -+ -+ assert(rstate->bitmap_size > idx); -+ -+ bit = cluster_num % BITS_PER_LONG; -+ val = rstate->bitmap[idx]; -+ if (dirty) { -+ if (!(val & (1UL << bit))) { -+ val |= 1UL << bit; -+ } -+ } else { -+ if (val & (1UL << bit)) { -+ val &= ~(1UL << bit); -+ } -+ } -+ rstate->bitmap[idx] = val; -+} -+ -+typedef struct VmaBlob { -+ uint32_t start; -+ uint32_t len; -+ void *data; -+} VmaBlob; -+ -+static const VmaBlob *get_header_blob(VmaReader *vmar, uint32_t pos) -+{ -+ assert(vmar); -+ assert(vmar->blob_hash); -+ -+ return g_hash_table_lookup(vmar->blob_hash, &pos); -+} -+ -+static const char *get_header_str(VmaReader *vmar, uint32_t pos) -+{ -+ const VmaBlob *blob = get_header_blob(vmar, pos); -+ if (!blob) { -+ return NULL; -+ } -+ const char *res = (char *)blob->data; -+ if (res[blob->len-1] != '\0') { -+ return NULL; -+ } -+ return res; -+} -+ -+static ssize_t -+safe_read(int fd, unsigned char *buf, size_t count) -+{ -+ ssize_t n; -+ -+ do { -+ n = read(fd, buf, count); -+ } while (n < 0 && errno == EINTR); -+ -+ return n; -+} -+ -+static ssize_t -+full_read(int fd, unsigned char *buf, size_t len) -+{ -+ ssize_t n; -+ size_t total; -+ -+ total = 0; -+ -+ while (len > 0) { -+ n = safe_read(fd, buf, len); -+ -+ if (n == 0) { -+ return total; -+ } -+ -+ if (n <= 0) { -+ break; -+ } -+ -+ buf += n; -+ total += n; -+ len -= n; -+ } -+ -+ if (len) { -+ return -1; -+ } -+ -+ return total; -+} -+ -+void vma_reader_destroy(VmaReader *vmar) -+{ -+ assert(vmar); -+ -+ if (vmar->fd >= 0) { -+ close(vmar->fd); -+ } -+ -+ if (vmar->cdata_list) { -+ g_list_free(vmar->cdata_list); -+ } -+ -+ int i; -+ for (i = 1; i < 256; i++) { -+ if (vmar->rstate[i].bitmap) { -+ g_free(vmar->rstate[i].bitmap); -+ } -+ } -+ -+ if (vmar->md5csum) { -+ g_checksum_free(vmar->md5csum); -+ } -+ -+ if (vmar->blob_hash) { -+ g_hash_table_destroy(vmar->blob_hash); -+ } -+ -+ if (vmar->head_data) { -+ g_free(vmar->head_data); -+ } -+ -+ g_free(vmar); -+ -+}; -+ -+static int vma_reader_read_head(VmaReader *vmar, Error **errp) -+{ -+ assert(vmar); -+ assert(errp); -+ assert(*errp == NULL); -+ -+ unsigned char md5sum[16]; -+ int i; -+ int ret = 0; -+ -+ vmar->head_data = g_malloc(sizeof(VmaHeader)); -+ -+ if (full_read(vmar->fd, vmar->head_data, sizeof(VmaHeader)) != -+ sizeof(VmaHeader)) { -+ error_setg(errp, "can't read vma header - %s", -+ errno ? g_strerror(errno) : "got EOF"); -+ return -1; -+ } -+ -+ VmaHeader *h = (VmaHeader *)vmar->head_data; -+ -+ if (h->magic != VMA_MAGIC) { -+ error_setg(errp, "not a vma file - wrong magic number"); -+ return -1; -+ } -+ -+ uint32_t header_size = GUINT32_FROM_BE(h->header_size); -+ int need = header_size - sizeof(VmaHeader); -+ if (need <= 0) { -+ error_setg(errp, "wrong vma header size %d", header_size); -+ return -1; -+ } -+ -+ vmar->head_data = g_realloc(vmar->head_data, header_size); -+ h = (VmaHeader *)vmar->head_data; -+ -+ if (full_read(vmar->fd, vmar->head_data + sizeof(VmaHeader), need) != -+ need) { -+ error_setg(errp, "can't read vma header data - %s", -+ errno ? g_strerror(errno) : "got EOF"); -+ return -1; -+ } -+ -+ memcpy(md5sum, h->md5sum, 16); -+ memset(h->md5sum, 0, 16); -+ -+ g_checksum_reset(vmar->md5csum); -+ g_checksum_update(vmar->md5csum, vmar->head_data, header_size); -+ gsize csize = 16; -+ g_checksum_get_digest(vmar->md5csum, (guint8 *)(h->md5sum), &csize); -+ -+ if (memcmp(md5sum, h->md5sum, 16) != 0) { -+ error_setg(errp, "wrong vma header chechsum"); -+ return -1; -+ } -+ -+ /* we can modify header data after checksum verify */ -+ h->header_size = header_size; -+ -+ h->version = GUINT32_FROM_BE(h->version); -+ if (h->version != 1) { -+ error_setg(errp, "wrong vma version %d", h->version); -+ return -1; -+ } -+ -+ h->ctime = GUINT64_FROM_BE(h->ctime); -+ h->blob_buffer_offset = GUINT32_FROM_BE(h->blob_buffer_offset); -+ h->blob_buffer_size = GUINT32_FROM_BE(h->blob_buffer_size); -+ -+ uint32_t bstart = h->blob_buffer_offset + 1; -+ uint32_t bend = h->blob_buffer_offset + h->blob_buffer_size; -+ -+ if (bstart <= sizeof(VmaHeader)) { -+ error_setg(errp, "wrong vma blob buffer offset %d", -+ h->blob_buffer_offset); -+ return -1; -+ } -+ -+ if (bend > header_size) { -+ error_setg(errp, "wrong vma blob buffer size %d/%d", -+ h->blob_buffer_offset, h->blob_buffer_size); -+ return -1; -+ } -+ -+ while ((bstart + 2) <= bend) { -+ uint32_t size = vmar->head_data[bstart] + -+ (vmar->head_data[bstart+1] << 8); -+ if ((bstart + size + 2) <= bend) { -+ VmaBlob *blob = g_new0(VmaBlob, 1); -+ blob->start = bstart - h->blob_buffer_offset; -+ blob->len = size; -+ blob->data = vmar->head_data + bstart + 2; -+ g_hash_table_insert(vmar->blob_hash, &blob->start, blob); -+ } -+ bstart += size + 2; -+ } -+ -+ -+ int count = 0; -+ for (i = 1; i < 256; i++) { -+ VmaDeviceInfoHeader *dih = &h->dev_info[i]; -+ uint32_t devname_ptr = GUINT32_FROM_BE(dih->devname_ptr); -+ uint64_t size = GUINT64_FROM_BE(dih->size); -+ const char *devname = get_header_str(vmar, devname_ptr); -+ -+ if (size && devname) { -+ count++; -+ vmar->devinfo[i].size = size; -+ vmar->devinfo[i].devname = devname; -+ -+ if (strcmp(devname, "vmstate") == 0) { -+ vmar->vmstate_stream = i; -+ } -+ } -+ } -+ -+ if (!count) { -+ error_setg(errp, "vma does not contain data"); -+ return -1; -+ } -+ -+ for (i = 0; i < VMA_MAX_CONFIGS; i++) { -+ uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]); -+ uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]); -+ -+ if (!(name_ptr && data_ptr)) { -+ continue; -+ } -+ const char *name = get_header_str(vmar, name_ptr); -+ const VmaBlob *blob = get_header_blob(vmar, data_ptr); -+ -+ if (!(name && blob)) { -+ error_setg(errp, "vma contains invalid data pointers"); -+ return -1; -+ } -+ -+ VmaConfigData *cdata = g_new0(VmaConfigData, 1); -+ cdata->name = name; -+ cdata->data = blob->data; -+ cdata->len = blob->len; -+ -+ vmar->cdata_list = g_list_append(vmar->cdata_list, cdata); -+ } -+ -+ return ret; -+}; -+ -+VmaReader *vma_reader_create(const char *filename, Error **errp) -+{ -+ assert(filename); -+ assert(errp); -+ -+ VmaReader *vmar = g_new0(VmaReader, 1); -+ -+ 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, -+ g_strerror(errno)); -+ goto err; -+ } -+ -+ vmar->md5csum = g_checksum_new(G_CHECKSUM_MD5); -+ if (!vmar->md5csum) { -+ error_setg(errp, "can't allocate cmsum\n"); -+ goto err; -+ } -+ -+ vmar->blob_hash = g_hash_table_new_full(g_int32_hash, g_int32_equal, -+ NULL, g_free); -+ -+ if (vma_reader_read_head(vmar, errp) < 0) { -+ goto err; -+ } -+ -+ return vmar; -+ -+err: -+ if (vmar) { -+ vma_reader_destroy(vmar); -+ } -+ -+ return NULL; -+} -+ -+VmaHeader *vma_reader_get_header(VmaReader *vmar) -+{ -+ assert(vmar); -+ assert(vmar->head_data); -+ -+ return (VmaHeader *)(vmar->head_data); -+} -+ -+GList *vma_reader_get_config_data(VmaReader *vmar) -+{ -+ assert(vmar); -+ assert(vmar->head_data); -+ -+ return vmar->cdata_list; -+} -+ -+VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id) -+{ -+ assert(vmar); -+ assert(dev_id); -+ -+ if (vmar->devinfo[dev_id].size && vmar->devinfo[dev_id].devname) { -+ return &vmar->devinfo[dev_id]; -+ } -+ -+ return NULL; -+} -+ -+int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, -+ bool write_zeroes, Error **errp) -+{ -+ assert(vmar); -+ assert(bs != NULL); -+ assert(dev_id); -+ assert(vmar->rstate[dev_id].bs == NULL); -+ -+ int64_t size = bdrv_getlength(bs); -+ if (size != vmar->devinfo[dev_id].size) { -+ error_setg(errp, "vma_reader_register_bs for stream %s failed - " -+ "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, -+ size, vmar->devinfo[dev_id].size); -+ return -1; -+ } -+ -+ vmar->rstate[dev_id].bs = bs; -+ vmar->rstate[dev_id].write_zeroes = write_zeroes; -+ -+ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + -+ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; -+ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; -+ -+ 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; -+} -+ -+static ssize_t safe_write(int fd, void *buf, size_t count) -+{ -+ ssize_t n; -+ -+ do { -+ n = write(fd, buf, count); -+ } while (n < 0 && errno == EINTR); -+ -+ return n; -+} -+ -+static size_t full_write(int fd, void *buf, size_t len) -+{ -+ ssize_t n; -+ size_t total; -+ -+ total = 0; -+ -+ while (len > 0) { -+ n = safe_write(fd, buf, len); -+ if (n < 0) { -+ return n; -+ } -+ buf += n; -+ total += n; -+ len -= n; -+ } -+ -+ if (len) { -+ /* incomplete write ? */ -+ return -1; -+ } -+ -+ return total; -+} -+ -+static int restore_write_data(VmaReader *vmar, guint8 dev_id, -+ BlockDriverState *bs, int vmstate_fd, -+ unsigned char *buf, int64_t sector_num, -+ int nb_sectors, Error **errp) -+{ -+ assert(vmar); -+ -+ if (dev_id == vmar->vmstate_stream) { -+ if (vmstate_fd >= 0) { -+ int len = nb_sectors * BDRV_SECTOR_SIZE; -+ int res = full_write(vmstate_fd, buf, len); -+ if (res < 0) { -+ error_setg(errp, "write vmstate failed %d", res); -+ return -1; -+ } -+ } -+ } else { -+ int res = bdrv_write(bs, sector_num, buf, nb_sectors); -+ if (res < 0) { -+ error_setg(errp, "bdrv_write to %s failed (%d)", -+ bdrv_get_device_name(bs), res); -+ return -1; -+ } -+ } -+ return 0; -+} -+static int restore_extent(VmaReader *vmar, unsigned char *buf, -+ int extent_size, int vmstate_fd, -+ bool verbose, Error **errp) -+{ -+ assert(vmar); -+ assert(buf); -+ -+ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; -+ int start = VMA_EXTENT_HEADER_SIZE; -+ int i; -+ -+ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { -+ uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]); -+ 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; -+ -+ if (!dev_id) { -+ continue; -+ } -+ -+ VmaRestoreState *rstate = &vmar->rstate[dev_id]; -+ BlockDriverState *bs = NULL; -+ -+ if (dev_id != vmar->vmstate_stream) { -+ bs = rstate->bs; -+ if (!bs) { -+ error_setg(errp, "got wrong dev id %d", dev_id); -+ return -1; -+ } -+ -+ if (vma_reader_get_bitmap(rstate, cluster_num)) { -+ error_setg(errp, "found duplicated cluster %zd for stream %s", -+ cluster_num, vmar->devinfo[dev_id].devname); -+ return -1; -+ } -+ vma_reader_set_bitmap(rstate, cluster_num, 1); -+ -+ max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE; -+ } else { -+ max_sector = G_MAXINT64; -+ if (cluster_num != vmar->vmstate_clusters) { -+ error_setg(errp, "found out of order vmstate data"); -+ return -1; -+ } -+ 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) { -+ error_setg(errp, "short vma extent - too many blocks"); -+ return -1; -+ } -+ int64_t sector_num = (cluster_num * VMA_CLUSTER_SIZE) / -+ BDRV_SECTOR_SIZE; -+ int64_t end_sector = sector_num + -+ VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE; -+ -+ if (end_sector > max_sector) { -+ end_sector = max_sector; -+ } -+ -+ if (end_sector <= sector_num) { -+ error_setg(errp, "got wrong block address - write bejond end"); -+ return -1; -+ } -+ -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, -+ sector_num, nb_sectors, errp) < 0) { -+ return -1; -+ } -+ -+ start += VMA_CLUSTER_SIZE; -+ } else { -+ int j; -+ int bit = 1; -+ -+ for (j = 0; j < 16; j++) { -+ int64_t sector_num = (cluster_num*VMA_CLUSTER_SIZE + -+ j*VMA_BLOCK_SIZE)/BDRV_SECTOR_SIZE; -+ -+ int64_t end_sector = sector_num + -+ VMA_BLOCK_SIZE/BDRV_SECTOR_SIZE; -+ if (end_sector > max_sector) { -+ end_sector = max_sector; -+ } -+ -+ if (mask & bit) { -+ if ((start + VMA_BLOCK_SIZE) > extent_size) { -+ error_setg(errp, "short vma extent - too many blocks"); -+ return -1; -+ } -+ -+ if (end_sector <= sector_num) { -+ error_setg(errp, "got wrong block address - " -+ "write bejond end"); -+ return -1; -+ } -+ -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ buf + start, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } -+ -+ start += VMA_BLOCK_SIZE; -+ -+ } else { -+ -+ if (rstate->write_zeroes && (end_sector > sector_num)) { -+ /* Todo: use bdrv_co_write_zeroes (but that need to -+ * be run inside coroutine?) -+ */ -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ zero_vma_block, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } -+ } -+ } -+ -+ bit = bit << 1; -+ } -+ } -+ } -+ -+ if (start != extent_size) { -+ error_setg(errp, "vma extent error - missing blocks"); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, -+ Error **errp) -+{ -+ assert(vmar); -+ assert(vmar->head_data); -+ -+ int ret = 0; -+ unsigned char buf[VMA_MAX_EXTENT_SIZE]; -+ int buf_pos = 0; -+ 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); -+ if (bytes < 0) { -+ error_setg(errp, "read failed - %s", g_strerror(errno)); -+ return -1; -+ } -+ -+ buf_pos += bytes; -+ -+ if (!buf_pos) { -+ break; /* EOF */ -+ } -+ -+ if (buf_pos < VMA_EXTENT_HEADER_SIZE) { -+ error_setg(errp, "read short extent (%d bytes)", buf_pos); -+ return -1; -+ } -+ -+ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; -+ -+ /* extract md5sum */ -+ memcpy(md5sum, ehead->md5sum, sizeof(ehead->md5sum)); -+ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); -+ -+ g_checksum_reset(vmar->md5csum); -+ g_checksum_update(vmar->md5csum, buf, VMA_EXTENT_HEADER_SIZE); -+ gsize csize = 16; -+ g_checksum_get_digest(vmar->md5csum, ehead->md5sum, &csize); -+ -+ if (memcmp(md5sum, ehead->md5sum, 16) != 0) { -+ error_setg(errp, "wrong vma extent header chechsum"); -+ return -1; -+ } -+ -+ if (memcmp(h->uuid, ehead->uuid, sizeof(ehead->uuid)) != 0) { -+ error_setg(errp, "wrong vma extent uuid"); -+ return -1; -+ } -+ -+ if (ehead->magic != VMA_EXTENT_MAGIC || ehead->reserved1 != 0) { -+ error_setg(errp, "wrong vma extent header magic"); -+ return -1; -+ } -+ -+ int block_count = GUINT16_FROM_BE(ehead->block_count); -+ int extent_size = VMA_EXTENT_HEADER_SIZE + block_count*VMA_BLOCK_SIZE; -+ -+ if (buf_pos < extent_size) { -+ error_setg(errp, "short vma extent (%d < %d)", buf_pos, -+ extent_size); -+ return -1; -+ } -+ -+ if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, -+ errp) < 0) { -+ return -1; -+ } -+ -+ if (buf_pos > extent_size) { -+ memmove(buf, buf + extent_size, buf_pos - extent_size); -+ buf_pos = buf_pos - extent_size; -+ } else { -+ buf_pos = 0; -+ } -+ } -+ -+ bdrv_drain_all(); -+ -+ int i; -+ for (i = 1; i < 256; i++) { -+ VmaRestoreState *rstate = &vmar->rstate[i]; -+ if (!rstate->bs) { -+ continue; -+ } -+ -+ if (bdrv_flush(rstate->bs) < 0) { -+ error_setg(errp, "vma bdrv_flush %s failed", -+ vmar->devinfo[i].devname); -+ return -1; -+ } -+ -+ if (vmar->devinfo[i].size && -+ (strcmp(vmar->devinfo[i].devname, "vmstate") != 0)) { -+ assert(rstate->bitmap); -+ -+ int64_t cluster_num, end; -+ -+ end = (vmar->devinfo[i].size + VMA_CLUSTER_SIZE - 1) / -+ VMA_CLUSTER_SIZE; -+ -+ for (cluster_num = 0; cluster_num < end; cluster_num++) { -+ if (!vma_reader_get_bitmap(rstate, cluster_num)) { -+ error_setg(errp, "detected missing cluster %zd " -+ "for stream %s", cluster_num, -+ vmar->devinfo[i].devname); -+ return -1; -+ } -+ } -+ } -+ } -+ -+ return ret; -+} -+ -diff --git a/vma-writer.c b/vma-writer.c -new file mode 100644 -index 0000000..9228ca6 ---- /dev/null -+++ b/vma-writer.c -@@ -0,0 +1,940 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) 2012 Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qemu-common.h" -+#include "vma.h" -+#include "block/block.h" -+#include "monitor/monitor.h" -+ -+#define DEBUG_VMA 0 -+ -+#define DPRINTF(fmt, ...)\ -+ do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0) -+ -+#define WRITE_BUFFERS 5 -+ -+typedef struct VmaAIOCB VmaAIOCB; -+struct VmaAIOCB { -+ unsigned char buffer[VMA_MAX_EXTENT_SIZE]; -+ VmaWriter *vmaw; -+ size_t bytes; -+ Coroutine *co; -+}; -+ -+struct VmaWriter { -+ int fd; -+ FILE *cmd; -+ int status; -+ char errmsg[8192]; -+ uuid_t uuid; -+ bool header_written; -+ bool closed; -+ -+ /* we always write extents */ -+ unsigned char outbuf[VMA_MAX_EXTENT_SIZE]; -+ int outbuf_pos; /* in bytes */ -+ int outbuf_count; /* in VMA_BLOCKS */ -+ uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; -+ -+ VmaAIOCB *aiocbs[WRITE_BUFFERS]; -+ CoQueue wqueue; -+ -+ GChecksum *md5csum; -+ CoMutex writer_lock; -+ CoMutex flush_lock; -+ Coroutine *co_writer; -+ -+ /* drive informations */ -+ VmaStreamInfo stream_info[256]; -+ guint stream_count; -+ -+ guint8 vmstate_stream; -+ uint32_t vmstate_clusters; -+ -+ /* header blob table */ -+ char *header_blob_table; -+ uint32_t header_blob_table_size; -+ uint32_t header_blob_table_pos; -+ -+ /* store for config blobs */ -+ uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ -+ uint32_t config_data[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ -+ uint32_t config_count; -+}; -+ -+void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) -+{ -+ va_list ap; -+ -+ if (vmaw->status < 0) { -+ return; -+ } -+ -+ vmaw->status = -1; -+ -+ va_start(ap, fmt); -+ g_vsnprintf(vmaw->errmsg, sizeof(vmaw->errmsg), fmt, ap); -+ va_end(ap); -+ -+ DPRINTF("vma_writer_set_error: %s\n", vmaw->errmsg); -+} -+ -+static uint32_t allocate_header_blob(VmaWriter *vmaw, const char *data, -+ size_t len) -+{ -+ if (len > 65535) { -+ return 0; -+ } -+ -+ if (!vmaw->header_blob_table || -+ (vmaw->header_blob_table_size < -+ (vmaw->header_blob_table_pos + len + 2))) { -+ int newsize = vmaw->header_blob_table_size + ((len + 2 + 511)/512)*512; -+ -+ vmaw->header_blob_table = g_realloc(vmaw->header_blob_table, newsize); -+ memset(vmaw->header_blob_table + vmaw->header_blob_table_size, -+ 0, newsize - vmaw->header_blob_table_size); -+ vmaw->header_blob_table_size = newsize; -+ } -+ -+ uint32_t cpos = vmaw->header_blob_table_pos; -+ vmaw->header_blob_table[cpos] = len & 255; -+ vmaw->header_blob_table[cpos+1] = (len >> 8) & 255; -+ memcpy(vmaw->header_blob_table + cpos + 2, data, len); -+ vmaw->header_blob_table_pos += len + 2; -+ return cpos; -+} -+ -+static uint32_t allocate_header_string(VmaWriter *vmaw, const char *str) -+{ -+ assert(vmaw); -+ -+ size_t len = strlen(str) + 1; -+ -+ return allocate_header_blob(vmaw, str, len); -+} -+ -+int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, -+ gsize len) -+{ -+ assert(vmaw); -+ assert(!vmaw->header_written); -+ assert(vmaw->config_count < VMA_MAX_CONFIGS); -+ assert(name); -+ assert(data); -+ assert(len); -+ -+ uint32_t name_ptr = allocate_header_string(vmaw, name); -+ if (!name_ptr) { -+ return -1; -+ } -+ -+ uint32_t data_ptr = allocate_header_blob(vmaw, data, len); -+ if (!data_ptr) { -+ return -1; -+ } -+ -+ vmaw->config_names[vmaw->config_count] = name_ptr; -+ vmaw->config_data[vmaw->config_count] = data_ptr; -+ -+ vmaw->config_count++; -+ -+ return 0; -+} -+ -+int vma_writer_register_stream(VmaWriter *vmaw, const char *devname, -+ size_t size) -+{ -+ assert(vmaw); -+ assert(devname); -+ assert(!vmaw->status); -+ -+ if (vmaw->header_written) { -+ vma_writer_set_error(vmaw, "vma_writer_register_stream: header " -+ "already written"); -+ return -1; -+ } -+ -+ guint n = vmaw->stream_count + 1; -+ -+ /* we can have dev_ids form 1 to 255 (0 reserved) -+ * 255(-1) reseverd for safety -+ */ -+ if (n > 254) { -+ vma_writer_set_error(vmaw, "vma_writer_register_stream: " -+ "too many drives"); -+ return -1; -+ } -+ -+ if (size <= 0) { -+ vma_writer_set_error(vmaw, "vma_writer_register_stream: " -+ "got strange size %zd", size); -+ return -1; -+ } -+ -+ DPRINTF("vma_writer_register_stream %s %zu %d\n", devname, size, n); -+ -+ vmaw->stream_info[n].devname = g_strdup(devname); -+ vmaw->stream_info[n].size = size; -+ -+ vmaw->stream_info[n].cluster_count = (size + VMA_CLUSTER_SIZE - 1) / -+ VMA_CLUSTER_SIZE; -+ -+ vmaw->stream_count = n; -+ -+ if (strcmp(devname, "vmstate") == 0) { -+ vmaw->vmstate_stream = n; -+ } -+ -+ return n; -+} -+ -+static void vma_co_continue_write(void *opaque) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ 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) -+{ -+ size_t done = 0; -+ ssize_t ret; -+ -+ /* atomic writes (we cannot interleave writes) */ -+ qemu_co_mutex_lock(&vmaw->writer_lock); -+ -+ 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) { -+ done += ret; -+ DPRINTF("vma_co_write written %zd %zd\n", done, ret); -+ } else if (ret < 0) { -+ if (errno == EAGAIN || errno == EWOULDBLOCK) { -+ DPRINTF("vma_co_write yield %zd\n", done); -+ qemu_coroutine_yield(); -+ DPRINTF("vma_co_write restart %zd\n", done); -+ } else { -+ vma_writer_set_error(vmaw, "vma_co_write write error - %s", -+ g_strerror(errno)); -+ done = -1; /* always return failure for partial writes */ -+ break; -+ } -+ } else if (ret == 0) { -+ /* should not happen - simply try again */ -+ } -+ } -+ -+ 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); -+ return done; -+} -+ -+static void coroutine_fn vma_co_writer_task(void *opaque) -+{ -+ VmaAIOCB *cb = opaque; -+ -+ DPRINTF("vma_co_writer_task start\n"); -+ -+ int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); -+ DPRINTF("vma_co_writer_task write done %zd\n", done); -+ -+ if (done != cb->bytes) { -+ DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); -+ vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", -+ done); -+ } -+ -+ cb->bytes = 0; -+ -+ qemu_co_queue_next(&cb->vmaw->wqueue); -+ -+ DPRINTF("vma_co_writer_task end\n"); -+} -+ -+static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) -+{ -+ DPRINTF("vma_queue_flush enter\n"); -+ -+ assert(vmaw); -+ -+ while (1) { -+ int i; -+ VmaAIOCB *cb = NULL; -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ if (vmaw->aiocbs[i]->bytes) { -+ cb = vmaw->aiocbs[i]; -+ DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, -+ vmaw->aiocbs[i]->bytes); -+ break; -+ } -+ } -+ if (!cb) { -+ break; -+ } -+ qemu_co_queue_wait(&vmaw->wqueue); -+ } -+ -+ DPRINTF("vma_queue_flush leave\n"); -+} -+ -+/** -+ * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') -+ * So we need to create a coroutione to allow 'parallel' execution. -+ */ -+static ssize_t coroutine_fn -+vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) -+{ -+ DPRINTF("vma_queue_write enter %zd\n", bytes); -+ -+ assert(vmaw); -+ assert(buf); -+ assert(bytes <= VMA_MAX_EXTENT_SIZE); -+ -+ VmaAIOCB *cb = NULL; -+ while (!cb) { -+ int i; -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ if (!vmaw->aiocbs[i]->bytes) { -+ cb = vmaw->aiocbs[i]; -+ break; -+ } -+ } -+ if (!cb) { -+ qemu_co_queue_wait(&vmaw->wqueue); -+ } -+ } -+ -+ memcpy(cb->buffer, buf, bytes); -+ cb->bytes = bytes; -+ cb->vmaw = vmaw; -+ -+ DPRINTF("vma_queue_write start %zd\n", bytes); -+ cb->co = qemu_coroutine_create(vma_co_writer_task); -+ qemu_coroutine_enter(cb->co, cb); -+ -+ DPRINTF("vma_queue_write leave\n"); -+ -+ return bytes; -+} -+ -+VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp) -+{ -+ const char *p; -+ -+ assert(sizeof(VmaHeader) == (4096 + 8192)); -+ assert(sizeof(VmaExtentHeader) == 512); -+ -+ VmaWriter *vmaw = g_new0(VmaWriter, 1); -+ vmaw->fd = -1; -+ -+ vmaw->md5csum = g_checksum_new(G_CHECKSUM_MD5); -+ if (!vmaw->md5csum) { -+ error_setg(errp, "can't allocate cmsum\n"); -+ goto err; -+ } -+ -+ if (strstart(filename, "exec:", &p)) { -+ vmaw->cmd = popen(p, "w"); -+ if (vmaw->cmd == NULL) { -+ error_setg(errp, "can't popen command '%s' - %s\n", p, -+ g_strerror(errno)); -+ goto err; -+ } -+ vmaw->fd = fileno(vmaw->cmd); -+ -+ /* 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 { -+ 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, -+ g_strerror(errno)); -+ goto err; -+ } -+ } -+ -+ /* 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; -+ -+ vmaw->header_blob_table_pos = 1; /* start at pos 1 */ -+ -+ qemu_co_mutex_init(&vmaw->writer_lock); -+ qemu_co_mutex_init(&vmaw->flush_lock); -+ qemu_co_queue_init(&vmaw->wqueue); -+ -+ uuid_copy(vmaw->uuid, uuid); -+ -+ return vmaw; -+ -+err: -+ if (vmaw) { -+ if (vmaw->cmd) { -+ pclose(vmaw->cmd); -+ } else if (vmaw->fd >= 0) { -+ close(vmaw->fd); -+ } -+ -+ if (vmaw->md5csum) { -+ g_checksum_free(vmaw->md5csum); -+ } -+ -+ g_free(vmaw); -+ } -+ -+ return NULL; -+} -+ -+static int coroutine_fn vma_write_header(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ int header_clusters = 8; -+ char buf[65536*header_clusters]; -+ VmaHeader *head = (VmaHeader *)buf; -+ -+ int i; -+ -+ DPRINTF("VMA WRITE HEADER\n"); -+ -+ if (vmaw->status < 0) { -+ return vmaw->status; -+ } -+ -+ memset(buf, 0, sizeof(buf)); -+ -+ head->magic = VMA_MAGIC; -+ head->version = GUINT32_TO_BE(1); /* v1 */ -+ memcpy(head->uuid, vmaw->uuid, 16); -+ -+ time_t ctime = time(NULL); -+ head->ctime = GUINT64_TO_BE(ctime); -+ -+ if (!vmaw->stream_count) { -+ return -1; -+ } -+ -+ for (i = 0; i < VMA_MAX_CONFIGS; i++) { -+ head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]); -+ head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]); -+ } -+ -+ /* 32 bytes per device (12 used currently) = 8192 bytes max */ -+ for (i = 1; i <= 254; i++) { -+ VmaStreamInfo *si = &vmaw->stream_info[i]; -+ if (si->size) { -+ assert(si->devname); -+ uint32_t devname_ptr = allocate_header_string(vmaw, si->devname); -+ if (!devname_ptr) { -+ return -1; -+ } -+ head->dev_info[i].devname_ptr = GUINT32_TO_BE(devname_ptr); -+ head->dev_info[i].size = GUINT64_TO_BE(si->size); -+ } -+ } -+ -+ uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size; -+ head->header_size = GUINT32_TO_BE(header_size); -+ -+ if (header_size > sizeof(buf)) { -+ return -1; /* just to be sure */ -+ } -+ -+ uint32_t blob_buffer_offset = sizeof(VmaHeader); -+ memcpy(buf + blob_buffer_offset, vmaw->header_blob_table, -+ vmaw->header_blob_table_size); -+ head->blob_buffer_offset = GUINT32_TO_BE(blob_buffer_offset); -+ head->blob_buffer_size = GUINT32_TO_BE(vmaw->header_blob_table_pos); -+ -+ g_checksum_reset(vmaw->md5csum); -+ g_checksum_update(vmaw->md5csum, (const guchar *)buf, header_size); -+ gsize csize = 16; -+ g_checksum_get_digest(vmaw->md5csum, (guint8 *)(head->md5sum), &csize); -+ -+ return vma_queue_write(vmaw, buf, header_size); -+} -+ -+static int coroutine_fn vma_writer_flush(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ -+ int ret; -+ int i; -+ -+ if (vmaw->status < 0) { -+ return vmaw->status; -+ } -+ -+ if (!vmaw->header_written) { -+ vmaw->header_written = true; -+ ret = vma_write_header(vmaw); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_flush: write header failed"); -+ return ret; -+ } -+ } -+ -+ DPRINTF("VMA WRITE FLUSH %d %d\n", vmaw->outbuf_count, vmaw->outbuf_pos); -+ -+ -+ VmaExtentHeader *ehead = (VmaExtentHeader *)vmaw->outbuf; -+ -+ ehead->magic = VMA_EXTENT_MAGIC; -+ ehead->reserved1 = 0; -+ -+ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { -+ ehead->blockinfo[i] = GUINT64_TO_BE(vmaw->outbuf_block_info[i]); -+ } -+ -+ guint16 block_count = (vmaw->outbuf_pos - VMA_EXTENT_HEADER_SIZE) / -+ VMA_BLOCK_SIZE; -+ -+ ehead->block_count = GUINT16_TO_BE(block_count); -+ -+ memcpy(ehead->uuid, vmaw->uuid, sizeof(ehead->uuid)); -+ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); -+ -+ g_checksum_reset(vmaw->md5csum); -+ g_checksum_update(vmaw->md5csum, vmaw->outbuf, VMA_EXTENT_HEADER_SIZE); -+ gsize csize = 16; -+ g_checksum_get_digest(vmaw->md5csum, ehead->md5sum, &csize); -+ -+ int bytes = vmaw->outbuf_pos; -+ ret = vma_queue_write(vmaw, vmaw->outbuf, bytes); -+ if (ret != bytes) { -+ vma_writer_set_error(vmaw, "vma_writer_flush: failed write"); -+ } -+ -+ vmaw->outbuf_count = 0; -+ vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE; -+ -+ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { -+ vmaw->outbuf_block_info[i] = 0; -+ } -+ -+ return vmaw->status; -+} -+ -+static int vma_count_open_streams(VmaWriter *vmaw) -+{ -+ g_assert(vmaw != NULL); -+ -+ int i; -+ int open_drives = 0; -+ for (i = 0; i <= 255; i++) { -+ if (vmaw->stream_info[i].size && !vmaw->stream_info[i].finished) { -+ open_drives++; -+ } -+ } -+ -+ return open_drives; -+} -+ -+/** -+ * all jobs should call this when there is no more data -+ * Returns: number of remaining stream (0 ==> finished) -+ */ -+int coroutine_fn -+vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id) -+{ -+ g_assert(vmaw != NULL); -+ -+ DPRINTF("vma_writer_set_status %d\n", dev_id); -+ if (!vmaw->stream_info[dev_id].size) { -+ vma_writer_set_error(vmaw, "vma_writer_close_stream: " -+ "no such stream %d", dev_id); -+ return -1; -+ } -+ if (vmaw->stream_info[dev_id].finished) { -+ vma_writer_set_error(vmaw, "vma_writer_close_stream: " -+ "stream already closed %d", dev_id); -+ return -1; -+ } -+ -+ vmaw->stream_info[dev_id].finished = true; -+ -+ int open_drives = vma_count_open_streams(vmaw); -+ -+ if (open_drives <= 0) { -+ DPRINTF("vma_writer_set_status all drives completed\n"); -+ qemu_co_mutex_lock(&vmaw->flush_lock); -+ int ret = vma_writer_flush(vmaw); -+ qemu_co_mutex_unlock(&vmaw->flush_lock); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed"); -+ } -+ } -+ -+ return open_drives; -+} -+ -+int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status) -+{ -+ int i; -+ -+ g_assert(vmaw != NULL); -+ -+ if (status) { -+ status->status = vmaw->status; -+ g_strlcpy(status->errmsg, vmaw->errmsg, sizeof(status->errmsg)); -+ for (i = 0; i <= 255; i++) { -+ status->stream_info[i] = vmaw->stream_info[i]; -+ } -+ -+ uuid_unparse_lower(vmaw->uuid, status->uuid_str); -+ } -+ -+ status->closed = vmaw->closed; -+ -+ return vmaw->status; -+} -+ -+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)) { -+ ret = vma_writer_flush(vmaw); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed"); -+ break; -+ } -+ } -+ -+ qemu_co_mutex_unlock(&vmaw->flush_lock); -+ -+ return ret; -+} -+ -+ -+int64_t coroutine_fn -+vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num, -+ unsigned char *buf, size_t *zero_bytes) -+{ -+ g_assert(vmaw != NULL); -+ g_assert(zero_bytes != NULL); -+ -+ *zero_bytes = 0; -+ -+ if (vmaw->status < 0) { -+ return vmaw->status; -+ } -+ -+ if (!dev_id || !vmaw->stream_info[dev_id].size) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "no such stream %d", dev_id); -+ return -1; -+ } -+ -+ if (vmaw->stream_info[dev_id].finished) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "stream already closed %d", dev_id); -+ return -1; -+ } -+ -+ -+ if (cluster_num >= (((uint64_t)1)<<32)) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "cluster number out of range"); -+ return -1; -+ } -+ -+ if (dev_id == vmaw->vmstate_stream) { -+ if (cluster_num != vmaw->vmstate_clusters) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "non sequential vmstate write"); -+ } -+ vmaw->vmstate_clusters++; -+ } else if (cluster_num >= vmaw->stream_info[dev_id].cluster_count) { -+ vma_writer_set_error(vmaw, "vma_writer_write: cluster number too big"); -+ return -1; -+ } -+ -+ /* wait until buffer is available */ -+ if (vma_writer_get_buffer(vmaw) < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "vma_writer_get_buffer failed"); -+ return -1; -+ } -+ -+ DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num); -+ -+ uint16_t mask = 0; -+ -+ 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); -+ block_info |= ((uint64_t)dev_id) << 32; -+ block_info |= (cluster_num & 0xffffffff); -+ vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info; -+ -+ DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info); -+ -+ vmaw->outbuf_count++; -+ -+ /** NOTE: We allways write whole clusters, but we correctly set -+ * transferred bytes. So transferred == size when when everything -+ * went OK. -+ */ -+ size_t transferred = VMA_CLUSTER_SIZE; -+ -+ if (dev_id != vmaw->vmstate_stream) { -+ uint64_t last = (cluster_num + 1) * VMA_CLUSTER_SIZE; -+ if (last > vmaw->stream_info[dev_id].size) { -+ uint64_t diff = last - vmaw->stream_info[dev_id].size; -+ if (diff >= VMA_CLUSTER_SIZE) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "read after last cluster"); -+ return -1; -+ } -+ transferred -= diff; -+ } -+ } -+ -+ vmaw->stream_info[dev_id].transferred += transferred; -+ -+ return transferred; -+} -+ -+int vma_writer_close(VmaWriter *vmaw, Error **errp) -+{ -+ g_assert(vmaw != NULL); -+ -+ int i; -+ -+ vma_queue_flush(vmaw); -+ -+ /* this should not happen - just to be sure */ -+ while (!qemu_co_queue_empty(&vmaw->wqueue)) { -+ DPRINTF("vma_writer_close wait\n"); -+ co_sleep_ns(rt_clock, 1000000); -+ } -+ -+ if (vmaw->cmd) { -+ if (pclose(vmaw->cmd) < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "pclose failed - %s", g_strerror(errno)); -+ } -+ } else { -+ if (close(vmaw->fd) < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "close failed - %s", g_strerror(errno)); -+ } -+ } -+ -+ for (i = 0; i <= 255; i++) { -+ VmaStreamInfo *si = &vmaw->stream_info[i]; -+ if (si->size) { -+ if (!si->finished) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "detected open stream '%s'", si->devname); -+ } else if ((si->transferred != si->size) && -+ (i != vmaw->vmstate_stream)) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "incomplete stream '%s' (%zd != %zd)", -+ si->devname, si->transferred, si->size); -+ } -+ } -+ } -+ -+ for (i = 0; i <= 255; i++) { -+ vmaw->stream_info[i].finished = 1; /* mark as closed */ -+ } -+ -+ vmaw->closed = 1; -+ -+ if (vmaw->status < 0 && *errp == NULL) { -+ error_setg(errp, "%s", vmaw->errmsg); -+ } -+ -+ return vmaw->status; -+} -+ -+void vma_writer_destroy(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ -+ int i; -+ -+ for (i = 0; i <= 255; i++) { -+ if (vmaw->stream_info[i].devname) { -+ g_free(vmaw->stream_info[i].devname); -+ } -+ } -+ -+ if (vmaw->md5csum) { -+ g_checksum_free(vmaw->md5csum); -+ } -+ -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ free(vmaw->aiocbs[i]); -+ } -+ -+ g_free(vmaw); -+} -+ -+/* backup driver plugin */ -+ -+static int vma_dump_cb(void *opaque, uint8_t dev_id, int64_t cluster_num, -+ unsigned char *buf, size_t *zero_bytes) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ return vma_writer_write(vmaw, dev_id, cluster_num, buf, zero_bytes); -+} -+ -+static int vma_close_cb(void *opaque, Error **errp) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ int res = vma_writer_close(vmaw, errp); -+ vma_writer_destroy(vmaw); -+ -+ return res; -+} -+ -+static int vma_complete_cb(void *opaque, uint8_t dev_id, int ret) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "backup_complete_cb %d", ret); -+ } -+ -+ return vma_writer_close_stream(vmaw, dev_id); -+} -+ -+static int vma_register_stream_cb(void *opaque, const char *devname, -+ size_t size) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ return vma_writer_register_stream(vmaw, devname, size); -+} -+ -+static int vma_register_config_cb(void *opaque, const char *name, -+ gpointer data, size_t data_len) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ return vma_writer_add_config(vmaw, name, data, data_len); -+} -+ -+static void *vma_open_cb(const char *filename, uuid_t uuid, Error **errp) -+{ -+ return vma_writer_create(filename, uuid, errp); -+} -+ -+const BackupDriver backup_vma_driver = { -+ .format = "vma", -+ .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..6633aa5 ---- /dev/null -+++ b/vma.c -@@ -0,0 +1,561 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) 2012 Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qemu-common.h" -+#include "qemu/error-report.h" -+#include "vma.h" -+#include "block/block.h" -+ -+static void help(void) -+{ -+ const char *help_msg = -+ "usage: vma command [command options]\n" -+ "\n" -+ "vma list \n" -+ "vma create [-c config] pathname ...\n" -+ "vma extract [-r] \n" -+ ; -+ -+ printf("%s", help_msg); -+ exit(1); -+} -+ -+static const char *extract_devname(const char *path, char **devname, int index) -+{ -+ assert(path); -+ -+ const char *sep = strchr(path, '='); -+ -+ if (sep) { -+ *devname = g_strndup(path, sep - path); -+ path = sep + 1; -+ } else { -+ if (index >= 0) { -+ *devname = g_strdup_printf("disk%d", index); -+ } else { -+ *devname = NULL; -+ } -+ } -+ -+ return path; -+} -+ -+static void print_content(VmaReader *vmar) -+{ -+ assert(vmar); -+ -+ VmaHeader *head = vma_reader_get_header(vmar); -+ -+ GList *l = vma_reader_get_config_data(vmar); -+ while (l && l->data) { -+ VmaConfigData *cdata = (VmaConfigData *)l->data; -+ l = g_list_next(l); -+ printf("CFG: size: %d name: %s\n", cdata->len, cdata->name); -+ } -+ -+ int i; -+ VmaDeviceInfo *di; -+ for (i = 1; i < 255; i++) { -+ di = vma_reader_get_device_info(vmar, i); -+ if (di) { -+ if (strcmp(di->devname, "vmstate") == 0) { -+ printf("VMSTATE: dev_id=%d memory: %zd\n", i, di->size); -+ } else { -+ printf("DEV: dev_id=%d size: %zd devname: %s\n", -+ i, di->size, di->devname); -+ } -+ } -+ } -+ /* ctime is the last entry we print */ -+ printf("CTIME: %s", ctime(&head->ctime)); -+ fflush(stdout); -+} -+ -+static int list_content(int argc, char **argv) -+{ -+ int c, ret = 0; -+ const char *filename; -+ -+ for (;;) { -+ c = getopt(argc, argv, "h"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 1) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ print_content(vmar); -+ -+ vma_reader_destroy(vmar); -+ -+ return ret; -+} -+ -+typedef struct RestoreMap { -+ char *devname; -+ char *path; -+ bool write_zero; -+} RestoreMap; -+ -+static int extract_content(int argc, char **argv) -+{ -+ int c, ret = 0; -+ int verbose = 0; -+ const char *filename; -+ const char *dirname; -+ const char *readmap = NULL; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hvr:"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'r': -+ readmap = optarg; -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ default: -+ help(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 2) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ dirname = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (mkdir(dirname, 0777) < 0) { -+ g_error("unable to create target directory %s - %s", -+ dirname, g_strerror(errno)); -+ } -+ -+ GList *l = vma_reader_get_config_data(vmar); -+ while (l && l->data) { -+ VmaConfigData *cdata = (VmaConfigData *)l->data; -+ l = g_list_next(l); -+ char *cfgfn = g_strdup_printf("%s/%s", dirname, cdata->name); -+ GError *err = NULL; -+ if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len, -+ &err)) { -+ g_error("unable to write file: %s", err->message); -+ } -+ } -+ -+ GHashTable *devmap = g_hash_table_new(g_str_hash, g_str_equal); -+ -+ if (readmap) { -+ print_content(vmar); -+ -+ FILE *map = fopen(readmap, "r"); -+ if (!map) { -+ g_error("unable to open fifo %s - %s", readmap, g_strerror(errno)); -+ } -+ -+ while (1) { -+ char inbuf[8192]; -+ char *line = fgets(inbuf, sizeof(inbuf), map); -+ if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { -+ break; -+ } -+ int len = strlen(line); -+ if (line[len - 1] == '\n') { -+ line[len - 1] = '\0'; -+ if (len == 1) { -+ break; -+ } -+ } -+ -+ const char *path; -+ bool write_zero; -+ if (line[0] == '0' && line[1] == ':') { -+ path = inbuf + 2; -+ write_zero = false; -+ } else if (line[0] == '1' && line[1] == ':') { -+ path = inbuf + 2; -+ write_zero = true; -+ } else { -+ g_error("read map failed - parse error ('%s')", inbuf); -+ } -+ -+ char *devname = NULL; -+ path = extract_devname(path, &devname, -1); -+ if (!devname) { -+ g_error("read map failed - no dev name specified ('%s')", -+ inbuf); -+ } -+ -+ RestoreMap *map = g_new0(RestoreMap, 1); -+ map->devname = g_strdup(devname); -+ map->path = g_strdup(path); -+ map->write_zero = write_zero; -+ -+ g_hash_table_insert(devmap, map->devname, map); -+ -+ }; -+ } -+ -+ int i; -+ int vmstate_fd = -1; -+ guint8 vmstate_stream = 0; -+ -+ for (i = 1; i < 255; i++) { -+ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); -+ if (di && (strcmp(di->devname, "vmstate") == 0)) { -+ vmstate_stream = i; -+ char *statefn = g_strdup_printf("%s/vmstate.bin", dirname); -+ vmstate_fd = open(statefn, O_WRONLY|O_CREAT|O_EXCL, 0644); -+ if (vmstate_fd < 0) { -+ g_error("create vmstate file '%s' failed - %s", statefn, -+ g_strerror(errno)); -+ } -+ g_free(statefn); -+ } else if (di) { -+ char *devfn = NULL; -+ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB; -+ bool write_zero = true; -+ -+ if (readmap) { -+ RestoreMap *map; -+ map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname); -+ if (map == NULL) { -+ g_error("no device name mapping for %s", di->devname); -+ } -+ devfn = map->path; -+ write_zero = map->write_zero; -+ } else { -+ devfn = g_strdup_printf("%s/tmp-disk-%s.raw", -+ dirname, di->devname); -+ printf("DEVINFO %s %zd\n", devfn, di->size); -+ -+ 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 -+ * need to write zeroes (so we generate a sparse file) -+ */ -+ write_zero = false; -+ } -+ -+ 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) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (!readmap) { -+ g_free(devfn); -+ } -+ } -+ } -+ -+ if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) { -+ g_error("restore failed - %s", error_get_pretty(errp)); -+ } -+ -+ if (!readmap) { -+ for (i = 1; i < 255; i++) { -+ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); -+ if (di && (i != vmstate_stream)) { -+ char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw", -+ dirname, di->devname); -+ char *fn = g_strdup_printf("%s/disk-%s.raw", -+ dirname, di->devname); -+ if (rename(tmpfn, fn) != 0) { -+ g_error("rename %s to %s failed - %s", -+ tmpfn, fn, g_strerror(errno)); -+ } -+ } -+ } -+ } -+ -+ vma_reader_destroy(vmar); -+ -+ bdrv_close_all(); -+ -+ return ret; -+} -+ -+typedef struct BackupCB { -+ VmaWriter *vmaw; -+ uint8_t dev_id; -+} BackupCB; -+ -+static int backup_dump_cb(void *opaque, BlockDriverState *bs, -+ int64_t cluster_num, unsigned char *buf) -+{ -+ BackupCB *bcb = opaque; -+ size_t zb = 0; -+ if (vma_writer_write(bcb->vmaw, bcb->dev_id, cluster_num, buf, &zb) < 0) { -+ g_warning("backup_dump_cb vma_writer_write failed"); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static void backup_complete_cb(void *opaque, int ret) -+{ -+ BackupCB *bcb = opaque; -+ -+ if (ret < 0) { -+ vma_writer_set_error(bcb->vmaw, "backup_complete_cb %d", ret); -+ } -+ -+ if (vma_writer_close_stream(bcb->vmaw, bcb->dev_id) <= 0) { -+ Error *err = NULL; -+ if (vma_writer_close(bcb->vmaw, &err) != 0) { -+ g_warning("vma_writer_close failed %s", error_get_pretty(err)); -+ } -+ } -+} -+ -+static int create_archive(int argc, char **argv) -+{ -+ int i, c, res; -+ int verbose = 0; -+ const char *archivename; -+ GList *config_files = NULL; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hvc:"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'c': -+ config_files = g_list_append(config_files, optarg); -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ } -+ -+ -+ /* make sure we have archive name and at least one path */ -+ if ((optind + 2) > argc) { -+ help(); -+ } -+ -+ archivename = argv[optind++]; -+ -+ uuid_t uuid; -+ uuid_generate(uuid); -+ -+ Error *local_err = NULL; -+ VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err); -+ -+ if (vmaw == NULL) { -+ g_error("%s", error_get_pretty(local_err)); -+ } -+ -+ GList *l = config_files; -+ while (l && l->data) { -+ char *name = l->data; -+ char *cdata = NULL; -+ gsize clen = 0; -+ GError *err = NULL; -+ if (!g_file_get_contents(name, &cdata, &clen, &err)) { -+ unlink(archivename); -+ g_error("Unable to read file: %s", err->message); -+ } -+ -+ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) { -+ unlink(archivename); -+ g_error("Unable to append config data %s (len = %zd)", -+ name, clen); -+ } -+ l = g_list_next(l); -+ } -+ -+ int ind = 0; -+ while (optind < argc) { -+ const char *path = argv[optind++]; -+ char *devname = NULL; -+ path = extract_devname(path, &devname, ind++); -+ -+ BlockDriver *drv = NULL; -+ BlockDriverState *bs = bdrv_new(devname); -+ -+ res = bdrv_open(bs, path, BDRV_O_CACHE_WB , drv); -+ if (res < 0) { -+ unlink(archivename); -+ g_error("bdrv_open '%s' failed", path); -+ } -+ int64_t size = bdrv_getlength(bs); -+ int dev_id = vma_writer_register_stream(vmaw, devname, size); -+ if (dev_id <= 0) { -+ unlink(archivename); -+ g_error("vma_writer_register_stream '%s' failed", devname); -+ } -+ -+ BackupCB *bcb = g_new0(BackupCB, 1); -+ bcb->vmaw = vmaw; -+ bcb->dev_id = dev_id; -+ -+ 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); -+ } -+ } -+ -+ VmaStatus vmastat; -+ int percent = 0; -+ int last_percent = -1; -+ -+ while (1) { -+ main_loop_wait(false); -+ vma_writer_get_status(vmaw, &vmastat); -+ -+ if (verbose) { -+ -+ uint64_t total = 0; -+ uint64_t transferred = 0; -+ uint64_t zero_bytes = 0; -+ -+ int i; -+ for (i = 0; i < 256; i++) { -+ if (vmastat.stream_info[i].size) { -+ total += vmastat.stream_info[i].size; -+ transferred += vmastat.stream_info[i].transferred; -+ zero_bytes += vmastat.stream_info[i].zero_bytes; -+ } -+ } -+ percent = (transferred*100)/total; -+ if (percent != last_percent) { -+ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, -+ transferred, total, zero_bytes); -+ fflush(stderr); -+ -+ last_percent = percent; -+ } -+ } -+ -+ if (vmastat.closed) { -+ break; -+ } -+ } -+ -+ bdrv_drain_all(); -+ -+ vma_writer_get_status(vmaw, &vmastat); -+ -+ if (verbose) { -+ for (i = 0; i < 256; i++) { -+ VmaStreamInfo *si = &vmastat.stream_info[i]; -+ if (si->size) { -+ fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n", -+ si->devname, si->size, si->zero_bytes, -+ si->size - si->zero_bytes); -+ } -+ } -+ } -+ -+ if (vmastat.status < 0) { -+ unlink(archivename); -+ g_error("creating vma archive failed"); -+ } -+ -+ return 0; -+} -+ -+int main(int argc, char **argv) -+{ -+ const char *cmdname; -+ -+ error_set_progname(argv[0]); -+ -+ qemu_init_main_loop(); -+ -+ bdrv_init(); -+ -+ if (argc < 2) { -+ help(); -+ } -+ -+ cmdname = argv[1]; -+ argc--; argv++; -+ -+ -+ if (!strcmp(cmdname, "list")) { -+ return list_content(argc, argv); -+ } else if (!strcmp(cmdname, "create")) { -+ return create_archive(argc, argv); -+ } else if (!strcmp(cmdname, "extract")) { -+ return extract_content(argc, argv); -+ } -+ -+ help(); -+ return 0; -+} -diff --git a/vma.h b/vma.h -new file mode 100644 -index 0000000..76d0dc8 ---- /dev/null -+++ b/vma.h -@@ -0,0 +1,145 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#ifndef BACKUP_VMA_H -+#define BACKUP_VMA_H -+ -+#include "backup.h" -+#include "error.h" -+ -+#define VMA_BLOCK_BITS 12 -+#define VMA_BLOCK_SIZE (1< -Date: Wed, 14 Nov 2012 09:57:04 +0100 -Subject: [PATCH v5 5/6] add regression tests for backup - -Simple regression tests using vma-reader and vma-writer. - -Signed-off-by: Dietmar Maurer ---- - tests/Makefile | 11 +- - 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 -index 567e36e..136be84 100644 ---- a/tests/Makefile -+++ b/tests/Makefile -@@ -59,6 +59,8 @@ gcov-files-test-mul64-y = util/host-utils.c - - check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh - -+check-backup-y = tests/backup-test$(EXESUF) -+ - # All QTests for now are POSIX-only, but the dependencies are - # really in libqtest, not in the testcases themselves. - check-qtest-i386-y = tests/fdc-test$(EXESUF) -@@ -102,6 +104,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil - tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a - tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a - tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a -+tests/backup-test$(EXESUF): tests/backup-test.o vma-reader.o $(block-obj-y) libqemuutil.a libqemustub.a - tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a - tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o - tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a -@@ -213,10 +216,14 @@ check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) - - # Consolidated targets - --.PHONY: check-qtest check-unit check -+.PHONY: check-backup check-qtest check-unit check - check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) - check-unit: $(patsubst %,check-%, $(check-unit-y)) - check-block: $(patsubst %,check-%, $(check-block-y)) --check: check-unit check-qtest -+ -+check-backup: tests/backup-test$(EXESUF) -+ $< -+ -+check: check-unit check-qtest check-backup - - -include $(wildcard tests/*.d) -diff --git a/tests/backup-test.c b/tests/backup-test.c -new file mode 100644 -index 0000000..47a9664 ---- /dev/null -+++ b/tests/backup-test.c -@@ -0,0 +1,529 @@ -+/* -+ * QEMU backup test suit -+ * -+ * 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. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qemu-common.h" -+#include "block/block.h" -+ -+#include "vma.h" -+ -+static int opt_debug; -+static int opt_loop; -+ -+#define DPRINTF(fmt, ...) \ -+ do { if (opt_debug) { printf(fmt, ## __VA_ARGS__); } } while (0) -+ -+#define CLUSTER(x) (x*BACKUP_CLUSTER_SIZE) -+ -+#define RUN_TEST(testfunc, speed) \ -+ backup_test(#testfunc " speed " #speed, speed, testfunc); -+ -+ -+static unsigned char buf_sec_pattern_cd[BDRV_SECTOR_SIZE]; -+static unsigned char buf_sec_pattern_32[BDRV_SECTOR_SIZE]; -+ -+#define TEST_IMG_SIZE (6*1024*1024+BDRV_SECTOR_SIZE) -+#define TEST_IMG_NAME "backuptest.raw" -+#define TEST_IMG_RESTORE_NAME "backuptest.raw.restore" -+#define TEST_VMA_NAME "backuptest.vma" -+ -+typedef struct BackupCB { -+ VmaWriter *vmaw; -+ uint8_t dev_id; -+} BackupCB; -+ -+static int backup_dump_cb(void *opaque, BlockDriverState *bs, -+ int64_t cluster_num, unsigned char *buf) -+{ -+ BackupCB *bcb = opaque; -+ -+ 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) { -+ printf("backup_dump_cb vma_writer_write failed\n"); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+static void backup_complete_cb(void *opaque, int ret) -+{ -+ BackupCB *bcb = opaque; -+ -+ DPRINTF("backup_complete_cb %d %d\n", bcb->dev_id, ret); -+ -+ if (ret < 0) { -+ vma_writer_set_error(bcb->vmaw, "backup_complete_cb %d", ret); -+ } -+ -+ if (vma_writer_close_stream(bcb->vmaw, bcb->dev_id) <= 0) { -+ Error *err = NULL; -+ if (vma_writer_close(bcb->vmaw, &err) != 0) { -+ g_error("vma_writer_close failed %s", error_get_pretty(err)); -+ } -+ } -+ DPRINTF("backup_complete_cb finish\n"); -+} -+ -+static void write_sec_pattern_cd(BlockDriverState *bs, int64_t offset) -+{ -+ int ret; -+ -+ DPRINTF("write_sec_pattern_cd %" PRId64 "\n", offset); -+ -+ if (offset & 0x1ff) { -+ 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 %" PRId64 " failed", offset); -+ } -+ -+} -+ -+static void read_sec(BlockDriverState *bs, int64_t offset, unsigned char *buf) -+{ -+ DPRINTF("read_sec C%" PRId64 " start %" PRId64 "\n", -+ offset>>VMA_CLUSTER_BITS, offset); -+ -+ if (offset & 0x1ff) { -+ g_error("read_sec offset %" PRId64 " is not sector aligned\n", offset); -+ } -+ -+ if (bdrv_read(bs, offset >> 9, buf, 1) < 0) { -+ g_error("bdrv_read failed"); -+ } -+} -+ -+static bool request_term; -+ -+typedef struct TestCB { -+ Coroutine *co; -+ BlockDriverState *bs; -+ bool finished; -+} TestCB; -+ -+static TestCB *enter_test_co(BlockDriverState *bs, CoroutineEntry *entry) -+{ -+ TestCB *cb = g_new0(TestCB, 1); -+ cb->bs = bs; -+ cb->co = qemu_coroutine_create(entry); -+ qemu_coroutine_enter(cb->co, cb); -+ return cb; -+} -+ -+static void test_co_sleep(double sec) -+{ -+ co_sleep_ns(rt_clock, (int64_t)(sec*1000000000)); -+}; -+ -+static void test_co_yield(void) -+{ -+ co_sleep_ns(rt_clock, (int64_t)(1000)); -+}; -+ -+static void coroutine_fn run_co_test1(void *opaque) -+{ -+ assert(opaque); -+ TestCB *cb = (TestCB *)opaque; -+ -+ test_co_sleep(0.2); -+ write_sec_pattern_cd(cb->bs, 5*BACKUP_CLUSTER_SIZE); -+ test_co_sleep(0.2); -+ write_sec_pattern_cd(cb->bs, 10*BACKUP_CLUSTER_SIZE); -+ test_co_sleep(0.2); -+ write_sec_pattern_cd(cb->bs, 10*BACKUP_CLUSTER_SIZE); -+ -+ cb->finished = true; -+} -+ -+static void coroutine_fn run_co_test2(void *opaque) -+{ -+ assert(opaque); -+ TestCB *cb = (TestCB *)opaque; -+ unsigned char buf[512]; -+ -+ test_co_sleep(0.2); -+ read_sec(cb->bs, 5*BACKUP_CLUSTER_SIZE, buf); -+ write_sec_pattern_cd(cb->bs, 6*BACKUP_CLUSTER_SIZE); -+ -+ cb->finished = true; -+} -+ -+static void coroutine_fn run_co_random_read(void *opaque) -+{ -+ assert(opaque); -+ TestCB *cb = (TestCB *)opaque; -+ int64_t sectors = bdrv_getlength(cb->bs)/BDRV_SECTOR_SIZE - 1; -+ unsigned char buf[512]; -+ -+ while (1) { -+ test_co_yield(); -+ if (request_term) { -+ DPRINTF("finish run_co_random_read\n"); -+ break; -+ } -+ int64_t s = (rand()*sectors)/RAND_MAX; -+ read_sec(cb->bs, s*BDRV_SECTOR_SIZE, buf); -+ } -+ -+ cb->finished = true; -+} -+ -+static void coroutine_fn run_co_random_write(void *opaque) -+{ -+ assert(opaque); -+ TestCB *cb = (TestCB *)opaque; -+ int64_t sectors = bdrv_getlength(cb->bs)/BDRV_SECTOR_SIZE; -+ -+ while (1) { -+ test_co_yield(); -+ if (request_term) { -+ DPRINTF("finish run_co_random_write\n"); -+ break; -+ } -+ int64_t s = (rand()*sectors)/RAND_MAX; -+ write_sec_pattern_cd(cb->bs, s*BDRV_SECTOR_SIZE); -+ } -+ -+ cb->finished = true; -+} -+ -+static void fill_test_sector(void *buf, size_t sector_num) -+{ -+ int64_t *i64buf = (int64_t *)buf; -+ int i; -+ -+ int data = sector_num; -+ if (sector_num >= 8 && sector_num < 8*(2*16+2)) { -+ 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; -+ } -+} -+ -+static void verify_archive(const char *archive, size_t size) -+{ -+ Error *errp = NULL; -+ -+ VmaReader *vmar = vma_reader_create(archive, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, 1); -+ if (!di || strcmp((char *)di->devname, "hda") || di->size != size) { -+ g_error("got wrong device info"); -+ } -+ -+ unlink(TEST_IMG_RESTORE_NAME); -+ -+ int flags = BDRV_O_NATIVE_AIO|BDRV_O_RDWR|BDRV_O_CACHE_WB; -+ -+ bdrv_img_create(TEST_IMG_RESTORE_NAME, "raw", NULL, NULL, NULL, -+ size, flags, &errp); -+ if (error_is_set(&errp)) { -+ g_error("can't create file %s: %s", TEST_IMG_RESTORE_NAME, -+ error_get_pretty(errp)); -+ } -+ -+ BlockDriverState *bs = NULL; -+ if (bdrv_file_open(&bs, TEST_IMG_RESTORE_NAME, flags)) { -+ g_error("can't open file %s", TEST_IMG_RESTORE_NAME); -+ } -+ if (vma_reader_register_bs(vmar, 1, bs, false, &errp) < 0) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (vma_reader_restore(vmar, -1, false, &errp) < 0) { -+ g_error("restore failed - %s", error_get_pretty(errp)); -+ } -+ -+ size_t i; -+ size_t sectors = size/BDRV_SECTOR_SIZE; -+ int64_t buf[512/sizeof(int64_t)]; -+ int64_t buf2[512/sizeof(int64_t)]; -+ -+ for (i = 0; i < sectors; i++) { -+ if (bdrv_read(bs, i, (uint8_t *)buf, 1) != 0) { -+ g_error("bdrv_read failed"); -+ } -+ fill_test_sector(buf2, i); -+ if (bcmp(buf, buf2, sizeof(buf))) { -+ g_error("data is different at sector %" PRId64, i); -+ } -+ } -+ -+ vma_reader_destroy(vmar); -+ -+ unlink(TEST_IMG_RESTORE_NAME); -+} -+ -+static void prepare_vm_image(const char *filename, size_t sectors) -+{ -+ int fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); -+ if (fd < 0) { -+ g_error("can't open file %s\n", filename); -+ } -+ -+ size_t 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; -+ while (1) { -+ res = pwrite(fd, buf, sizeof(buf), i*512); -+ if (!(res < 0 && errno == EINTR)) { -+ break; -+ } -+ } -+ if (res != sizeof(buf)) { -+ g_error("can't initialize file %s - %s %d\n", -+ filename, g_strerror(errno), res); -+ } -+ } -+ -+ if (close(fd) != 0) { -+ g_error("close failed"); -+ } -+} -+ -+static GList *simple_test(BlockDriverState *bs) -+{ -+ GList *cb_list = NULL; -+ -+ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_test1)); -+ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_test2)); -+ -+ return cb_list; -+} -+ -+static GList *random_read_write_test(BlockDriverState *bs) -+{ -+ GList *cb_list = NULL; -+ -+ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_read)); -+ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_read)); -+ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_write)); -+ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_write)); -+ -+ return cb_list; -+} -+ -+static void backup_test(const char *testname, int64_t speed, -+ GList *(*start_test_cb)(BlockDriverState *bs)) -+{ -+ BlockDriverState *bs = bdrv_new("hda"); -+ -+ static int test_counter; -+ -+ test_counter++; -+ -+ printf("starting test #%d '%s'\n", test_counter, testname); -+ -+ const char *filename = TEST_IMG_NAME; -+ -+ prepare_vm_image(TEST_IMG_NAME, TEST_IMG_SIZE/BDRV_SECTOR_SIZE); -+ -+ int flags = BDRV_O_NATIVE_AIO|BDRV_O_RDWR|BDRV_O_CACHE_WB; -+ -+ if (bdrv_open(bs, filename, flags, NULL) < 0) { -+ g_error("can't open device %s\n", filename); -+ } -+ -+ Error *err = NULL; -+ uuid_t uuid; -+ uuid_generate(uuid); -+ -+ unlink(TEST_VMA_NAME); -+ -+ VmaWriter *vmaw = vma_writer_create(TEST_VMA_NAME, uuid, &err); -+ if (!vmaw) { -+ g_error("%s", error_get_pretty(err)); -+ } -+ -+ BackupCB bcb; -+ bcb.vmaw = vmaw; -+ bcb.dev_id = vma_writer_register_stream(vmaw, bdrv_get_device_name(bs), -+ bdrv_getlength(bs)); -+ if (backup_job_create(bs, backup_dump_cb, backup_complete_cb, &bcb, -+ speed) < 0) { -+ g_error("backup_job_create failed"); -+ } else { -+ backup_job_start(bs, false); -+ } -+ -+ request_term = false; -+ -+ GList *cb_list = start_test_cb(bs); -+ -+ while (1) { -+ main_loop_wait(false); -+ -+ VmaStatus vmastat; -+ vma_writer_get_status(vmaw, &vmastat); -+ if (vmastat.closed) { -+ break; -+ } -+ } -+ -+ request_term = true; -+ -+ while (1) { -+ GList *l = cb_list; -+ bool active = 0; -+ while (l && l->data) { -+ TestCB *cb = (TestCB *)l->data; -+ l = g_list_next(l); -+ if (!cb->finished) { -+ active = true; -+ break; -+ } -+ } -+ if (!active) { -+ DPRINTF("All test coroutines finished\n"); -+ break; -+ } -+ main_loop_wait(false); -+ } -+ -+ /* Make sure all outstanding requests complete */ -+ bdrv_drain_all(); -+ -+ VmaStatus vmastat; -+ vma_writer_get_status(vmaw, &vmastat); -+ 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); -+ -+ vma_writer_destroy(vmaw); -+ -+ bdrv_delete(bs); -+ -+ /* start verification */ -+ verify_archive(TEST_VMA_NAME, TEST_IMG_SIZE); -+ -+ bdrv_close_all(); -+ -+ unlink(TEST_IMG_NAME); -+ unlink(TEST_VMA_NAME); -+ -+ printf("finish test #%d '%s' OK\n", test_counter, testname); -+} -+ -+static void help(void) -+{ -+ const char *help_msg = -+ "usage: backup-test [options]\n" -+ "\n" -+ "backup-test run default regression test (fast)\n" -+ "backup-test -l run long running test loop (endless)\n" -+ "\n" -+ "use option -d to turn on verbose debug output\n" -+ ; -+ -+ printf("%s", help_msg); -+ exit(1); -+} -+ -+int main(int argc, char **argv) -+{ -+ 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 (;;) { -+ c = getopt(argc, argv, "hdl"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'd': -+ opt_debug = 1; -+ break; -+ case 'l': -+ opt_loop = 1; -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ } -+ -+ memset(buf_sec_pattern_cd, 0xcd, sizeof(buf_sec_pattern_cd)); -+ memset(buf_sec_pattern_32, 0x32, sizeof(buf_sec_pattern_32)); -+ -+ srand(1234); -+ -+ /* ignore SIGPIPE */ -+ struct sigaction act; -+ sigfillset(&act.sa_mask); -+ act.sa_flags = 0; -+ act.sa_handler = SIG_IGN; -+ sigaction(SIGPIPE, &act, NULL); -+ -+ qemu_init_main_loop(); -+ -+ bdrv_init(); -+ -+ if (opt_loop) { /* endless test loop */ -+ while (1) { -+ RUN_TEST(random_read_write_test, 0); -+ } -+ return 0; -+ } -+ -+ if (opt_debug) { /* run simple test (rate limited) */ -+ RUN_TEST(simple_test, 1024*1024); -+ return 0; -+ } -+ -+ /* run default regression tests at full speed */ -+ -+ RUN_TEST(simple_test, 0); -+ RUN_TEST(random_read_write_test, 0); -+ -+ return 0; -+} --- -1.7.2.5 - diff --git a/debian/patches/0006-add-vm-state-to-backups.patch b/debian/patches/0006-add-vm-state-to-backups.patch deleted file mode 100644 index d68b427..0000000 --- a/debian/patches/0006-add-vm-state-to-backups.patch +++ /dev/null @@ -1,305 +0,0 @@ -From c209554171466e2aa8b1eca92d952429faca5fc6 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Thu, 29 Nov 2012 10:46:49 +0100 -Subject: [PATCH v5 6/6] add vm state to backups - - -Signed-off-by: Dietmar Maurer ---- - blockdev.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++- - hmp.c | 3 +- - qapi-schema.json | 5 +- - 3 files changed, 200 insertions(+), 4 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 683f7da..dd20631 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -22,6 +22,8 @@ - #include "sysemu/arch_init.h" - #include "backup.h" - #include "vma.h" -+#include "migration/qemu-file.h" -+#include "migration/migration.h" - - static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); - -@@ -1355,6 +1357,10 @@ static struct GenericBackupState { - size_t total; - size_t transferred; - size_t zero_bytes; -+ unsigned char buf[BACKUP_CLUSTER_SIZE]; -+ int buf_index; -+ size_t buf_cluster_num; -+ guint8 vmstate_dev_id; - } backup_state; - - typedef struct BackupCB { -@@ -1510,10 +1516,170 @@ static void backup_start_jobs(void) - backup_run_next_job(); - } - -+static int backup_state_close(void *opaque) -+{ -+ if (!backup_state.buf_index) { -+ return 0; -+ } -+ -+ size_t zero_bytes = 0; -+ if (backup_state.buf_index < BACKUP_CLUSTER_SIZE) { -+ memset(backup_state.buf + backup_state.buf_index, 0, -+ BACKUP_CLUSTER_SIZE - backup_state.buf_index); -+ } -+ 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_state.buf_index = 0; -+ -+ return bytes < 0 ? -1 : 0; -+} -+ -+static int backup_state_put_buffer(void *opaque, const uint8_t *buf, -+ int64_t pos, int size) -+{ -+ assert(backup_state.driver); -+ assert(backup_state.writer); -+ assert(backup_state.driver->dump); -+ -+ /* Note: our backup driver expects to get whole clusters (64KB) */ -+ -+ int ret = size; -+ -+ while (size > 0) { -+ int l = BACKUP_CLUSTER_SIZE - backup_state.buf_index; -+ l = l > size ? size : l; -+ memcpy(backup_state.buf + backup_state.buf_index, buf, l); -+ backup_state.buf_index += l; -+ buf += l; -+ size -= l; -+ if (backup_state.buf_index == BACKUP_CLUSTER_SIZE) { -+ size_t zero_bytes = 0; -+ 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_state.buf_index = 0; -+ if (bytes < 0) { -+ return -1; -+ } -+ } -+ } -+ -+ return ret; -+} -+ -+static const QEMUFileOps backup_file_ops = { -+ .put_buffer = backup_state_put_buffer, -+ .close = backup_state_close, -+}; -+ -+static void coroutine_fn backup_start_savevm(void *opaque) -+{ -+ assert(backup_state.driver); -+ assert(backup_state.writer); -+ int ret; -+ char *err = NULL; -+ uint64_t remaining; -+ int64_t maxlen; -+ MigrationParams params = { -+ .blk = 0, -+ .shared = 0 -+ }; -+ -+ int restart = 0; -+ -+ QEMUFile *file = qemu_fopen_ops(NULL, &backup_file_ops); -+ -+ ret = qemu_savevm_state_begin(file, ¶ms); -+ if (ret < 0) { -+ qemu_fclose(file); -+ err = g_strdup("qemu_savevm_state_begin failed"); -+ goto abort; -+ } -+ -+ while (1) { -+ ret = qemu_savevm_state_iterate(file); -+ remaining = ram_bytes_remaining(); -+ -+ if (ret < 0) { -+ qemu_fclose(file); -+ err = g_strdup_printf("qemu_savevm_state_iterate error %d", ret); -+ goto abort; -+ } -+ -+ /* stop the VM if we use too much space, -+ * or if remaining is just a few MB -+ */ -+ maxlen = ram_bytes_total(); -+ size_t cpos = backup_state.buf_cluster_num * BACKUP_CLUSTER_SIZE; -+ if ((remaining < 100000) || ((cpos + remaining) >= maxlen)) { -+ if (runstate_is_running()) { -+ restart = 1; -+ vm_stop(RUN_STATE_SAVE_VM); -+ } -+ } -+ -+ if (ret == 1) { /* finished */ -+ if (runstate_is_running()) { -+ restart = 1; -+ vm_stop(RUN_STATE_SAVE_VM); -+ } -+ -+ ret = qemu_savevm_state_complete(file); -+ if (ret < 0) { -+ qemu_fclose(file); -+ err = g_strdup("qemu_savevm_state_complete error"); -+ goto abort; -+ -+ } else { -+ if (qemu_fclose(file) < 0) { -+ error_setg(&backup_state.error, -+ "backup_start_savevm: qemu_fclose failed"); -+ goto abort; -+ } -+ if (backup_state.driver->complete(backup_state.writer, -+ backup_state.vmstate_dev_id, 0) < 0) { -+ err = g_strdup("backup_start_savevm: complete failed"); -+ goto abort; -+ } -+ backup_run_next_job(); -+ goto out; -+ } -+ } -+ } -+ -+out: -+ if (restart) { -+ vm_start(); -+ } -+ return; -+ -+abort: -+ backup_state.end_time = time(NULL); -+ -+ Error *local_err = NULL; -+ backup_state.driver->close(backup_state.writer, &local_err); -+ backup_state.writer = NULL; -+ -+ error_propagate(&backup_state.error, local_err); -+ -+ if (err) { -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, "%s", err); -+ } -+ g_free(err); -+ } -+ -+ goto out; -+} -+ - char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, - bool has_config_file, const char *config_file, - bool has_devlist, const char *devlist, -- bool has_speed, int64_t speed, Error **errp) -+ bool has_speed, int64_t speed, -+ bool has_state, bool state, Error **errp) - { - BlockDriverState *bs; - Error *local_err = NULL; -@@ -1528,6 +1694,8 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, - return NULL; - } - -+ bool save_state = has_state ? state : false; -+ - /* Todo: try to auto-detect format based on file name */ - format = has_format ? format : BACKUP_FORMAT_VMA; - -@@ -1608,6 +1776,22 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, - size_t total = 0; - - /* register all devices for vma writer */ -+ -+ guint8 vmstate_dev_id = 0; -+ if (save_state) { -+ /* Note: we pass ram_bytes_total() for vmstate size -+ * The backup driver needs to be aware of the fact -+ * that the real stream size can be different (we do -+ * not know that size in advance). -+ */ -+ size_t ramsize = ram_bytes_total(); -+ vmstate_dev_id = driver->register_stream(writer, "vmstate", ramsize); -+ if (vmstate_dev_id <= 0) { -+ error_setg(errp, "register vmstate stream failed"); -+ goto err; -+ } -+ } -+ - l = bcblist; - while (l) { - BackupCB *bcb = l->data; -@@ -1675,6 +1859,9 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, - backup_state.total = total; - backup_state.transferred = 0; - backup_state.zero_bytes = 0; -+ backup_state.buf_index = 0; -+ backup_state.buf_cluster_num = 0; -+ backup_state.vmstate_dev_id = vmstate_dev_id; - - /* Grab a reference so hotplug does not delete the - * BlockDriverState from underneath us. -@@ -1686,7 +1873,12 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, - drive_get_ref(drive_get_by_blockdev(bcb->bs)); - } - -- backup_start_jobs(); -+ if (save_state) { -+ Coroutine *co = qemu_coroutine_create(backup_start_savevm); -+ qemu_coroutine_enter(co, NULL); -+ } else { -+ backup_start_jobs(); -+ } - - return g_strdup(backup_state.uuid_str); - -diff --git a/hmp.c b/hmp.c -index b2c1f23..370cdf8 100644 ---- a/hmp.c -+++ b/hmp.c -@@ -1052,7 +1052,8 @@ void hmp_backup(Monitor *mon, const QDict *qdict) - Error *errp = NULL; - - qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, -- devlist, qdict_haskey(qdict, "speed"), speed, &errp); -+ devlist, qdict_haskey(qdict, "speed"), speed, false, false, -+ &errp); - - if (error_is_set(&errp)) { - monitor_printf(mon, "%s\n", error_get_pretty(errp)); -diff --git a/qapi-schema.json b/qapi-schema.json -index 09ca8ef..1fabb67 100644 ---- a/qapi-schema.json -+++ b/qapi-schema.json -@@ -1885,6 +1885,8 @@ - # @devlist: #optional list of block device names (separated by ',', ';' - # or ':'). By default the backup includes all writable block devices. - # -+# @state: #optional flag to include vm state -+# - # Returns: the uuid of the backup job - # - # Since: 1.5.0 -@@ -1892,7 +1894,8 @@ - { 'command': 'backup', 'data': { 'backup-file': 'str', - '*format': 'BackupFormat', - '*config-file': 'str', -- '*devlist': 'str', '*speed': 'int' }, -+ '*devlist': 'str', '*speed': 'int', -+ '*state': 'bool' }, - 'returns': 'str' } - - ## --- -1.7.2.5 - diff --git a/debian/patches/0007-use-extra-thread-for-vma-writer.patch b/debian/patches/0007-use-extra-thread-for-vma-writer.patch deleted file mode 100644 index d6d1f5f..0000000 --- a/debian/patches/0007-use-extra-thread-for-vma-writer.patch +++ /dev/null @@ -1,471 +0,0 @@ -From a6f324d47b810809de2a6106849527c6a9590175 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Mon, 14 Jan 2013 08:05:40 +0100 -Subject: [PATCH v3 7/7] use extra thread for vma writer - -The previous AIO approach has problem with bdrv_drain_all(), because writer -coroutines are not considered there. Those coroutines are not restarted, so -bdrv_drain_all() can fail (tracked_requests list not empty). - -We now use a thread, so we could also add compression here. - -Signed-off-by: Dietmar Maurer ---- - vma-writer.c | 296 +++++++++++++++++++++++++++++++++++----------------------- - 1 files changed, 180 insertions(+), 116 deletions(-) - -Index: new/vma-writer.c -=================================================================== ---- new.orig/vma-writer.c 2013-01-23 07:35:12.000000000 +0100 -+++ new/vma-writer.c 2013-01-23 09:24:19.000000000 +0100 -@@ -37,13 +37,21 @@ - - #define WRITE_BUFFERS 5 - --typedef struct VmaAIOCB VmaAIOCB; --struct VmaAIOCB { -- VmaWriter *vmaw; -+typedef struct WriteBuffer { - unsigned char buffer[VMA_MAX_EXTENT_SIZE]; - size_t bytes; -- Coroutine *co; --}; -+} WriteBuffer; -+ -+typedef struct WriterThread { -+ int fd; -+ int error; -+ bool cancel; -+ GThread *thread; -+ GMutex *mutex; -+ GCond *change_cond; -+ WriteBuffer wbuf[WRITE_BUFFERS]; -+ CoQueue wqueue; -+} WriterThread; - - struct VmaWriter { - int fd; -@@ -60,8 +68,7 @@ - int outbuf_count; /* in VMA_BLOCKS */ - uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; - -- VmaAIOCB aiocbs[WRITE_BUFFERS]; -- CoQueue wqueue; -+ WriterThread wt; - - GChecksum *md5csum; - CoMutex writer_lock; -@@ -86,6 +93,107 @@ - uint32_t config_count; - }; - -+static gpointer vma_writer_thread(gpointer data) -+{ -+ WriterThread *wt = (WriterThread *)data; -+ -+ while (1) { -+ WriteBuffer *b = NULL; -+ -+ qemu_mutex_lock_iothread(); -+ int i; -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ if (wt->wbuf[i].bytes) { -+ b = &wt->wbuf[i]; -+ break; -+ } -+ } -+ qemu_mutex_unlock_iothread(); -+ -+ if (b) { -+ size_t done = 0; -+ while (done < b->bytes) { -+ int ret = write(wt->fd, b->buffer + done, b->bytes - done); -+ if (ret > 0) { -+ done += ret; -+ } else if (ret < 0) { -+ if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { -+ qemu_mutex_lock_iothread(); -+ wt->error = errno; -+ qemu_mutex_unlock_iothread(); -+ break; -+ } -+ } else if (ret == 0) { -+ /* should not happen - simply try again */ -+ } -+ } -+ qemu_mutex_lock_iothread(); -+ b->bytes = 0; -+ DPRINTF("AWAKE JOB %d\n", wt->error); -+ if (wt->error) { -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ wt->wbuf[i].bytes = 0; -+ } -+ qemu_co_queue_restart_all(&wt->wqueue); -+ } else { -+ qemu_co_queue_next(&wt->wqueue); -+ } -+ qemu_mutex_unlock_iothread(); -+ DPRINTF("AWAKE JOB END\n"); -+ } -+ -+ if (wt->error) { -+ DPRINTF("WRITER THREAD ERROR %d - exit thread\n", wt->error); -+ g_thread_exit(NULL); -+ } -+ -+ g_mutex_lock(wt->mutex); -+ bool cancel = wt->cancel; -+ if (!b && !cancel) { -+ DPRINTF("WRITER THREAD WAIT FOR DATA\n"); -+ g_cond_wait(wt->change_cond, wt->mutex); -+ cancel = wt->cancel; -+ } -+ g_mutex_unlock(wt->mutex); -+ -+ if (cancel) { -+ qemu_mutex_lock_iothread(); -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ wt->wbuf[i].bytes = 0; -+ } -+ qemu_co_queue_restart_all(&wt->wqueue); -+ qemu_mutex_unlock_iothread(); -+ DPRINTF("END WRITER THREAD\n"); -+ g_thread_exit(NULL); -+ } -+ } -+ -+ return NULL; -+} -+ -+static void vma_stop_writer_thread(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ -+ DPRINTF("vma_stop_writer_thread start\n"); -+ -+ if (vmaw->wt.thread) { -+ DPRINTF("vma_stop_writer_thread 1\n"); -+ g_mutex_lock(vmaw->wt.mutex); -+ DPRINTF("vma_stop_writer_thread 2\n"); -+ vmaw->wt.cancel = true; -+ g_cond_signal(vmaw->wt.change_cond); -+ g_mutex_unlock(vmaw->wt.mutex); -+ DPRINTF("vma_stop_writer_thread 3\n"); -+ qemu_mutex_unlock_iothread(); -+ g_thread_join(vmaw->wt.thread); -+ qemu_mutex_lock_iothread(); -+ DPRINTF("vma_stop_writer_thread 4\n"); -+ vmaw->wt.thread = NULL; -+ } -+ DPRINTF("vma_stop_writer_thread end\n"); -+} -+ - void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) - { - va_list ap; -@@ -213,111 +321,45 @@ - return n; - } - --static void vma_co_continue_write(void *opaque) --{ -- 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 ssize_t coroutine_fn --vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes) --{ -- size_t done = 0; -- ssize_t ret; -- -- /* atomic writes (we cannot interleave writes) */ -- qemu_co_mutex_lock(&vmaw->writer_lock); -- -- DPRINTF("vma_co_write enter %zd\n", bytes); -- -- while (done < bytes) { -- ret = write(vmaw->fd, buf + done, bytes - done); -- if (ret > 0) { -- done += ret; -- DPRINTF("vma_co_write written %zd %zd\n", done, ret); -- } 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 { -- vma_writer_set_error(vmaw, "vma_co_write write error - %s", -- strerror(errno)); -- done = -1; /* always return failure for partial writes */ -- break; -- } -- } else if (ret == 0) { -- /* should not happen - simply try again */ -- } -- } -- -- qemu_co_mutex_unlock(&vmaw->writer_lock); -- -- DPRINTF("vma_co_write leave %zd\n", done); -- return done; --} -- --static void coroutine_fn vma_co_writer_task(void *opaque) --{ -- VmaAIOCB *cb = opaque; -- -- DPRINTF("vma_co_writer_task start\n"); -- -- int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); -- DPRINTF("vma_co_writer_task write done %zd\n", done); -- -- if (done != cb->bytes) { -- DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); -- vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", -- done); -- } -- -- cb->bytes = 0; -- -- qemu_co_queue_next(&cb->vmaw->wqueue); -- -- DPRINTF("vma_co_writer_task end\n"); --} -- - static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) - { - DPRINTF("vma_queue_flush enter\n"); - - assert(vmaw); - -+ int error; -+ - while (1) { - int i; -- VmaAIOCB *cb = NULL; -+ WriteBuffer *b = NULL; -+ -+ error = vmaw->wt.error; -+ - for (i = 0; i < WRITE_BUFFERS; i++) { -- if (vmaw->aiocbs[i].bytes) { -- cb = &vmaw->aiocbs[i]; -- DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, -- vmaw->aiocbs[i].bytes); -+ if (vmaw->wt.wbuf[i].bytes) { -+ b = &vmaw->wt.wbuf[i]; -+ DPRINTF("FOUND USED WRITE BUFFER %d %zd\n", i, -+ vmaw->wt.wbuf[i].bytes); - break; - } - } -- if (!cb) { -+ -+ if (!b || error) { - break; - } -- qemu_co_queue_wait(&vmaw->wqueue); -+ DPRINTF("WAIT FOR BUFFER FLUSH\n"); -+ qemu_co_queue_wait(&vmaw->wt.wqueue); -+ DPRINTF("WAIT FOR BUFFER FLUSH END\n"); -+ } -+ -+ if (error) { -+ vma_writer_set_error(vmaw, "vma_queue_flush write error - %s", -+ strerror(error)); - } - - DPRINTF("vma_queue_flush leave\n"); - } - --/** -- * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') -- * So we need to create a coroutione to allow 'parallel' execution. -- */ - static ssize_t coroutine_fn - vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) - { -@@ -327,29 +369,42 @@ - assert(buf); - assert(bytes <= VMA_MAX_EXTENT_SIZE); - -- VmaAIOCB *cb = NULL; -- while (!cb) { -+ int error = 0; -+ -+ /* wait for a free output buffer */ -+ WriteBuffer *b = NULL; -+ while (!b) { -+ error = vmaw->wt.error; -+ if (error) { -+ vma_writer_set_error(vmaw, "vma_queue_write error - %s", -+ strerror(error)); -+ return -1; -+ } -+ - int i; - for (i = 0; i < WRITE_BUFFERS; i++) { -- if (!vmaw->aiocbs[i].bytes) { -- cb = &vmaw->aiocbs[i]; -+ if (!vmaw->wt.wbuf[i].bytes) { -+ b = &vmaw->wt.wbuf[i]; - break; - } - } -- if (!cb) { -- qemu_co_queue_wait(&vmaw->wqueue); -+ if (!b) { -+ DPRINTF("WAIT FOR BUFFER\n"); -+ qemu_co_queue_wait(&vmaw->wt.wqueue); -+ DPRINTF("WAIT FOR BUFFER DONE\n"); - } - } - -- memcpy(cb->buffer, buf, bytes); -- cb->bytes = bytes; -- cb->vmaw = vmaw; -+ /* copy data to output buffer */ -+ memcpy(b->buffer, buf, bytes); -+ b->bytes = bytes; - -- DPRINTF("vma_queue_write start %zd\n", bytes); -- cb->co = qemu_coroutine_create(vma_co_writer_task); -- qemu_coroutine_enter(cb->co, cb); -+ g_mutex_lock(vmaw->wt.mutex); -+ /* signal writer thread that we have new data */ -+ g_cond_signal(vmaw->wt.change_cond); -+ g_mutex_unlock(vmaw->wt.mutex); - -- DPRINTF("vma_queue_write leave\n"); -+ DPRINTF("vma_queue_write queued %zd\n", bytes); - - return bytes; - } -@@ -386,10 +441,10 @@ - const char *tmp_id_str; - - if ((stat(filename, &st) == 0) && S_ISFIFO(st.st_mode)) { -- oflags = O_NONBLOCK|O_WRONLY; -+ oflags = O_WRONLY; - vmaw->fd = qemu_open(filename, oflags, 0644); - } else if (strstart(filename, "/dev/fdset/", &tmp_id_str)) { -- oflags = O_NONBLOCK|O_WRONLY; -+ oflags = 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); -@@ -397,7 +452,7 @@ - goto err; - } - } else { -- oflags = O_NONBLOCK|O_WRONLY|O_CREAT|O_EXCL; -+ oflags = O_WRONLY|O_CREAT|O_EXCL; - vmaw->fd = qemu_open(filename, oflags, 0644); - } - -@@ -415,10 +470,19 @@ - - qemu_co_mutex_init(&vmaw->writer_lock); - qemu_co_mutex_init(&vmaw->flush_lock); -- qemu_co_queue_init(&vmaw->wqueue); -+ qemu_co_queue_init(&vmaw->wt.wqueue); - - uuid_copy(vmaw->uuid, uuid); - -+ vmaw->wt.mutex = g_mutex_new(); -+ vmaw->wt.change_cond = g_cond_new(); -+ vmaw->wt.fd = vmaw->fd; -+ vmaw->wt.thread = g_thread_create(vma_writer_thread, &vmaw->wt, true, NULL); -+ if (vmaw->wt.thread == NULL) { -+ error_setg(errp, "can't allocate writer thread\n"); -+ goto err; -+ } -+ - return vmaw; - - err: -@@ -433,6 +497,14 @@ - g_checksum_free(vmaw->md5csum); - } - -+ if (vmaw->wt.mutex) { -+ g_mutex_free(vmaw->wt.mutex); -+ } -+ -+ if (vmaw->wt.change_cond) { -+ g_cond_free(vmaw->wt.change_cond); -+ } -+ - g_free(vmaw); - } - -@@ -672,6 +744,14 @@ - - *zero_bytes = 0; - -+ int error = vmaw->wt.error; -+ -+ if (error) { -+ vma_writer_set_error(vmaw, "vma_writer_get_buffer write error - %s", -+ strerror(error)); -+ return -1; -+ } -+ - if (vmaw->status < 0) { - return vmaw->status; - } -@@ -783,14 +863,17 @@ - - int i; - -+ DPRINTF("vma_writer_close start\n"); - vma_queue_flush(vmaw); - - /* this should not happen - just to be sure */ -- while (!qemu_co_queue_empty(&vmaw->wqueue)) { -+ while (!qemu_co_queue_empty(&vmaw->wt.wqueue)) { - DPRINTF("vma_writer_close wait\n"); - co_sleep_ns(rt_clock, 1000000); - } - -+ vma_stop_writer_thread(vmaw); -+ - if (vmaw->cmd) { - if (pclose(vmaw->cmd) < 0) { - vma_writer_set_error(vmaw, "vma_writer_close: " -@@ -835,8 +918,9 @@ - { - assert(vmaw); - -- int i; -+ vma_stop_writer_thread(vmaw); - -+ int i; - for (i = 0; i <= 255; i++) { - if (vmaw->stream_info[i].devname) { - g_free(vmaw->stream_info[i].devname); -@@ -847,6 +931,14 @@ - g_checksum_free(vmaw->md5csum); - } - -+ if (vmaw->wt.mutex) { -+ g_mutex_free(vmaw->wt.mutex); -+ } -+ -+ if (vmaw->wt.change_cond) { -+ g_cond_free(vmaw->wt.change_cond); -+ } -+ - g_free(vmaw); - } - diff --git a/debian/patches/0007-vma-add-verify-command.patch b/debian/patches/0007-vma-add-verify-command.patch deleted file mode 100644 index a6aff85..0000000 --- a/debian/patches/0007-vma-add-verify-command.patch +++ /dev/null @@ -1,320 +0,0 @@ -From acdcd483ac6977e096ef7cde746d22bbf82e04d3 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Mon, 11 Mar 2013 07:07:46 +0100 -Subject: [PATCH v5 7/7] vma: add verify command - -Users wants to verify the archive after backup. - -Examples: - - # vma verify -v test.vma - - # lzop -d -c test.vma.lzo |vma verify - - -Signed-off-by: Dietmar Maurer ---- - vma-reader.c | 118 +++++++++++++++++++++++++++++++++++++++++++--------------- - vma.c | 57 +++++++++++++++++++++++++++- - vma.h | 1 + - 3 files changed, 145 insertions(+), 31 deletions(-) - -diff --git a/vma-reader.c b/vma-reader.c -index bc36cba..a740f04 100644 ---- a/vma-reader.c -+++ b/vma-reader.c -@@ -53,6 +53,8 @@ struct VmaReader { - time_t start_time; - int64_t cluster_count; - int64_t clusters_read; -+ int64_t zero_cluster_data; -+ int64_t partial_zero_cluster_data; - int clusters_read_per; - }; - -@@ -433,6 +435,27 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id) - return NULL; - } - -+static void allocate_rstate(VmaReader *vmar, guint8 dev_id, -+ BlockDriverState *bs, bool write_zeroes) -+{ -+ assert(vmar); -+ assert(dev_id); -+ -+ vmar->rstate[dev_id].bs = bs; -+ vmar->rstate[dev_id].write_zeroes = write_zeroes; -+ -+ int64_t size = vmar->devinfo[dev_id].size; -+ -+ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + -+ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; -+ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; -+ -+ 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; -+} -+ - int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, - bool write_zeroes, Error **errp) - { -@@ -449,17 +472,7 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, - return -1; - } - -- vmar->rstate[dev_id].bs = bs; -- vmar->rstate[dev_id].write_zeroes = write_zeroes; -- -- int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + -- (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; -- bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; -- -- 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; -+ allocate_rstate(vmar, dev_id, bs, write_zeroes); - - return 0; - } -@@ -526,9 +539,10 @@ static int restore_write_data(VmaReader *vmar, guint8 dev_id, - } - return 0; - } -+ - static int restore_extent(VmaReader *vmar, unsigned char *buf, - int extent_size, int vmstate_fd, -- bool verbose, Error **errp) -+ bool verbose, bool verify, Error **errp) - { - assert(vmar); - assert(buf); -@@ -553,7 +567,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, - - if (dev_id != vmar->vmstate_stream) { - bs = rstate->bs; -- if (!bs) { -+ if (!verify && !bs) { - error_setg(errp, "got wrong dev id %d", dev_id); - return -1; - } -@@ -609,10 +623,13 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, - return -1; - } - -- int nb_sectors = end_sector - sector_num; -- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, -- sector_num, nb_sectors, errp) < 0) { -- return -1; -+ if (!verify) { -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ buf + start, sector_num, nb_sectors, -+ errp) < 0) { -+ return -1; -+ } - } - - start += VMA_CLUSTER_SIZE; -@@ -642,26 +659,37 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, - return -1; - } - -- int nb_sectors = end_sector - sector_num; -- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -- buf + start, sector_num, -- nb_sectors, errp) < 0) { -- return -1; -+ if (!verify) { -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ buf + start, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } - } - - start += VMA_BLOCK_SIZE; - - } else { - -- if (rstate->write_zeroes && (end_sector > sector_num)) { -+ -+ if (end_sector > sector_num) { - /* Todo: use bdrv_co_write_zeroes (but that need to - * be run inside coroutine?) - */ - int nb_sectors = end_sector - sector_num; -- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -- zero_vma_block, sector_num, -- nb_sectors, errp) < 0) { -- return -1; -+ int zero_size = BDRV_SECTOR_SIZE*nb_sectors; -+ vmar->zero_cluster_data += zero_size; -+ if (mask != 0) { -+ vmar->partial_zero_cluster_data += zero_size; -+ } -+ -+ if (rstate->write_zeroes && !verify) { -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ zero_vma_block, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } - } - } - } -@@ -679,8 +707,9 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, - return 0; - } - --int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, -- Error **errp) -+static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd, -+ bool verbose, bool verify, -+ Error **errp) - { - assert(vmar); - assert(vmar->head_data); -@@ -747,7 +776,7 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, - } - - if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, -- errp) < 0) { -+ verify, errp) < 0) { - return -1; - } - -@@ -794,6 +823,35 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, - } - } - -+ if (verbose) { -+ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", -+ vmar->clusters_read*VMA_CLUSTER_SIZE, -+ vmar->zero_cluster_data, -+ (double)(100.0*vmar->zero_cluster_data)/ -+ (vmar->clusters_read*VMA_CLUSTER_SIZE)); -+ printf("space reduction due to 4K zero blocks %.3g%%\n", -+ (double)(100.0*vmar->partial_zero_cluster_data) / -+ (vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data)); -+ } - return ret; - } - -+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, -+ Error **errp) -+{ -+ return vma_reader_restore_full(vmar, vmstate_fd, verbose, false, errp); -+} -+ -+int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp) -+{ -+ guint8 dev_id; -+ -+ for (dev_id = 1; dev_id < 255; dev_id++) { -+ if (vma_reader_get_device_info(vmar, dev_id)) { -+ allocate_rstate(vmar, dev_id, NULL, false); -+ } -+ } -+ -+ return vma_reader_restore_full(vmar, -1, verbose, true, errp); -+} -+ -diff --git a/vma.c b/vma.c -index 6633aa5..bcde379 100644 ---- a/vma.c -+++ b/vma.c -@@ -33,7 +33,8 @@ static void help(void) - "\n" - "vma list \n" - "vma create [-c config] pathname ...\n" -- "vma extract [-r] \n" -+ "vma extract [-v] [-r] \n" -+ "vma verify [-v]\n" - ; - - printf("%s", help_msg); -@@ -337,6 +338,58 @@ static int extract_content(int argc, char **argv) - return ret; - } - -+static int verify_content(int argc, char **argv) -+{ -+ int c, ret = 0; -+ int verbose = 0; -+ const char *filename; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hv"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ default: -+ help(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 1) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (verbose) { -+ print_content(vmar); -+ } -+ -+ if (vma_reader_verify(vmar, verbose, &errp) < 0) { -+ g_error("verify failed - %s", error_get_pretty(errp)); -+ } -+ -+ vma_reader_destroy(vmar); -+ -+ bdrv_close_all(); -+ -+ return ret; -+} -+ - typedef struct BackupCB { - VmaWriter *vmaw; - uint8_t dev_id; -@@ -554,6 +607,8 @@ int main(int argc, char **argv) - return create_archive(argc, argv); - } else if (!strcmp(cmdname, "extract")) { - return extract_content(argc, argv); -+ } else if (!strcmp(cmdname, "verify")) { -+ return verify_content(argc, argv); - } - - help(); -diff --git a/vma.h b/vma.h -index 76d0dc8..ebd96c1 100644 ---- a/vma.h -+++ b/vma.h -@@ -141,5 +141,6 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, - Error **errp); - int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, - Error **errp); -+int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp); - - #endif /* BACKUP_VMA_H */ --- -1.7.2.5 - diff --git a/debian/patches/0008-vma-restore-tolerate-a-size-difference-up-to-4M.patch b/debian/patches/0008-vma-restore-tolerate-a-size-difference-up-to-4M.patch deleted file mode 100644 index 2e76dae..0000000 --- a/debian/patches/0008-vma-restore-tolerate-a-size-difference-up-to-4M.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 4ca68d0ccfd64f2fdc63ba44813bbafab8d90c81 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 26 Mar 2013 06:21:16 +0100 -Subject: [PATCH v5 8/8] vma restore: tolerate a size difference up to 4M - - -Signed-off-by: Dietmar Maurer ---- - vma-reader.c | 8 +++++++- - 1 files changed, 7 insertions(+), 1 deletions(-) - -diff --git a/vma-reader.c b/vma-reader.c -index a740f04..69e7578 100644 ---- a/vma-reader.c -+++ b/vma-reader.c -@@ -465,7 +465,13 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, - assert(vmar->rstate[dev_id].bs == NULL); - - int64_t size = bdrv_getlength(bs); -- if (size != vmar->devinfo[dev_id].size) { -+ int64_t size_diff = size - vmar->devinfo[dev_id].size; -+ -+ /* storage types can have different size restrictions, so it -+ * is not always possible to create an image with exact size. -+ * So we tolerate a size difference up to 4MB. -+ */ -+ if ((size_diff < 0) || (size_diff > 4*1024*1024)) { - error_setg(errp, "vma_reader_register_bs for stream %s failed - " - "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, - size, vmar->devinfo[dev_id].size); --- -1.7.2.5 - diff --git a/debian/patches/0009-vma-only-store-the-basename-of-a-configuration-file.patch b/debian/patches/0009-vma-only-store-the-basename-of-a-configuration-file.patch deleted file mode 100644 index f6b3a66..0000000 --- a/debian/patches/0009-vma-only-store-the-basename-of-a-configuration-file.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 41811d473df9a1cb09bc4f44eba3147dcec231b1 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Wed, 8 May 2013 10:01:29 +0200 -Subject: [PATCH v5 9/9] vma: only store the basename of a configuration file - -It makes no sense to store the whole filesystem path here. - -Signed-off-by: Dietmar Maurer ---- - vma-writer.c | 5 ++++- - vma.c | 2 +- - 2 files changed, 5 insertions(+), 2 deletions(-) - -diff --git a/vma-writer.c b/vma-writer.c -index 9228ca6..3cc9cf2 100644 ---- a/vma-writer.c -+++ b/vma-writer.c -@@ -145,7 +145,10 @@ int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, - assert(data); - assert(len); - -- uint32_t name_ptr = allocate_header_string(vmaw, name); -+ gchar *basename = g_path_get_basename(name); -+ uint32_t name_ptr = allocate_header_string(vmaw, basename); -+ g_free(basename); -+ - if (!name_ptr) { - return -1; - } -diff --git a/vma.c b/vma.c -index bcde379..f4a8218 100644 ---- a/vma.c -+++ b/vma.c -@@ -33,7 +33,7 @@ static void help(void) - "\n" - "vma list \n" - "vma create [-c config] pathname ...\n" -- "vma extract [-v] [-r] \n" -+ "vma extract [-v] [-r ] \n" - "vma verify [-v]\n" - ; - --- -1.7.10.4 - diff --git a/debian/patches/CVE-2015-7549-msix-pba-write-ro.patch b/debian/patches/CVE-2015-7549-msix-pba-write-ro.patch deleted file mode 100644 index 631ae37..0000000 --- a/debian/patches/CVE-2015-7549-msix-pba-write-ro.patch +++ /dev/null @@ -1,52 +0,0 @@ -commit 43b11a91dd861a946b231b89b7542856ade23d1b -Author: Marc-André Lureau -Date: Fri Jun 26 14:25:29 2015 +0200 - - msix: implement pba write (but read-only) - - qpci_msix_pending() writes on pba region, causing qemu to SEGV: - - Program received signal SIGSEGV, Segmentation fault. - [Switching to Thread 0x7ffff7fba8c0 (LWP 25882)] - 0x0000000000000000 in ?? () - (gdb) bt - #0 0x0000000000000000 in () - #1 0x00005555556556c5 in memory_region_oldmmio_write_accessor (mr=0x5555579f3f80, addr=0, value=0x7fffffffbf68, size=4, shift=0, mask=4294967295, attrs=...) at /home/elmarco/src/qemu/memory.c:434 - #2 0x00005555556558e1 in access_with_adjusted_size (addr=0, value=0x7fffffffbf68, size=4, access_size_min=1, access_size_max=4, access=0x55555565563e , mr=0x5555579f3f80, attrs=...) at /home/elmarco/src/qemu/memory.c:506 - #3 0x00005555556581eb in memory_region_dispatch_write (mr=0x5555579f3f80, addr=0, data=0, size=4, attrs=...) at /home/elmarco/src/qemu/memory.c:1176 - #4 0x000055555560b6f9 in address_space_rw (as=0x555555eff4e0 , addr=3759147008, attrs=..., buf=0x7fffffffc1b0 "", len=4, is_write=true) at /home/elmarco/src/qemu/exec.c:2439 - #5 0x000055555560baa2 in cpu_physical_memory_rw (addr=3759147008, buf=0x7fffffffc1b0 "", len=4, is_write=1) at /home/elmarco/src/qemu/exec.c:2534 - #6 0x000055555564c005 in cpu_physical_memory_write (addr=3759147008, buf=0x7fffffffc1b0, len=4) at /home/elmarco/src/qemu/include/exec/cpu-common.h:80 - #7 0x000055555564cd9c in qtest_process_command (chr=0x55555642b890, words=0x5555578de4b0) at /home/elmarco/src/qemu/qtest.c:378 - #8 0x000055555564db77 in qtest_process_inbuf (chr=0x55555642b890, inbuf=0x55555641b340) at /home/elmarco/src/qemu/qtest.c:569 - #9 0x000055555564dc07 in qtest_read (opaque=0x55555642b890, buf=0x7fffffffc2e0 "writel 0xe0100800 0x0\n", size=22) at /home/elmarco/src/qemu/qtest.c:581 - #10 0x000055555574ce3e in qemu_chr_be_write (s=0x55555642b890, buf=0x7fffffffc2e0 "writel 0xe0100800 0x0\n", len=22) at qemu-char.c:306 - #11 0x0000555555751263 in tcp_chr_read (chan=0x55555642bcf0, cond=G_IO_IN, opaque=0x55555642b890) at qemu-char.c:2876 - #12 0x00007ffff64c9a8a in g_main_context_dispatch (context=0x55555641c400) at gmain.c:3122 - - (without this patch, this can be reproduced with the ivshmem qtest) - - Implement an empty mmio write to avoid the crash. - - Signed-off-by: Marc-André Lureau - Reviewed-by: Paolo Bonzini - -diff --git a/hw/pci/msix.c b/hw/pci/msix.c -index 2fdada4..64c93d8 100644 ---- a/hw/pci/msix.c -+++ b/hw/pci/msix.c -@@ -200,8 +200,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, - return pci_get_long(dev->msix_pba + addr); - } - -+static void msix_pba_mmio_write(void *opaque, hwaddr addr, -+ uint64_t val, unsigned size) -+{ -+} -+ - static const MemoryRegionOps msix_pba_mmio_ops = { - .read = msix_pba_mmio_read, -+ .write = msix_pba_mmio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, diff --git a/debian/patches/CVE-2015-8558-ehci_make_idt_processing_more_robust.patch b/debian/patches/CVE-2015-8558-ehci_make_idt_processing_more_robust.patch deleted file mode 100644 index 743b68d..0000000 --- a/debian/patches/CVE-2015-8558-ehci_make_idt_processing_more_robust.patch +++ /dev/null @@ -1,49 +0,0 @@ -From: Gerd Hoffmann -Subject: [Qemu-devel] [PULL 5/5] ehci: make idt processing more robust - -Make ehci_process_itd return an error in case we didn't do any actual -iso transfer because we've found no active transaction. That'll avoid -ehci happily run in circles forever if the guest builds a loop out of -idts. - -This is CVE-2015-8558. - -Cc: qemu-stable@nongnu.org -Reported-by: Qinghao Tang -Tested-by: P J P -Signed-off-by: Gerd Hoffmann ---- - hw/usb/hcd-ehci.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c -index 4e2161b..d07f228 100644 ---- a/hw/usb/hcd-ehci.c -+++ b/hw/usb/hcd-ehci.c -@@ -1389,7 +1389,7 @@ static int ehci_process_itd(EHCIState *ehci, - { - USBDevice *dev; - USBEndpoint *ep; -- uint32_t i, len, pid, dir, devaddr, endp; -+ uint32_t i, len, pid, dir, devaddr, endp, xfers = 0; - uint32_t pg, off, ptr1, ptr2, max, mult; - - ehci->periodic_sched_active = PERIODIC_ACTIVE; -@@ -1479,9 +1479,10 @@ static int ehci_process_itd(EHCIState *ehci, - ehci_raise_irq(ehci, USBSTS_INT); - } - itd->transact[i] &= ~ITD_XACT_ACTIVE; -+ xfers++; - } - } -- return 0; -+ return xfers ? 0 : -1; - } - - --- -1.8.3.1 - - - - diff --git a/debian/patches/CVE-2015-8613-scsi-initialize-info-object.patch b/debian/patches/CVE-2015-8613-scsi-initialize-info-object.patch deleted file mode 100644 index ee0f36d..0000000 --- a/debian/patches/CVE-2015-8613-scsi-initialize-info-object.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 5823b4a214ede884f4ba597fdd629862620e0f92 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Mon, 21 Dec 2015 14:48:18 +0530 -Subject: [PATCH] scsi: initialise info object with appropriate size - -While processing controller 'CTRL_GET_INFO' command, the routine -'megasas_ctrl_get_info' overflows the '&info' object size. Use its -appropriate size to null initialise it. - -Reported-by: Qinghao Tang -Signed-off-by: Prasad J Pandit ---- - hw/scsi/megasas.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c -index d7dc667..576f56c 100644 ---- a/hw/scsi/megasas.c -+++ b/hw/scsi/megasas.c -@@ -718,7 +718,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) - BusChild *kid; - int num_pd_disks = 0; - -- memset(&info, 0x0, cmd->iov_size); -+ memset(&info, 0x0, dcmd_size); - if (cmd->iov_size < dcmd_size) { - trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, - dcmd_size); --- -2.4.3 -=== diff --git a/debian/patches/CVE-2015-8619-hmp-sendkey-oob-fix.patch b/debian/patches/CVE-2015-8619-hmp-sendkey-oob-fix.patch deleted file mode 100644 index 78aea0c..0000000 --- a/debian/patches/CVE-2015-8619-hmp-sendkey-oob-fix.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 8da4a3bf8fb076314f986a0d58cb94f5458e3659 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Mon, 11 Jan 2016 08:21:25 +0100 -Subject: [PATCH] hmp: fix sendkey out of bounds write (CVE-2015-8619) - -When processing 'sendkey' command, hmp_sendkey routine null -terminates the 'keyname_buf' array. This results in an OOB -write issue, if 'keyname_len' was to fall outside of -'keyname_buf' array. - -Now checking the length against the buffer size before using -it. - -Reported-by: Ling Liu -Signed-off-by: Wolfgang Bumiller ---- - hmp.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/hmp.c b/hmp.c -index c2b2c16..0c7a04c 100644 ---- a/hmp.c -+++ b/hmp.c -@@ -1749,6 +1749,8 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) - while (1) { - separator = strchr(keys, '-'); - keyname_len = separator ? separator - keys : strlen(keys); -+ if (keyname_len >= sizeof(keyname_buf)) -+ goto err_out; - pstrcpy(keyname_buf, sizeof(keyname_buf), keys); - - /* Be compatible with old interface, convert user inputted "<" */ -@@ -1800,7 +1802,7 @@ out: - return; - - err_out: -- monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); -+ monitor_printf(mon, "invalid parameter: %s\n", keys); - goto out; - } - --- -2.1.4 - diff --git a/debian/patches/CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch b/debian/patches/CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch deleted file mode 100644 index b46ab48..0000000 --- a/debian/patches/CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch +++ /dev/null @@ -1,45 +0,0 @@ -From d9a3b33d2c9f996537b7f1d0246dee2d0120cefb Mon Sep 17 00:00:00 2001 -From: "Michael S. Tsirkin" -Date: Thu, 19 Nov 2015 15:14:07 +0200 -Subject: [PATCH] acpi: fix buffer overrun on migration - -ich calls acpi_gpe_init with length ICH9_PMIO_GPE0_LEN so -ICH9_PMIO_GPE0_LEN/2 bytes are allocated, but then the full -ICH9_PMIO_GPE0_LEN bytes are migrated. - -As a quick work-around, allocate twice the memory. -We'll probably want to tweak code to avoid -migrating the extra ICH9_PMIO_GPE0_LEN/2 bytes, -but that is a bit trickier to do without breaking -migration compatibility. - -Tested-by: "Dr. David Alan Gilbert" -Reported-by: "Dr. David Alan Gilbert" -Cc: qemu-stable@nongnu.org -Signed-off-by: Michael S. Tsirkin ---- - hw/acpi/core.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/hw/acpi/core.c b/hw/acpi/core.c -index fe6215a..21e113d 100644 ---- a/hw/acpi/core.c -+++ b/hw/acpi/core.c -@@ -625,8 +625,12 @@ void acpi_pm1_cnt_reset(ACPIREGS *ar) - void acpi_gpe_init(ACPIREGS *ar, uint8_t len) - { - ar->gpe.len = len; -- ar->gpe.sts = g_malloc0(len / 2); -- ar->gpe.en = g_malloc0(len / 2); -+ /* Only first len / 2 bytes are ever used, -+ * but the caller in ich9.c migrates full len bytes. -+ * TODO: fix ich9.c and drop the extra allocation. -+ */ -+ ar->gpe.sts = g_malloc0(len); -+ ar->gpe.en = g_malloc0(len); - } - - void acpi_gpe_reset(ACPIREGS *ar) --- -2.1.4 - diff --git a/debian/patches/CVE-2015-8701-net-rocker-off-by-one.patch b/debian/patches/CVE-2015-8701-net-rocker-off-by-one.patch deleted file mode 100644 index 7b17355..0000000 --- a/debian/patches/CVE-2015-8701-net-rocker-off-by-one.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 60e8fd72b0faaf940e220a0514001b86b7149e09 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Mon, 28 Dec 2015 16:24:08 +0530 -Subject: [PATCH] net: rocker: fix an incorrect array bounds check - -While processing transmit(tx) descriptors in 'tx_consume' routine -the switch emulator suffers from an off-by-one error, if a -descriptor was to have more than allowed(ROCKER_TX_FRAGS_MAX=16) -fragments. Fix an incorrect bounds check to avoid it. - -Reported-by: Qinghao Tang -Cc: qemu-stable@nongnu.org -Signed-off-by: Prasad J Pandit -Signed-off-by: Jason Wang ---- - hw/net/rocker/rocker.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c -index c57f1a6..2e77e50 100644 ---- a/hw/net/rocker/rocker.c -+++ b/hw/net/rocker/rocker.c -@@ -232,6 +232,9 @@ static int tx_consume(Rocker *r, DescInfo *info) - frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]); - frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]); - -+ if (iovcnt >= ROCKER_TX_FRAGS_MAX) { -+ goto err_too_many_frags; -+ } - iov[iovcnt].iov_len = frag_len; - iov[iovcnt].iov_base = g_malloc(frag_len); - if (!iov[iovcnt].iov_base) { -@@ -244,10 +247,7 @@ static int tx_consume(Rocker *r, DescInfo *info) - err = -ROCKER_ENXIO; - goto err_bad_io; - } -- -- if (++iovcnt > ROCKER_TX_FRAGS_MAX) { -- goto err_too_many_frags; -- } -+ iovcnt++; - } - - if (iovcnt) { --- -2.1.4 - diff --git a/debian/patches/CVE-2015-8743-ne2000-ioport-bounds-check.patch b/debian/patches/CVE-2015-8743-ne2000-ioport-bounds-check.patch deleted file mode 100644 index 5b34ecf..0000000 --- a/debian/patches/CVE-2015-8743-ne2000-ioport-bounds-check.patch +++ /dev/null @@ -1,48 +0,0 @@ -From ab216355b6d509dce42fda4391f61b49df2ddc93 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Thu, 31 Dec 2015 17:05:27 +0530 -Subject: [PATCH] net: ne2000: fix bounds check in ioport operations - -While doing ioport r/w operations, ne2000 device emulation suffers -from OOB r/w errors. Update respective array bounds check to avoid -OOB access. - -Reported-by: Ling Liu -Cc: qemu-stable@nongnu.org -Signed-off-by: Prasad J Pandit -Signed-off-by: Jason Wang ---- - hw/net/ne2000.c | 10 ++++++---- - 1 file changed, 6 insertions(+), 4 deletions(-) - -diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c -index 010f9ef..a3dffff 100644 ---- a/hw/net/ne2000.c -+++ b/hw/net/ne2000.c -@@ -467,8 +467,9 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, - uint32_t val) - { - addr &= ~1; /* XXX: check exact behaviour if not even */ -- if (addr < 32 || -- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { -+ if (addr < 32 -+ || (addr >= NE2000_PMEM_START -+ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { - stl_le_p(s->mem + addr, val); - } - } -@@ -497,8 +498,9 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) - static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) - { - addr &= ~1; /* XXX: check exact behaviour if not even */ -- if (addr < 32 || -- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { -+ if (addr < 32 -+ || (addr >= NE2000_PMEM_START -+ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { - return ldl_le_p(s->mem + addr); - } else { - return 0xffffffff; --- -2.1.4 - diff --git a/debian/patches/CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch b/debian/patches/CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch deleted file mode 100644 index fbb9f54..0000000 --- a/debian/patches/CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch +++ /dev/null @@ -1,76 +0,0 @@ -From a7278b36fcab9af469563bd7b9dadebe2ae25e48 Mon Sep 17 00:00:00 2001 -From: Dana Rubin -Date: Tue, 18 Aug 2015 12:45:55 +0300 -Subject: [PATCH] net/vmxnet3: Refine l2 header validation - -Validation of l2 header length assumed minimal packet size as -eth_header + 2 * vlan_header regardless of the actual protocol. - -This caused crash for valid non-IP packets shorter than 22 bytes, as -'tx_pkt->packet_type' hasn't been assigned for such packets, and -'vmxnet3_on_tx_done_update_stats()' expects it to be properly set. - -Refine header length validation in 'vmxnet_tx_pkt_parse_headers'. -Check its return value during packet processing flow. - -As a side effect, in case IPv4 and IPv6 header validation failure, -corrupt packets will be dropped. - -Signed-off-by: Dana Rubin -Signed-off-by: Shmulik Ladkani -Signed-off-by: Jason Wang ---- - hw/net/vmxnet3.c | 4 +--- - hw/net/vmxnet_tx_pkt.c | 19 ++++++++++++++++--- - 2 files changed, 17 insertions(+), 6 deletions(-) - -diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c -index 04159c8..48ced71 100644 ---- a/hw/net/vmxnet3.c -+++ b/hw/net/vmxnet3.c -@@ -729,9 +729,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) - } - - if (txd.eop) { -- if (!s->skip_current_tx_pkt) { -- vmxnet_tx_pkt_parse(s->tx_pkt); -- -+ if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) { - if (s->needs_vlan) { - vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); - } -diff --git a/hw/net/vmxnet_tx_pkt.c b/hw/net/vmxnet_tx_pkt.c -index f7344c4..eb88ddf 100644 ---- a/hw/net/vmxnet_tx_pkt.c -+++ b/hw/net/vmxnet_tx_pkt.c -@@ -142,11 +142,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) - - bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, - ETH_MAX_L2_HDR_LEN); -- if (bytes_read < ETH_MAX_L2_HDR_LEN) { -+ if (bytes_read < sizeof(struct eth_header)) { -+ l2_hdr->iov_len = 0; -+ return false; -+ } -+ -+ l2_hdr->iov_len = sizeof(struct eth_header); -+ switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { -+ case ETH_P_VLAN: -+ l2_hdr->iov_len += sizeof(struct vlan_header); -+ break; -+ case ETH_P_DVLAN: -+ l2_hdr->iov_len += 2 * sizeof(struct vlan_header); -+ break; -+ } -+ -+ if (bytes_read < l2_hdr->iov_len) { - l2_hdr->iov_len = 0; - return false; -- } else { -- l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); - } - - l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); --- -2.1.4 - diff --git a/debian/patches/CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch b/debian/patches/CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch deleted file mode 100644 index deb755f..0000000 --- a/debian/patches/CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch +++ /dev/null @@ -1,37 +0,0 @@ -From c6048f849c7e3f009786df76206e895a69de032c Mon Sep 17 00:00:00 2001 -From: Shmulik Ladkani -Date: Mon, 21 Sep 2015 17:09:02 +0300 -Subject: [PATCH] vmxnet3: Support reading IMR registers on bar0 - -Instead of asserting, return the actual IMR register value. -This is aligned with what's returned on ESXi. - -Signed-off-by: Shmulik Ladkani -Tested-by: Dana Rubin -Signed-off-by: Jason Wang ---- - hw/net/vmxnet3.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c -index 48ced71..057f0dc 100644 ---- a/hw/net/vmxnet3.c -+++ b/hw/net/vmxnet3.c -@@ -1163,9 +1163,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr, - static uint64_t - vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size) - { -+ VMXNET3State *s = opaque; -+ - if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, - VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { -- g_assert_not_reached(); -+ int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, -+ VMXNET3_REG_ALIGN); -+ return s->interrupt_states[l].is_masked; - } - - VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size); --- -2.1.4 - diff --git a/debian/patches/CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch b/debian/patches/CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch deleted file mode 100644 index e7d5ebc..0000000 --- a/debian/patches/CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch +++ /dev/null @@ -1,37 +0,0 @@ -From b9a60d97fac671f31df599e7e48fd28fc203c0af Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Sat, 9 Jan 2016 01:18:31 +0530 -Subject: [PATCH] ide: ahci: reset ncq object to unused on error - -When processing NCQ commands, ACHI device emulation prepares a -NCQ transfer object; To which an aio control block(aiocb) object -is assigned in 'execute_ncq_command'. In case, when the NCQ -command is invalid, the 'aiocb' object is not assigned, and NCQ -transfer object is left as 'used'. This leads to a use after -free kind of error in 'bdrv_aio_cancel_async' via 'ahci_reset_port'. -Reset NCQ transfer object to 'unused' to avoid it. - -Reported-by: Qinghao Tang -Signed-off-by: Prasad J Pandit -Reviewed-by: John Snow -Message-id: 1452282511-4116-1-git-send-email-ppandit@redhat.com -Signed-off-by: John Snow ---- - hw/ide/ahci.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c -index dd1912e..17f1cbd 100644 ---- a/hw/ide/ahci.c -+++ b/hw/ide/ahci.c -@@ -910,6 +910,7 @@ static void ncq_err(NCQTransferState *ncq_tfs) - ide_state->error = ABRT_ERR; - ide_state->status = READY_STAT | ERR_STAT; - ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); -+ ncq_tfs->used = 0; - } - - static void ncq_finish(NCQTransferState *ncq_tfs) --- -2.1.4 - diff --git a/debian/patches/add-qmp-get-link-status.patch b/debian/patches/add-qmp-get-link-status.patch deleted file mode 100644 index 83df213..0000000 --- a/debian/patches/add-qmp-get-link-status.patch +++ /dev/null @@ -1,111 +0,0 @@ -Index: new/scripts/qapi.py -=================================================================== ---- new.orig/scripts/qapi.py 2015-07-13 10:00:00.000000000 +0100 -+++ new/scripts/qapi.py 2015-07-13 15:50:00.000000000 +0100 -@@ -37,6 +37,8 @@ - - # Whitelist of commands allowed to return a non-dictionary - returns_whitelist = [ -+ 'get_link_status', -+ - # From QMP: - 'human-monitor-command', - 'query-migrate-cache-size', -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-12-10 09:15:50.890262765 +0100 -+++ new/qapi-schema.json 2014-12-11 09:20:31.072561486 +0100 -@@ -1366,6 +1366,21 @@ - ## - { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } - -+## -+# @get_link_status -+# -+# Get the current link state of the nics or nic. -+# -+# @name: name of the nic you get the state of -+# -+# Return: If link is up 1 -+# If link is down 0 -+# If an error occure an empty string. -+# -+# Notes: this is an Proxmox VE extension and not offical part of Qemu. -+## -+{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'} -+ - ## - # @balloon: - # -Index: new/net/net.c -=================================================================== ---- new.orig/net/net.c 2014-12-10 10:24:39.790496356 +0100 -+++ new/net/net.c 2014-12-11 09:37:55.971321170 +0100 -@@ -1141,6 +1141,33 @@ - } - } - -+int64_t qmp_get_link_status(const char *name, Error **errp) -+{ -+ NetClientState *ncs[MAX_QUEUE_NUM]; -+ NetClientState *nc; -+ int queues; -+ bool ret; -+ -+ queues = qemu_find_net_clients_except(name, ncs, -+ NET_CLIENT_OPTIONS_KIND_MAX, -+ MAX_QUEUE_NUM); -+ -+ if (queues == 0) { -+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, -+ "Device '%s' not found", name); -+ return (int64_t) -1; -+ } -+ -+ nc = ncs[0]; -+ ret = ncs[0]->link_down; -+ -+ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { -+ ret = ncs[0]->peer->link_down; -+ } -+ -+ return (int64_t) ret ? 0 : 1; -+} -+ - void qmp_set_link(const char *name, bool up, Error **errp) - { - NetClientState *ncs[MAX_QUEUE_NUM]; -Index: new/qmp-commands.hx -=================================================================== ---- new.orig/qmp-commands.hx 2014-12-10 09:15:50.891262737 +0100 -+++ new/qmp-commands.hx 2014-12-11 08:36:26.583532314 +0100 -@@ -1473,6 +1473,29 @@ - EQMP - - { -+ .name = "get_link_status", -+ .args_type = "name:s", -+ .mhandler.cmd_new = qmp_marshal_input_get_link_status, -+ }, -+ -+SQMP -+get_link_status -+-------- -+ -+Get the link status of a network adapter. -+ -+Arguments: -+ -+- "name": network device name (json-string) -+ -+Example: -+ -+-> { "execute": "get_link_status", "arguments": { "name": "e1000.0" } } -+<- { "return": {1} } -+ -+EQMP -+ -+ { - .name = "getfd", - .args_type = "fdname:s", - .params = "getfd name", diff --git a/debian/patches/add_firewall_to_vma.patch b/debian/patches/add_firewall_to_vma.patch deleted file mode 100644 index 0ab20a0..0000000 --- a/debian/patches/add_firewall_to_vma.patch +++ /dev/null @@ -1,143 +0,0 @@ -Index: qemu-kvm-dev/qapi-schema.json -=================================================================== ---- qemu-kvm-dev.orig/qapi-schema.json -+++ qemu-kvm-dev/qapi-schema.json -@@ -416,6 +416,7 @@ - { 'command': 'backup', 'data': { 'backup-file': 'str', - '*format': 'BackupFormat', - '*config-file': 'str', -+ '*firewall-file': 'str', - '*devlist': 'str', '*speed': 'int' }, - 'returns': 'UuidInfo' } - -Index: qemu-kvm-dev/qmp-commands.hx -=================================================================== ---- qemu-kvm-dev.orig/qmp-commands.hx -+++ qemu-kvm-dev/qmp-commands.hx -@@ -1204,7 +1204,7 @@ EQMP - - { - .name = "backup", -- .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?", -+ .args_type = "backup-file:s,format:s?,config-file:F?,firewall-file:F?,speed:o?,devlist:s?", - .mhandler.cmd_new = qmp_marshal_input_backup, - }, - -Index: qemu-kvm-dev/blockdev.c -=================================================================== ---- qemu-kvm-dev.orig/blockdev.c -+++ qemu-kvm-dev/blockdev.c -@@ -2457,6 +2457,44 @@ void qmp_backup_cancel(Error **errp) - } - } - -+static int config_to_vma(const char *file, BackupFormat format, -+ const char *backup_dir, VmaWriter *vmaw, -+ Error **errp) -+{ -+ char *cdata = NULL; -+ gsize clen = 0; -+ GError *err = NULL; -+ if (!g_file_get_contents(file, &cdata, &clen, &err)) { -+ error_setg(errp, "unable to read file '%s'", file); -+ return 1; -+ } -+ -+ char *basename = g_path_get_basename(file); -+ -+ if (format == BACKUP_FORMAT_VMA) { -+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -+ error_setg(errp, "unable to add %s config data to vma archive", file); -+ g_free(cdata); -+ g_free(basename); -+ return 1; -+ } -+ } else if (format == BACKUP_FORMAT_DIR) { -+ char config_path[PATH_MAX]; -+ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); -+ if (!g_file_set_contents(config_path, cdata, clen, &err)) { -+ error_setg(errp, "unable to write config file '%s'", config_path); -+ g_free(cdata); -+ g_free(basename); -+ return 1; -+ } -+ } -+ -+ g_free(basename); -+ g_free(cdata); -+ -+ return 0; -+} -+ - static void pvebackup_run_next_job(void) - { - GList *l = backup_state.di_list; -@@ -2483,6 +2521,7 @@ static void pvebackup_run_next_job(void) - UuidInfo *qmp_backup(const char *backup_file, bool has_format, - BackupFormat format, - bool has_config_file, const char *config_file, -+ bool has_firewall_file, const char *firewall_file, - bool has_devlist, const char *devlist, - bool has_speed, int64_t speed, Error **errp) - { -@@ -2635,38 +2674,17 @@ UuidInfo *qmp_backup(const char *backup_ - - /* add configuration file to archive */ - if (has_config_file) { -- char *cdata = NULL; -- gsize clen = 0; -- GError *err = NULL; -- if (!g_file_get_contents(config_file, &cdata, &clen, &err)) { -- error_setg(errp, "unable to read file '%s'", config_file); -- goto err; -- } -- -- char *basename = g_path_get_basename(config_file); -- -- if (format == BACKUP_FORMAT_VMA) { -- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -- error_setg(errp, "unable to add config data to vma archive"); -- g_free(cdata); -- g_free(basename); -- goto err; -- } -- } else if (format == BACKUP_FORMAT_DIR) { -- char config_path[PATH_MAX]; -- snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); -- if (!g_file_set_contents(config_path, cdata, clen, &err)) { -- error_setg(errp, "unable to write config file '%s'", config_path); -- g_free(cdata); -- g_free(basename); -- goto err; -- } -- } -- -- g_free(basename); -- g_free(cdata); -+ if(config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) { -+ goto err; -+ } - } - -+ /* add firewall file to archive */ -+ if (has_firewall_file) { -+ if(config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) { -+ goto err; -+ } -+ } - /* initialize global backup_state now */ - - backup_state.cancel = false; -Index: qemu-kvm-dev/hmp.c -=================================================================== ---- qemu-kvm-dev.orig/hmp.c -+++ qemu-kvm-dev/hmp.c -@@ -1465,7 +1465,7 @@ void hmp_backup(Monitor *mon, const QDic - int64_t speed = qdict_get_try_int(qdict, "speed", 0); - - qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, -- false, NULL, !!devlist, -+ false, NULL, false, NULL, !!devlist, - devlist, qdict_haskey(qdict, "speed"), speed, &error); - - hmp_handle_error(mon, &error); diff --git a/debian/patches/adjust-path.diff b/debian/patches/adjust-path.diff deleted file mode 100644 index ea72b60..0000000 --- a/debian/patches/adjust-path.diff +++ /dev/null @@ -1,16 +0,0 @@ -Index: new/include/net/net.h -=================================================================== ---- new.orig/include/net/net.h 2014-11-20 06:45:06.000000000 +0100 -+++ new/include/net/net.h 2014-11-20 06:47:47.000000000 +0100 -@@ -195,8 +195,9 @@ - int net_hub_id_for_client(NetClientState *nc, int *id); - NetClientState *net_hub_port_find(int hub_id); - --#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" --#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" -+#define DEFAULT_NETWORK_SCRIPT "/etc/kvm/kvm-ifup" -+#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/kvm/kvm-ifdown" -+ - #define DEFAULT_BRIDGE_HELPER CONFIG_QEMU_HELPERDIR "/qemu-bridge-helper" - #define DEFAULT_BRIDGE_INTERFACE "br0" - diff --git a/debian/patches/ahci-add_migration-support.patch b/debian/patches/ahci-add_migration-support.patch deleted file mode 100644 index a5791f7..0000000 --- a/debian/patches/ahci-add_migration-support.patch +++ /dev/null @@ -1,154 +0,0 @@ -From: Jason Baron -Subject: [Qemu-devel] [PATCH] ahci: add migration support -Date: Thu, 30 Aug 2012 14:00:04 -0400 - -Add support for ahci migration. This patch builds upon the patches posted -previously by Andreas Faerber: - -http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg01538.html - -(I hope I am giving Andreas proper credit for his work.) - -I've tested these patches by migrating Windows 7 and Fedora 16 guests on -both piix with ahci attached and on q35 (which has a built-in ahci controller). - -Signed-off-by: Jason Baron ---- - hw/ide/ahci.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- - hw/ide/ahci.h | 10 +++++++++ - hw/ide/ich.c | 11 +++++++-- - 3 files changed, 81 insertions(+), 4 deletions(-) - -Index: new/hw/ide/ahci.c -=================================================================== ---- new.orig/hw/ide/ahci.c 2012-09-24 07:15:00.000000000 +0200 -+++ new/hw/ide/ahci.c 2012-09-24 07:15:28.000000000 +0200 -@@ -1203,6 +1203,65 @@ - } - } - -+static const VMStateDescription vmstate_ahci_device = { -+ .name = "ahci port", -+ .version_id = 1, -+ .fields = (VMStateField []) { -+ VMSTATE_IDE_BUS(port, AHCIDevice), -+ VMSTATE_UINT32(port_state, AHCIDevice), -+ VMSTATE_UINT32(finished, AHCIDevice), -+ VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), -+ VMSTATE_UINT32(port_regs.lst_addr_hi, AHCIDevice), -+ VMSTATE_UINT32(port_regs.fis_addr, AHCIDevice), -+ VMSTATE_UINT32(port_regs.fis_addr_hi, AHCIDevice), -+ VMSTATE_UINT32(port_regs.irq_stat, AHCIDevice), -+ VMSTATE_UINT32(port_regs.irq_mask, AHCIDevice), -+ VMSTATE_UINT32(port_regs.cmd, AHCIDevice), -+ VMSTATE_UINT32(port_regs.tfdata, AHCIDevice), -+ VMSTATE_UINT32(port_regs.sig, AHCIDevice), -+ VMSTATE_UINT32(port_regs.scr_stat, AHCIDevice), -+ VMSTATE_UINT32(port_regs.scr_ctl, AHCIDevice), -+ VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), -+ VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), -+ VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), -+ VMSTATE_END_OF_LIST() -+ }, -+}; -+ -+static int ahci_state_post_load(void *opaque, int version_id) -+{ -+ int i; -+ AHCIState *s = opaque; -+ -+ for (i = 0; i < s->ports; i++) { -+ AHCIPortRegs *pr = &s->dev[i].port_regs; -+ -+ map_page(&s->dev[i].lst, -+ ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); -+ map_page(&s->dev[i].res_fis, -+ ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); -+ } -+ -+ return 0; -+} -+ -+const VMStateDescription vmstate_ahci = { -+ .name = "ahci", -+ .version_id = 1, -+ .post_load = ahci_state_post_load, -+ .fields = (VMStateField []) { -+ VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports, -+ vmstate_ahci_device, AHCIDevice), -+ VMSTATE_UINT32(control_regs.cap, AHCIState), -+ VMSTATE_UINT32(control_regs.ghc, AHCIState), -+ VMSTATE_UINT32(control_regs.irqstatus, AHCIState), -+ VMSTATE_UINT32(control_regs.impl, AHCIState), -+ VMSTATE_UINT32(control_regs.version, AHCIState), -+ VMSTATE_UINT32(idp_index, AHCIState), -+ VMSTATE_END_OF_LIST() -+ }, -+}; -+ - typedef struct SysbusAHCIState { - SysBusDevice busdev; - AHCIState ahci; -@@ -1211,7 +1270,10 @@ - - static const VMStateDescription vmstate_sysbus_ahci = { - .name = "sysbus-ahci", -- .unmigratable = 1, -+ .fields = (VMStateField []) { -+ VMSTATE_AHCI(ahci, AHCIPCIState), -+ VMSTATE_END_OF_LIST() -+ }, - }; - - static void sysbus_ahci_reset(DeviceState *dev) -Index: new/hw/ide/ahci.h -=================================================================== ---- new.orig/hw/ide/ahci.h 2012-09-24 07:15:00.000000000 +0200 -+++ new/hw/ide/ahci.h 2012-09-24 07:15:28.000000000 +0200 -@@ -307,6 +307,16 @@ - AHCIState ahci; - } AHCIPCIState; - -+extern const VMStateDescription vmstate_ahci; -+ -+#define VMSTATE_AHCI(_field, _state) { \ -+ .name = (stringify(_field)), \ -+ .size = sizeof(AHCIState), \ -+ .vmsd = &vmstate_ahci, \ -+ .flags = VMS_STRUCT, \ -+ .offset = vmstate_offset_value(_state, _field, AHCIState), \ -+} -+ - typedef struct NCQFrame { - uint8_t fis_type; - uint8_t c; -Index: new/hw/ide/ich.c -=================================================================== ---- new.orig/hw/ide/ich.c 2012-09-24 07:15:00.000000000 +0200 -+++ new/hw/ide/ich.c 2012-09-24 07:15:28.000000000 +0200 -@@ -79,9 +79,14 @@ - #define ICH9_IDP_INDEX 0x10 - #define ICH9_IDP_INDEX_LOG2 0x04 - --static const VMStateDescription vmstate_ahci = { -+static const VMStateDescription vmstate_ich9_ahci = { - .name = "ahci", -- .unmigratable = 1, -+ .version_id = 1, -+ .fields = (VMStateField []) { -+ VMSTATE_PCI_DEVICE(card, AHCIPCIState), -+ VMSTATE_AHCI(ahci, AHCIPCIState), -+ VMSTATE_END_OF_LIST() -+ }, - }; - - static void pci_ich9_reset(DeviceState *dev) -@@ -152,7 +157,7 @@ - k->device_id = PCI_DEVICE_ID_INTEL_82801IR; - k->revision = 0x02; - k->class_id = PCI_CLASS_STORAGE_SATA; -- dc->vmsd = &vmstate_ahci; -+ dc->vmsd = &vmstate_ich9_ahci; - dc->reset = pci_ich9_reset; - } - diff --git a/debian/patches/backup-add-dir-format.patch b/debian/patches/backup-add-dir-format.patch deleted file mode 100644 index 0d32af5..0000000 --- a/debian/patches/backup-add-dir-format.patch +++ /dev/null @@ -1,262 +0,0 @@ -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-11-20 08:59:29.000000000 +0100 -+++ new/qapi-schema.json 2014-11-20 09:03:23.000000000 +0100 -@@ -391,7 +391,7 @@ - # @vma: Proxmox vma backup format - ## - { 'enum': 'BackupFormat', -- 'data': [ 'vma' ] } -+ 'data': [ 'vma', 'dir' ] } - - ## - # @backup: -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 08:59:29.000000000 +0100 -+++ new/blockdev.c 2014-11-20 09:04:05.000000000 +0100 -@@ -2301,6 +2301,8 @@ - uint8_t dev_id; - //bool started; - bool completed; -+ char targetfile[PATH_MAX]; -+ BlockDriverState *target; - } PVEBackupDevInfo; - - static void pvebackup_run_next_job(void); -@@ -2369,8 +2371,6 @@ - { - PVEBackupDevInfo *di = opaque; - -- assert(backup_state.vmaw); -- - di->completed = true; - - if (ret < 0 && !backup_state.error) { -@@ -2381,8 +2381,11 @@ - BlockDriverState *bs = di->bs; - - di->bs = NULL; -+ di->target = NULL; - -- vma_writer_close_stream(backup_state.vmaw, di->dev_id); -+ if (backup_state.vmaw) { -+ vma_writer_close_stream(backup_state.vmaw, di->dev_id); -+ } - - block_job_cb(bs, ret); - -@@ -2461,6 +2464,7 @@ - { - BlockBackend *blk; - BlockDriverState *bs = NULL; -+ const char *backup_dir = NULL; - Error *local_err = NULL; - uuid_t uuid; - VmaWriter *vmaw = NULL; -@@ -2478,11 +2482,6 @@ - /* Todo: try to auto-detect format based on file name */ - format = has_format ? format : BACKUP_FORMAT_VMA; - -- if (format != BACKUP_FORMAT_VMA) { -- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); -- return NULL; -- } -- - if (has_devlist) { - devs = g_strsplit_set(devlist, ",;:", -1); - -@@ -2551,27 +2550,63 @@ - - uuid_generate(uuid); - -- vmaw = vma_writer_create(backup_file, uuid, &local_err); -- if (!vmaw) { -- if (local_err) { -- error_propagate(errp, local_err); -+ if (format == BACKUP_FORMAT_VMA) { -+ vmaw = vma_writer_create(backup_file, uuid, &local_err); -+ if (!vmaw) { -+ if (local_err) { -+ error_propagate(errp, local_err); -+ } -+ goto err; - } -- goto err; -- } - -- /* register all devices for vma writer */ -- l = di_list; -- while (l) { -- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -- l = g_list_next(l); -+ /* register all devices for vma writer */ -+ l = di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); - -- const char *devname = bdrv_get_device_name(di->bs); -- di->dev_id = vma_writer_register_stream(vmaw, devname, di->size); -- if (di->dev_id <= 0) { -- error_set(errp, ERROR_CLASS_GENERIC_ERROR, -- "register_stream failed"); -+ const char *devname = bdrv_get_device_name(di->bs); -+ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size); -+ if (di->dev_id <= 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "register_stream failed"); -+ goto err; -+ } -+ } -+ } else if (format == BACKUP_FORMAT_DIR) { -+ if (mkdir(backup_file, 0640) != 0) { -+ error_setg_errno(errp, errno, "can't create directory '%s'\n", -+ backup_file); - goto err; - } -+ backup_dir = backup_file; -+ -+ l = di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ -+ const char *devname = bdrv_get_device_name(di->bs); -+ snprintf(di->targetfile, PATH_MAX, "%s/%s.raw", backup_dir, devname); -+ -+ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB; -+ bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL, -+ di->size, flags, &local_err, false); -+ if (local_err) { -+ error_propagate(errp, local_err); -+ goto err; -+ } -+ -+ di->target = bdrv_new(); -+ if (bdrv_open(&di->target, di->targetfile, NULL, NULL, flags, NULL, &local_err) < 0) { -+ bdrv_unref(di->target); -+ error_propagate(errp, local_err); -+ goto err; -+ } -+ } -+ } else { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); -+ goto err; - } - - /* add configuration file to archive */ -@@ -2584,12 +2619,27 @@ - goto err; - } - -- const char *basename = g_path_get_basename(config_file); -- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -- error_setg(errp, "unable to add config data to vma archive"); -- g_free(cdata); -- goto err; -+ char *basename = g_path_get_basename(config_file); -+ -+ if (format == BACKUP_FORMAT_VMA) { -+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -+ error_setg(errp, "unable to add config data to vma archive"); -+ g_free(cdata); -+ g_free(basename); -+ goto err; -+ } -+ } else if (format == BACKUP_FORMAT_DIR) { -+ char config_path[PATH_MAX]; -+ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); -+ if (!g_file_set_contents(config_path, cdata, clen, &err)) { -+ error_setg(errp, "unable to write config file '%s'", config_path); -+ g_free(cdata); -+ g_free(basename); -+ goto err; -+ } - } -+ -+ g_free(basename); - g_free(cdata); - } - -@@ -2629,10 +2679,11 @@ - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); - -- backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL, -+ backup_start(di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL, - BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, - pvebackup_dump_cb, pvebackup_complete_cb, di, - 1, &local_err); -+ - if (local_err != NULL) { - error_setg(&backup_state.error, "backup_job_create failed"); - pvebackup_cancel(NULL); -@@ -2651,8 +2702,17 @@ - - l = di_list; - while (l) { -- g_free(l->data); -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); -+ -+ if (di->target) { -+ bdrv_unref(di->target); -+ } -+ -+ if (di->targetfile[0]) { -+ unlink(di->targetfile); -+ } -+ g_free(di); - } - g_list_free(di_list); - -@@ -2666,6 +2726,10 @@ - unlink(backup_file); - } - -+ if (backup_dir) { -+ rmdir(backup_dir); -+ } -+ - return NULL; - } - -Index: new/hmp-commands.hx -=================================================================== ---- new.orig/hmp-commands.hx 2014-11-20 08:59:29.000000000 +0100 -+++ new/hmp-commands.hx 2014-11-20 09:03:23.000000000 +0100 -@@ -89,9 +89,11 @@ - - { - .name = "backup", -- .args_type = "backupfile:s,speed:o?,devlist:s?", -- .params = "backupfile [speed [devlist]]", -- .help = "create a VM Backup.", -+ .args_type = "directory:-d,backupfile:s,speed:o?,devlist:s?", -+ .params = "[-d] backupfile [speed [devlist]]", -+ .help = "create a VM Backup." -+ "\n\t\t\t Use -d to dump data into a directory instead" -+ "\n\t\t\t of using VMA format.", - .mhandler.cmd = hmp_backup, - }, - -Index: new/hmp.c -=================================================================== ---- new.orig/hmp.c 2014-11-20 08:59:29.000000000 +0100 -+++ new/hmp.c 2014-11-20 09:03:23.000000000 +0100 -@@ -1459,11 +1459,13 @@ - { - Error *error = NULL; - -+ int dir = qdict_get_try_bool(qdict, "directory", 0); - const char *backup_file = qdict_get_str(qdict, "backupfile"); - const char *devlist = qdict_get_try_str(qdict, "devlist"); - int64_t speed = qdict_get_try_int(qdict, "speed", 0); - -- qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, -+ qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, -+ false, NULL, !!devlist, - devlist, qdict_haskey(qdict, "speed"), speed, &error); - - hmp_handle_error(mon, &error); diff --git a/debian/patches/backup-add-pve-monitor-commands.patch b/debian/patches/backup-add-pve-monitor-commands.patch deleted file mode 100644 index 2b45b2c..0000000 --- a/debian/patches/backup-add-pve-monitor-commands.patch +++ /dev/null @@ -1,752 +0,0 @@ -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 07:36:12.000000000 +0100 -+++ new/blockdev.c 2014-11-20 07:47:31.000000000 +0100 -@@ -49,6 +49,7 @@ - #include "qmp-commands.h" - #include "trace.h" - #include "sysemu/arch_init.h" -+#include "vma.h" - - static const char *const if_name[IF_COUNT] = { - [IF_NONE] = "none", -@@ -2276,6 +2277,443 @@ - bdrv_put_ref_bh_schedule(bs); - } - -+/* PVE backup related function */ -+ -+static struct PVEBackupState { -+ Error *error; -+ bool cancel; -+ uuid_t uuid; -+ char uuid_str[37]; -+ int64_t speed; -+ time_t start_time; -+ time_t end_time; -+ char *backup_file; -+ VmaWriter *vmaw; -+ GList *di_list; -+ size_t total; -+ size_t transferred; -+ size_t zero_bytes; -+} backup_state; -+ -+typedef struct PVEBackupDevInfo { -+ BlockDriverState *bs; -+ size_t size; -+ uint8_t dev_id; -+ //bool started; -+ bool completed; -+} PVEBackupDevInfo; -+ -+static void pvebackup_run_next_job(void); -+ -+static int pvebackup_dump_cb(void *opaque, BlockDriverState *target, -+ int64_t sector_num, int n_sectors, -+ unsigned char *buf) -+{ -+ PVEBackupDevInfo *di = opaque; -+ -+ if (sector_num & 0x7f) { -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, -+ "got unaligned write inside backup dump " -+ "callback (sector %ld)", sector_num); -+ } -+ return -1; // not aligned to cluster size -+ } -+ -+ int64_t cluster_num = sector_num >> 7; -+ int size = n_sectors * BDRV_SECTOR_SIZE; -+ -+ int ret = -1; -+ -+ if (backup_state.vmaw) { -+ size_t zero_bytes = 0; -+ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, -+ buf, &zero_bytes); -+ backup_state.zero_bytes += zero_bytes; -+ } else { -+ ret = size; -+ if (!buf) { -+ backup_state.zero_bytes += size; -+ } -+ } -+ -+ backup_state.transferred += size; -+ -+ return ret; -+} -+ -+static void pvebackup_cleanup(void) -+{ -+ backup_state.end_time = time(NULL); -+ -+ if (backup_state.vmaw) { -+ Error *local_err = NULL; -+ vma_writer_close(backup_state.vmaw, &local_err); -+ error_propagate(&backup_state.error, local_err); -+ backup_state.vmaw = NULL; -+ } -+ -+ if (backup_state.di_list) { -+ GList *l = backup_state.di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ g_free(di); -+ } -+ g_list_free(backup_state.di_list); -+ backup_state.di_list = NULL; -+ } -+} -+ -+static void pvebackup_complete_cb(void *opaque, int ret) -+{ -+ PVEBackupDevInfo *di = opaque; -+ -+ assert(backup_state.vmaw); -+ -+ di->completed = true; -+ -+ if (ret < 0 && !backup_state.error) { -+ error_setg(&backup_state.error, "job failed with err %d - %s", -+ ret, strerror(-ret)); -+ } -+ -+ BlockDriverState *bs = di->bs; -+ -+ di->bs = NULL; -+ -+ vma_writer_close_stream(backup_state.vmaw, di->dev_id); -+ -+ block_job_cb(bs, ret); -+ -+ if (!backup_state.cancel) { -+ pvebackup_run_next_job(); -+ } -+} -+ -+static void pvebackup_cancel(void *opaque) -+{ -+ backup_state.cancel = true; -+ -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, "backup cancelled"); -+ } -+ -+ /* drain all i/o (awake jobs waiting for aio) */ -+ bdrv_drain_all(); -+ -+ GList *l = backup_state.di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ if (!di->completed && di->bs) { -+ BlockJob *job = di->bs->job; -+ if (job) { -+ if (!di->completed) { -+ block_job_cancel_sync(job); -+ } -+ } -+ } -+ } -+ -+ pvebackup_cleanup(); -+} -+ -+void qmp_backup_cancel(Error **errp) -+{ -+ Coroutine *co = qemu_coroutine_create(pvebackup_cancel); -+ qemu_coroutine_enter(co, NULL); -+ -+ while (backup_state.vmaw) { -+ /* vma writer use main aio context */ -+ aio_poll(qemu_get_aio_context(), true); -+ } -+} -+ -+static void pvebackup_run_next_job(void) -+{ -+ GList *l = backup_state.di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ if (!di->completed && di->bs && di->bs->job) { -+ BlockJob *job = di->bs->job; -+ if (block_job_is_paused(job)) { -+ bool cancel = backup_state.error || backup_state.cancel; -+ if (cancel) { -+ block_job_cancel(job); -+ } else { -+ block_job_resume(job); -+ } -+ } -+ return; -+ } -+ } -+ -+ pvebackup_cleanup(); -+} -+ -+UuidInfo *qmp_backup(const char *backup_file, bool has_format, -+ BackupFormat format, -+ bool has_config_file, const char *config_file, -+ bool has_devlist, const char *devlist, -+ bool has_speed, int64_t speed, Error **errp) -+{ -+ BlockBackend *blk; -+ BlockDriverState *bs = NULL; -+ Error *local_err = NULL; -+ uuid_t uuid; -+ VmaWriter *vmaw = NULL; -+ gchar **devs = NULL; -+ GList *di_list = NULL; -+ GList *l; -+ UuidInfo *uuid_info; -+ -+ if (backup_state.di_list) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "previous backup not finished"); -+ return NULL; -+ } -+ -+ /* Todo: try to auto-detect format based on file name */ -+ format = has_format ? format : BACKUP_FORMAT_VMA; -+ -+ if (format != BACKUP_FORMAT_VMA) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); -+ return NULL; -+ } -+ -+ if (has_devlist) { -+ devs = g_strsplit_set(devlist, ",;:", -1); -+ -+ gchar **d = devs; -+ while (d && *d) { -+ blk = blk_by_name(*d); -+ if (blk) { -+ bs = blk_bs(blk); -+ if (bdrv_is_read_only(bs)) { -+ error_setg(errp, "Node '%s' is read only", *d); -+ goto err; -+ } -+ if (!bdrv_is_inserted(bs)) { -+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d); -+ goto err; -+ } -+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1); -+ di->bs = bs; -+ di_list = g_list_append(di_list, di); -+ } else { -+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, -+ "Device '%s' not found", *d); -+ goto err; -+ } -+ d++; -+ } -+ -+ } else { -+ -+ bs = NULL; -+ while ((bs = bdrv_next(bs))) { -+ -+ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { -+ continue; -+ } -+ -+ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1); -+ di->bs = bs; -+ di_list = g_list_append(di_list, di); -+ } -+ } -+ -+ if (!di_list) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list"); -+ goto err; -+ } -+ -+ size_t total = 0; -+ -+ l = di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { -+ goto err; -+ } -+ -+ ssize_t size = bdrv_getlength(di->bs); -+ if (size < 0) { -+ error_setg_errno(errp, -di->size, "bdrv_getlength failed"); -+ goto err; -+ } -+ di->size = size; -+ total += size; -+ } -+ -+ uuid_generate(uuid); -+ -+ vmaw = vma_writer_create(backup_file, uuid, &local_err); -+ if (!vmaw) { -+ if (local_err) { -+ error_propagate(errp, local_err); -+ } -+ goto err; -+ } -+ -+ /* register all devices for vma writer */ -+ l = di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ -+ const char *devname = bdrv_get_device_name(di->bs); -+ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size); -+ if (di->dev_id <= 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "register_stream failed"); -+ goto err; -+ } -+ } -+ -+ /* add configuration file to archive */ -+ if (has_config_file) { -+ char *cdata = NULL; -+ gsize clen = 0; -+ GError *err = NULL; -+ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) { -+ error_setg(errp, "unable to read file '%s'", config_file); -+ goto err; -+ } -+ -+ const char *basename = g_path_get_basename(config_file); -+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -+ error_setg(errp, "unable to add config data to vma archive"); -+ g_free(cdata); -+ goto err; -+ } -+ g_free(cdata); -+ } -+ -+ /* initialize global backup_state now */ -+ -+ backup_state.cancel = false; -+ -+ if (backup_state.error) { -+ error_free(backup_state.error); -+ backup_state.error = NULL; -+ } -+ -+ backup_state.speed = (has_speed && speed > 0) ? speed : 0; -+ -+ backup_state.start_time = time(NULL); -+ backup_state.end_time = 0; -+ -+ if (backup_state.backup_file) { -+ g_free(backup_state.backup_file); -+ } -+ backup_state.backup_file = g_strdup(backup_file); -+ -+ backup_state.vmaw = vmaw; -+ -+ uuid_copy(backup_state.uuid, uuid); -+ uuid_unparse_lower(uuid, backup_state.uuid_str); -+ -+ backup_state.di_list = di_list; -+ -+ backup_state.total = total; -+ backup_state.transferred = 0; -+ backup_state.zero_bytes = 0; -+ -+ /* start all jobs (paused state) */ -+ l = di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ -+ backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL, -+ BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, -+ pvebackup_dump_cb, pvebackup_complete_cb, di, -+ 1, &local_err); -+ if (local_err != NULL) { -+ error_setg(&backup_state.error, "backup_job_create failed"); -+ pvebackup_cancel(NULL); -+ } -+ } -+ -+ if (!backup_state.error) { -+ pvebackup_run_next_job(); // run one job -+ } -+ -+ uuid_info = g_malloc0(sizeof(*uuid_info)); -+ uuid_info->UUID = g_strdup(backup_state.uuid_str); -+ return uuid_info; -+ -+err: -+ -+ l = di_list; -+ while (l) { -+ g_free(l->data); -+ l = g_list_next(l); -+ } -+ g_list_free(di_list); -+ -+ if (devs) { -+ g_strfreev(devs); -+ } -+ -+ if (vmaw) { -+ Error *err = NULL; -+ vma_writer_close(vmaw, &err); -+ unlink(backup_file); -+ } -+ -+ return NULL; -+} -+ -+BackupStatus *qmp_query_backup(Error **errp) -+{ -+ BackupStatus *info = g_malloc0(sizeof(*info)); -+ -+ if (!backup_state.start_time) { -+ /* not started, return {} */ -+ return info; -+ } -+ -+ info->has_status = true; -+ info->has_start_time = true; -+ info->start_time = backup_state.start_time; -+ -+ if (backup_state.backup_file) { -+ info->has_backup_file = true; -+ info->backup_file = g_strdup(backup_state.backup_file); -+ } -+ -+ info->has_uuid = true; -+ info->uuid = g_strdup(backup_state.uuid_str); -+ -+ if (backup_state.end_time) { -+ if (backup_state.error) { -+ info->status = g_strdup("error"); -+ info->has_errmsg = true; -+ info->errmsg = g_strdup(error_get_pretty(backup_state.error)); -+ } else { -+ info->status = g_strdup("done"); -+ } -+ info->has_end_time = true; -+ info->end_time = backup_state.end_time; -+ } else { -+ info->status = g_strdup("active"); -+ } -+ -+ info->has_total = true; -+ info->total = backup_state.total; -+ info->has_zero_bytes = true; -+ info->zero_bytes = backup_state.zero_bytes; -+ info->has_transferred = true; -+ info->transferred = backup_state.transferred; -+ -+ return info; -+} -+ - void qmp_block_stream(const char *device, - bool has_base, const char *base, - bool has_backing_file, const char *backing_file, -Index: new/hmp-commands.hx -=================================================================== ---- new.orig/hmp-commands.hx 2014-11-20 06:45:05.000000000 +0100 -+++ new/hmp-commands.hx 2014-11-20 07:47:31.000000000 +0100 -@@ -87,6 +87,35 @@ - Copy data from a backing file into a block device. - ETEXI - -+ { -+ .name = "backup", -+ .args_type = "backupfile:s,speed:o?,devlist:s?", -+ .params = "backupfile [speed [devlist]]", -+ .help = "create a VM Backup.", -+ .mhandler.cmd = hmp_backup, -+ }, -+ -+STEXI -+@item backup -+@findex backup -+Create a VM backup. -+ETEXI -+ -+ { -+ .name = "backup_cancel", -+ .args_type = "", -+ .params = "", -+ .help = "cancel the current VM backup", -+ .mhandler.cmd = hmp_backup_cancel, -+ }, -+ -+STEXI -+@item backup_cancel -+@findex backup_cancel -+Cancel the current VM backup. -+ -+ETEXI -+ - { - .name = "block_job_set_speed", - .args_type = "device:B,speed:o", -@@ -1768,6 +1797,8 @@ - show CPU statistics - @item info usernet - show user network stack connection states -+@item info backup -+show backup status - @item info migrate - show migration status - @item info migrate_capabilities -Index: new/hmp.c -=================================================================== ---- new.orig/hmp.c 2014-11-20 07:26:23.000000000 +0100 -+++ new/hmp.c 2014-11-20 07:47:31.000000000 +0100 -@@ -145,6 +145,44 @@ - qapi_free_MouseInfoList(mice_list); - } - -+void hmp_info_backup(Monitor *mon, const QDict *qdict) -+{ -+ BackupStatus *info; -+ -+ info = qmp_query_backup(NULL); -+ if (info->has_status) { -+ if (info->has_errmsg) { -+ monitor_printf(mon, "Backup status: %s - %s\n", -+ info->status, info->errmsg); -+ } else { -+ monitor_printf(mon, "Backup status: %s\n", info->status); -+ } -+ } -+ -+ if (info->has_backup_file) { -+ monitor_printf(mon, "Start time: %s", ctime(&info->start_time)); -+ if (info->end_time) { -+ monitor_printf(mon, "End time: %s", ctime(&info->end_time)); -+ } -+ -+ int per = (info->has_total && info->total && -+ info->has_transferred && info->transferred) ? -+ (info->transferred * 100)/info->total : 0; -+ int zero_per = (info->has_total && info->total && -+ info->has_zero_bytes && info->zero_bytes) ? -+ (info->zero_bytes * 100)/info->total : 0; -+ monitor_printf(mon, "Backup file: %s\n", info->backup_file); -+ monitor_printf(mon, "Backup uuid: %s\n", info->uuid); -+ monitor_printf(mon, "Total size: %zd\n", info->total); -+ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n", -+ info->transferred, per); -+ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n", -+ info->zero_bytes, zero_per); -+ } -+ -+ qapi_free_BackupStatus(info); -+} -+ - void hmp_info_migrate(Monitor *mon, const QDict *qdict) - { - MigrationInfo *info; -@@ -1407,6 +1445,29 @@ - - hmp_handle_error(mon, &error); - } -+ -+void hmp_backup_cancel(Monitor *mon, const QDict *qdict) -+{ -+ Error *error = NULL; -+ -+ qmp_backup_cancel(&error); -+ -+ hmp_handle_error(mon, &error); -+} -+ -+void hmp_backup(Monitor *mon, const QDict *qdict) -+{ -+ Error *error = NULL; -+ -+ const char *backup_file = qdict_get_str(qdict, "backupfile"); -+ const char *devlist = qdict_get_try_str(qdict, "devlist"); -+ int64_t speed = qdict_get_try_int(qdict, "speed", 0); -+ -+ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, -+ devlist, qdict_haskey(qdict, "speed"), speed, &error); -+ -+ hmp_handle_error(mon, &error); -+} - - void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) - { -Index: new/hmp.h -=================================================================== ---- new.orig/hmp.h 2014-11-20 06:45:05.000000000 +0100 -+++ new/hmp.h 2014-11-20 07:47:31.000000000 +0100 -@@ -30,6 +30,7 @@ - void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); - void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict); - void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict); -+void hmp_info_backup(Monitor *mon, const QDict *qdict); - void hmp_info_cpus(Monitor *mon, const QDict *qdict); - void hmp_info_block(Monitor *mon, const QDict *qdict); - void hmp_info_blockstats(Monitor *mon, const QDict *qdict); -@@ -74,6 +75,8 @@ - void hmp_change(Monitor *mon, const QDict *qdict); - void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); - void hmp_block_stream(Monitor *mon, const QDict *qdict); -+void hmp_backup(Monitor *mon, const QDict *qdict); -+void hmp_backup_cancel(Monitor *mon, const QDict *qdict); - void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); - void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); - void hmp_block_job_pause(Monitor *mon, const QDict *qdict); -Index: new/monitor.c -=================================================================== ---- new.orig/monitor.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/monitor.c 2014-11-20 07:47:31.000000000 +0100 -@@ -2759,6 +2759,13 @@ - }, - #endif - { -+ .name = "backup", -+ .args_type = "", -+ .params = "", -+ .help = "show backup status", -+ .mhandler.cmd = hmp_info_backup, -+ }, -+ { - .name = "migrate", - .args_type = "", - .params = "", -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-11-20 07:26:43.000000000 +0100 -+++ new/qapi-schema.json 2014-11-20 07:47:31.000000000 +0100 -@@ -352,6 +352,95 @@ - ## - { 'command': 'query-events', 'returns': ['EventInfo'] } - -+# @BackupStatus: -+# -+# Detailed backup status. -+# -+# @status: #optional string describing the current backup status. -+# This can be 'active', 'done', 'error'. If this field is not -+# returned, no backup process has been initiated -+# -+# @errmsg: #optional error message (only returned if status is 'error') -+# -+# @total: #optional total amount of bytes involved in the backup process -+# -+# @transferred: #optional amount of bytes already backed up. -+# -+# @zero-bytes: #optional amount of 'zero' bytes detected. -+# -+# @start-time: #optional time (epoch) when backup job started. -+# -+# @end-time: #optional time (epoch) when backup job finished. -+# -+# @backupfile: #optional backup file name -+# -+# @uuid: #optional uuid for this backup job -+# -+## -+{ 'struct': 'BackupStatus', -+ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', -+ '*transferred': 'int', '*zero-bytes': 'int', -+ '*start-time': 'int', '*end-time': 'int', -+ '*backup-file': 'str', '*uuid': 'str' } } -+ -+## -+# @BackupFormat -+# -+# An enumeration of supported backup formats. -+# -+# @vma: Proxmox vma backup format -+## -+{ 'enum': 'BackupFormat', -+ 'data': [ 'vma' ] } -+ -+## -+# @backup: -+# -+# Starts a VM backup. -+# -+# @backup-file: the backup file name -+# -+# @format: format of the backup file -+# -+# @config-filename: #optional name of a configuration file to include into -+# the backup archive. -+# -+# @speed: #optional the maximum speed, in bytes per second -+# -+# @devlist: #optional list of block device names (separated by ',', ';' -+# or ':'). By default the backup includes all writable block devices. -+# -+# Returns: the uuid of the backup job -+# -+## -+{ 'command': 'backup', 'data': { 'backup-file': 'str', -+ '*format': 'BackupFormat', -+ '*config-file': 'str', -+ '*devlist': 'str', '*speed': 'int' }, -+ 'returns': 'UuidInfo' } -+ -+## -+# @query-backup -+# -+# Returns information about current/last backup task. -+# -+# Returns: @BackupStatus -+# -+## -+{ 'command': 'query-backup', 'returns': 'BackupStatus' } -+ -+## -+# @backup-cancel -+# -+# Cancel the current executing backup process. -+# -+# Returns: nothing on success -+# -+# Notes: This command succeeds even if there is no backup process running. -+# -+## -+{ 'command': 'backup-cancel' } -+ - ## - # @MigrationStats - # -Index: new/qmp-commands.hx -=================================================================== ---- new.orig/qmp-commands.hx 2014-11-20 07:26:23.000000000 +0100 -+++ new/qmp-commands.hx 2014-11-20 07:47:31.000000000 +0100 -@@ -1203,6 +1203,24 @@ - EQMP - - { -+ .name = "backup", -+ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?", -+ .mhandler.cmd_new = qmp_marshal_input_backup, -+ }, -+ -+ { -+ .name = "backup-cancel", -+ .args_type = "", -+ .mhandler.cmd_new = qmp_marshal_input_backup_cancel, -+ }, -+ -+ { -+ .name = "query-backup", -+ .args_type = "", -+ .mhandler.cmd_new = qmp_marshal_input_query_backup, -+ }, -+ -+ { - .name = "block-job-set-speed", - .args_type = "device:B,speed:o", - .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed, diff --git a/debian/patches/backup-add-vma-binary.patch b/debian/patches/backup-add-vma-binary.patch deleted file mode 100644 index 6fbd7ad..0000000 --- a/debian/patches/backup-add-vma-binary.patch +++ /dev/null @@ -1,2480 +0,0 @@ -From 6289a43696ca6f713a5d3bb9f95a5adb608a5e13 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 13 Nov 2012 11:11:38 +0100 -Subject: [PATCH v5 4/6] introduce new vma archive format - -This is a very simple archive format, see docs/specs/vma_spec.txt - -Signed-off-by: Dietmar Maurer ---- - Makefile | 3 +- - Makefile.objs | 2 +- - blockdev.c | 6 +- - docs/specs/vma_spec.txt | 24 ++ - vma-reader.c | 799 ++++++++++++++++++++++++++++++++++++++++ - vma-writer.c | 940 +++++++++++++++++++++++++++++++++++++++++++++++ - vma.c | 561 ++++++++++++++++++++++++++++ - vma.h | 145 ++++++++ - 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 - create mode 100644 vma.c - create mode 100644 vma.h - -Index: new/Makefile -=================================================================== ---- new.orig/Makefile 2014-11-20 08:12:54.000000000 +0100 -+++ new/Makefile 2014-11-20 08:15:12.000000000 +0100 -@@ -156,7 +156,7 @@ - include $(SRC_PATH)/libcacard/Makefile - endif - --all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules -+all: $(DOCS) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules - - config-host.h: config-host.h-timestamp - config-host.h-timestamp: config-host.mak -@@ -224,6 +224,7 @@ - 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 $(block-obj-y) libqemuutil.a libqemustub.a -+vma$(EXESUF): vma.o vma-reader.o $(block-obj-y) libqemuutil.a libqemustub.a - - qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o - -Index: new/vma-reader.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ new/vma-reader.c 2014-11-20 08:15:12.000000000 +0100 -@@ -0,0 +1,799 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) 2012 Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qemu-common.h" -+#include "qemu/timer.h" -+#include "qemu/ratelimit.h" -+#include "vma.h" -+#include "block/block.h" -+ -+#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT) -+ -+static unsigned char zero_vma_block[VMA_BLOCK_SIZE]; -+ -+typedef struct VmaRestoreState { -+ BlockDriverState *bs; -+ bool write_zeroes; -+ unsigned long *bitmap; -+ int bitmap_size; -+} VmaRestoreState; -+ -+struct VmaReader { -+ int fd; -+ GChecksum *md5csum; -+ GHashTable *blob_hash; -+ unsigned char *head_data; -+ VmaDeviceInfo devinfo[256]; -+ VmaRestoreState rstate[256]; -+ 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 -+g_int32_hash(gconstpointer v) -+{ -+ return *(const uint32_t *)v; -+} -+ -+static gboolean -+g_int32_equal(gconstpointer v1, gconstpointer v2) -+{ -+ return *((const uint32_t *)v1) == *((const uint32_t *)v2); -+} -+ -+static int vma_reader_get_bitmap(VmaRestoreState *rstate, int64_t cluster_num) -+{ -+ assert(rstate); -+ assert(rstate->bitmap); -+ -+ unsigned long val, idx, bit; -+ -+ idx = cluster_num / BITS_PER_LONG; -+ -+ assert(rstate->bitmap_size > idx); -+ -+ bit = cluster_num % BITS_PER_LONG; -+ val = rstate->bitmap[idx]; -+ -+ return !!(val & (1UL << bit)); -+} -+ -+static void vma_reader_set_bitmap(VmaRestoreState *rstate, int64_t cluster_num, -+ int dirty) -+{ -+ assert(rstate); -+ assert(rstate->bitmap); -+ -+ unsigned long val, idx, bit; -+ -+ idx = cluster_num / BITS_PER_LONG; -+ -+ assert(rstate->bitmap_size > idx); -+ -+ bit = cluster_num % BITS_PER_LONG; -+ val = rstate->bitmap[idx]; -+ if (dirty) { -+ if (!(val & (1UL << bit))) { -+ val |= 1UL << bit; -+ } -+ } else { -+ if (val & (1UL << bit)) { -+ val &= ~(1UL << bit); -+ } -+ } -+ rstate->bitmap[idx] = val; -+} -+ -+typedef struct VmaBlob { -+ uint32_t start; -+ uint32_t len; -+ void *data; -+} VmaBlob; -+ -+static const VmaBlob *get_header_blob(VmaReader *vmar, uint32_t pos) -+{ -+ assert(vmar); -+ assert(vmar->blob_hash); -+ -+ return g_hash_table_lookup(vmar->blob_hash, &pos); -+} -+ -+static const char *get_header_str(VmaReader *vmar, uint32_t pos) -+{ -+ const VmaBlob *blob = get_header_blob(vmar, pos); -+ if (!blob) { -+ return NULL; -+ } -+ const char *res = (char *)blob->data; -+ if (res[blob->len-1] != '\0') { -+ return NULL; -+ } -+ return res; -+} -+ -+static ssize_t -+safe_read(int fd, unsigned char *buf, size_t count) -+{ -+ ssize_t n; -+ -+ do { -+ n = read(fd, buf, count); -+ } while (n < 0 && errno == EINTR); -+ -+ return n; -+} -+ -+static ssize_t -+full_read(int fd, unsigned char *buf, size_t len) -+{ -+ ssize_t n; -+ size_t total; -+ -+ total = 0; -+ -+ while (len > 0) { -+ n = safe_read(fd, buf, len); -+ -+ if (n == 0) { -+ return total; -+ } -+ -+ if (n <= 0) { -+ break; -+ } -+ -+ buf += n; -+ total += n; -+ len -= n; -+ } -+ -+ if (len) { -+ return -1; -+ } -+ -+ return total; -+} -+ -+void vma_reader_destroy(VmaReader *vmar) -+{ -+ assert(vmar); -+ -+ if (vmar->fd >= 0) { -+ close(vmar->fd); -+ } -+ -+ if (vmar->cdata_list) { -+ g_list_free(vmar->cdata_list); -+ } -+ -+ int i; -+ for (i = 1; i < 256; i++) { -+ if (vmar->rstate[i].bitmap) { -+ g_free(vmar->rstate[i].bitmap); -+ } -+ } -+ -+ if (vmar->md5csum) { -+ g_checksum_free(vmar->md5csum); -+ } -+ -+ if (vmar->blob_hash) { -+ g_hash_table_destroy(vmar->blob_hash); -+ } -+ -+ if (vmar->head_data) { -+ g_free(vmar->head_data); -+ } -+ -+ g_free(vmar); -+ -+}; -+ -+static int vma_reader_read_head(VmaReader *vmar, Error **errp) -+{ -+ assert(vmar); -+ assert(errp); -+ assert(*errp == NULL); -+ -+ unsigned char md5sum[16]; -+ int i; -+ int ret = 0; -+ -+ vmar->head_data = g_malloc(sizeof(VmaHeader)); -+ -+ if (full_read(vmar->fd, vmar->head_data, sizeof(VmaHeader)) != -+ sizeof(VmaHeader)) { -+ error_setg(errp, "can't read vma header - %s", -+ errno ? g_strerror(errno) : "got EOF"); -+ return -1; -+ } -+ -+ VmaHeader *h = (VmaHeader *)vmar->head_data; -+ -+ if (h->magic != VMA_MAGIC) { -+ error_setg(errp, "not a vma file - wrong magic number"); -+ return -1; -+ } -+ -+ uint32_t header_size = GUINT32_FROM_BE(h->header_size); -+ int need = header_size - sizeof(VmaHeader); -+ if (need <= 0) { -+ error_setg(errp, "wrong vma header size %d", header_size); -+ return -1; -+ } -+ -+ vmar->head_data = g_realloc(vmar->head_data, header_size); -+ h = (VmaHeader *)vmar->head_data; -+ -+ if (full_read(vmar->fd, vmar->head_data + sizeof(VmaHeader), need) != -+ need) { -+ error_setg(errp, "can't read vma header data - %s", -+ errno ? g_strerror(errno) : "got EOF"); -+ return -1; -+ } -+ -+ memcpy(md5sum, h->md5sum, 16); -+ memset(h->md5sum, 0, 16); -+ -+ g_checksum_reset(vmar->md5csum); -+ g_checksum_update(vmar->md5csum, vmar->head_data, header_size); -+ gsize csize = 16; -+ g_checksum_get_digest(vmar->md5csum, (guint8 *)(h->md5sum), &csize); -+ -+ if (memcmp(md5sum, h->md5sum, 16) != 0) { -+ error_setg(errp, "wrong vma header chechsum"); -+ return -1; -+ } -+ -+ /* we can modify header data after checksum verify */ -+ h->header_size = header_size; -+ -+ h->version = GUINT32_FROM_BE(h->version); -+ if (h->version != 1) { -+ error_setg(errp, "wrong vma version %d", h->version); -+ return -1; -+ } -+ -+ h->ctime = GUINT64_FROM_BE(h->ctime); -+ h->blob_buffer_offset = GUINT32_FROM_BE(h->blob_buffer_offset); -+ h->blob_buffer_size = GUINT32_FROM_BE(h->blob_buffer_size); -+ -+ uint32_t bstart = h->blob_buffer_offset + 1; -+ uint32_t bend = h->blob_buffer_offset + h->blob_buffer_size; -+ -+ if (bstart <= sizeof(VmaHeader)) { -+ error_setg(errp, "wrong vma blob buffer offset %d", -+ h->blob_buffer_offset); -+ return -1; -+ } -+ -+ if (bend > header_size) { -+ error_setg(errp, "wrong vma blob buffer size %d/%d", -+ h->blob_buffer_offset, h->blob_buffer_size); -+ return -1; -+ } -+ -+ while ((bstart + 2) <= bend) { -+ uint32_t size = vmar->head_data[bstart] + -+ (vmar->head_data[bstart+1] << 8); -+ if ((bstart + size + 2) <= bend) { -+ VmaBlob *blob = g_new0(VmaBlob, 1); -+ blob->start = bstart - h->blob_buffer_offset; -+ blob->len = size; -+ blob->data = vmar->head_data + bstart + 2; -+ g_hash_table_insert(vmar->blob_hash, &blob->start, blob); -+ } -+ bstart += size + 2; -+ } -+ -+ -+ int count = 0; -+ for (i = 1; i < 256; i++) { -+ VmaDeviceInfoHeader *dih = &h->dev_info[i]; -+ uint32_t devname_ptr = GUINT32_FROM_BE(dih->devname_ptr); -+ uint64_t size = GUINT64_FROM_BE(dih->size); -+ const char *devname = get_header_str(vmar, devname_ptr); -+ -+ if (size && devname) { -+ count++; -+ vmar->devinfo[i].size = size; -+ vmar->devinfo[i].devname = devname; -+ -+ if (strcmp(devname, "vmstate") == 0) { -+ vmar->vmstate_stream = i; -+ } -+ } -+ } -+ -+ if (!count) { -+ error_setg(errp, "vma does not contain data"); -+ return -1; -+ } -+ -+ for (i = 0; i < VMA_MAX_CONFIGS; i++) { -+ uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]); -+ uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]); -+ -+ if (!(name_ptr && data_ptr)) { -+ continue; -+ } -+ const char *name = get_header_str(vmar, name_ptr); -+ const VmaBlob *blob = get_header_blob(vmar, data_ptr); -+ -+ if (!(name && blob)) { -+ error_setg(errp, "vma contains invalid data pointers"); -+ return -1; -+ } -+ -+ VmaConfigData *cdata = g_new0(VmaConfigData, 1); -+ cdata->name = name; -+ cdata->data = blob->data; -+ cdata->len = blob->len; -+ -+ vmar->cdata_list = g_list_append(vmar->cdata_list, cdata); -+ } -+ -+ return ret; -+}; -+ -+VmaReader *vma_reader_create(const char *filename, Error **errp) -+{ -+ assert(filename); -+ assert(errp); -+ -+ VmaReader *vmar = g_new0(VmaReader, 1); -+ -+ 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, -+ g_strerror(errno)); -+ goto err; -+ } -+ -+ vmar->md5csum = g_checksum_new(G_CHECKSUM_MD5); -+ if (!vmar->md5csum) { -+ error_setg(errp, "can't allocate cmsum\n"); -+ goto err; -+ } -+ -+ vmar->blob_hash = g_hash_table_new_full(g_int32_hash, g_int32_equal, -+ NULL, g_free); -+ -+ if (vma_reader_read_head(vmar, errp) < 0) { -+ goto err; -+ } -+ -+ return vmar; -+ -+err: -+ if (vmar) { -+ vma_reader_destroy(vmar); -+ } -+ -+ return NULL; -+} -+ -+VmaHeader *vma_reader_get_header(VmaReader *vmar) -+{ -+ assert(vmar); -+ assert(vmar->head_data); -+ -+ return (VmaHeader *)(vmar->head_data); -+} -+ -+GList *vma_reader_get_config_data(VmaReader *vmar) -+{ -+ assert(vmar); -+ assert(vmar->head_data); -+ -+ return vmar->cdata_list; -+} -+ -+VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id) -+{ -+ assert(vmar); -+ assert(dev_id); -+ -+ if (vmar->devinfo[dev_id].size && vmar->devinfo[dev_id].devname) { -+ return &vmar->devinfo[dev_id]; -+ } -+ -+ return NULL; -+} -+ -+int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, -+ bool write_zeroes, Error **errp) -+{ -+ assert(vmar); -+ assert(bs != NULL); -+ assert(dev_id); -+ assert(vmar->rstate[dev_id].bs == NULL); -+ -+ int64_t size = bdrv_getlength(bs); -+ if (size != vmar->devinfo[dev_id].size) { -+ error_setg(errp, "vma_reader_register_bs for stream %s failed - " -+ "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, -+ size, vmar->devinfo[dev_id].size); -+ return -1; -+ } -+ -+ vmar->rstate[dev_id].bs = bs; -+ vmar->rstate[dev_id].write_zeroes = write_zeroes; -+ -+ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + -+ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; -+ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; -+ -+ 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; -+} -+ -+static ssize_t safe_write(int fd, void *buf, size_t count) -+{ -+ ssize_t n; -+ -+ do { -+ n = write(fd, buf, count); -+ } while (n < 0 && errno == EINTR); -+ -+ return n; -+} -+ -+static size_t full_write(int fd, void *buf, size_t len) -+{ -+ ssize_t n; -+ size_t total; -+ -+ total = 0; -+ -+ while (len > 0) { -+ n = safe_write(fd, buf, len); -+ if (n < 0) { -+ return n; -+ } -+ buf += n; -+ total += n; -+ len -= n; -+ } -+ -+ if (len) { -+ /* incomplete write ? */ -+ return -1; -+ } -+ -+ return total; -+} -+ -+static int restore_write_data(VmaReader *vmar, guint8 dev_id, -+ BlockDriverState *bs, int vmstate_fd, -+ unsigned char *buf, int64_t sector_num, -+ int nb_sectors, Error **errp) -+{ -+ assert(vmar); -+ -+ if (dev_id == vmar->vmstate_stream) { -+ if (vmstate_fd >= 0) { -+ int len = nb_sectors * BDRV_SECTOR_SIZE; -+ int res = full_write(vmstate_fd, buf, len); -+ if (res < 0) { -+ error_setg(errp, "write vmstate failed %d", res); -+ return -1; -+ } -+ } -+ } else { -+ int res = bdrv_write(bs, sector_num, buf, nb_sectors); -+ if (res < 0) { -+ error_setg(errp, "bdrv_write to %s failed (%d)", -+ bdrv_get_device_name(bs), res); -+ return -1; -+ } -+ } -+ return 0; -+} -+static int restore_extent(VmaReader *vmar, unsigned char *buf, -+ int extent_size, int vmstate_fd, -+ bool verbose, Error **errp) -+{ -+ assert(vmar); -+ assert(buf); -+ -+ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; -+ int start = VMA_EXTENT_HEADER_SIZE; -+ int i; -+ -+ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { -+ uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]); -+ 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; -+ -+ if (!dev_id) { -+ continue; -+ } -+ -+ VmaRestoreState *rstate = &vmar->rstate[dev_id]; -+ BlockDriverState *bs = NULL; -+ -+ if (dev_id != vmar->vmstate_stream) { -+ bs = rstate->bs; -+ if (!bs) { -+ error_setg(errp, "got wrong dev id %d", dev_id); -+ return -1; -+ } -+ -+ if (vma_reader_get_bitmap(rstate, cluster_num)) { -+ error_setg(errp, "found duplicated cluster %zd for stream %s", -+ cluster_num, vmar->devinfo[dev_id].devname); -+ return -1; -+ } -+ vma_reader_set_bitmap(rstate, cluster_num, 1); -+ -+ max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE; -+ } else { -+ max_sector = G_MAXINT64; -+ if (cluster_num != vmar->vmstate_clusters) { -+ error_setg(errp, "found out of order vmstate data"); -+ return -1; -+ } -+ 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) { -+ error_setg(errp, "short vma extent - too many blocks"); -+ return -1; -+ } -+ int64_t sector_num = (cluster_num * VMA_CLUSTER_SIZE) / -+ BDRV_SECTOR_SIZE; -+ int64_t end_sector = sector_num + -+ VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE; -+ -+ if (end_sector > max_sector) { -+ end_sector = max_sector; -+ } -+ -+ if (end_sector <= sector_num) { -+ error_setg(errp, "got wrong block address - write bejond end"); -+ return -1; -+ } -+ -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, -+ sector_num, nb_sectors, errp) < 0) { -+ return -1; -+ } -+ -+ start += VMA_CLUSTER_SIZE; -+ } else { -+ int j; -+ int bit = 1; -+ -+ for (j = 0; j < 16; j++) { -+ int64_t sector_num = (cluster_num*VMA_CLUSTER_SIZE + -+ j*VMA_BLOCK_SIZE)/BDRV_SECTOR_SIZE; -+ -+ int64_t end_sector = sector_num + -+ VMA_BLOCK_SIZE/BDRV_SECTOR_SIZE; -+ if (end_sector > max_sector) { -+ end_sector = max_sector; -+ } -+ -+ if (mask & bit) { -+ if ((start + VMA_BLOCK_SIZE) > extent_size) { -+ error_setg(errp, "short vma extent - too many blocks"); -+ return -1; -+ } -+ -+ if (end_sector <= sector_num) { -+ error_setg(errp, "got wrong block address - " -+ "write bejond end"); -+ return -1; -+ } -+ -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ buf + start, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } -+ -+ start += VMA_BLOCK_SIZE; -+ -+ } else { -+ -+ if (rstate->write_zeroes && (end_sector > sector_num)) { -+ /* Todo: use bdrv_co_write_zeroes (but that need to -+ * be run inside coroutine?) -+ */ -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ zero_vma_block, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } -+ } -+ } -+ -+ bit = bit << 1; -+ } -+ } -+ } -+ -+ if (start != extent_size) { -+ error_setg(errp, "vma extent error - missing blocks"); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, -+ Error **errp) -+{ -+ assert(vmar); -+ assert(vmar->head_data); -+ -+ int ret = 0; -+ unsigned char buf[VMA_MAX_EXTENT_SIZE]; -+ int buf_pos = 0; -+ 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); -+ if (bytes < 0) { -+ error_setg(errp, "read failed - %s", g_strerror(errno)); -+ return -1; -+ } -+ -+ buf_pos += bytes; -+ -+ if (!buf_pos) { -+ break; /* EOF */ -+ } -+ -+ if (buf_pos < VMA_EXTENT_HEADER_SIZE) { -+ error_setg(errp, "read short extent (%d bytes)", buf_pos); -+ return -1; -+ } -+ -+ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; -+ -+ /* extract md5sum */ -+ memcpy(md5sum, ehead->md5sum, sizeof(ehead->md5sum)); -+ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); -+ -+ g_checksum_reset(vmar->md5csum); -+ g_checksum_update(vmar->md5csum, buf, VMA_EXTENT_HEADER_SIZE); -+ gsize csize = 16; -+ g_checksum_get_digest(vmar->md5csum, ehead->md5sum, &csize); -+ -+ if (memcmp(md5sum, ehead->md5sum, 16) != 0) { -+ error_setg(errp, "wrong vma extent header chechsum"); -+ return -1; -+ } -+ -+ if (memcmp(h->uuid, ehead->uuid, sizeof(ehead->uuid)) != 0) { -+ error_setg(errp, "wrong vma extent uuid"); -+ return -1; -+ } -+ -+ if (ehead->magic != VMA_EXTENT_MAGIC || ehead->reserved1 != 0) { -+ error_setg(errp, "wrong vma extent header magic"); -+ return -1; -+ } -+ -+ int block_count = GUINT16_FROM_BE(ehead->block_count); -+ int extent_size = VMA_EXTENT_HEADER_SIZE + block_count*VMA_BLOCK_SIZE; -+ -+ if (buf_pos < extent_size) { -+ error_setg(errp, "short vma extent (%d < %d)", buf_pos, -+ extent_size); -+ return -1; -+ } -+ -+ if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, -+ errp) < 0) { -+ return -1; -+ } -+ -+ if (buf_pos > extent_size) { -+ memmove(buf, buf + extent_size, buf_pos - extent_size); -+ buf_pos = buf_pos - extent_size; -+ } else { -+ buf_pos = 0; -+ } -+ } -+ -+ bdrv_drain_all(); -+ -+ int i; -+ for (i = 1; i < 256; i++) { -+ VmaRestoreState *rstate = &vmar->rstate[i]; -+ if (!rstate->bs) { -+ continue; -+ } -+ -+ if (bdrv_flush(rstate->bs) < 0) { -+ error_setg(errp, "vma bdrv_flush %s failed", -+ vmar->devinfo[i].devname); -+ return -1; -+ } -+ -+ if (vmar->devinfo[i].size && -+ (strcmp(vmar->devinfo[i].devname, "vmstate") != 0)) { -+ assert(rstate->bitmap); -+ -+ int64_t cluster_num, end; -+ -+ end = (vmar->devinfo[i].size + VMA_CLUSTER_SIZE - 1) / -+ VMA_CLUSTER_SIZE; -+ -+ for (cluster_num = 0; cluster_num < end; cluster_num++) { -+ if (!vma_reader_get_bitmap(rstate, cluster_num)) { -+ error_setg(errp, "detected missing cluster %zd " -+ "for stream %s", cluster_num, -+ vmar->devinfo[i].devname); -+ return -1; -+ } -+ } -+ } -+ } -+ -+ return ret; -+} -+ -Index: new/vma-writer.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ new/vma-writer.c 2014-11-20 08:31:06.000000000 +0100 -@@ -0,0 +1,876 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) 2012 Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "qemu-common.h" -+#include "vma.h" -+#include "block/block.h" -+#include "monitor/monitor.h" -+#include "qemu/main-loop.h" -+ -+#define DEBUG_VMA 0 -+ -+#define DPRINTF(fmt, ...)\ -+ do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0) -+ -+#define WRITE_BUFFERS 5 -+ -+typedef struct VmaAIOCB VmaAIOCB; -+struct VmaAIOCB { -+ unsigned char buffer[VMA_MAX_EXTENT_SIZE]; -+ VmaWriter *vmaw; -+ size_t bytes; -+ Coroutine *co; -+}; -+ -+struct VmaWriter { -+ int fd; -+ FILE *cmd; -+ int status; -+ char errmsg[8192]; -+ uuid_t uuid; -+ bool header_written; -+ bool closed; -+ -+ /* we always write extents */ -+ unsigned char outbuf[VMA_MAX_EXTENT_SIZE]; -+ int outbuf_pos; /* in bytes */ -+ int outbuf_count; /* in VMA_BLOCKS */ -+ uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; -+ -+ VmaAIOCB *aiocbs[WRITE_BUFFERS]; -+ CoQueue wqueue; -+ -+ GChecksum *md5csum; -+ CoMutex writer_lock; -+ CoMutex flush_lock; -+ Coroutine *co_writer; -+ -+ /* drive informations */ -+ VmaStreamInfo stream_info[256]; -+ guint stream_count; -+ -+ guint8 vmstate_stream; -+ uint32_t vmstate_clusters; -+ -+ /* header blob table */ -+ char *header_blob_table; -+ uint32_t header_blob_table_size; -+ uint32_t header_blob_table_pos; -+ -+ /* store for config blobs */ -+ uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ -+ uint32_t config_data[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ -+ uint32_t config_count; -+}; -+ -+void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) -+{ -+ va_list ap; -+ -+ if (vmaw->status < 0) { -+ return; -+ } -+ -+ vmaw->status = -1; -+ -+ va_start(ap, fmt); -+ g_vsnprintf(vmaw->errmsg, sizeof(vmaw->errmsg), fmt, ap); -+ va_end(ap); -+ -+ DPRINTF("vma_writer_set_error: %s\n", vmaw->errmsg); -+} -+ -+static uint32_t allocate_header_blob(VmaWriter *vmaw, const char *data, -+ size_t len) -+{ -+ if (len > 65535) { -+ return 0; -+ } -+ -+ if (!vmaw->header_blob_table || -+ (vmaw->header_blob_table_size < -+ (vmaw->header_blob_table_pos + len + 2))) { -+ int newsize = vmaw->header_blob_table_size + ((len + 2 + 511)/512)*512; -+ -+ vmaw->header_blob_table = g_realloc(vmaw->header_blob_table, newsize); -+ memset(vmaw->header_blob_table + vmaw->header_blob_table_size, -+ 0, newsize - vmaw->header_blob_table_size); -+ vmaw->header_blob_table_size = newsize; -+ } -+ -+ uint32_t cpos = vmaw->header_blob_table_pos; -+ vmaw->header_blob_table[cpos] = len & 255; -+ vmaw->header_blob_table[cpos+1] = (len >> 8) & 255; -+ memcpy(vmaw->header_blob_table + cpos + 2, data, len); -+ vmaw->header_blob_table_pos += len + 2; -+ return cpos; -+} -+ -+static uint32_t allocate_header_string(VmaWriter *vmaw, const char *str) -+{ -+ assert(vmaw); -+ -+ size_t len = strlen(str) + 1; -+ -+ return allocate_header_blob(vmaw, str, len); -+} -+ -+int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, -+ gsize len) -+{ -+ assert(vmaw); -+ assert(!vmaw->header_written); -+ assert(vmaw->config_count < VMA_MAX_CONFIGS); -+ assert(name); -+ assert(data); -+ assert(len); -+ -+ gchar *basename = g_path_get_basename(name); -+ uint32_t name_ptr = allocate_header_string(vmaw, basename); -+ g_free(basename); -+ -+ if (!name_ptr) { -+ return -1; -+ } -+ -+ uint32_t data_ptr = allocate_header_blob(vmaw, data, len); -+ if (!data_ptr) { -+ return -1; -+ } -+ -+ vmaw->config_names[vmaw->config_count] = name_ptr; -+ vmaw->config_data[vmaw->config_count] = data_ptr; -+ -+ vmaw->config_count++; -+ -+ return 0; -+} -+ -+int vma_writer_register_stream(VmaWriter *vmaw, const char *devname, -+ size_t size) -+{ -+ assert(vmaw); -+ assert(devname); -+ assert(!vmaw->status); -+ -+ if (vmaw->header_written) { -+ vma_writer_set_error(vmaw, "vma_writer_register_stream: header " -+ "already written"); -+ return -1; -+ } -+ -+ guint n = vmaw->stream_count + 1; -+ -+ /* we can have dev_ids form 1 to 255 (0 reserved) -+ * 255(-1) reseverd for safety -+ */ -+ if (n > 254) { -+ vma_writer_set_error(vmaw, "vma_writer_register_stream: " -+ "too many drives"); -+ return -1; -+ } -+ -+ if (size <= 0) { -+ vma_writer_set_error(vmaw, "vma_writer_register_stream: " -+ "got strange size %zd", size); -+ return -1; -+ } -+ -+ DPRINTF("vma_writer_register_stream %s %zu %d\n", devname, size, n); -+ -+ vmaw->stream_info[n].devname = g_strdup(devname); -+ vmaw->stream_info[n].size = size; -+ -+ vmaw->stream_info[n].cluster_count = (size + VMA_CLUSTER_SIZE - 1) / -+ VMA_CLUSTER_SIZE; -+ -+ vmaw->stream_count = n; -+ -+ if (strcmp(devname, "vmstate") == 0) { -+ vmaw->vmstate_stream = n; -+ } -+ -+ return n; -+} -+ -+static void vma_co_continue_write(void *opaque) -+{ -+ VmaWriter *vmaw = opaque; -+ -+ DPRINTF("vma_co_continue_write\n"); -+ qemu_coroutine_enter(vmaw->co_writer, NULL); -+} -+ -+static ssize_t coroutine_fn -+vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes) -+{ -+ size_t done = 0; -+ ssize_t ret; -+ -+ /* atomic writes (we cannot interleave writes) */ -+ qemu_co_mutex_lock(&vmaw->writer_lock); -+ -+ DPRINTF("vma_co_write enter %zd\n", bytes); -+ -+ assert(vmaw->co_writer == NULL); -+ -+ vmaw->co_writer = qemu_coroutine_self(); -+ -+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, 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) { -+ done += ret; -+ DPRINTF("vma_co_write written %zd %zd\n", done, ret); -+ } else if (ret < 0) { -+ if (errno == EAGAIN || errno == EWOULDBLOCK) { -+ DPRINTF("vma_co_write yield %zd\n", done); -+ qemu_coroutine_yield(); -+ DPRINTF("vma_co_write restart %zd\n", done); -+ } else { -+ vma_writer_set_error(vmaw, "vma_co_write write error - %s", -+ g_strerror(errno)); -+ done = -1; /* always return failure for partial writes */ -+ break; -+ } -+ } else if (ret == 0) { -+ /* should not happen - simply try again */ -+ } -+ } -+ -+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL); -+ -+ vmaw->co_writer = NULL; -+ -+ qemu_co_mutex_unlock(&vmaw->writer_lock); -+ -+ DPRINTF("vma_co_write leave %zd\n", done); -+ return done; -+} -+ -+static void coroutine_fn vma_co_writer_task(void *opaque) -+{ -+ VmaAIOCB *cb = opaque; -+ -+ DPRINTF("vma_co_writer_task start\n"); -+ -+ int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); -+ DPRINTF("vma_co_writer_task write done %zd\n", done); -+ -+ if (done != cb->bytes) { -+ DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); -+ vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", -+ done); -+ } -+ -+ cb->bytes = 0; -+ -+ qemu_co_queue_next(&cb->vmaw->wqueue); -+ -+ DPRINTF("vma_co_writer_task end\n"); -+} -+ -+static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) -+{ -+ DPRINTF("vma_queue_flush enter\n"); -+ -+ assert(vmaw); -+ -+ while (1) { -+ int i; -+ VmaAIOCB *cb = NULL; -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ if (vmaw->aiocbs[i]->bytes) { -+ cb = vmaw->aiocbs[i]; -+ DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, -+ vmaw->aiocbs[i]->bytes); -+ break; -+ } -+ } -+ if (!cb) { -+ break; -+ } -+ qemu_co_queue_wait(&vmaw->wqueue); -+ } -+ -+ DPRINTF("vma_queue_flush leave\n"); -+} -+ -+/** -+ * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') -+ * So we need to create a coroutione to allow 'parallel' execution. -+ */ -+static ssize_t coroutine_fn -+vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) -+{ -+ DPRINTF("vma_queue_write enter %zd\n", bytes); -+ -+ assert(vmaw); -+ assert(buf); -+ assert(bytes <= VMA_MAX_EXTENT_SIZE); -+ -+ VmaAIOCB *cb = NULL; -+ while (!cb) { -+ int i; -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ if (!vmaw->aiocbs[i]->bytes) { -+ cb = vmaw->aiocbs[i]; -+ break; -+ } -+ } -+ if (!cb) { -+ qemu_co_queue_wait(&vmaw->wqueue); -+ } -+ } -+ -+ memcpy(cb->buffer, buf, bytes); -+ cb->bytes = bytes; -+ cb->vmaw = vmaw; -+ -+ DPRINTF("vma_queue_write start %zd\n", bytes); -+ cb->co = qemu_coroutine_create(vma_co_writer_task); -+ qemu_coroutine_enter(cb->co, cb); -+ -+ DPRINTF("vma_queue_write leave\n"); -+ -+ return bytes; -+} -+ -+VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp) -+{ -+ const char *p; -+ -+ assert(sizeof(VmaHeader) == (4096 + 8192)); -+ assert(G_STRUCT_OFFSET(VmaHeader, config_names) == 2044); -+ assert(G_STRUCT_OFFSET(VmaHeader, config_data) == 3068); -+ assert(G_STRUCT_OFFSET(VmaHeader, dev_info) == 4096); -+ assert(sizeof(VmaExtentHeader) == 512); -+ -+ VmaWriter *vmaw = g_new0(VmaWriter, 1); -+ vmaw->fd = -1; -+ -+ vmaw->md5csum = g_checksum_new(G_CHECKSUM_MD5); -+ if (!vmaw->md5csum) { -+ error_setg(errp, "can't allocate cmsum\n"); -+ goto err; -+ } -+ -+ if (strstart(filename, "exec:", &p)) { -+ vmaw->cmd = popen(p, "w"); -+ if (vmaw->cmd == NULL) { -+ error_setg(errp, "can't popen command '%s' - %s\n", p, -+ g_strerror(errno)); -+ goto err; -+ } -+ vmaw->fd = fileno(vmaw->cmd); -+ -+ /* 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 { -+ 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, -+ g_strerror(errno)); -+ goto err; -+ } -+ } -+ -+ /* 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; -+ -+ vmaw->header_blob_table_pos = 1; /* start at pos 1 */ -+ -+ qemu_co_mutex_init(&vmaw->writer_lock); -+ qemu_co_mutex_init(&vmaw->flush_lock); -+ qemu_co_queue_init(&vmaw->wqueue); -+ -+ uuid_copy(vmaw->uuid, uuid); -+ -+ return vmaw; -+ -+err: -+ if (vmaw) { -+ if (vmaw->cmd) { -+ pclose(vmaw->cmd); -+ } else if (vmaw->fd >= 0) { -+ close(vmaw->fd); -+ } -+ -+ if (vmaw->md5csum) { -+ g_checksum_free(vmaw->md5csum); -+ } -+ -+ g_free(vmaw); -+ } -+ -+ return NULL; -+} -+ -+static int coroutine_fn vma_write_header(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ int header_clusters = 8; -+ char buf[65536*header_clusters]; -+ VmaHeader *head = (VmaHeader *)buf; -+ -+ int i; -+ -+ DPRINTF("VMA WRITE HEADER\n"); -+ -+ if (vmaw->status < 0) { -+ return vmaw->status; -+ } -+ -+ memset(buf, 0, sizeof(buf)); -+ -+ head->magic = VMA_MAGIC; -+ head->version = GUINT32_TO_BE(1); /* v1 */ -+ memcpy(head->uuid, vmaw->uuid, 16); -+ -+ time_t ctime = time(NULL); -+ head->ctime = GUINT64_TO_BE(ctime); -+ -+ if (!vmaw->stream_count) { -+ return -1; -+ } -+ -+ for (i = 0; i < VMA_MAX_CONFIGS; i++) { -+ head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]); -+ head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]); -+ } -+ -+ /* 32 bytes per device (12 used currently) = 8192 bytes max */ -+ for (i = 1; i <= 254; i++) { -+ VmaStreamInfo *si = &vmaw->stream_info[i]; -+ if (si->size) { -+ assert(si->devname); -+ uint32_t devname_ptr = allocate_header_string(vmaw, si->devname); -+ if (!devname_ptr) { -+ return -1; -+ } -+ head->dev_info[i].devname_ptr = GUINT32_TO_BE(devname_ptr); -+ head->dev_info[i].size = GUINT64_TO_BE(si->size); -+ } -+ } -+ -+ uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size; -+ head->header_size = GUINT32_TO_BE(header_size); -+ -+ if (header_size > sizeof(buf)) { -+ return -1; /* just to be sure */ -+ } -+ -+ uint32_t blob_buffer_offset = sizeof(VmaHeader); -+ memcpy(buf + blob_buffer_offset, vmaw->header_blob_table, -+ vmaw->header_blob_table_size); -+ head->blob_buffer_offset = GUINT32_TO_BE(blob_buffer_offset); -+ head->blob_buffer_size = GUINT32_TO_BE(vmaw->header_blob_table_pos); -+ -+ g_checksum_reset(vmaw->md5csum); -+ g_checksum_update(vmaw->md5csum, (const guchar *)buf, header_size); -+ gsize csize = 16; -+ g_checksum_get_digest(vmaw->md5csum, (guint8 *)(head->md5sum), &csize); -+ -+ return vma_queue_write(vmaw, buf, header_size); -+} -+ -+static int coroutine_fn vma_writer_flush(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ -+ int ret; -+ int i; -+ -+ if (vmaw->status < 0) { -+ return vmaw->status; -+ } -+ -+ if (!vmaw->header_written) { -+ vmaw->header_written = true; -+ ret = vma_write_header(vmaw); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_flush: write header failed"); -+ return ret; -+ } -+ } -+ -+ DPRINTF("VMA WRITE FLUSH %d %d\n", vmaw->outbuf_count, vmaw->outbuf_pos); -+ -+ -+ VmaExtentHeader *ehead = (VmaExtentHeader *)vmaw->outbuf; -+ -+ ehead->magic = VMA_EXTENT_MAGIC; -+ ehead->reserved1 = 0; -+ -+ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { -+ ehead->blockinfo[i] = GUINT64_TO_BE(vmaw->outbuf_block_info[i]); -+ } -+ -+ guint16 block_count = (vmaw->outbuf_pos - VMA_EXTENT_HEADER_SIZE) / -+ VMA_BLOCK_SIZE; -+ -+ ehead->block_count = GUINT16_TO_BE(block_count); -+ -+ memcpy(ehead->uuid, vmaw->uuid, sizeof(ehead->uuid)); -+ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); -+ -+ g_checksum_reset(vmaw->md5csum); -+ g_checksum_update(vmaw->md5csum, vmaw->outbuf, VMA_EXTENT_HEADER_SIZE); -+ gsize csize = 16; -+ g_checksum_get_digest(vmaw->md5csum, ehead->md5sum, &csize); -+ -+ int bytes = vmaw->outbuf_pos; -+ ret = vma_queue_write(vmaw, vmaw->outbuf, bytes); -+ if (ret != bytes) { -+ vma_writer_set_error(vmaw, "vma_writer_flush: failed write"); -+ } -+ -+ vmaw->outbuf_count = 0; -+ vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE; -+ -+ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { -+ vmaw->outbuf_block_info[i] = 0; -+ } -+ -+ return vmaw->status; -+} -+ -+static int vma_count_open_streams(VmaWriter *vmaw) -+{ -+ g_assert(vmaw != NULL); -+ -+ int i; -+ int open_drives = 0; -+ for (i = 0; i <= 255; i++) { -+ if (vmaw->stream_info[i].size && !vmaw->stream_info[i].finished) { -+ open_drives++; -+ } -+ } -+ -+ return open_drives; -+} -+ -+/** -+ * all jobs should call this when there is no more data -+ * Returns: number of remaining stream (0 ==> finished) -+ */ -+int coroutine_fn -+vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id) -+{ -+ g_assert(vmaw != NULL); -+ -+ DPRINTF("vma_writer_set_status %d\n", dev_id); -+ if (!vmaw->stream_info[dev_id].size) { -+ vma_writer_set_error(vmaw, "vma_writer_close_stream: " -+ "no such stream %d", dev_id); -+ return -1; -+ } -+ if (vmaw->stream_info[dev_id].finished) { -+ vma_writer_set_error(vmaw, "vma_writer_close_stream: " -+ "stream already closed %d", dev_id); -+ return -1; -+ } -+ -+ vmaw->stream_info[dev_id].finished = true; -+ -+ int open_drives = vma_count_open_streams(vmaw); -+ -+ if (open_drives <= 0) { -+ DPRINTF("vma_writer_set_status all drives completed\n"); -+ qemu_co_mutex_lock(&vmaw->flush_lock); -+ int ret = vma_writer_flush(vmaw); -+ qemu_co_mutex_unlock(&vmaw->flush_lock); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed"); -+ } -+ } -+ -+ return open_drives; -+} -+ -+int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status) -+{ -+ int i; -+ -+ g_assert(vmaw != NULL); -+ -+ if (status) { -+ status->status = vmaw->status; -+ g_strlcpy(status->errmsg, vmaw->errmsg, sizeof(status->errmsg)); -+ for (i = 0; i <= 255; i++) { -+ status->stream_info[i] = vmaw->stream_info[i]; -+ } -+ -+ uuid_unparse_lower(vmaw->uuid, status->uuid_str); -+ } -+ -+ status->closed = vmaw->closed; -+ -+ return vmaw->status; -+} -+ -+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)) { -+ ret = vma_writer_flush(vmaw); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed"); -+ break; -+ } -+ } -+ -+ qemu_co_mutex_unlock(&vmaw->flush_lock); -+ -+ return ret; -+} -+ -+ -+int64_t coroutine_fn -+vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num, -+ unsigned char *buf, size_t *zero_bytes) -+{ -+ g_assert(vmaw != NULL); -+ g_assert(zero_bytes != NULL); -+ -+ *zero_bytes = 0; -+ -+ if (vmaw->status < 0) { -+ return vmaw->status; -+ } -+ -+ if (!dev_id || !vmaw->stream_info[dev_id].size) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "no such stream %d", dev_id); -+ return -1; -+ } -+ -+ if (vmaw->stream_info[dev_id].finished) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "stream already closed %d", dev_id); -+ return -1; -+ } -+ -+ -+ if (cluster_num >= (((uint64_t)1)<<32)) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "cluster number out of range"); -+ return -1; -+ } -+ -+ if (dev_id == vmaw->vmstate_stream) { -+ if (cluster_num != vmaw->vmstate_clusters) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "non sequential vmstate write"); -+ } -+ vmaw->vmstate_clusters++; -+ } else if (cluster_num >= vmaw->stream_info[dev_id].cluster_count) { -+ vma_writer_set_error(vmaw, "vma_writer_write: cluster number too big"); -+ return -1; -+ } -+ -+ /* wait until buffer is available */ -+ if (vma_writer_get_buffer(vmaw) < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "vma_writer_get_buffer failed"); -+ return -1; -+ } -+ -+ DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num); -+ -+ uint16_t mask = 0; -+ -+ 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); -+ block_info |= ((uint64_t)dev_id) << 32; -+ block_info |= (cluster_num & 0xffffffff); -+ vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info; -+ -+ DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info); -+ -+ vmaw->outbuf_count++; -+ -+ /** NOTE: We allways write whole clusters, but we correctly set -+ * transferred bytes. So transferred == size when when everything -+ * went OK. -+ */ -+ size_t transferred = VMA_CLUSTER_SIZE; -+ -+ if (dev_id != vmaw->vmstate_stream) { -+ uint64_t last = (cluster_num + 1) * VMA_CLUSTER_SIZE; -+ if (last > vmaw->stream_info[dev_id].size) { -+ uint64_t diff = last - vmaw->stream_info[dev_id].size; -+ if (diff >= VMA_CLUSTER_SIZE) { -+ vma_writer_set_error(vmaw, "vma_writer_write: " -+ "read after last cluster"); -+ return -1; -+ } -+ transferred -= diff; -+ } -+ } -+ -+ vmaw->stream_info[dev_id].transferred += transferred; -+ -+ return transferred; -+} -+ -+int vma_writer_close(VmaWriter *vmaw, Error **errp) -+{ -+ g_assert(vmaw != NULL); -+ -+ int i; -+ -+ vma_queue_flush(vmaw); -+ -+ /* this should not happen - just to be sure */ -+ while (!qemu_co_queue_empty(&vmaw->wqueue)) { -+ DPRINTF("vma_writer_close wait\n"); -+ co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 1000000); -+ } -+ -+ if (vmaw->cmd) { -+ if (pclose(vmaw->cmd) < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "pclose failed - %s", g_strerror(errno)); -+ } -+ } else { -+ if (close(vmaw->fd) < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "close failed - %s", g_strerror(errno)); -+ } -+ } -+ -+ for (i = 0; i <= 255; i++) { -+ VmaStreamInfo *si = &vmaw->stream_info[i]; -+ if (si->size) { -+ if (!si->finished) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "detected open stream '%s'", si->devname); -+ } else if ((si->transferred != si->size) && -+ (i != vmaw->vmstate_stream)) { -+ vma_writer_set_error(vmaw, "vma_writer_close: " -+ "incomplete stream '%s' (%zd != %zd)", -+ si->devname, si->transferred, si->size); -+ } -+ } -+ } -+ -+ for (i = 0; i <= 255; i++) { -+ vmaw->stream_info[i].finished = 1; /* mark as closed */ -+ } -+ -+ vmaw->closed = 1; -+ -+ if (vmaw->status < 0 && *errp == NULL) { -+ error_setg(errp, "%s", vmaw->errmsg); -+ } -+ -+ return vmaw->status; -+} -+ -+void vma_writer_destroy(VmaWriter *vmaw) -+{ -+ assert(vmaw); -+ -+ int i; -+ -+ for (i = 0; i <= 255; i++) { -+ if (vmaw->stream_info[i].devname) { -+ g_free(vmaw->stream_info[i].devname); -+ } -+ } -+ -+ if (vmaw->md5csum) { -+ g_checksum_free(vmaw->md5csum); -+ } -+ -+ for (i = 0; i < WRITE_BUFFERS; i++) { -+ free(vmaw->aiocbs[i]); -+ } -+ -+ g_free(vmaw); -+} -Index: new/vma.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ new/vma.c 2014-11-20 08:47:23.000000000 +0100 -@@ -0,0 +1,582 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) 2012-2013 Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "vma.h" -+#include "qemu-common.h" -+#include "qemu/error-report.h" -+#include "qemu/main-loop.h" -+ -+static void help(void) -+{ -+ const char *help_msg = -+ "usage: vma command [command options]\n" -+ "\n" -+ "vma list \n" -+ "vma create [-c config] pathname ...\n" -+ "vma extract [-r ] \n" -+ ; -+ -+ printf("%s", help_msg); -+ exit(1); -+} -+ -+static const char *extract_devname(const char *path, char **devname, int index) -+{ -+ assert(path); -+ -+ const char *sep = strchr(path, '='); -+ -+ if (sep) { -+ *devname = g_strndup(path, sep - path); -+ path = sep + 1; -+ } else { -+ if (index >= 0) { -+ *devname = g_strdup_printf("disk%d", index); -+ } else { -+ *devname = NULL; -+ } -+ } -+ -+ return path; -+} -+ -+static void print_content(VmaReader *vmar) -+{ -+ assert(vmar); -+ -+ VmaHeader *head = vma_reader_get_header(vmar); -+ -+ GList *l = vma_reader_get_config_data(vmar); -+ while (l && l->data) { -+ VmaConfigData *cdata = (VmaConfigData *)l->data; -+ l = g_list_next(l); -+ printf("CFG: size: %d name: %s\n", cdata->len, cdata->name); -+ } -+ -+ int i; -+ VmaDeviceInfo *di; -+ for (i = 1; i < 255; i++) { -+ di = vma_reader_get_device_info(vmar, i); -+ if (di) { -+ if (strcmp(di->devname, "vmstate") == 0) { -+ printf("VMSTATE: dev_id=%d memory: %zd\n", i, di->size); -+ } else { -+ printf("DEV: dev_id=%d size: %zd devname: %s\n", -+ i, di->size, di->devname); -+ } -+ } -+ } -+ /* ctime is the last entry we print */ -+ printf("CTIME: %s", ctime(&head->ctime)); -+ fflush(stdout); -+} -+ -+static int list_content(int argc, char **argv) -+{ -+ int c, ret = 0; -+ const char *filename; -+ -+ for (;;) { -+ c = getopt(argc, argv, "h"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 1) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ print_content(vmar); -+ -+ vma_reader_destroy(vmar); -+ -+ return ret; -+} -+ -+typedef struct RestoreMap { -+ char *devname; -+ char *path; -+ bool write_zero; -+} RestoreMap; -+ -+static int extract_content(int argc, char **argv) -+{ -+ int c, ret = 0; -+ int verbose = 0; -+ const char *filename; -+ const char *dirname; -+ const char *readmap = NULL; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hvr:"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'r': -+ readmap = optarg; -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ default: -+ help(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 2) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ dirname = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (mkdir(dirname, 0777) < 0) { -+ g_error("unable to create target directory %s - %s", -+ dirname, g_strerror(errno)); -+ } -+ -+ GList *l = vma_reader_get_config_data(vmar); -+ while (l && l->data) { -+ VmaConfigData *cdata = (VmaConfigData *)l->data; -+ l = g_list_next(l); -+ char *cfgfn = g_strdup_printf("%s/%s", dirname, cdata->name); -+ GError *err = NULL; -+ if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len, -+ &err)) { -+ g_error("unable to write file: %s", err->message); -+ } -+ } -+ -+ GHashTable *devmap = g_hash_table_new(g_str_hash, g_str_equal); -+ -+ if (readmap) { -+ print_content(vmar); -+ -+ FILE *map = fopen(readmap, "r"); -+ if (!map) { -+ g_error("unable to open fifo %s - %s", readmap, g_strerror(errno)); -+ } -+ -+ while (1) { -+ char inbuf[8192]; -+ char *line = fgets(inbuf, sizeof(inbuf), map); -+ if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { -+ break; -+ } -+ int len = strlen(line); -+ if (line[len - 1] == '\n') { -+ line[len - 1] = '\0'; -+ if (len == 1) { -+ break; -+ } -+ } -+ -+ const char *path; -+ bool write_zero; -+ if (line[0] == '0' && line[1] == ':') { -+ path = inbuf + 2; -+ write_zero = false; -+ } else if (line[0] == '1' && line[1] == ':') { -+ path = inbuf + 2; -+ write_zero = true; -+ } else { -+ g_error("read map failed - parse error ('%s')", inbuf); -+ } -+ -+ char *devname = NULL; -+ path = extract_devname(path, &devname, -1); -+ if (!devname) { -+ g_error("read map failed - no dev name specified ('%s')", -+ inbuf); -+ } -+ -+ RestoreMap *map = g_new0(RestoreMap, 1); -+ map->devname = g_strdup(devname); -+ map->path = g_strdup(path); -+ map->write_zero = write_zero; -+ -+ g_hash_table_insert(devmap, map->devname, map); -+ -+ }; -+ } -+ -+ int i; -+ int vmstate_fd = -1; -+ guint8 vmstate_stream = 0; -+ -+ for (i = 1; i < 255; i++) { -+ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); -+ if (di && (strcmp(di->devname, "vmstate") == 0)) { -+ vmstate_stream = i; -+ char *statefn = g_strdup_printf("%s/vmstate.bin", dirname); -+ vmstate_fd = open(statefn, O_WRONLY|O_CREAT|O_EXCL, 0644); -+ if (vmstate_fd < 0) { -+ g_error("create vmstate file '%s' failed - %s", statefn, -+ g_strerror(errno)); -+ } -+ g_free(statefn); -+ } else if (di) { -+ char *devfn = NULL; -+ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB; -+ bool write_zero = true; -+ -+ if (readmap) { -+ RestoreMap *map; -+ map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname); -+ if (map == NULL) { -+ g_error("no device name mapping for %s", di->devname); -+ } -+ devfn = map->path; -+ write_zero = map->write_zero; -+ } else { -+ devfn = g_strdup_printf("%s/tmp-disk-%s.raw", -+ dirname, di->devname); -+ printf("DEVINFO %s %zd\n", devfn, di->size); -+ -+ bdrv_img_create(devfn, "raw", NULL, NULL, NULL, di->size, -+ flags, &errp, 0); -+ if (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 -+ * need to write zeroes (so we generate a sparse file) -+ */ -+ write_zero = false; -+ } -+ -+ BlockDriverState *bs = bdrv_new(); -+ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, NULL, &errp)) { -+ g_error("can't open file %s - %s", devfn, -+ error_get_pretty(errp)); -+ } -+ if (vma_reader_register_bs(vmar, i, bs, write_zero, &errp) < 0) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (!readmap) { -+ g_free(devfn); -+ } -+ } -+ } -+ -+ if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) { -+ g_error("restore failed - %s", error_get_pretty(errp)); -+ } -+ -+ if (!readmap) { -+ for (i = 1; i < 255; i++) { -+ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); -+ if (di && (i != vmstate_stream)) { -+ char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw", -+ dirname, di->devname); -+ char *fn = g_strdup_printf("%s/disk-%s.raw", -+ dirname, di->devname); -+ if (rename(tmpfn, fn) != 0) { -+ g_error("rename %s to %s failed - %s", -+ tmpfn, fn, g_strerror(errno)); -+ } -+ } -+ } -+ } -+ -+ vma_reader_destroy(vmar); -+ -+ bdrv_close_all(); -+ -+ return ret; -+} -+ -+typedef struct BackupJob { -+ BlockDriverState *bs; -+ int64_t len; -+ VmaWriter *vmaw; -+ uint8_t dev_id; -+} BackupJob; -+ -+#define BACKUP_SECTORS_PER_CLUSTER (VMA_CLUSTER_SIZE / BDRV_SECTOR_SIZE) -+ -+static void coroutine_fn backup_run(void *opaque) -+{ -+ BackupJob *job = (BackupJob *)opaque; -+ struct iovec iov; -+ QEMUIOVector qiov; -+ -+ int64_t start, end; -+ int ret = 0; -+ -+ unsigned char *buf = qemu_blockalign(job->bs, VMA_CLUSTER_SIZE); -+ -+ start = 0; -+ end = DIV_ROUND_UP(job->len / BDRV_SECTOR_SIZE, -+ BACKUP_SECTORS_PER_CLUSTER); -+ -+ for (; start < end; start++) { -+ iov.iov_base = buf; -+ iov.iov_len = VMA_CLUSTER_SIZE; -+ qemu_iovec_init_external(&qiov, &iov, 1); -+ -+ ret = bdrv_co_readv(job->bs, start * BACKUP_SECTORS_PER_CLUSTER, -+ BACKUP_SECTORS_PER_CLUSTER, &qiov); -+ if (ret < 0) { -+ vma_writer_set_error(job->vmaw, "read error", -1); -+ goto out; -+ } -+ -+ size_t zb = 0; -+ if (vma_writer_write(job->vmaw, job->dev_id, start, buf, &zb) < 0) { -+ vma_writer_set_error(job->vmaw, "backup_dump_cb vma_writer_write failed", -1); -+ goto out; -+ } -+ } -+ -+ -+out: -+ if (vma_writer_close_stream(job->vmaw, job->dev_id) <= 0) { -+ Error *err = NULL; -+ if (vma_writer_close(job->vmaw, &err) != 0) { -+ g_warning("vma_writer_close failed %s", error_get_pretty(err)); -+ } -+ } -+} -+ -+static int create_archive(int argc, char **argv) -+{ -+ int i, c, res; -+ int verbose = 0; -+ const char *archivename; -+ GList *config_files = NULL; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hvc:"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'c': -+ config_files = g_list_append(config_files, optarg); -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ } -+ -+ -+ /* make sure we have archive name and at least one path */ -+ if ((optind + 2) > argc) { -+ help(); -+ } -+ -+ archivename = argv[optind++]; -+ -+ uuid_t uuid; -+ uuid_generate(uuid); -+ -+ Error *local_err = NULL; -+ VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err); -+ -+ if (vmaw == NULL) { -+ g_error("%s", error_get_pretty(local_err)); -+ } -+ -+ GList *l = config_files; -+ while (l && l->data) { -+ char *name = l->data; -+ char *cdata = NULL; -+ gsize clen = 0; -+ GError *err = NULL; -+ if (!g_file_get_contents(name, &cdata, &clen, &err)) { -+ unlink(archivename); -+ g_error("Unable to read file: %s", err->message); -+ } -+ -+ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) { -+ unlink(archivename); -+ g_error("Unable to append config data %s (len = %zd)", -+ name, clen); -+ } -+ l = g_list_next(l); -+ } -+ -+ int ind = 0; -+ while (optind < argc) { -+ const char *path = argv[optind++]; -+ char *devname = NULL; -+ path = extract_devname(path, &devname, ind++); -+ -+ BlockDriver *drv = NULL; -+ Error *errp = NULL; -+ BlockDriverState *bs = bdrv_new(); -+ -+ res = bdrv_open(&bs, path, NULL, NULL, BDRV_O_CACHE_WB , drv, &errp); -+ if (res < 0) { -+ unlink(archivename); -+ g_error("bdrv_open '%s' failed - %s", path, error_get_pretty(errp)); -+ } -+ int64_t size = bdrv_getlength(bs); -+ int dev_id = vma_writer_register_stream(vmaw, devname, size); -+ if (dev_id <= 0) { -+ unlink(archivename); -+ g_error("vma_writer_register_stream '%s' failed", devname); -+ } -+ -+ BackupJob *job = g_new0(BackupJob, 1); -+ job->len = size; -+ job->bs = bs; -+ job->vmaw = vmaw; -+ job->dev_id = dev_id; -+ -+ Coroutine *co = qemu_coroutine_create(backup_run); -+ qemu_coroutine_enter(co, job); -+ } -+ -+ VmaStatus vmastat; -+ int percent = 0; -+ int last_percent = -1; -+ -+ while (1) { -+ main_loop_wait(false); -+ vma_writer_get_status(vmaw, &vmastat); -+ -+ if (verbose) { -+ -+ uint64_t total = 0; -+ uint64_t transferred = 0; -+ uint64_t zero_bytes = 0; -+ -+ int i; -+ for (i = 0; i < 256; i++) { -+ if (vmastat.stream_info[i].size) { -+ total += vmastat.stream_info[i].size; -+ transferred += vmastat.stream_info[i].transferred; -+ zero_bytes += vmastat.stream_info[i].zero_bytes; -+ } -+ } -+ percent = (transferred*100)/total; -+ if (percent != last_percent) { -+ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, -+ transferred, total, zero_bytes); -+ fflush(stderr); -+ -+ last_percent = percent; -+ } -+ } -+ -+ if (vmastat.closed) { -+ break; -+ } -+ } -+ -+ bdrv_drain_all(); -+ -+ vma_writer_get_status(vmaw, &vmastat); -+ -+ if (verbose) { -+ for (i = 0; i < 256; i++) { -+ VmaStreamInfo *si = &vmastat.stream_info[i]; -+ if (si->size) { -+ fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n", -+ si->devname, si->size, si->zero_bytes, -+ si->size - si->zero_bytes); -+ } -+ } -+ } -+ -+ if (vmastat.status < 0) { -+ unlink(archivename); -+ g_error("creating vma archive failed"); -+ } -+ -+ return 0; -+} -+ -+int main(int argc, char **argv) -+{ -+ const char *cmdname; -+ Error *main_loop_err = NULL; -+ -+ error_set_progname(argv[0]); -+ -+ if (qemu_init_main_loop(&main_loop_err)) { -+ g_error("%s", error_get_pretty(main_loop_err)); -+ } -+ -+ bdrv_init(); -+ -+ if (argc < 2) { -+ help(); -+ } -+ -+ cmdname = argv[1]; -+ argc--; argv++; -+ -+ -+ if (!strcmp(cmdname, "list")) { -+ return list_content(argc, argv); -+ } else if (!strcmp(cmdname, "create")) { -+ return create_archive(argc, argv); -+ } else if (!strcmp(cmdname, "extract")) { -+ return extract_content(argc, argv); -+ } -+ -+ help(); -+ return 0; -+} -Index: new/vma.h -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ new/vma.h 2014-11-20 08:15:12.000000000 +0100 -@@ -0,0 +1,146 @@ -+/* -+ * VMA: Virtual Machine Archive -+ * -+ * Copyright (C) Proxmox Server Solutions -+ * -+ * Authors: -+ * Dietmar Maurer (dietmar@proxmox.com) -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ * -+ */ -+ -+#ifndef BACKUP_VMA_H -+#define BACKUP_VMA_H -+ -+#include -+#include "qapi/error.h" -+#include "block/block.h" -+ -+#define VMA_BLOCK_BITS 12 -+#define VMA_BLOCK_SIZE (1< -Date: Mon, 11 Mar 2013 07:07:46 +0100 -Subject: [PATCH v5 7/7] vma: add verify command - -Users wants to verify the archive after backup. - -Examples: - - # vma verify -v test.vma - - # lzop -d -c test.vma.lzo |vma verify - - -Signed-off-by: Dietmar Maurer ---- - vma-reader.c | 118 +++++++++++++++++++++++++++++++++++++++++++--------------- - vma.c | 57 +++++++++++++++++++++++++++- - vma.h | 1 + - 3 files changed, 145 insertions(+), 31 deletions(-) - -Index: new/vma-reader.c -=================================================================== ---- new.orig/vma-reader.c 2014-11-20 08:15:12.000000000 +0100 -+++ new/vma-reader.c 2014-11-20 08:47:30.000000000 +0100 -@@ -53,6 +53,8 @@ - time_t start_time; - int64_t cluster_count; - int64_t clusters_read; -+ int64_t zero_cluster_data; -+ int64_t partial_zero_cluster_data; - int clusters_read_per; - }; - -@@ -433,6 +435,27 @@ - return NULL; - } - -+static void allocate_rstate(VmaReader *vmar, guint8 dev_id, -+ BlockDriverState *bs, bool write_zeroes) -+{ -+ assert(vmar); -+ assert(dev_id); -+ -+ vmar->rstate[dev_id].bs = bs; -+ vmar->rstate[dev_id].write_zeroes = write_zeroes; -+ -+ int64_t size = vmar->devinfo[dev_id].size; -+ -+ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + -+ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; -+ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; -+ -+ 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; -+} -+ - int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, - bool write_zeroes, Error **errp) - { -@@ -449,17 +472,7 @@ - return -1; - } - -- vmar->rstate[dev_id].bs = bs; -- vmar->rstate[dev_id].write_zeroes = write_zeroes; -- -- int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + -- (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; -- bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; -- -- 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; -+ allocate_rstate(vmar, dev_id, bs, write_zeroes); - - return 0; - } -@@ -526,9 +539,10 @@ - } - return 0; - } -+ - static int restore_extent(VmaReader *vmar, unsigned char *buf, - int extent_size, int vmstate_fd, -- bool verbose, Error **errp) -+ bool verbose, bool verify, Error **errp) - { - assert(vmar); - assert(buf); -@@ -553,7 +567,7 @@ - - if (dev_id != vmar->vmstate_stream) { - bs = rstate->bs; -- if (!bs) { -+ if (!verify && !bs) { - error_setg(errp, "got wrong dev id %d", dev_id); - return -1; - } -@@ -609,10 +623,13 @@ - return -1; - } - -- int nb_sectors = end_sector - sector_num; -- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, -- sector_num, nb_sectors, errp) < 0) { -- return -1; -+ if (!verify) { -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ buf + start, sector_num, nb_sectors, -+ errp) < 0) { -+ return -1; -+ } - } - - start += VMA_CLUSTER_SIZE; -@@ -642,26 +659,37 @@ - return -1; - } - -- int nb_sectors = end_sector - sector_num; -- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -- buf + start, sector_num, -- nb_sectors, errp) < 0) { -- return -1; -+ if (!verify) { -+ int nb_sectors = end_sector - sector_num; -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ buf + start, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } - } - - start += VMA_BLOCK_SIZE; - - } else { - -- if (rstate->write_zeroes && (end_sector > sector_num)) { -+ -+ if (end_sector > sector_num) { - /* Todo: use bdrv_co_write_zeroes (but that need to - * be run inside coroutine?) - */ - int nb_sectors = end_sector - sector_num; -- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -- zero_vma_block, sector_num, -- nb_sectors, errp) < 0) { -- return -1; -+ int zero_size = BDRV_SECTOR_SIZE*nb_sectors; -+ vmar->zero_cluster_data += zero_size; -+ if (mask != 0) { -+ vmar->partial_zero_cluster_data += zero_size; -+ } -+ -+ if (rstate->write_zeroes && !verify) { -+ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, -+ zero_vma_block, sector_num, -+ nb_sectors, errp) < 0) { -+ return -1; -+ } - } - } - } -@@ -679,8 +707,9 @@ - return 0; - } - --int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, -- Error **errp) -+static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd, -+ bool verbose, bool verify, -+ Error **errp) - { - assert(vmar); - assert(vmar->head_data); -@@ -747,7 +776,7 @@ - } - - if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, -- errp) < 0) { -+ verify, errp) < 0) { - return -1; - } - -@@ -794,6 +823,38 @@ - } - } - -+ if (verbose) { -+ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", -+ vmar->clusters_read*VMA_CLUSTER_SIZE, -+ vmar->zero_cluster_data, -+ (double)(100.0*vmar->zero_cluster_data)/ -+ (vmar->clusters_read*VMA_CLUSTER_SIZE)); -+ -+ int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data; -+ if (datasize) { // this does not make sense for empty files -+ printf("space reduction due to 4K zero blocks %.3g%%\n", -+ (double)(100.0*vmar->partial_zero_cluster_data) / datasize); -+ } -+ } - return ret; - } - -+int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, -+ Error **errp) -+{ -+ return vma_reader_restore_full(vmar, vmstate_fd, verbose, false, errp); -+} -+ -+int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp) -+{ -+ guint8 dev_id; -+ -+ for (dev_id = 1; dev_id < 255; dev_id++) { -+ if (vma_reader_get_device_info(vmar, dev_id)) { -+ allocate_rstate(vmar, dev_id, NULL, false); -+ } -+ } -+ -+ return vma_reader_restore_full(vmar, -1, verbose, true, errp); -+} -+ -Index: new/vma.c -=================================================================== ---- new.orig/vma.c 2014-11-20 08:47:23.000000000 +0100 -+++ new/vma.c 2014-11-20 08:47:30.000000000 +0100 -@@ -34,6 +34,7 @@ - "vma list \n" - "vma create [-c config] pathname ...\n" - "vma extract [-r ] \n" -+ "vma verify [-v]\n" - ; - - printf("%s", help_msg); -@@ -338,6 +339,58 @@ - return ret; - } - -+static int verify_content(int argc, char **argv) -+{ -+ int c, ret = 0; -+ int verbose = 0; -+ const char *filename; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hv"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'v': -+ verbose = 1; -+ break; -+ default: -+ help(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 1) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ if (verbose) { -+ print_content(vmar); -+ } -+ -+ if (vma_reader_verify(vmar, verbose, &errp) < 0) { -+ g_error("verify failed - %s", error_get_pretty(errp)); -+ } -+ -+ vma_reader_destroy(vmar); -+ -+ bdrv_close_all(); -+ -+ return ret; -+} -+ - typedef struct BackupJob { - BlockDriverState *bs; - int64_t len; -@@ -575,6 +628,8 @@ - return create_archive(argc, argv); - } else if (!strcmp(cmdname, "extract")) { - return extract_content(argc, argv); -+ } else if (!strcmp(cmdname, "verify")) { -+ return verify_content(argc, argv); - } - - help(); -Index: new/vma.h -=================================================================== ---- new.orig/vma.h 2014-11-20 08:15:12.000000000 +0100 -+++ new/vma.h 2014-11-20 08:47:30.000000000 +0100 -@@ -142,5 +142,6 @@ - Error **errp); - int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, - Error **errp); -+int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp); - - #endif /* BACKUP_VMA_H */ diff --git a/debian/patches/backup-do-not-return-errors-in-dump-callback.patch b/debian/patches/backup-do-not-return-errors-in-dump-callback.patch deleted file mode 100644 index d217968..0000000 --- a/debian/patches/backup-do-not-return-errors-in-dump-callback.patch +++ /dev/null @@ -1,71 +0,0 @@ -The backup dump_cb should always return success. Else the original write -does not succeed and the VM gets notified. - -We simply report success and cancel the backup job instead. - - -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 07:49:05.000000000 +0100 -+++ new/blockdev.c 2014-11-20 07:49:14.000000000 +0100 -@@ -1991,6 +1991,11 @@ - { - PVEBackupDevInfo *di = opaque; - -+ int size = n_sectors * BDRV_SECTOR_SIZE; -+ if (backup_state.cancel) { -+ return size; // return success -+ } -+ - if (sector_num & 0x7f) { - if (!backup_state.error) { - error_setg(&backup_state.error, -@@ -2001,7 +2006,6 @@ - } - - int64_t cluster_num = sector_num >> 7; -- int size = n_sectors * BDRV_SECTOR_SIZE; - - int ret = -1; - -@@ -2009,17 +2013,27 @@ - size_t zero_bytes = 0; - ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, - buf, &zero_bytes); -- backup_state.zero_bytes += zero_bytes; -+ if (ret < 0) { -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, "vma_writer_write error %d", ret); -+ } -+ if (di->bs && di->bs->job) { -+ block_job_cancel(di->bs->job); -+ } -+ } else { -+ backup_state.zero_bytes += zero_bytes; -+ backup_state.transferred += size; -+ } - } else { -- ret = size; - if (!buf) { - backup_state.zero_bytes += size; - } -+ backup_state.transferred += size; - } - -- backup_state.transferred += size; -+ // Note: always return success, because we want that writes succeed anyways. - -- return ret; -+ return size; - } - - static void pvebackup_cleanup(void) -@@ -2091,7 +2105,7 @@ - BlockJob *job = di->bs->job; - if (job) { - if (!di->completed) { -- block_job_cancel_sync(job); -+ block_job_cancel_sync(job); - } - } - } diff --git a/debian/patches/backup-do-not-use-bdrv-drain-all.patch b/debian/patches/backup-do-not-use-bdrv-drain-all.patch deleted file mode 100644 index c0c8e6d..0000000 --- a/debian/patches/backup-do-not-use-bdrv-drain-all.patch +++ /dev/null @@ -1,28 +0,0 @@ -do not use bdrv_drain_all() - -This seems to hang now (qemu 2.2), causing timeouts. - -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-12-29 06:27:15.000000000 +0100 -+++ new/blockdev.c 2014-12-29 07:11:53.000000000 +0100 -@@ -2107,9 +2107,6 @@ - vma_writer_set_error(backup_state.vmaw, "backup cancelled"); - } - -- /* drain all i/o (awake jobs waiting for aio) */ -- bdrv_drain_all(); -- - GList *l = backup_state.di_list; - while (l) { - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -@@ -2118,8 +2115,7 @@ - BlockJob *job = di->bs->job; - if (job) { - if (!di->completed) { -- block_job_cancel_sync(job); -- bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */ -+ block_job_cancel_sync(job); - } - } - } diff --git a/debian/patches/backup-modify-job-api.patch b/debian/patches/backup-modify-job-api.patch deleted file mode 100644 index 2f330e2..0000000 --- a/debian/patches/backup-modify-job-api.patch +++ /dev/null @@ -1,194 +0,0 @@ -Index: new/block/backup.c -=================================================================== ---- new.orig/block/backup.c 2014-11-20 07:55:31.000000000 +0100 -+++ new/block/backup.c 2014-11-20 08:56:23.000000000 +0100 -@@ -42,6 +42,7 @@ - BdrvDirtyBitmap *sync_bitmap; - MirrorSyncMode sync_mode; - RateLimit limit; -+ BackupDumpFunc *dump_cb; - BlockdevOnError on_source_error; - BlockdevOnError on_target_error; - CoRwlock flush_rwlock; -@@ -139,14 +140,21 @@ - goto out; - } - -+ int64_t start_sec = start * BACKUP_SECTORS_PER_CLUSTER; - if (buffer_is_zero(iov.iov_base, iov.iov_len)) { -- ret = bdrv_co_write_zeroes(job->target, -- start * BACKUP_SECTORS_PER_CLUSTER, -- n, BDRV_REQ_MAY_UNMAP); -+ if (job->dump_cb) { -+ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, NULL); -+ } -+ if (job->target) { -+ ret = bdrv_co_write_zeroes(job->target, start_sec, n, BDRV_REQ_MAY_UNMAP); -+ } - } else { -- ret = bdrv_co_writev(job->target, -- start * BACKUP_SECTORS_PER_CLUSTER, n, -- &bounce_qiov); -+ if (job->dump_cb) { -+ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, bounce_buffer); -+ } -+ if (job->target) { -+ ret = bdrv_co_writev(job->target, start_sec, n, &bounce_qiov); -+ } - } - if (ret < 0) { - trace_backup_do_cow_write_fail(job, start, ret); -@@ -208,7 +216,9 @@ - { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); - -- bdrv_iostatus_reset(s->target); -+ if (s->target) { -+ bdrv_iostatus_reset(s->target); -+ } - } - - static const BlockJobDriver backup_job_driver = { -@@ -224,9 +234,11 @@ - if (read) { - return block_job_error_action(&job->common, job->common.bs, - job->on_source_error, true, error); -- } else { -+ } else if (job->target) { - return block_job_error_action(&job->common, job->target, - job->on_target_error, false, error); -+ } else { -+ return BLOCK_ERROR_ACTION_REPORT; - } - } - -@@ -351,9 +363,11 @@ - - job->bitmap = hbitmap_alloc(end, 0); - -- bdrv_set_enable_write_cache(target, true); -- bdrv_set_on_error(target, on_target_error, on_target_error); -- bdrv_iostatus_enable(target); -+ if (target) { -+ bdrv_set_enable_write_cache(target, true); -+ bdrv_set_on_error(target, on_target_error, on_target_error); -+ bdrv_iostatus_enable(target); -+ } - - bdrv_add_before_write_notifier(bs, &before_write); - -@@ -443,8 +461,10 @@ - } - hbitmap_free(job->bitmap); - -- bdrv_iostatus_disable(target); -- bdrv_op_unblock_all(target, job->common.blocker); -+ if (target) { -+ bdrv_iostatus_disable(target); -+ bdrv_op_unblock_all(target, job->common.blocker); -+ } - - data = g_malloc(sizeof(*data)); - data->ret = ret; -@@ -456,13 +472,15 @@ for backup_start - BdrvDirtyBitmap *sync_bitmap, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, -+ BackupDumpFunc *dump_cb, - BlockCompletionFunc *cb, void *opaque, -+ int pause_count, - Error **errp) - { - int64_t len; - - assert(bs); -- assert(target); -+ assert(target || dump_cb); - assert(cb); - - if (bs == target) { -@@ -483,7 +501,7 @@ in backup_start - return; - } - -- if (!bdrv_is_inserted(target)) { -+ if (target && !bdrv_is_inserted(target)) { - error_setg(errp, "Device is not inserted: %s", - bdrv_get_device_name(target)); - return; -@@ -493,7 +511,7 @@ in backup_start - return; - } - -- if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { -+ if (target && bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { - return; - } - -@@ -529,14 +547,17 @@ in backup_start - goto error; - } - -- bdrv_op_block_all(target, job->common.blocker); -+ if (target) -+ bdrv_op_block_all(target, job->common.blocker); - -+ job->dump_cb = dump_cb; - job->on_source_error = on_source_error; - job->on_target_error = on_target_error; - job->target = target; - job->sync_mode = sync_mode; - job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ? - sync_bitmap : NULL; -+ job->common.pause_count = pause_count; - job->common.len = len; - job->common.co = qemu_coroutine_create(backup_run); - qemu_coroutine_enter(job->common.co, job); -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 07:55:31.000000000 +0100 -+++ new/blockdev.c 2014-11-20 08:48:02.000000000 +0100 -@@ -2571,8 +2571,8 @@ qmp_drive_backup - } - - backup_start(bs, target_bs, speed, sync, bmap, -- on_source_error, on_target_error, -- block_job_cb, bs, &local_err); -+ on_source_error, on_target_error, NULL, -+ block_job_cb, bs, 0, &local_err); - if (local_err != NULL) { - bdrv_unref(target_bs); - error_propagate(errp, local_err); -@@ -2633,7 +2633,7 @@ qmp_blockdev_backup - bdrv_ref(target_bs); - bdrv_set_aio_context(target_bs, aio_context); - backup_start(bs, target_bs, speed, sync, NULL, on_source_error, -- on_target_error, block_job_cb, bs, &local_err); -+ on_target_error, NULL, block_job_cb, bs, 0, &local_err); - if (local_err != NULL) { - bdrv_unref(target_bs); - error_propagate(errp, local_err); -Index: new/include/block/block_int.h -=================================================================== ---- new.orig/include/block/block_int.h 2014-11-20 07:55:31.000000000 +0100 -+++ new/include/block/block_int.h 2014-11-20 08:52:59.000000000 +0100 -@@ -59,6 +59,9 @@ - - #define BLOCK_PROBE_BUF_SIZE 512 - -+typedef int BackupDumpFunc(void *opaque, BlockDriverState *bs, -+ int64_t sector_num, int n_sectors, unsigned char *buf); -+ - typedef struct BdrvTrackedRequest { - BlockDriverState *bs; - int64_t offset; -@@ -651,7 +654,9 @@ - BdrvDirtyBitmap *sync_bitmap, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, -+ BackupDumpFunc *dump_cb, - BlockCompletionFunc *cb, void *opaque, -+ int pause_count, - Error **errp); - - void blk_dev_change_media_cb(BlockBackend *blk, bool load); diff --git a/debian/patches/backup-run-flush-inside-coroutine.patch b/debian/patches/backup-run-flush-inside-coroutine.patch deleted file mode 100644 index 913eb08..0000000 --- a/debian/patches/backup-run-flush-inside-coroutine.patch +++ /dev/null @@ -1,50 +0,0 @@ -run vma_writer_close_stream inside coroutine - -Newer qemu (> 2.2.0-rc2) runs pvebackup_complete_cb from main loop, - -But vma_writer_close_stream triggers an async write (flush), which uses qemu_coroutine_yield(). This only works if called from a coroutine. - - -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 12:17:17.000000000 +0100 -+++ new/blockdev.c 2014-11-20 12:17:23.000000000 +0100 -@@ -2059,6 +2059,13 @@ - } - } - -+static void coroutine_fn backup_close_vma_stream(void *opaque) -+{ -+ PVEBackupDevInfo *di = opaque; -+ -+ vma_writer_close_stream(backup_state.vmaw, di->dev_id); -+} -+ - static void pvebackup_complete_cb(void *opaque, int ret) - { - PVEBackupDevInfo *di = opaque; -@@ -2076,7 +2083,8 @@ - di->target = NULL; - - if (backup_state.vmaw) { -- vma_writer_close_stream(backup_state.vmaw, di->dev_id); -+ Coroutine *co = qemu_coroutine_create(backup_close_vma_stream); -+ qemu_coroutine_enter(co, di); - } - - block_job_cb(bs, ret); -Index: new/vma-writer.c -=================================================================== ---- new.orig/vma-writer.c 2014-11-20 12:17:17.000000000 +0100 -+++ new/vma-writer.c 2014-11-20 12:17:23.000000000 +0100 -@@ -706,6 +706,10 @@ - - int i; - -+ while (vmaw->co_writer) { -+ aio_poll(qemu_get_aio_context(), true); -+ } -+ - assert(vmaw->co_writer == NULL); - - if (vmaw->cmd) { diff --git a/debian/patches/backup-vma-add-dump-config.patch b/debian/patches/backup-vma-add-dump-config.patch deleted file mode 100644 index 6febcc4..0000000 --- a/debian/patches/backup-vma-add-dump-config.patch +++ /dev/null @@ -1,89 +0,0 @@ -Index: new/vma.c -=================================================================== ---- new.orig/vma.c 2014-11-20 08:47:30.000000000 +0100 -+++ new/vma.c 2014-11-20 08:47:47.000000000 +0100 -@@ -32,6 +32,7 @@ - "usage: vma command [command options]\n" - "\n" - "vma list \n" -+ "vma config [-c config]\n" - "vma create [-c config] pathname ...\n" - "vma extract [-r ] \n" - "vma verify [-v]\n" -@@ -601,6 +602,67 @@ - return 0; - } - -+static int dump_config(int argc, char **argv) -+{ -+ int c, ret = 0; -+ const char *filename; -+ const char *config_name = "qemu-server.conf"; -+ -+ for (;;) { -+ c = getopt(argc, argv, "hc:"); -+ if (c == -1) { -+ break; -+ } -+ switch (c) { -+ case '?': -+ case 'h': -+ help(); -+ break; -+ case 'c': -+ config_name = optarg; -+ break; -+ default: -+ help(); -+ } -+ } -+ -+ /* Get the filename */ -+ if ((optind + 1) != argc) { -+ help(); -+ } -+ filename = argv[optind++]; -+ -+ Error *errp = NULL; -+ VmaReader *vmar = vma_reader_create(filename, &errp); -+ -+ if (!vmar) { -+ g_error("%s", error_get_pretty(errp)); -+ } -+ -+ int found = 0; -+ GList *l = vma_reader_get_config_data(vmar); -+ while (l && l->data) { -+ VmaConfigData *cdata = (VmaConfigData *)l->data; -+ l = g_list_next(l); -+ if (strcmp(cdata->name, config_name) == 0) { -+ found = 1; -+ fwrite(cdata->data, cdata->len, 1, stdout); -+ break; -+ } -+ } -+ -+ vma_reader_destroy(vmar); -+ -+ bdrv_close_all(); -+ -+ if (!found) { -+ fprintf(stderr, "unable to find configuration data '%s'\n", config_name); -+ return -1; -+ } -+ -+ return ret; -+} -+ - int main(int argc, char **argv) - { - const char *cmdname; -@@ -630,6 +692,8 @@ - return extract_content(argc, argv); - } else if (!strcmp(cmdname, "verify")) { - return verify_content(argc, argv); -+ } else if (!strcmp(cmdname, "config")) { -+ return dump_config(argc, argv); - } - - help(); diff --git a/debian/patches/backup-vma-allow-empty-backups.patch b/debian/patches/backup-vma-allow-empty-backups.patch deleted file mode 100644 index 0fa4eef..0000000 --- a/debian/patches/backup-vma-allow-empty-backups.patch +++ /dev/null @@ -1,259 +0,0 @@ -This patch allows the creation of vma files without data streams. - -Such files only contain configuration data. This is useful if a -user set backup=no to all VM disks. - -Index: new/vma-reader.c -=================================================================== ---- new.orig/vma-reader.c -+++ new/vma-reader.c -@@ -334,11 +334,6 @@ static int vma_reader_read_head(VmaReade - } - } - -- if (!count) { -- error_setg(errp, "vma does not contain data"); -- return -1; -- } -- - for (i = 0; i < VMA_MAX_CONFIGS; i++) { - uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]); - uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]); -@@ -830,16 +825,20 @@ static int vma_reader_restore_full(VmaRe - } - - if (verbose) { -- printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", -- vmar->clusters_read*VMA_CLUSTER_SIZE, -- vmar->zero_cluster_data, -- (double)(100.0*vmar->zero_cluster_data)/ -- (vmar->clusters_read*VMA_CLUSTER_SIZE)); -- -- int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data; -- if (datasize) { // this does not make sense for empty files -- printf("space reduction due to 4K zero blocks %.3g%%\n", -- (double)(100.0*vmar->partial_zero_cluster_data) / datasize); -+ if (vmar->clusters_read) { -+ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", -+ vmar->clusters_read*VMA_CLUSTER_SIZE, -+ vmar->zero_cluster_data, -+ (double)(100.0*vmar->zero_cluster_data)/ -+ (vmar->clusters_read*VMA_CLUSTER_SIZE)); -+ -+ int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data; -+ if (datasize) { // this does not make sense for empty files -+ printf("space reduction due to 4K zero blocks %.3g%%\n", -+ (double)(100.0*vmar->partial_zero_cluster_data) / datasize); -+ } -+ } else { -+ printf("vma archive contains no image data\n"); - } - } - return ret; -Index: new/vma-writer.c -=================================================================== ---- new.orig/vma-writer.c -+++ new/vma-writer.c -@@ -258,7 +258,7 @@ vma_queue_write(VmaWriter *vmaw, const v - } - - vmaw->co_writer = NULL; -- -+ - return (done == bytes) ? bytes : -1; - } - -@@ -382,10 +382,6 @@ static int coroutine_fn vma_write_header - time_t ctime = time(NULL); - head->ctime = GUINT64_TO_BE(ctime); - -- if (!vmaw->stream_count) { -- return -1; -- } -- - for (i = 0; i < VMA_MAX_CONFIGS; i++) { - head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]); - head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]); -@@ -502,6 +498,23 @@ static int vma_count_open_streams(VmaWri - return open_drives; - } - -+ -+/** -+ * You need to call this if the vma archive does not contain -+ * any data stream. -+ */ -+int coroutine_fn -+vma_writer_flush_output(VmaWriter *vmaw) -+{ -+ qemu_co_mutex_lock(&vmaw->flush_lock); -+ int ret = vma_writer_flush(vmaw); -+ qemu_co_mutex_unlock(&vmaw->flush_lock); -+ if (ret < 0) { -+ vma_writer_set_error(vmaw, "vma_writer_flush_header failed"); -+ } -+ return ret; -+} -+ - /** - * all jobs should call this when there is no more data - * Returns: number of remaining stream (0 ==> finished) -@@ -529,12 +542,7 @@ vma_writer_close_stream(VmaWriter *vmaw, - - if (open_drives <= 0) { - DPRINTF("vma_writer_set_status all drives completed\n"); -- qemu_co_mutex_lock(&vmaw->flush_lock); -- int ret = vma_writer_flush(vmaw); -- qemu_co_mutex_unlock(&vmaw->flush_lock); -- if (ret < 0) { -- vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed"); -- } -+ vma_writer_flush_output(vmaw); - } - - return open_drives; -Index: new/vma.c -=================================================================== ---- new.orig/vma.c -+++ new/vma.c -@@ -33,7 +33,7 @@ static void help(void) - "\n" - "vma list \n" - "vma config [-c config]\n" -- "vma create [-c config] pathname ...\n" -+ "vma create [-c config] pathname ...\n" - "vma extract [-r ] \n" - "vma verify [-v]\n" - ; -@@ -401,6 +401,18 @@ typedef struct BackupJob { - - #define BACKUP_SECTORS_PER_CLUSTER (VMA_CLUSTER_SIZE / BDRV_SECTOR_SIZE) - -+static void coroutine_fn backup_run_empty(void *opaque) -+{ -+ VmaWriter *vmaw = (VmaWriter *)opaque; -+ -+ vma_writer_flush_output(vmaw); -+ -+ Error *err = NULL; -+ if (vma_writer_close(vmaw, &err) != 0) { -+ g_warning("vma_writer_close failed %s", error_get_pretty(err)); -+ } -+} -+ - static void coroutine_fn backup_run(void *opaque) - { - BackupJob *job = (BackupJob *)opaque; -@@ -474,8 +486,8 @@ static int create_archive(int argc, char - } - - -- /* make sure we have archive name and at least one path */ -- if ((optind + 2) > argc) { -+ /* make sure we an archive name */ -+ if ((optind + 1) > argc) { - help(); - } - -@@ -510,11 +522,11 @@ static int create_archive(int argc, char - l = g_list_next(l); - } - -- int ind = 0; -+ int devcount = 0; - while (optind < argc) { - const char *path = argv[optind++]; - char *devname = NULL; -- path = extract_devname(path, &devname, ind++); -+ path = extract_devname(path, &devname, devcount++); - - BlockDriver *drv = NULL; - Error *errp = NULL; -@@ -546,37 +558,49 @@ static int create_archive(int argc, char - int percent = 0; - int last_percent = -1; - -- while (1) { -- main_loop_wait(false); -- vma_writer_get_status(vmaw, &vmastat); -- -- if (verbose) { -- -- uint64_t total = 0; -- uint64_t transferred = 0; -- uint64_t zero_bytes = 0; -- -- int i; -- for (i = 0; i < 256; i++) { -- if (vmastat.stream_info[i].size) { -- total += vmastat.stream_info[i].size; -- transferred += vmastat.stream_info[i].transferred; -- zero_bytes += vmastat.stream_info[i].zero_bytes; -+ if (devcount) { -+ while (1) { -+ main_loop_wait(false); -+ vma_writer_get_status(vmaw, &vmastat); -+ -+ if (verbose) { -+ -+ uint64_t total = 0; -+ uint64_t transferred = 0; -+ uint64_t zero_bytes = 0; -+ -+ int i; -+ for (i = 0; i < 256; i++) { -+ if (vmastat.stream_info[i].size) { -+ total += vmastat.stream_info[i].size; -+ transferred += vmastat.stream_info[i].transferred; -+ zero_bytes += vmastat.stream_info[i].zero_bytes; -+ } - } -- } -- percent = (transferred*100)/total; -- if (percent != last_percent) { -- fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, -- transferred, total, zero_bytes); -- fflush(stderr); -+ percent = (transferred*100)/total; -+ if (percent != last_percent) { -+ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, -+ transferred, total, zero_bytes); -+ fflush(stderr); - -- last_percent = percent; -+ last_percent = percent; -+ } - } -- } - -- if (vmastat.closed) { -- break; -+ if (vmastat.closed) { -+ break; -+ } - } -+ } else { -+ Coroutine *co = qemu_coroutine_create(backup_run_empty); -+ qemu_coroutine_enter(co, vmaw); -+ while (1) { -+ main_loop_wait(false); -+ vma_writer_get_status(vmaw, &vmastat); -+ if (vmastat.closed) { -+ break; -+ } -+ } - } - - bdrv_drain_all(); -Index: new/vma.h -=================================================================== ---- new.orig/vma.h -+++ new/vma.h -@@ -128,6 +128,7 @@ int64_t coroutine_fn vma_writer_write(Vm - size_t *zero_bytes); - - int coroutine_fn vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id); -+int coroutine_fn vma_writer_flush_output(VmaWriter *vmaw); - - int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status); - void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...); diff --git a/debian/patches/backup-vma-correctly-propagate-error.patch b/debian/patches/backup-vma-correctly-propagate-error.patch deleted file mode 100644 index b351947..0000000 --- a/debian/patches/backup-vma-correctly-propagate-error.patch +++ /dev/null @@ -1,43 +0,0 @@ -Index: new/vma-writer.c -=================================================================== ---- new.orig/vma-writer.c 2014-11-20 07:34:19.000000000 +0100 -+++ new/vma-writer.c 2014-11-20 07:49:17.000000000 +0100 -@@ -798,6 +798,13 @@ - return transferred; - } - -+void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp) -+{ -+ if (vmaw->status < 0 && *errp == NULL) { -+ error_setg(errp, "%s", vmaw->errmsg); -+ } -+} -+ - int vma_writer_close(VmaWriter *vmaw, Error **errp) - { - g_assert(vmaw != NULL); -Index: new/vma.h -=================================================================== ---- new.orig/vma.h 2014-11-20 07:34:27.000000000 +0100 -+++ new/vma.h 2014-11-20 07:49:17.000000000 +0100 -@@ -116,6 +116,7 @@ - - VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp); - int vma_writer_close(VmaWriter *vmaw, Error **errp); -+void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp); - void vma_writer_destroy(VmaWriter *vmaw); - int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, - size_t len); -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 07:49:14.000000000 +0100 -+++ new/blockdev.c 2014-11-20 07:49:17.000000000 +0100 -@@ -2015,7 +2015,7 @@ - buf, &zero_bytes); - if (ret < 0) { - if (!backup_state.error) { -- error_setg(&backup_state.error, "vma_writer_write error %d", ret); -+ vma_writer_error_propagate(backup_state.vmaw, &backup_state.error); - } - if (di->bs && di->bs->job) { - block_job_cancel(di->bs->job); diff --git a/debian/patches/backup-vma-extract-add-block-driver-type.patch b/debian/patches/backup-vma-extract-add-block-driver-type.patch deleted file mode 100644 index 6f6dabb..0000000 --- a/debian/patches/backup-vma-extract-add-block-driver-type.patch +++ /dev/null @@ -1,19 +0,0 @@ -Index: new/vma.c -=================================================================== ---- new.orig/vma.c -+++ new/vma.c -@@ -299,7 +299,13 @@ static int extract_content(int argc, cha - } - - BlockDriverState *bs = bdrv_new(); -- if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, NULL, &errp)) { -+ -+ const char *tmp = g_strrstr(devfn, "."); -+ const char *format = (tmp == NULL) ? "raw" : ++tmp; -+ -+ BlockDriver *drv = bdrv_find_format(format); -+ -+ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, drv, &errp)) { - g_error("can't open file %s - %s", devfn, - error_get_pretty(errp)); - } diff --git a/debian/patches/backup-vma-remove-async-queue.patch b/debian/patches/backup-vma-remove-async-queue.patch deleted file mode 100644 index 73f2c96..0000000 --- a/debian/patches/backup-vma-remove-async-queue.patch +++ /dev/null @@ -1,309 +0,0 @@ -We do not gain much speed here, so I removed the whole queue code -to make things simpler. - -Also, previous code produced segmentation faults in qemu_co_mutex_lock(). - -Index: new/vma-writer.c -=================================================================== ---- new.orig/vma-writer.c 2014-11-20 09:08:33.000000000 +0100 -+++ new/vma-writer.c 2014-11-20 09:10:14.000000000 +0100 -@@ -34,14 +34,8 @@ - do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0) - - #define WRITE_BUFFERS 5 -- --typedef struct VmaAIOCB VmaAIOCB; --struct VmaAIOCB { -- unsigned char buffer[VMA_MAX_EXTENT_SIZE]; -- VmaWriter *vmaw; -- size_t bytes; -- Coroutine *co; --}; -+#define HEADER_CLUSTERS 8 -+#define HEADERBUF_SIZE (VMA_CLUSTER_SIZE*HEADER_CLUSTERS) - - struct VmaWriter { - int fd; -@@ -53,16 +47,14 @@ - bool closed; - - /* we always write extents */ -- unsigned char outbuf[VMA_MAX_EXTENT_SIZE]; -+ unsigned char *outbuf; - int outbuf_pos; /* in bytes */ - int outbuf_count; /* in VMA_BLOCKS */ - uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; - -- VmaAIOCB *aiocbs[WRITE_BUFFERS]; -- CoQueue wqueue; -+ unsigned char *headerbuf; - - GChecksum *md5csum; -- CoMutex writer_lock; - CoMutex flush_lock; - Coroutine *co_writer; - -@@ -223,38 +215,39 @@ - } - - static ssize_t coroutine_fn --vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes) -+vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) - { -- size_t done = 0; -- ssize_t ret; -+ DPRINTF("vma_queue_write enter %zd\n", bytes); - -- /* atomic writes (we cannot interleave writes) */ -- qemu_co_mutex_lock(&vmaw->writer_lock); -+ assert(vmaw); -+ assert(buf); -+ assert(bytes <= VMA_MAX_EXTENT_SIZE); - -- DPRINTF("vma_co_write enter %zd\n", bytes); -+ size_t done = 0; -+ ssize_t ret; - - assert(vmaw->co_writer == NULL); - - vmaw->co_writer = qemu_coroutine_self(); - -- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw); -- -- DPRINTF("vma_co_write wait until writable\n"); -- qemu_coroutine_yield(); -- DPRINTF("vma_co_write starting %zd\n", bytes); -- - while (done < bytes) { -+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw); -+ qemu_coroutine_yield(); -+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL); -+ if (vmaw->status < 0) { -+ DPRINTF("vma_queue_write detected canceled backup\n"); -+ done = -1; -+ break; -+ } - ret = write(vmaw->fd, buf + done, bytes - done); - if (ret > 0) { - done += ret; -- DPRINTF("vma_co_write written %zd %zd\n", done, ret); -+ DPRINTF("vma_queue_write written %zd %zd\n", done, ret); - } else if (ret < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { -- DPRINTF("vma_co_write yield %zd\n", done); -- qemu_coroutine_yield(); -- DPRINTF("vma_co_write restart %zd\n", done); -- } else { -- vma_writer_set_error(vmaw, "vma_co_write write error - %s", -+ /* try again */ -+ } else { -+ vma_writer_set_error(vmaw, "vma_queue_write: write error - %s", - g_strerror(errno)); - done = -1; /* always return failure for partial writes */ - break; -@@ -264,102 +257,9 @@ - } - } - -- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL); -- - vmaw->co_writer = NULL; -- -- qemu_co_mutex_unlock(&vmaw->writer_lock); -- -- DPRINTF("vma_co_write leave %zd\n", done); -- return done; --} -- --static void coroutine_fn vma_co_writer_task(void *opaque) --{ -- VmaAIOCB *cb = opaque; -- -- DPRINTF("vma_co_writer_task start\n"); -- -- int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); -- DPRINTF("vma_co_writer_task write done %zd\n", done); -- -- if (done != cb->bytes) { -- DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); -- vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", -- done); -- } -- -- cb->bytes = 0; -- -- qemu_co_queue_next(&cb->vmaw->wqueue); -- -- DPRINTF("vma_co_writer_task end\n"); --} -- --static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) --{ -- DPRINTF("vma_queue_flush enter\n"); -- -- assert(vmaw); -- -- while (1) { -- int i; -- VmaAIOCB *cb = NULL; -- for (i = 0; i < WRITE_BUFFERS; i++) { -- if (vmaw->aiocbs[i]->bytes) { -- cb = vmaw->aiocbs[i]; -- DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, -- vmaw->aiocbs[i]->bytes); -- break; -- } -- } -- if (!cb) { -- break; -- } -- qemu_co_queue_wait(&vmaw->wqueue); -- } -- -- DPRINTF("vma_queue_flush leave\n"); --} -- --/** -- * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') -- * So we need to create a coroutione to allow 'parallel' execution. -- */ --static ssize_t coroutine_fn --vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) --{ -- DPRINTF("vma_queue_write enter %zd\n", bytes); -- -- assert(vmaw); -- assert(buf); -- assert(bytes <= VMA_MAX_EXTENT_SIZE); -- -- VmaAIOCB *cb = NULL; -- while (!cb) { -- int i; -- for (i = 0; i < WRITE_BUFFERS; i++) { -- if (!vmaw->aiocbs[i]->bytes) { -- cb = vmaw->aiocbs[i]; -- break; -- } -- } -- if (!cb) { -- qemu_co_queue_wait(&vmaw->wqueue); -- } -- } -- -- memcpy(cb->buffer, buf, bytes); -- cb->bytes = bytes; -- cb->vmaw = vmaw; -- -- DPRINTF("vma_queue_write start %zd\n", bytes); -- cb->co = qemu_coroutine_create(vma_co_writer_task); -- qemu_coroutine_enter(cb->co, cb); -- -- DPRINTF("vma_queue_write leave\n"); -- -- return bytes; -+ -+ return (done == bytes) ? bytes : -1; - } - - VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp) -@@ -426,20 +326,16 @@ - } - - /* 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 = qemu_memalign(512, VMA_MAX_EXTENT_SIZE); -+ vmaw->headerbuf = qemu_memalign(512, HEADERBUF_SIZE); - - vmaw->outbuf_count = 0; - vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE; - - vmaw->header_blob_table_pos = 1; /* start at pos 1 */ - -- qemu_co_mutex_init(&vmaw->writer_lock); - qemu_co_mutex_init(&vmaw->flush_lock); -- qemu_co_queue_init(&vmaw->wqueue); - - uuid_copy(vmaw->uuid, uuid); - -@@ -466,8 +362,7 @@ - static int coroutine_fn vma_write_header(VmaWriter *vmaw) - { - assert(vmaw); -- int header_clusters = 8; -- char buf[65536*header_clusters]; -+ unsigned char *buf = vmaw->headerbuf; - VmaHeader *head = (VmaHeader *)buf; - - int i; -@@ -478,7 +373,7 @@ - return vmaw->status; - } - -- memset(buf, 0, sizeof(buf)); -+ memset(buf, 0, HEADERBUF_SIZE); - - head->magic = VMA_MAGIC; - head->version = GUINT32_TO_BE(1); /* v1 */ -@@ -513,7 +408,7 @@ - uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size; - head->header_size = GUINT32_TO_BE(header_size); - -- if (header_size > sizeof(buf)) { -+ if (header_size > HEADERBUF_SIZE) { - return -1; /* just to be sure */ - } - -@@ -811,13 +706,7 @@ - - int i; - -- vma_queue_flush(vmaw); -- -- /* this should not happen - just to be sure */ -- while (!qemu_co_queue_empty(&vmaw->wqueue)) { -- DPRINTF("vma_writer_close wait\n"); -- co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 1000000); -- } -+ assert(vmaw->co_writer == NULL); - - if (vmaw->cmd) { - if (pclose(vmaw->cmd) < 0) { -@@ -875,9 +764,5 @@ - g_checksum_free(vmaw->md5csum); - } - -- for (i = 0; i < WRITE_BUFFERS; i++) { -- free(vmaw->aiocbs[i]); -- } -- - g_free(vmaw); - } -Index: new/blockdev.c -=================================================================== ---- new.orig/blockdev.c 2014-11-20 09:08:33.000000000 +0100 -+++ new/blockdev.c 2014-11-20 09:08:49.000000000 +0100 -@@ -2094,6 +2094,11 @@ - error_setg(&backup_state.error, "backup cancelled"); - } - -+ if (backup_state.vmaw) { -+ /* make sure vma writer does not block anymore */ -+ vma_writer_set_error(backup_state.vmaw, "backup cancelled"); -+ } -+ - /* drain all i/o (awake jobs waiting for aio) */ - bdrv_drain_all(); - -@@ -2106,6 +2111,7 @@ - if (job) { - if (!di->completed) { - block_job_cancel_sync(job); -+ bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */ - } - } - } diff --git a/debian/patches/backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch b/debian/patches/backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch deleted file mode 100644 index 779d4c6..0000000 --- a/debian/patches/backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 4ca68d0ccfd64f2fdc63ba44813bbafab8d90c81 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 26 Mar 2013 06:21:16 +0100 -Subject: [PATCH v5 8/8] vma restore: tolerate a size difference up to 4M - - -Signed-off-by: Dietmar Maurer ---- - vma-reader.c | 8 +++++++- - 1 files changed, 7 insertions(+), 1 deletions(-) - -Index: new/vma-reader.c -=================================================================== ---- new.orig/vma-reader.c 2014-07-16 12:01:32.000000000 +0200 -+++ new/vma-reader.c 2014-07-16 12:01:33.000000000 +0200 -@@ -465,7 +465,13 @@ - assert(vmar->rstate[dev_id].bs == NULL); - - int64_t size = bdrv_getlength(bs); -- if (size != vmar->devinfo[dev_id].size) { -+ int64_t size_diff = size - vmar->devinfo[dev_id].size; -+ -+ /* storage types can have different size restrictions, so it -+ * is not always possible to create an image with exact size. -+ * So we tolerate a size difference up to 4MB. -+ */ -+ if ((size_diff < 0) || (size_diff > 4*1024*1024)) { - error_setg(errp, "vma_reader_register_bs for stream %s failed - " - "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, - size, vmar->devinfo[dev_id].size); diff --git a/debian/patches/cpuid-fix.diff b/debian/patches/cpuid-fix.diff deleted file mode 100644 index 1052ad5..0000000 --- a/debian/patches/cpuid-fix.diff +++ /dev/null @@ -1,18 +0,0 @@ -see http://git.kernel.org/?p=virt/kvm/qemu-kvm.git;a=commitdiff;h=8fa3b3ce6e#patch1 - -KVM_GET_SUPPORTED_CPUID has been known to fail to return -E2BIG -when it runs out of entries. Detect this by always trying again -with a bigger table if the ioctl() fills the table. - ---- a/kvm/libkvm/libkvm-x86.c -+++ b/kvm/libkvm/libkvm-x86.c -@@ -575,6 +575,8 @@ static struct kvm_cpuid2 *try_get_cpuid(kvm_context_t kvm, int max) - r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, cpuid); - if (r == -1) - r = -errno; -+ else if (r == 0 && cpuid->nent >= max) -+ r = -E2BIG; - if (r < 0) { - if (r == -E2BIG) { - free(cpuid); - diff --git a/debian/patches/disable-efi-enable-pxe-roms.patch b/debian/patches/disable-efi-enable-pxe-roms.patch deleted file mode 100644 index 16c69ce..0000000 --- a/debian/patches/disable-efi-enable-pxe-roms.patch +++ /dev/null @@ -1,65 +0,0 @@ -Index: new/hw/net/e1000.c -=================================================================== ---- new.orig/hw/net/e1000.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/net/e1000.c 2014-11-20 07:54:14.000000000 +0100 -@@ -1606,7 +1606,7 @@ - - k->realize = pci_e1000_realize; - k->exit = pci_e1000_uninit; -- k->romfile = "efi-e1000.rom"; -+ k->romfile = "pxe-e1000.rom"; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = info->device_id; - k->revision = info->revision; -Index: new/hw/net/ne2000.c -=================================================================== ---- new.orig/hw/net/ne2000.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/net/ne2000.c 2014-11-20 07:54:14.000000000 +0100 -@@ -773,7 +773,7 @@ - - k->realize = pci_ne2000_realize; - k->exit = pci_ne2000_exit; -- k->romfile = "efi-ne2k_pci.rom", -+ k->romfile = "pxe-ne2k_pci.rom", - k->vendor_id = PCI_VENDOR_ID_REALTEK; - k->device_id = PCI_DEVICE_ID_REALTEK_8029; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; -Index: new/hw/net/pcnet-pci.c -=================================================================== ---- new.orig/hw/net/pcnet-pci.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/net/pcnet-pci.c 2014-11-20 07:54:14.000000000 +0100 -@@ -367,7 +367,7 @@ - - k->realize = pci_pcnet_realize; - k->exit = pci_pcnet_uninit; -- k->romfile = "efi-pcnet.rom", -+ k->romfile = "pxe-pcnet.rom", - k->vendor_id = PCI_VENDOR_ID_AMD; - k->device_id = PCI_DEVICE_ID_AMD_LANCE; - k->revision = 0x10; -Index: new/hw/net/rtl8139.c -=================================================================== ---- new.orig/hw/net/rtl8139.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/net/rtl8139.c 2014-11-20 07:54:14.000000000 +0100 -@@ -3562,7 +3562,7 @@ - - k->realize = pci_rtl8139_realize; - k->exit = pci_rtl8139_uninit; -- k->romfile = "efi-rtl8139.rom"; -+ k->romfile = "pxe-rtl8139.rom"; - k->vendor_id = PCI_VENDOR_ID_REALTEK; - k->device_id = PCI_DEVICE_ID_REALTEK_8139; - k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ -Index: new/hw/virtio/virtio-pci.c -=================================================================== ---- new.orig/hw/virtio/virtio-pci.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/virtio/virtio-pci.c 2014-11-20 07:54:14.000000000 +0100 -@@ -1445,7 +1445,7 @@ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); - -- k->romfile = "efi-virtio.rom"; -+ k->romfile = "pxe-virtio.rom"; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_NET; - k->revision = VIRTIO_PCI_ABI_VERSION; diff --git a/debian/patches/enable-kvm-by-default.patch b/debian/patches/enable-kvm-by-default.patch deleted file mode 100644 index 329170f..0000000 --- a/debian/patches/enable-kvm-by-default.patch +++ /dev/null @@ -1,15 +0,0 @@ -Index: new/accel.c -=================================================================== ---- new.orig/accel.c 2014-11-20 06:45:05.000000000 +0100 -+++ new/accel.c 2014-11-20 07:25:45.000000000 +0100 -@@ -87,8 +87,8 @@ - - p = qemu_opt_get(qemu_get_machine_opts(), "accel"); - if (p == NULL) { -- /* Use the default "accelerator", tcg */ -- p = "tcg"; -+ /* Use the default "accelerator", kvm */ -+ p = "kvm"; - } - - while (!accel_initialised && *p != '\0') { diff --git a/debian/patches/fix-emulator-version-string.patch b/debian/patches/fix-emulator-version-string.patch deleted file mode 100644 index 6cf849b..0000000 --- a/debian/patches/fix-emulator-version-string.patch +++ /dev/null @@ -1,18 +0,0 @@ -use whith spave between VERSION and QEMU_PKGVERSION - -Our kvm version parser expects a white space ot comma after version, -see PVE::QemuServer::kvm_user_version() - -Index: new/vl.c -=================================================================== ---- new.orig/vl.c -+++ new/vl.c -@@ -1913,7 +1913,7 @@ static void main_loop(void) - - static void version(void) - { -- printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); -+ printf("QEMU emulator version " QEMU_VERSION " " QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); - } - - static void help(int exitcode) diff --git a/debian/patches/fix-qemu-img-snapshot-removal.patch b/debian/patches/fix-qemu-img-snapshot-removal.patch deleted file mode 100644 index 58d9ecc..0000000 --- a/debian/patches/fix-qemu-img-snapshot-removal.patch +++ /dev/null @@ -1,14 +0,0 @@ -Index: new/qemu-img.c -=================================================================== ---- new.orig/qemu-img.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/qemu-img.c 2014-11-20 06:51:05.000000000 +0100 -@@ -2080,7 +2080,8 @@ - - list = collect_image_info_list(filename, fmt, chain); - if (!list) { -- return 1; -+ // return success if snapshot does not exists -+ return 0; - } - - switch (output_format) { diff --git a/debian/patches/fr-ca-keymap-corrections.diff b/debian/patches/fr-ca-keymap-corrections.diff deleted file mode 100644 index a1a3941..0000000 --- a/debian/patches/fr-ca-keymap-corrections.diff +++ /dev/null @@ -1,36 +0,0 @@ -Index: new/pc-bios/keymaps/fr-ca -=================================================================== ---- new.orig/pc-bios/keymaps/fr-ca 2014-07-16 11:53:56.000000000 +0200 -+++ new/pc-bios/keymaps/fr-ca 2014-07-16 12:01:20.000000000 +0200 -@@ -14,22 +14,31 @@ - twosuperior 0x9 altgr - threesuperior 0xa altgr - onequarter 0xb altgr -+minus 0x0c - onehalf 0xc altgr -+equal 0xd - threequarters 0xd altgr - section 0x18 altgr - paragraph 0x19 altgr - bracketleft 0x1a altgr - bracketright 0x1b altgr -+semicolon 0x27 -+colon 0x27 shift - asciitilde 0x27 altgr - braceleft 0x28 altgr -+numbersign 0x29 - braceright 0x2b altgr - less 0x2b - greater 0x2b shift - guillemotleft 0x56 - guillemotright 0x56 shift - degree 0x56 altgr -+comma 0x33 - mu 0x32 altgr -+apostrophe 0x33 shift -+period 0x34 shift - eacute 0x35 -+Eacute 0x35 shift - dead_acute 0x35 altgr - dead_grave 0x28 - dead_circumflex 0x1a diff --git a/debian/patches/gluster-backupserver.patch b/debian/patches/gluster-backupserver.patch deleted file mode 100644 index ef96541..0000000 --- a/debian/patches/gluster-backupserver.patch +++ /dev/null @@ -1,131 +0,0 @@ -Allow to specify a backup volfile server - -Unfortunately, it dos now work as expected: - - # qemu-img info gluster:///myvolume/test.raw - -works, but (1.2.3.4 is a non-existent server) - - # qemu-img info gluster://1.2.3.4/myvolume/test.raw?s2= - -does not work. Also - - # qemu-img info gluster:///myvolume/test.raw?s2= - -simply hangs forever. - -Index: new/block/gluster.c -=================================================================== ---- new.orig/block/gluster.c 2014-08-26 11:48:49.000000000 +0200 -+++ new/block/gluster.c 2014-08-26 12:51:53.000000000 +0200 -@@ -26,6 +26,7 @@ - - typedef struct GlusterConf { - char *server; -+ char *backupserver; - int port; - char *volname; - char *image; -@@ -36,6 +37,7 @@ - { - if (gconf) { - g_free(gconf->server); -+ g_free(gconf->backupserver); - g_free(gconf->volname); - g_free(gconf->image); - g_free(gconf->transport); -@@ -69,7 +71,7 @@ - } - - /* -- * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...] -+ * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...|?s2=...] - * - * 'gluster' is the protocol. - * -@@ -85,6 +87,8 @@ - * The 'socket' field needs to be populated with the path to unix domain - * socket. - * -+ * 's2' can be used to specifies a second volfile server. -+ * - * 'port' is the port number on which glusterd is listening. This is optional - * and if not specified, QEMU will send 0 which will make gluster to use the - * default port. If the transport type is unix, then 'port' should not be -@@ -97,6 +101,7 @@ - * Examples: - * - * file=gluster://1.2.3.4/testvol/a.img -+ * file=gluster://1.2.3.4/testvol/a.img?s2=1.2.3.5 - * file=gluster+tcp://1.2.3.4/testvol/a.img - * file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img - * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img -@@ -111,6 +116,8 @@ - QueryParams *qp = NULL; - bool is_unix = false; - int ret = 0; -+ int i; -+ char *socket = NULL; - - uri = uri_parse(filename); - if (!uri) { -@@ -138,21 +145,28 @@ - } - - qp = query_params_parse(uri->query); -- if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { -+ for (i = 0; i < qp->n; i++) { -+ if (!is_unix && strcmp(qp->p[i].name, "s2") == 0) { -+ gconf->backupserver = g_strdup(qp->p[i].value); -+ } else if (is_unix && strcmp(qp->p[i].name, "socket") == 0) { -+ socket = qp->p[i].value; -+ } else { -+ ret = -EINVAL; -+ goto out; -+ } -+ } -+ -+ if (is_unix && !socket) { - ret = -EINVAL; - goto out; - } - - if (is_unix) { -- if (uri->server || uri->port) { -+ if (!socket || uri->server || uri->port) { - ret = -EINVAL; - goto out; - } -- if (strcmp(qp->p[0].name, "socket")) { -- ret = -EINVAL; -- goto out; -- } -- gconf->server = g_strdup(qp->p[0].value); -+ gconf->server = g_strdup(socket); - } else { - gconf->server = g_strdup(uri->server ? uri->server : "localhost"); - gconf->port = uri->port; -@@ -176,7 +190,7 @@ - ret = qemu_gluster_parseuri(gconf, filename); - if (ret < 0) { - error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/" -- "volname/image[?socket=...]"); -+ "volname/image[?socket=...|?s2=...]"); - errno = -ret; - goto out; - } -@@ -192,6 +206,14 @@ - goto out; - } - -+ if (gconf->backupserver) { -+ ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->backupserver, -+ gconf->port); -+ if (ret < 0) { -+ goto out; -+ } -+ } -+ - /* - * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when - * GlusterFS makes GF_LOG_* macros available to libgfapi users. diff --git a/debian/patches/glusterfs-daemonize.patch b/debian/patches/glusterfs-daemonize.patch deleted file mode 100644 index e657dcc..0000000 --- a/debian/patches/glusterfs-daemonize.patch +++ /dev/null @@ -1,23 +0,0 @@ -glusterfs: do not log to stdout if daemonized - -Else stdout is not closed correctly. - -Index: new/block/gluster.c -=================================================================== ---- new.orig/block/gluster.c 2014-08-22 13:21:39.000000000 +0200 -+++ new/block/gluster.c 2014-08-22 13:25:18.000000000 +0200 -@@ -196,9 +196,11 @@ - * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when - * GlusterFS makes GF_LOG_* macros available to libgfapi users. - */ -- ret = glfs_set_logging(glfs, "-", 4); -- if (ret < 0) { -- goto out; -+ if (!is_daemonized()) { -+ ret = glfs_set_logging(glfs, "-", 4); -+ if (ret < 0) { -+ goto out; -+ } - } - - ret = glfs_init(glfs); diff --git a/debian/patches/internal-snapshot-async.patch b/debian/patches/internal-snapshot-async.patch deleted file mode 100644 index c66dfb8..0000000 --- a/debian/patches/internal-snapshot-async.patch +++ /dev/null @@ -1,986 +0,0 @@ -From 46fd4bb673a91d40352c95e9d3f62f63b5021053 Mon Sep 17 00:00:00 2001 -From: Stefan Priebe -Date: Fri, 29 Nov 2013 22:17:03 +0100 -Subject: [PATCH] internal-snapshot-async-qemu1.7.patch - ---- - Makefile.objs | 1 + - block.c | 2 +- - hmp-commands.hx | 34 ++++ - hmp.c | 57 ++++++ - hmp.h | 5 + - include/block/block.h | 1 + - include/sysemu/sysemu.h | 5 +- - monitor.c | 7 + - qapi-schema.json | 46 +++++ - qemu-options.hx | 13 ++ - qmp-commands.hx | 31 +++ - savevm-async.c | 478 +++++++++++++++++++++++++++++++++++++++++++++++ - savevm.c | 10 +- - vl.c | 9 + - 14 files changed, 692 insertions(+), 7 deletions(-) - create mode 100644 savevm-async.c - -Index: new/Makefile.objs -=================================================================== ---- new.orig/Makefile.objs 2014-11-20 09:13:01.000000000 +0100 -+++ new/Makefile.objs 2014-11-20 09:16:47.000000000 +0100 -@@ -53,6 +53,7 @@ - common-obj-y += qemu-char.o #aio.o - common-obj-y += page_cache.o - common-obj-y += qjson.o -+common-obj-y += savevm-async.o - - common-obj-$(CONFIG_SPICE) += spice-qemu-char.o - -Index: new/block.c -=================================================================== ---- new.orig/block.c 2014-11-20 09:13:01.000000000 +0100 -+++ new/block.c 2014-11-20 09:16:47.000000000 +0100 -@@ -2202,7 +2202,7 @@ - bdrv_attach_child(bs_top, bs_new, &child_backing); - } - --static void bdrv_delete(BlockDriverState *bs) -+void bdrv_delete(BlockDriverState *bs) - { - assert(!bs->job); - assert(bdrv_op_blocker_is_empty(bs)); -Index: new/hmp-commands.hx -=================================================================== ---- new.orig/hmp-commands.hx 2014-11-20 09:13:01.000000000 +0100 -+++ new/hmp-commands.hx 2014-11-20 09:16:47.000000000 +0100 -@@ -1809,6 +1809,8 @@ - show current migration parameters - @item info migrate_cache_size - show current migration XBZRLE cache size -+@item info savevm -+show savevm status - @item info balloon - show balloon information - @item info qtree -@@ -1857,3 +1859,35 @@ - STEXI - @end table - ETEXI -+ -+ { -+ .name = "savevm-start", -+ .args_type = "statefile:s?", -+ .params = "[statefile]", -+ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.", -+ .mhandler.cmd = hmp_savevm_start, -+ }, -+ -+ { -+ .name = "snapshot-drive", -+ .args_type = "device:s,name:s", -+ .params = "device name", -+ .help = "Create internal snapshot.", -+ .mhandler.cmd = hmp_snapshot_drive, -+ }, -+ -+ { -+ .name = "delete-drive-snapshot", -+ .args_type = "device:s,name:s", -+ .params = "device name", -+ .help = "Delete internal snapshot.", -+ .mhandler.cmd = hmp_delete_drive_snapshot, -+ }, -+ -+ { -+ .name = "savevm-end", -+ .args_type = "", -+ .params = "", -+ .help = "Resume VM after snaphot.", -+ .mhandler.cmd = hmp_savevm_end, -+ }, -Index: new/hmp.c -=================================================================== ---- new.orig/hmp.c 2014-11-20 09:13:01.000000000 +0100 -+++ new/hmp.c 2014-11-20 09:16:47.000000000 +0100 -@@ -1849,6 +1849,63 @@ - - qapi_free_MemoryDeviceInfoList(info_list); - } -+ -+void hmp_savevm_start(Monitor *mon, const QDict *qdict) -+{ -+ Error *errp = NULL; -+ const char *statefile = qdict_get_try_str(qdict, "statefile"); -+ -+ qmp_savevm_start(statefile != NULL, statefile, &errp); -+ hmp_handle_error(mon, &errp); -+} -+ -+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict) -+{ -+ Error *errp = NULL; -+ const char *name = qdict_get_str(qdict, "name"); -+ const char *device = qdict_get_str(qdict, "device"); -+ -+ qmp_snapshot_drive(device, name, &errp); -+ hmp_handle_error(mon, &errp); -+} -+ -+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict) -+{ -+ Error *errp = NULL; -+ const char *name = qdict_get_str(qdict, "name"); -+ const char *device = qdict_get_str(qdict, "device"); -+ -+ qmp_delete_drive_snapshot(device, name, &errp); -+ hmp_handle_error(mon, &errp); -+} -+ -+void hmp_savevm_end(Monitor *mon, const QDict *qdict) -+{ -+ Error *errp = NULL; -+ -+ qmp_savevm_end(&errp); -+ hmp_handle_error(mon, &errp); -+} -+ -+void hmp_info_savevm(Monitor *mon, const QDict *qdict) -+{ -+ SaveVMInfo *info; -+ info = qmp_query_savevm(NULL); -+ -+ if (info->has_status) { -+ monitor_printf(mon, "savevm status: %s\n", info->status); -+ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n", -+ info->total_time); -+ } else { -+ monitor_printf(mon, "savevm status: not running\n"); -+ } -+ if (info->has_bytes) { -+ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes); -+ } -+ if (info->has_error) { -+ monitor_printf(mon, "Error: %s\n", info->error); -+ } -+} - - void hmp_qom_list(Monitor *mon, const QDict *qdict) - { -Index: new/hmp.h -=================================================================== ---- new.orig/hmp.h 2014-11-20 09:13:01.000000000 +0100 -+++ new/hmp.h 2014-11-20 09:16:47.000000000 +0100 -@@ -26,6 +26,7 @@ - void hmp_info_uuid(Monitor *mon, const QDict *qdict); - void hmp_info_chardev(Monitor *mon, const QDict *qdict); - void hmp_info_mice(Monitor *mon, const QDict *qdict); -+void hmp_info_savevm(Monitor *mon, const QDict *qdict); - void hmp_info_migrate(Monitor *mon, const QDict *qdict); - void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); - void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict); -@@ -85,6 +86,10 @@ - void hmp_netdev_del(Monitor *mon, const QDict *qdict); - void hmp_getfd(Monitor *mon, const QDict *qdict); - void hmp_closefd(Monitor *mon, const QDict *qdict); -+void hmp_savevm_start(Monitor *mon, const QDict *qdict); -+void hmp_snapshot_drive(Monitor *mon, const QDict *qdict); -+void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict); -+void hmp_savevm_end(Monitor *mon, const QDict *qdict); - void hmp_sendkey(Monitor *mon, const QDict *qdict); - void hmp_screendump(Monitor *mon, const QDict *qdict); - void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); -Index: new/include/block/block.h -=================================================================== ---- new.orig/include/block/block.h 2014-11-20 09:13:01.000000000 +0100 -+++ new/include/block/block.h 2014-11-20 09:16:47.000000000 +0100 -@@ -235,6 +235,7 @@ - int bdrv_get_backing_file_depth(BlockDriverState *bs); - void bdrv_refresh_filename(BlockDriverState *bs); - int bdrv_truncate(BlockDriverState *bs, int64_t offset); -+void bdrv_delete(BlockDriverState *bs); - int64_t bdrv_nb_sectors(BlockDriverState *bs); - int64_t bdrv_getlength(BlockDriverState *bs); - int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); -Index: new/include/sysemu/sysemu.h -=================================================================== ---- new.orig/include/sysemu/sysemu.h 2014-11-20 09:13:01.000000000 +0100 -+++ new/include/sysemu/sysemu.h 2014-11-20 09:16:47.000000000 +0100 -@@ -76,17 +76,18 @@ - - void hmp_savevm(Monitor *mon, const QDict *qdict); - int load_vmstate(const char *name); -+int load_state_from_blockdev(const char *filename); - void hmp_delvm(Monitor *mon, const QDict *qdict); - void hmp_info_snapshots(Monitor *mon, const QDict *qdict); - - void qemu_announce_self(void); - - bool qemu_savevm_state_blocked(Error **errp); --void qemu_savevm_state_begin(QEMUFile *f, -+int qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params); - void qemu_savevm_state_header(QEMUFile *f); - int qemu_savevm_state_iterate(QEMUFile *f); --void qemu_savevm_state_complete(QEMUFile *f); -+int qemu_savevm_state_complete(QEMUFile *f); - void qemu_savevm_state_cancel(void); - uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size); - int qemu_loadvm_state(QEMUFile *f); -Index: new/monitor.c -=================================================================== ---- new.orig/monitor.c 2014-11-20 09:13:01.000000000 +0100 -+++ new/monitor.c 2014-11-20 09:16:47.000000000 +0100 -@@ -2876,6 +2876,13 @@ - .mhandler.cmd = hmp_info_migrate_cache_size, - }, - { -+ .name = "savevm", -+ .args_type = "", -+ .params = "", -+ .help = "show savevm status", -+ .mhandler.cmd = hmp_info_savevm, -+ }, -+ { - .name = "balloon", - .args_type = "", - .params = "", -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-11-20 09:13:01.000000000 +0100 -+++ new/qapi-schema.json 2014-11-20 09:16:47.000000000 +0100 -@@ -550,6 +550,42 @@ - '*downtime': 'int', - '*setup-time': 'int'} } - -+ -+# @SaveVMInfo -+# -+# Information about current migration process. -+# -+# @status: #optional string describing the current savevm status. -+# This can be 'active', 'completed', 'failed'. -+# If this field is not returned, no savevm process -+# has been initiated -+# -+# @error: #optional string containing error message is status is failed. -+# -+# @total-time: #optional total amount of milliseconds since savevm started. -+# If savevm has ended, it returns the total save time -+# -+# @bytes: #optional total amount of data transfered -+# -+# Since: 1.3 -+## -+{ 'struct': 'SaveVMInfo', -+ 'data': {'*status': 'str', '*error': 'str', -+ '*total-time': 'int', '*bytes': 'int'} } -+ -+## -+# @query-savevm -+# -+# Returns information about current savevm process. -+# -+# Returns: @SaveVMInfo -+# -+# Since: 1.3 -+## -+{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' } -+ -+## -+ - ## - # @query-migrate - # -@@ -2649,8 +2685,18 @@ - # - # Since: 1.2.0 - ## -+ - { 'command': 'query-target', 'returns': 'TargetInfo' } - -+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } } -+ -+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } } -+ -+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } } -+ -+{ 'command': 'savevm-end' } -+ -+ - ## - # @QKeyCode: - # -Index: new/qemu-options.hx -=================================================================== ---- new.orig/qemu-options.hx 2014-11-20 09:13:01.000000000 +0100 -+++ new/qemu-options.hx 2014-11-20 09:16:47.000000000 +0100 -@@ -2975,6 +2975,19 @@ - Start right away with a saved state (@code{loadvm} in monitor) - ETEXI - -+DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \ -+ "-loadstate file\n" \ -+ " start right away with a saved state\n", -+ QEMU_ARCH_ALL) -+STEXI -+@item -loadstate @var{file} -+@findex -loadstate -+Start right away with a saved state. This option does not rollback -+disk state like @code{loadvm}, so user must make sure that disk -+have correct state. @var{file} can be any valid device URL. See the section -+for "Device URL Syntax" for more information. -+ETEXI -+ - #ifndef _WIN32 - DEF("daemonize", 0, QEMU_OPTION_daemonize, \ - "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) -Index: new/qmp-commands.hx -=================================================================== ---- new.orig/qmp-commands.hx 2014-11-20 09:13:01.000000000 +0100 -+++ new/qmp-commands.hx 2014-11-20 09:16:47.000000000 +0100 -@@ -4200,6 +4200,36 @@ - <- { "return": {} } - - EQMP -+ -+ { -+ .name = "savevm-start", -+ .args_type = "statefile:s?", -+ .mhandler.cmd_new = qmp_marshal_input_savevm_start, -+ }, -+ -+ { -+ .name = "snapshot-drive", -+ .args_type = "device:s,name:s", -+ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive, -+ }, -+ -+ { -+ .name = "delete-drive-snapshot", -+ .args_type = "device:s,name:s", -+ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot, -+ }, -+ -+ { -+ .name = "savevm-end", -+ .args_type = "", -+ .mhandler.cmd_new = qmp_marshal_input_savevm_end, -+ }, -+ -+ { -+ .name = "query-savevm", -+ .args_type = "", -+ .mhandler.cmd_new = qmp_marshal_input_query_savevm, -+ }, - - { - .name = "query-rocker", -Index: new/savevm-async.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ new/savevm-async.c 2014-11-20 09:17:48.000000000 +0100 -@@ -0,0 +1,522 @@ -+#include "qemu-common.h" -+#include "qapi/qmp/qerror.h" -+#include "qemu/error-report.h" -+#include "sysemu/sysemu.h" -+#include "qmp-commands.h" -+#include "qemu-options.h" -+#include "migration/qemu-file.h" -+#include "qom/qom-qobject.h" -+#include "migration/migration.h" -+#include "block/snapshot.h" -+#include "block/qapi.h" -+#include "block/block.h" -+#include "qemu/timer.h" -+#include "sysemu/block-backend.h" -+#include "qapi/qmp/qstring.h" -+ -+/* #define DEBUG_SAVEVM_STATE */ -+ -+#ifdef DEBUG_SAVEVM_STATE -+#define DPRINTF(fmt, ...) \ -+ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0) -+#else -+#define DPRINTF(fmt, ...) \ -+ do { } while (0) -+#endif -+ -+enum { -+ SAVE_STATE_DONE, -+ SAVE_STATE_ERROR, -+ SAVE_STATE_ACTIVE, -+ SAVE_STATE_COMPLETED, -+ SAVE_STATE_CANCELLED -+}; -+ -+ -+static struct SnapshotState { -+ BlockDriverState *bs; -+ size_t bs_pos; -+ int state; -+ Error *error; -+ Error *blocker; -+ int saved_vm_running; -+ QEMUFile *file; -+ int64_t total_time; -+} snap_state; -+ -+SaveVMInfo *qmp_query_savevm(Error **errp) -+{ -+ SaveVMInfo *info = g_malloc0(sizeof(*info)); -+ struct SnapshotState *s = &snap_state; -+ -+ if (s->state != SAVE_STATE_DONE) { -+ info->has_bytes = true; -+ info->bytes = s->bs_pos; -+ switch (s->state) { -+ case SAVE_STATE_ERROR: -+ info->has_status = true; -+ info->status = g_strdup("failed"); -+ info->has_total_time = true; -+ info->total_time = s->total_time; -+ if (s->error) { -+ info->has_error = true; -+ info->error = g_strdup(error_get_pretty(s->error)); -+ } -+ break; -+ case SAVE_STATE_ACTIVE: -+ info->has_status = true; -+ info->status = g_strdup("active"); -+ info->has_total_time = true; -+ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) -+ - s->total_time; -+ break; -+ case SAVE_STATE_COMPLETED: -+ info->has_status = true; -+ info->status = g_strdup("completed"); -+ info->has_total_time = true; -+ info->total_time = s->total_time; -+ break; -+ } -+ } -+ -+ return info; -+} -+ -+static int save_snapshot_cleanup(void) -+{ -+ int ret = 0; -+ -+ DPRINTF("save_snapshot_cleanup\n"); -+ -+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - -+ snap_state.total_time; -+ -+ if (snap_state.file) { -+ ret = qemu_fclose(snap_state.file); -+ } -+ -+ if (snap_state.bs) { -+ /* try to truncate, but ignore errors (will fail on block devices). -+ * note: bdrv_read() need whole blocks, so we round up -+ */ -+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK; -+ bdrv_truncate(snap_state.bs, size); -+ bdrv_op_unblock_all(snap_state.bs, snap_state.blocker); -+ error_free(snap_state.blocker); -+ snap_state.blocker = NULL; -+ bdrv_unref(snap_state.bs); -+ snap_state.bs = NULL; -+ } -+ -+ return ret; -+} -+ -+static void save_snapshot_error(const char *fmt, ...) -+{ -+ va_list ap; -+ char *msg; -+ -+ va_start(ap, fmt); -+ msg = g_strdup_vprintf(fmt, ap); -+ va_end(ap); -+ -+ DPRINTF("save_snapshot_error: %s\n", msg); -+ -+ if (!snap_state.error) { -+ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg); -+ } -+ -+ g_free (msg); -+ -+ snap_state.state = SAVE_STATE_ERROR; -+ -+ save_snapshot_cleanup(); -+} -+ -+static void save_snapshot_completed(void) -+{ -+ DPRINTF("save_snapshot_completed\n"); -+ -+ if (save_snapshot_cleanup() < 0) { -+ snap_state.state = SAVE_STATE_ERROR; -+ } else { -+ snap_state.state = SAVE_STATE_COMPLETED; -+ } -+} -+ -+static int block_state_close(void *opaque) -+{ -+ snap_state.file = NULL; -+ return bdrv_flush(snap_state.bs); -+} -+ -+static int block_state_put_buffer(void *opaque, const uint8_t *buf, -+ int64_t pos, int size) -+{ -+ int ret; -+ -+ assert(pos == snap_state.bs_pos); -+ -+ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) { -+ snap_state.bs_pos += ret; -+ } -+ -+ return ret; -+} -+ -+static int store_and_stop(void) { -+ if (global_state_store()) { -+ save_snapshot_error("Error saving global state"); -+ return 1; -+ } -+ if (runstate_is_running()) { -+ vm_stop(RUN_STATE_SAVE_VM); -+ } -+ return 0; -+} -+ -+static void process_savevm_co(void *opaque) -+{ -+ int ret; -+ int64_t maxlen; -+ MigrationParams params = { -+ .blk = 0, -+ .shared = 0 -+ }; -+ -+ snap_state.state = SAVE_STATE_ACTIVE; -+ -+ qemu_mutex_unlock_iothread(); -+ qemu_savevm_state_header(snap_state.file); -+ ret = qemu_savevm_state_begin(snap_state.file, ¶ms); -+ qemu_mutex_lock_iothread(); -+ -+ if (ret < 0) { -+ save_snapshot_error("qemu_savevm_state_begin failed"); -+ return; -+ } -+ -+ while (snap_state.state == SAVE_STATE_ACTIVE) { -+ uint64_t pending_size; -+ -+ pending_size = qemu_savevm_state_pending(snap_state.file, 0); -+ -+ if (pending_size) { -+ ret = qemu_savevm_state_iterate(snap_state.file); -+ if (ret < 0) { -+ save_snapshot_error("qemu_savevm_state_iterate error %d", ret); -+ break; -+ } -+ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret); -+ } else { -+ DPRINTF("done iterating\n"); -+ if (store_and_stop()) -+ break; -+ DPRINTF("savevm inerate finished\n"); -+ qemu_savevm_state_complete(snap_state.file); -+ DPRINTF("save complete\n"); -+ save_snapshot_completed(); -+ break; -+ } -+ -+ /* stop the VM if we get to the end of available space, -+ * or if pending_size is just a few MB -+ */ -+ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024; -+ if ((pending_size < 100000) || -+ ((snap_state.bs_pos + pending_size) >= maxlen)) { -+ if (store_and_stop()) -+ break; -+ } -+ } -+ -+ if(snap_state.state == SAVE_STATE_CANCELLED) { -+ save_snapshot_completed(); -+ Error *errp = NULL; -+ qmp_savevm_end(&errp); -+ } -+ -+} -+ -+static const QEMUFileOps block_file_ops = { -+ .put_buffer = block_state_put_buffer, -+ .close = block_state_close, -+}; -+ -+ -+void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp) -+{ -+ BlockDriver *drv = NULL; -+ Error *local_err = NULL; -+ -+ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR; -+ int ret; -+ -+ if (snap_state.state != SAVE_STATE_DONE) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "VM snapshot already started\n"); -+ return; -+ } -+ -+ /* initialize snapshot info */ -+ snap_state.saved_vm_running = runstate_is_running(); -+ snap_state.bs_pos = 0; -+ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); -+ snap_state.blocker = NULL; -+ -+ if (snap_state.error) { -+ error_free(snap_state.error); -+ snap_state.error = NULL; -+ } -+ -+ if (!has_statefile) { -+ vm_stop(RUN_STATE_SAVE_VM); -+ snap_state.state = SAVE_STATE_COMPLETED; -+ return; -+ } -+ -+ if (qemu_savevm_state_blocked(errp)) { -+ return; -+ } -+ -+ /* Open the image */ -+ snap_state.bs = bdrv_new(); -+ -+ QDict *options = NULL; -+ options = qdict_new(); -+ qdict_put(options, "driver", qstring_from_str("raw")); -+ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err); -+ if (ret < 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile); -+ goto restart; -+ } -+ -+ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops); -+ -+ if (!snap_state.file) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile); -+ goto restart; -+ } -+ -+ -+ error_setg(&snap_state.blocker, "block device is in use by savevm"); -+ bdrv_op_block_all(snap_state.bs, snap_state.blocker); -+ -+ Coroutine *co = qemu_coroutine_create(process_savevm_co); -+ qemu_coroutine_enter(co, NULL); -+ -+ return; -+ -+restart: -+ -+ save_snapshot_error("setup failed"); -+ -+ if (snap_state.saved_vm_running) { -+ vm_start(); -+ } -+} -+ -+void qmp_savevm_end(Error **errp) -+{ -+ if (snap_state.state == SAVE_STATE_DONE) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "VM snapshot not started\n"); -+ return; -+ } -+ -+ if (snap_state.state == SAVE_STATE_ACTIVE) { -+ snap_state.state = SAVE_STATE_CANCELLED; -+ return; -+ } -+ -+ if (snap_state.saved_vm_running) { -+ vm_start(); -+ } -+ -+ snap_state.state = SAVE_STATE_DONE; -+} -+ -+void qmp_snapshot_drive(const char *device, const char *name, Error **errp) -+{ -+ BlockBackend *blk; -+ BlockDriverState *bs; -+ QEMUSnapshotInfo sn1, *sn = &sn1; -+ int ret; -+#ifdef _WIN32 -+ struct _timeb tb; -+#else -+ struct timeval tv; -+#endif -+ -+ if (snap_state.state != SAVE_STATE_COMPLETED) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "VM snapshot not ready/started\n"); -+ return; -+ } -+ -+ blk = blk_by_name(device); -+ if (!blk) { -+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, -+ "Device '%s' not found", device); -+ return; -+ } -+ -+ bs = blk_bs(blk); -+ if (!bdrv_is_inserted(bs)) { -+ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); -+ return; -+ } -+ -+ if (bdrv_is_read_only(bs)) { -+ error_setg(errp, "Node '%s' is read only", device); -+ return; -+ } -+ -+ if (!bdrv_can_snapshot(bs)) { -+ error_setg(errp, QERR_UNSUPPORTED); -+ return; -+ } -+ -+ if (bdrv_snapshot_find(bs, sn, name) >= 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "snapshot '%s' already exists", name); -+ return; -+ } -+ -+ sn = &sn1; -+ memset(sn, 0, sizeof(*sn)); -+ -+#ifdef _WIN32 -+ _ftime(&tb); -+ sn->date_sec = tb.time; -+ sn->date_nsec = tb.millitm * 1000000; -+#else -+ gettimeofday(&tv, NULL); -+ sn->date_sec = tv.tv_sec; -+ sn->date_nsec = tv.tv_usec * 1000; -+#endif -+ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); -+ -+ pstrcpy(sn->name, sizeof(sn->name), name); -+ -+ sn->vm_state_size = 0; /* do not save state */ -+ -+ ret = bdrv_snapshot_create(bs, sn); -+ if (ret < 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "Error while creating snapshot on '%s'\n", device); -+ return; -+ } -+} -+ -+void qmp_delete_drive_snapshot(const char *device, const char *name, -+ Error **errp) -+{ -+ BlockBackend *blk; -+ BlockDriverState *bs; -+ QEMUSnapshotInfo sn1, *sn = &sn1; -+ Error *local_err = NULL; -+ -+ int ret; -+ -+ blk = blk_by_name(device); -+ if (!blk) { -+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, -+ "Device '%s' not found", device); -+ return; -+ } -+ -+ bs = blk_bs(blk); -+ if (bdrv_is_read_only(bs)) { -+ error_setg(errp, "Node '%s' is read only", device); -+ return; -+ } -+ -+ if (!bdrv_can_snapshot(bs)) { -+ error_setg(errp, QERR_UNSUPPORTED); -+ return; -+ } -+ -+ if (bdrv_snapshot_find(bs, sn, name) < 0) { -+ /* return success if snapshot does not exists */ -+ return; -+ } -+ -+ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err); -+ if (ret < 0) { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ "Error while deleting snapshot on '%s'\n", device); -+ return; -+ } -+} -+ -+static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos, -+ int size) -+{ -+ BlockDriverState *bs = (BlockDriverState *)opaque; -+ int64_t maxlen = bdrv_getlength(bs); -+ if (pos > maxlen) { -+ return -EIO; -+ } -+ if ((pos + size) > maxlen) { -+ size = maxlen - pos - 1; -+ } -+ if (size == 0) { -+ return 0; -+ } -+ return bdrv_pread(bs, pos, buf, size); -+} -+ -+static const QEMUFileOps loadstate_file_ops = { -+ .get_buffer = loadstate_get_buffer, -+}; -+ -+int load_state_from_blockdev(const char *filename) -+{ -+ BlockDriverState *bs = NULL; -+ BlockDriver *drv = NULL; -+ Error *local_err = NULL; -+ Error *blocker = NULL; -+ -+ QEMUFile *f; -+ int ret; -+ -+ bs = bdrv_new(); -+ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, drv, &local_err); -+ error_setg(&blocker, "block device is in use by load state"); -+ bdrv_op_block_all(bs, blocker); -+ -+ if (ret < 0) { -+ error_report("Could not open VM state file"); -+ goto the_end; -+ } -+ -+ /* restore the VM state */ -+ f = qemu_fopen_ops(bs, &loadstate_file_ops); -+ if (!f) { -+ error_report("Could not open VM state file"); -+ ret = -EINVAL; -+ goto the_end; -+ } -+ -+ qemu_system_reset(VMRESET_SILENT); -+ migration_incoming_state_new(f); -+ ret = qemu_loadvm_state(f); -+ -+ qemu_fclose(f); -+ migration_incoming_state_destroy(); -+ if (ret < 0) { -+ error_report("Error %d while loading VM state", ret); -+ goto the_end; -+ } -+ -+ ret = 0; -+ -+ the_end: -+ if (bs) { -+ bdrv_op_unblock_all(bs, blocker); -+ error_free(blocker); -+ bdrv_unref(bs); -+ } -+ return ret; -+} -Index: new/savevm.c -=================================================================== ---- new.orig/migration/savevm.c 2014-11-20 09:13:01.000000000 +0100 -+++ new/migration/savevm.c 2014-11-20 09:16:47.000000000 +0100 -@@ -718,11 +718,11 @@ - qemu_put_be32(f, QEMU_VM_FILE_VERSION); - } - --void qemu_savevm_state_begin(QEMUFile *f, -+int qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params) - { - SaveStateEntry *se; -- int ret; -+ int ret = 0; - - trace_savevm_state_begin(); - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { -@@ -642,6 +642,7 @@ - break; - } - } -+ return ret; - } - - /* -@@ -690,7 +691,7 @@ - return !machine->suppress_vmdesc; - } - --void qemu_savevm_state_complete(QEMUFile *f) -+int qemu_savevm_state_complete(QEMUFile *f) - { - QJSON *vmdesc; - int vmdesc_len; -@@ -838,7 +839,7 @@ - save_section_footer(f, se); - if (ret < 0) { - qemu_file_set_error(f, ret); -- return; -+ return ret; - } - } - -@@ -746,6 +747,7 @@ - object_unref(OBJECT(vmdesc)); - - qemu_fflush(f); -+ return qemu_file_get_error(f); - } - - uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) -Index: new/vl.c -=================================================================== ---- new.orig/vl.c 2014-11-20 09:13:01.000000000 +0100 -+++ new/vl.c 2014-11-20 09:16:47.000000000 +0100 -@@ -2760,6 +2760,7 @@ - int optind; - const char *optarg; - const char *loadvm = NULL; -+ const char *loadstate = NULL; - MachineClass *machine_class; - const char *cpu_model; - const char *vga_model = NULL; -@@ -3457,6 +3458,9 @@ - case QEMU_OPTION_loadvm: - loadvm = optarg; - break; -+ case QEMU_OPTION_loadstate: -+ loadstate = optarg; -+ break; - case QEMU_OPTION_full_screen: - full_screen = 1; - break; -@@ -4428,6 +4432,10 @@ - if (load_vmstate(loadvm) < 0) { - autostart = 0; - } -+ } else if (loadstate) { -+ if (load_state_from_blockdev(loadstate) < 0) { -+ autostart = 0; -+ } - } - - qdev_prop_check_globals(); diff --git a/debian/patches/jemalloc.patch b/debian/patches/jemalloc.patch deleted file mode 100644 index 839689f..0000000 --- a/debian/patches/jemalloc.patch +++ /dev/null @@ -1,143 +0,0 @@ -From patchwork Fri Jun 19 10:56:58 2015 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: configure: Add support for jemalloc -From: Alexandre DERUMIER -X-Patchwork-Id: 486671 -Message-Id: <1434711418-20429-1-git-send-email-aderumier@odiso.com> -To: qemu-devel@nongnu.org -Cc: Alexandre Derumier -Date: Fri, 19 Jun 2015 12:56:58 +0200 - -This adds "--enable-jemalloc" and "--disable-jemalloc" to allow linking -to jemalloc memory allocator. - -We have already tcmalloc support, -but it seem to not working well with a lot of iothreads/disks. - -The main problem is that tcmalloc use a shared thread cache of 16MB -by default. -With more threads, this cache is shared, and some bad garbage collections -can occur if the cache is too low. - -It's possible to tcmalloc cache increase it with a env var: -TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES=256MB - -With default 16MB, performances are really bad with more than 2 disks. -Increasing to 256MB, it's helping but still have problem with 16 disks/iothreads. - -Jemalloc don't have performance problem with default configuration. - -Here the benchmark results in iops of 1 qemu vm randread 4K iodepth=32, -with rbd block backend (librbd is doing a lot of memory allocation), -1 iothread by disk - -glibc malloc ------------- - -1 disk 29052 -2 disks 55878 -4 disks 127899 -8 disks 240566 -15 disks 269976 - -jemalloc --------- - -1 disk 41278 -2 disks 75781 -4 disks 195351 -8 disks 294241 -15 disks 298199 - -tcmalloc 2.2.1 default 16M cache --------------------------------- - -1 disk 37911 -2 disks 67698 -4 disks 41076 -8 disks 43312 -15 disks 37569 - -tcmalloc : 256M cache ---------------------------- - -1 disk 33914 -2 disks 58839 -4 disks 148205 -8 disks 213298 -15 disks 218383 - -Signed-off-by: Alexandre Derumier -Reviewed-by: Fam Zheng ---- - configure | 29 +++++++++++++++++++++++++++++ - 1 file changed, 29 insertions(+) - -diff --git a/configure b/configure -index 222694f..2fe1e05 100755 ---- a/configure -+++ b/configure -@@ -338,6 +338,7 @@ vhdx="" - vhdx="" - numa="" - tcmalloc="no" -+jemalloc="no" - - # parse CC options first - for opt do -@@ -1149,6 +1150,10 @@ for opt do - ;; - --enable-tcmalloc) tcmalloc="yes" - ;; -+ --disable-jemalloc) jemalloc="no" -+ ;; -+ --enable-jemalloc) jemalloc="yes" -+ ;; - *) - echo "ERROR: unknown option $opt" - echo "Try '$0 --help' for more information" -@@ -3345,6 +3352,11 @@ EOF - fi - fi - -+if test "$tcmalloc" = "yes" && test "$jemalloc" = "yes" ; then -+ echo "ERROR: tcmalloc && jemalloc can't be used at the same time" -+ exit 1 -+fi -+ - ########################################## - # tcmalloc probe - -@@ -3362,6 +3374,22 @@ EOF - fi - - ########################################## -+# jemalloc probe -+ -+if test "$jemalloc" = "yes" ; then -+ cat > $TMPC << EOF -+#include -+int main(void) { malloc(1); return 0; } -+EOF -+ -+ if compile_prog "" "-ljemalloc" ; then -+ LIBS="-ljemalloc $LIBS" -+ else -+ feature_not_found "jemalloc" "install jemalloc devel" -+ fi -+fi -+ -+########################################## - # signalfd probe - signalfd="no" - cat > $TMPC << EOF -@@ -4573,6 +4591,7 @@ echo "snappy support $snappy" - echo "bzip2 support $bzip2" - echo "NUMA host support $numa" - echo "tcmalloc support $tcmalloc" -+echo "jemalloc support $jemalloc" - - if test "$sdl_too_old" = "yes"; then - echo "-> Your SDL version is too old - please upgrade to have SDL support" diff --git a/debian/patches/keymap.diff b/debian/patches/keymap.diff deleted file mode 100644 index 682cb9b..0000000 --- a/debian/patches/keymap.diff +++ /dev/null @@ -1,50 +0,0 @@ -Index: new/ui/vnc.c -=================================================================== ---- new.orig/ui/vnc.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/ui/vnc.c 2014-11-20 06:50:51.000000000 +0100 -@@ -1633,6 +1633,10 @@ - - static void do_key_event(VncState *vs, int down, int keycode, int sym) - { -+ int mods = keycode & 0xf00; -+ -+ keycode &= SCANCODE_KEYMASK; -+ - /* QEMU console switch */ - switch(keycode) { - case 0x2a: /* Left Shift */ -@@ -1712,7 +1716,24 @@ - } - - if (qemu_console_is_graphic(NULL)) { -+ -+ /* our java vnc client never sends ALTGR, so we create -+ an artificial up/down event */ -+ -+ int emul_altgr = (mods & SCANCODE_ALTGR) && -+ !vs->modifiers_state[0xb8]; -+ -+ if (emul_altgr) { -+ reset_keys(vs); -+ qemu_input_event_send_key_number(vs->vd->dcl.con, 0xb8, true); -+ } -+ - qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); -+ -+ if (emul_altgr) { -+ qemu_input_event_send_key_number(vs->vd->dcl.con, 0xb8, false); -+ } -+ - } else { - bool numlock = vs->modifiers_state[0x45]; - bool control = (vs->modifiers_state[0x1d] || -@@ -1851,7 +1872,8 @@ - lsym = lsym - 'A' + 'a'; - } - -- keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK; -+ keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF); -+ - trace_vnc_key_event_map(down, sym, keycode, code2name(keycode)); - do_key_event(vs, down, keycode, sym); - } diff --git a/debian/patches/live-migration-fixes.diff b/debian/patches/live-migration-fixes.diff deleted file mode 100644 index a7a2729..0000000 --- a/debian/patches/live-migration-fixes.diff +++ /dev/null @@ -1,48 +0,0 @@ -Index: new/arch_init.c -=================================================================== ---- new.orig/arch_init.c 2012-02-09 13:15:26.000000000 +0100 -+++ new/arch_init.c 2012-02-09 13:22:53.000000000 +0100 -@@ -264,6 +264,8 @@ - uint64_t bytes_transferred_last; - double bwidth = 0; - uint64_t expected_time = 0; -+ static int64_t starttime = 0; -+ double timediff; - int ret; - - if (stage < 0) { -@@ -299,10 +301,10 @@ - qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); - qemu_put_be64(f, block->length); - } -+ -+ starttime = qemu_get_clock_ns(rt_clock); - } - -- bytes_transferred_last = bytes_transferred; -- bwidth = qemu_get_clock_ns(rt_clock); - - while ((ret = qemu_file_rate_limit(f)) == 0) { - int bytes_sent; -@@ -318,8 +320,8 @@ - return ret; - } - -- bwidth = qemu_get_clock_ns(rt_clock) - bwidth; -- bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; -+ timediff = qemu_get_clock_ns(rt_clock) - starttime; -+ bwidth = bytes_transferred / timediff; - - /* if we haven't transferred anything this round, force expected_time to a - * a very high value, but without crashing */ -@@ -340,6 +342,10 @@ - - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - -+ if ((stage == 2) && (bytes_transferred > 2*ram_bytes_total())) { -+ return 1; -+ } -+ - expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; - - return (stage == 2) && (expected_time <= migrate_max_downtime()); diff --git a/debian/patches/mirror-fix-zero-init.patch b/debian/patches/mirror-fix-zero-init.patch deleted file mode 100644 index cd76df5..0000000 --- a/debian/patches/mirror-fix-zero-init.patch +++ /dev/null @@ -1,61 +0,0 @@ -From patchwork Thu Oct 1 19:05:28 2015 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [PULL, - 1/1] block: mirror - fix full sync mode when target does not support - zero init -From: Jeff Cody -X-Patchwork-Id: 525278 -Message-Id: <1443726328-29484-2-git-send-email-jcody@redhat.com> -To: qemu-block@nongnu.org -Cc: peter.maydell@linaro.org, jcody@redhat.com, qemu-devel@nongnu.org -Date: Thu, 1 Oct 2015 15:05:28 -0400 - -During mirror, if the target device does not support zero init, a -mirror may result in a corrupted image for sync="full" mode. - -This is due to how the initial dirty bitmap is set up prior to copying -data - we did not mark sectors as dirty that are unallocated. This -means those unallocated sectors are skipped over on the target, and for -a device without zero init, invalid data may reside in those holes. - -If both of the following conditions are true, then we will explicitly -mark all sectors as dirty: - - 1.) sync = "full" - 2.) bdrv_has_zero_init(target) == false - -If the target does support zero init, but a target image is passed in -with data already present (i.e. an "existing" image), it is assumed the -data present in the existing image is valid data for those sectors. - -Reviewed-by: Paolo Bonzini -Message-id: 91ed4bc5bda7e2b09eb508b07c83f4071fe0b3c9.1443705220.git.jcody@redhat.com -Signed-off-by: Jeff Cody ---- - block/mirror.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/block/mirror.c b/block/mirror.c -index a258926..87928ab 100644 ---- a/block/mirror.c -+++ b/block/mirror.c -@@ -455,6 +455,8 @@ static void coroutine_fn mirror_run(void *opaque) - if (!s->is_none_mode) { - /* First part, loop on the sectors and initialize the dirty bitmap. */ - BlockDriverState *base = s->base; -+ bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(s->target); -+ - for (sector_num = 0; sector_num < end; ) { - /* Just to make sure we are not exceeding int limit. */ - int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS, -@@ -477,7 +479,7 @@ static void coroutine_fn mirror_run(void *opaque) - } - - assert(n > 0); -- if (ret == 1) { -+ if (ret == 1 || mark_all_dirty) { - bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); - } - sector_num += n; diff --git a/debian/patches/modify-query-machines.patch b/debian/patches/modify-query-machines.patch deleted file mode 100644 index 5edcdd2..0000000 --- a/debian/patches/modify-query-machines.patch +++ /dev/null @@ -1,38 +0,0 @@ -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-11-20 07:26:23.000000000 +0100 -+++ new/qapi-schema.json 2014-11-20 07:26:39.000000000 +0100 -@@ -2393,6 +2393,8 @@ - # - # @default: #optional whether the machine is default - # -+# @current: #optional whether this machine is currently used -+# - # @cpu-max: maximum number of CPUs supported by the machine type - # (since 1.5.0) - # -@@ -2400,7 +2402,7 @@ - ## - { 'struct': 'MachineInfo', - 'data': { 'name': 'str', '*alias': 'str', -- '*is-default': 'bool', 'cpu-max': 'int' } } -+ '*is-default': 'bool', '*is-current': 'bool', 'cpu-max': 'int' } } - - ## - # @query-machines: -Index: new/vl.c -=================================================================== ---- new.orig/vl.c 2014-11-20 06:50:55.000000000 +0100 -+++ new/vl.c 2014-11-20 07:26:39.000000000 +0100 -@@ -1534,6 +1534,11 @@ - info->name = g_strdup(mc->name); - info->cpu_max = !mc->max_cpus ? 1 : mc->max_cpus; - -+ if (strcmp(mc->name, MACHINE_GET_CLASS(current_machine)->name) == 0) { -+ info->has_is_current = true; -+ info->is_current = true; -+ } -+ - entry = g_malloc0(sizeof(*entry)); - entry->value = info; - entry->next = mach_list; diff --git a/debian/patches/modify-query-spice.patch b/debian/patches/modify-query-spice.patch deleted file mode 100644 index 4e5222d..0000000 --- a/debian/patches/modify-query-spice.patch +++ /dev/null @@ -1,44 +0,0 @@ -Return last ticket with query-spice - -We use this to implement spice seemless migration. The current spice code -reconnect to the migrated VM using the original ticket. So we need -a way to read the original ticket. - -Limits: This only works for a single spice session. - - -Index: new/ui/spice-core.c -=================================================================== ---- new.orig/ui/spice-core.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/ui/spice-core.c 2014-11-20 07:26:43.000000000 +0100 -@@ -544,6 +544,11 @@ - micro = SPICE_SERVER_VERSION & 0xff; - info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro); - -+ if (auth_passwd) { -+ info->has_ticket = true; -+ info->ticket = g_strdup(auth_passwd); -+ } -+ - if (port) { - info->has_port = true; - info->port = port; -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-11-20 07:26:39.000000000 +0100 -+++ new/qapi-schema.json 2014-11-20 07:26:43.000000000 +0100 -@@ -868,11 +868,14 @@ - # - # @channels: a list of @SpiceChannel for each active spice channel - # -+# @ticket: #optional The last ticket set with set_password -+# - # Since: 0.14.0 - ## - { 'struct': 'SpiceInfo', - 'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int', - '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str', -+ '*ticket': 'str', - 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} } - - ## diff --git a/debian/patches/move-bdrv-snapshot-find.patch b/debian/patches/move-bdrv-snapshot-find.patch deleted file mode 100644 index 0cb7d48..0000000 --- a/debian/patches/move-bdrv-snapshot-find.patch +++ /dev/null @@ -1,79 +0,0 @@ -Index: new/block.c -=================================================================== ---- new.orig/block.c 2013-02-12 12:05:14.000000000 +0100 -+++ new/block.c 2013-02-12 12:06:52.000000000 +0100 -@@ -3162,6 +3162,28 @@ - return -ENOTSUP; - } - -+int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, -+ const char *name) -+{ -+ QEMUSnapshotInfo *sn_tab, *sn; -+ int nb_sns, i, ret; -+ -+ ret = -ENOENT; -+ nb_sns = bdrv_snapshot_list(bs, &sn_tab); -+ if (nb_sns < 0) -+ return ret; -+ for(i = 0; i < nb_sns; i++) { -+ sn = &sn_tab[i]; -+ if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { -+ *sn_info = *sn; -+ ret = 0; -+ break; -+ } -+ } -+ g_free(sn_tab); -+ return ret; -+} -+ - int bdrv_snapshot_load_tmp(BlockDriverState *bs, - const char *snapshot_name) - { -Index: new/include/block/block.h -=================================================================== ---- new.orig/include/block/block.h 2013-02-12 12:05:14.000000000 +0100 -+++ new/include/block/block.h 2013-02-12 12:06:52.000000000 +0100 -@@ -331,6 +331,8 @@ - int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); - int bdrv_snapshot_list(BlockDriverState *bs, - QEMUSnapshotInfo **psn_info); -+int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, -+ const char *name); - int bdrv_snapshot_load_tmp(BlockDriverState *bs, - const char *snapshot_name); - char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); -Index: new/savevm.c -=================================================================== ---- new.orig/savevm.c 2013-02-12 12:05:14.000000000 +0100 -+++ new/savevm.c 2013-02-12 12:06:52.000000000 +0100 -@@ -2055,28 +2055,6 @@ - return ret; - } - --static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, -- const char *name) --{ -- QEMUSnapshotInfo *sn_tab, *sn; -- int nb_sns, i, ret; -- -- ret = -ENOENT; -- nb_sns = bdrv_snapshot_list(bs, &sn_tab); -- if (nb_sns < 0) -- return ret; -- for(i = 0; i < nb_sns; i++) { -- sn = &sn_tab[i]; -- if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { -- *sn_info = *sn; -- ret = 0; -- break; -- } -- } -- g_free(sn_tab); -- return ret; --} -- - /* - * Deletes snapshots of a given name in all opened images. - */ diff --git a/debian/patches/multicore.diff b/debian/patches/multicore.diff deleted file mode 100644 index f4a4674..0000000 --- a/debian/patches/multicore.diff +++ /dev/null @@ -1,201 +0,0 @@ -Index: kvm-86/cpu-defs.h -=================================================================== ---- kvm-86.orig/cpu-defs.h 2009-09-24 14:19:14.000000000 +0200 -+++ kvm-86/cpu-defs.h 2009-09-24 14:47:00.000000000 +0200 -@@ -194,6 +194,8 @@ - int cpu_index; /* CPU index (informative) */ \ - uint32_t host_tid; /* host thread ID */ \ - int numa_node; /* NUMA node this cpu is belonging to */ \ -+ int nr_cores; /* number of cores within this CPU package */ \ -+ int nr_threads;/* number of threads within this CPU */ \ - int running; /* Nonzero if cpu is currently running(usermode). */ \ - int thread_id; \ - /* user data */ \ -Index: kvm-86/target-i386/helper.c -=================================================================== ---- kvm-86.orig/target-i386/helper.c 2009-09-24 14:19:14.000000000 +0200 -+++ kvm-86/target-i386/helper.c 2009-09-24 14:50:18.000000000 +0200 -@@ -121,7 +121,7 @@ - #ifdef TARGET_X86_64 - { - .name = "qemu64", -- .level = 2, -+ .level = 4, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, -@@ -192,7 +192,7 @@ - #endif - { - .name = "qemu32", -- .level = 2, -+ .level = 4, - .family = 6, - .model = 3, - .stepping = 3, -@@ -1638,6 +1638,12 @@ - *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ - *ecx = env->cpuid_ext_features; - *edx = env->cpuid_features; -+ -+ if (env->nr_cores * env->nr_threads > 1) { -+ *ebx |= (env->nr_cores * env->nr_threads) << 16; -+ *edx |= 1 << 28; /* HTT bit */ -+ } -+ - break; - case 2: - /* cache info: needed for Pentium Pro compatibility */ -@@ -1648,21 +1654,29 @@ - break; - case 4: - /* cache info: needed for Core compatibility */ -+ if (env->nr_cores > 1) { -+ *eax = (env->nr_cores - 1) << 26; -+ } else { -+ *eax = 0; -+ } - switch (count) { - case 0: /* L1 dcache info */ -- *eax = 0x0000121; -+ *eax |= 0x0000121; - *ebx = 0x1c0003f; - *ecx = 0x000003f; - *edx = 0x0000001; - break; - case 1: /* L1 icache info */ -- *eax = 0x0000122; -+ *eax |= 0x0000122; - *ebx = 0x1c0003f; - *ecx = 0x000003f; - *edx = 0x0000001; - break; - case 2: /* L2 cache info */ -- *eax = 0x0000143; -+ *eax |= 0x0000143; -+ if (env->nr_threads > 1) { -+ *eax |= (env->nr_threads - 1) << 14; -+ } - *ebx = 0x3c0003f; - *ecx = 0x0000fff; - *edx = 0x0000001; -@@ -1715,6 +1729,16 @@ - *ecx = env->cpuid_ext3_features; - *edx = env->cpuid_ext2_features; - -+ if (env->nr_cores * env->nr_threads > 1) { -+ uint32_t teax, tebx, tecx, tedx; -+ cpu_x86_cpuid(env, 0, 0, &teax, &tebx, &tecx, &tedx); -+ if ( tebx == CPUID_VENDOR_AMD_1 && -+ tedx == CPUID_VENDOR_AMD_2 && -+ tecx == CPUID_VENDOR_AMD_3) { -+ *ecx |= 1 << 1; /* CmpLegacy bit */ -+ } -+ } -+ - if (kvm_enabled()) { - uint32_t h_eax, h_edx; - -@@ -1790,6 +1814,9 @@ - *ebx = 0; - *ecx = 0; - *edx = 0; -+ if (env->nr_cores * env->nr_threads > 1) { -+ *ecx |= (env->nr_cores * env->nr_threads) - 1; -+ } - break; - case 0x8000000A: - *eax = 0x00000001; /* SVM Revision */ -Index: kvm-86/vl.c -=================================================================== ---- kvm-86.orig/vl.c 2009-09-24 14:30:14.000000000 +0200 -+++ kvm-86/vl.c 2009-09-24 14:47:00.000000000 +0200 -@@ -230,6 +230,8 @@ - const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE]; - int assigned_devices_index; - int smp_cpus = 1; -+int smp_cores = 1; -+int smp_threads = 1; - int fairsched_id = 0; - const char *vnc_display; - int acpi_enabled = 1; -@@ -2499,6 +2501,52 @@ - return; - } - -+static void smp_parse(const char *optarg) -+{ -+ int smp, sockets = 0, threads = 0, cores = 0; -+ char *endptr; -+ char option[128]; -+ -+ smp = strtoul(optarg, &endptr, 10); -+ if (endptr != optarg) { -+ if (*endptr == ',') { -+ endptr++; -+ } -+ } -+ if (get_param_value(option, 128, "sockets", endptr) != 0) -+ sockets = strtoull(option, NULL, 10); -+ if (get_param_value(option, 128, "cores", endptr) != 0) -+ cores = strtoull(option, NULL, 10); -+ if (get_param_value(option, 128, "threads", endptr) != 0) -+ threads = strtoull(option, NULL, 10); -+ -+ /* compute missing values, prefer sockets over cores over threads */ -+ if (smp == 0 || sockets == 0) { -+ sockets = sockets > 0 ? sockets : 1; -+ cores = cores > 0 ? cores : 1; -+ threads = threads > 0 ? threads : 1; -+ if (smp == 0) { -+ smp = cores * threads * sockets; -+ } else { -+ sockets = smp / (cores * threads); -+ } -+ } else { -+ if (cores == 0) { -+ threads = threads > 0 ? threads : 1; -+ cores = smp / (sockets * threads); -+ } else { -+ if (sockets == 0) { -+ sockets = smp / (cores * threads); -+ } else { -+ threads = smp / (cores * sockets); -+ } -+ } -+ } -+ smp_cpus = smp; -+ smp_cores = cores > 0 ? cores : 1; -+ smp_threads = threads > 0 ? threads : 1; -+} -+ - /***********************************************************/ - /* USB devices */ - -@@ -3727,6 +3775,8 @@ - - if (kvm_enabled()) - kvm_init_vcpu(env); -+ env->nr_cores = smp_cores; -+ env->nr_threads = smp_threads; - return; - } - -@@ -4060,6 +4110,8 @@ - kvm_start_vcpu(env); - else - tcg_init_vcpu(env); -+ env->nr_cores = smp_cores; -+ env->nr_threads = smp_threads; - } - - void qemu_notify_event(void) -@@ -5560,7 +5612,7 @@ - usb_devices_index++; - break; - case QEMU_OPTION_smp: -- smp_cpus = atoi(optarg); -+ smp_parse(optarg); - if (smp_cpus < 1) { - fprintf(stderr, "Invalid number of CPUs\n"); - exit(1); diff --git a/debian/patches/old/0000-cover-letter.patch b/debian/patches/old/0000-cover-letter.patch new file mode 100644 index 0000000..2c2f006 --- /dev/null +++ b/debian/patches/old/0000-cover-letter.patch @@ -0,0 +1,98 @@ +From aa580be154923056a52fdeec429f7e80f04b456e Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +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. + +* Backup to a single archive file +* Backup contain all data to restore VM (full backup) +* Do not depend on storage type or image format +* Avoid use of temporary storage +* store sparse images efficiently + +The file docs/backup.txt contains more details. + +Changes since v1: + +* fix spelling errors +* move BackupInfo from BDS to BackupBlockJob +* introduce BackupDriver to allow more than one backup format +* vma: add suport to store vmstate (size is not known in advance) +* add ability to store VM state + +Changes since v2: + +* BackupDriver: remove cancel_cb +* use enum for BackupFormat +* vma: use bdrv_open instead of bdrv_file_open +* vma: fix aio, use O_DIRECT +* backup one drive after another (try to avoid high load) + +Changes since v3: + +* move reviewer info from commit messages to cover-letter +* remove 'RFC' from log headers and file names +* removed comment about slow qcow2 snapshot bug +* fix spelling errors +* fixed copyright +* change 'backupfile' parameter name to 'backup-file' +* change 'config-filename' parameter name to 'config-file' +* 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 + add basic backup support to block driver + add backup related monitor commands + introduce new vma archive format + add regression tests for backup + add vm state to backups + + Makefile | 3 +- + Makefile.objs | 1 + + backup.c | 355 +++++++++++++++++ + backup.h | 45 +++ + block.c | 71 ++++- + blockdev.c | 617 ++++++++++++++++++++++++++++++ + docs/backup.txt | 116 ++++++ + docs/specs/vma_spec.txt | 24 ++ + hmp-commands.hx | 31 ++ + hmp.c | 64 ++++ + hmp.h | 3 + + include/block/block.h | 2 + + include/block/blockjob.h | 10 + + monitor.c | 7 + + qapi-schema.json | 98 +++++ + qmp-commands.hx | 27 ++ + tests/Makefile | 11 +- + tests/backup-test.c | 529 ++++++++++++++++++++++++++ + vma-reader.c | 799 +++++++++++++++++++++++++++++++++++++++ + vma-writer.c | 940 ++++++++++++++++++++++++++++++++++++++++++++++ + vma.c | 561 +++++++++++++++++++++++++++ + vma.h | 145 +++++++ + 22 files changed, 4450 insertions(+), 9 deletions(-) + create mode 100644 backup.c + create mode 100644 backup.h + create mode 100644 docs/backup.txt + create mode 100644 docs/specs/vma_spec.txt + create mode 100644 tests/backup-test.c + create mode 100644 vma-reader.c + create mode 100644 vma-writer.c + create mode 100644 vma.c + create mode 100644 vma.h + +-- +1.7.2.5 + diff --git a/debian/patches/old/0001-add-documenation-for-new-backup-framework.patch b/debian/patches/old/0001-add-documenation-for-new-backup-framework.patch new file mode 100644 index 0000000..bb3b9bf --- /dev/null +++ b/debian/patches/old/0001-add-documenation-for-new-backup-framework.patch @@ -0,0 +1,137 @@ +From 2f0dcd89a0de8b656d33ce6997c09879bd287af7 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 13 Nov 2012 09:24:50 +0100 +Subject: [PATCH v5 1/6] add documenation for new backup framework + + +Signed-off-by: Dietmar Maurer +--- + docs/backup.txt | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 116 insertions(+), 0 deletions(-) + create mode 100644 docs/backup.txt + +diff --git a/docs/backup.txt b/docs/backup.txt +new file mode 100644 +index 0000000..927d787 +--- /dev/null ++++ b/docs/backup.txt +@@ -0,0 +1,116 @@ ++Efficient VM backup for qemu ++ ++=Requirements= ++ ++* Backup to a single archive file ++* Backup needs to contain all data to restore VM (full backup) ++* Do not depend on storage type or image format ++* Avoid use of temporary storage ++* store sparse images efficiently ++ ++=Introduction= ++ ++Most VM backup solutions use some kind of snapshot to get a consistent ++VM view at a specific point in time. For example, we previously used ++LVM to create a snapshot of all used VM images, which are then copied ++into a tar file. ++ ++That basically means that any data written during backup involve ++considerable overhead. For LVM we get the following steps: ++ ++1.) read original data (VM write) ++2.) write original data into snapshot (VM write) ++3.) write new data (VM write) ++4.) read data from snapshot (backup) ++5.) write data from snapshot into tar file (backup) ++ ++Another approach to backup VM images is to create a new qcow2 image ++which use the old image as base. During backup, writes are redirected ++to the new image, so the old image represents a 'snapshot'. After ++backup, data need to be copied back from new image into the old ++one (commit). So a simple write during backup triggers the following ++steps: ++ ++1.) write new data to new image (VM write) ++2.) read data from old image (backup) ++3.) write data from old image into tar file (backup) ++ ++4.) read data from new image (commit) ++5.) write data to old image (commit) ++ ++This is in fact the same overhead as before. Other tools like qemu ++livebackup produces similar overhead (2 reads, 3 writes). ++ ++Some storage types/formats supports internal snapshots using some kind ++of reference counting (rados, sheepdog, dm-thin, qcow2). It would be possible ++to use that for backups, but for now we want to be storage-independent. ++ ++=Make it more efficient= ++ ++The be more efficient, we simply need to avoid unnecessary steps. The ++following steps are always required: ++ ++1.) read old data before it gets overwritten ++2.) write that data into the backup archive ++3.) write new data (VM write) ++ ++As you can see, this involves only one read, and two writes. ++ ++To make that work, our backup archive need to be able to store image ++data 'out of order'. It is important to notice that this will not work ++with traditional archive formats like tar. ++ ++During backup we simply intercept writes, then read existing data and ++store that directly into the archive. After that we can continue the ++write. ++ ++==Advantages== ++ ++* very good performance (1 read, 2 writes) ++* works on any storage type and image format. ++* avoid usage of temporary storage ++* we can define a new and simple archive format, which is able to ++ store sparse files efficiently. ++ ++Note: Storing sparse files is a mess with existing archive ++formats. For example, tar requires information about holes at the ++beginning of the archive. ++ ++==Disadvantages== ++ ++* we need to define a new archive format ++ ++Note: Most existing archive formats are optimized to store small files ++including file attributes. We simply do not need that for VM archives. ++ ++* archive contains data 'out of order' ++ ++If you want to access image data in sequential order, you need to ++re-order archive data. It would be possible to to that on the fly, ++using temporary files. ++ ++Fortunately, a normal restore/extract works perfectly with 'out of ++order' data, because the target files are seekable. ++ ++* slow backup storage can slow down VM during backup ++ ++It is important to note that we only do sequential writes to the ++backup storage. Furthermore one can compress the backup stream. IMHO, ++it is better to slow down the VM a bit. All other solutions creates ++large amounts of temporary data during backup. ++ ++=Archive format requirements= ++ ++The basic requirement for such new format is that we can store image ++date 'out of order'. It is also very likely that we have less than 256 ++drives/images per VM, and we want to be able to store VM configuration ++files. ++ ++We have defined a very simply format with those properties, see: ++ ++docs/specs/vma_spec.txt ++ ++Please let us know if you know an existing format which provides the ++same functionality. ++ ++ +-- +1.7.2.5 + diff --git a/debian/patches/old/0001-aio-Fix-use-after-free-in-cancellation-path.patch b/debian/patches/old/0001-aio-Fix-use-after-free-in-cancellation-path.patch new file mode 100644 index 0000000..df88f44 --- /dev/null +++ b/debian/patches/old/0001-aio-Fix-use-after-free-in-cancellation-path.patch @@ -0,0 +1,75 @@ +From 271c0f68b4eae72691721243a1c37f46a3232d61 Mon Sep 17 00:00:00 2001 +From: Fam Zheng +Date: Wed, 21 May 2014 10:42:13 +0800 +Subject: [PATCH] aio: Fix use-after-free in cancellation path + +The current flow of canceling a thread from THREAD_ACTIVE state is: + + 1) Caller wants to cancel a request, so it calls thread_pool_cancel. + + 2) thread_pool_cancel waits on the conditional variable + elem->check_cancel. + + 3) The worker thread changes state to THREAD_DONE once the task is + done, and notifies elem->check_cancel to allow thread_pool_cancel + to continue execution, and signals the notifier (pool->notifier) to + allow callback function to be called later. But because of the + global mutex, the notifier won't get processed until step 4) and 5) + are done. + + 4) thread_pool_cancel continues, leaving the notifier signaled, it + just returns to caller. + + 5) Caller thinks the request is already canceled successfully, so it + releases any related data, such as freeing elem->common.opaque. + + 6) In the next main loop iteration, the notifier handler, + event_notifier_ready, is called. It finds the canceled thread in + THREAD_DONE state, so calls elem->common.cb, with an (likely) + dangling opaque pointer. This is a use-after-free. + +Fix it by calling event_notifier_ready before leaving +thread_pool_cancel. + +Test case update: This change will let cancel complete earlier than +test-thread-pool.c expects, so update the code to check this case: if +it's already done, done_cb sets .aiocb to NULL, skip calling +bdrv_aio_cancel on them. + +Reported-by: Ulrich Obergfell +Suggested-by: Paolo Bonzini +Signed-off-by: Fam Zheng +Signed-off-by: Stefan Hajnoczi +--- + tests/test-thread-pool.c | 2 +- + thread-pool.c | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c +index c1f8e13..aa156bc 100644 +--- a/tests/test-thread-pool.c ++++ b/tests/test-thread-pool.c +@@ -180,7 +180,7 @@ static void test_cancel(void) + + /* Canceling the others will be a blocking operation. */ + for (i = 0; i < 100; i++) { +- if (data[i].n != 3) { ++ if (data[i].aiocb && data[i].n != 3) { + bdrv_aio_cancel(data[i].aiocb); + } + } +diff --git a/thread-pool.c b/thread-pool.c +index fbdd3ff..dfb699d 100644 +--- a/thread-pool.c ++++ b/thread-pool.c +@@ -224,6 +224,7 @@ static void thread_pool_cancel(BlockDriverAIOCB *acb) + pool->pending_cancellations--; + } + qemu_mutex_unlock(&pool->lock); ++ event_notifier_ready(&pool->notifier); + } + + static const AIOCBInfo thread_pool_aiocb_info = { +-- +1.7.10.4 + diff --git a/debian/patches/old/0001-aio-fix-qemu_bh_schedule-bh-ctx-race-condition.patch b/debian/patches/old/0001-aio-fix-qemu_bh_schedule-bh-ctx-race-condition.patch new file mode 100644 index 0000000..a01765b --- /dev/null +++ b/debian/patches/old/0001-aio-fix-qemu_bh_schedule-bh-ctx-race-condition.patch @@ -0,0 +1,55 @@ +From 4535f739edfdea392e381811963823bf05649e42 Mon Sep 17 00:00:00 2001 +From: Stefan Hajnoczi +Date: Tue, 3 Jun 2014 11:21:01 +0200 +Subject: [PATCH] aio: fix qemu_bh_schedule() bh->ctx race condition + +qemu_bh_schedule() is supposed to be thread-safe at least the first time +it is called. Unfortunately this is not quite true: + + bh->scheduled = 1; + aio_notify(bh->ctx); + +Since another thread may run the BH callback once it has been scheduled, +there is a race condition if the callback frees the BH before +aio_notify(bh->ctx) has a chance to run. + +Reported-by: Stefan Priebe +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Stefan Priebe +--- + async.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/async.c b/async.c +index 6930185..5b6fe6b 100644 +--- a/async.c ++++ b/async.c +@@ -117,15 +117,21 @@ void qemu_bh_schedule_idle(QEMUBH *bh) + + void qemu_bh_schedule(QEMUBH *bh) + { ++ AioContext *ctx; ++ + if (bh->scheduled) + return; ++ ctx = bh->ctx; + bh->idle = 0; +- /* Make sure that idle & any writes needed by the callback are done +- * before the locations are read in the aio_bh_poll. ++ /* Make sure that: ++ * 1. idle & any writes needed by the callback are done before the ++ * locations are read in the aio_bh_poll. ++ * 2. ctx is loaded before scheduled is set and the callback has a chance ++ * to execute. + */ +- smp_wmb(); ++ smp_mb(); + bh->scheduled = 1; +- aio_notify(bh->ctx); ++ aio_notify(ctx); + } + + +-- +1.7.10.4 + diff --git a/debian/patches/old/0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch b/debian/patches/old/0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch new file mode 100644 index 0000000..39eceba --- /dev/null +++ b/debian/patches/old/0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch @@ -0,0 +1,48 @@ +From 6314c83ae14ee32835668e38bb55f4b93b800736 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Mon, 30 Nov 2015 15:38:22 +0800 +Subject: [PATCH 1/2] net: pcnet: add check to validate receive data + size(CVE-2015-7504) + +In loopback mode, pcnet_receive routine appends CRC code to the +receive buffer. If the data size given is same as the buffer size, +the appended CRC code overwrites 4 bytes after s->buffer. Added a +check to avoid that. + +Reported by: Qinghao Tang +Cc: qemu-stable@nongnu.org +Signed-off-by: Prasad J Pandit +Signed-off-by: Jason Wang +--- + hw/net/pcnet.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c +index 0eb3cc4..309c40b 100644 +--- a/hw/net/pcnet.c ++++ b/hw/net/pcnet.c +@@ -1084,7 +1084,7 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) + uint32_t fcs = ~0; + uint8_t *p = src; + +- while (p != &src[size-4]) ++ while (p != &src[size]) + CRC(fcs, *p++); + crc_err = (*(uint32_t *)p != htonl(fcs)); + } +@@ -1233,8 +1233,10 @@ static void pcnet_transmit(PCNetState *s) + bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); + + /* if multi-tmd packet outsizes s->buffer then skip it silently. +- Note: this is not what real hw does */ +- if (s->xmit_pos + bcnt > sizeof(s->buffer)) { ++ * Note: this is not what real hw does. ++ * Last four bytes of s->buffer are used to store CRC FCS code. ++ */ ++ if (s->xmit_pos + bcnt > sizeof(s->buffer) - 4) { + s->xmit_pos = -1; + goto txdone; + } +-- +2.1.4 + diff --git a/debian/patches/old/0001-smm_available-false.patch b/debian/patches/old/0001-smm_available-false.patch new file mode 100644 index 0000000..8e4d6de --- /dev/null +++ b/debian/patches/old/0001-smm_available-false.patch @@ -0,0 +1,26 @@ +From 4a1e7790e45da3b0dae61bd483b37556bae338ee Mon Sep 17 00:00:00 2001 +From: Alexandre Derumier +Date: Tue, 29 Sep 2015 15:37:44 +0200 +Subject: [PATCH] smm_available = false + +Signed-off-by: Alexandre Derumier +--- + hw/i386/pc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index 7661ea9..5529dc8 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -1842,7 +1842,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) + if (tcg_enabled() || qtest_enabled()) { + smm_available = true; + } else if (kvm_enabled()) { +- smm_available = kvm_has_smm(); ++ smm_available = false; + } + + if (smm_available) { +-- +2.1.4 + diff --git a/debian/patches/old/0001-ui-vnc-avoid-floating-point-exception.patch b/debian/patches/old/0001-ui-vnc-avoid-floating-point-exception.patch new file mode 100644 index 0000000..f8adea7 --- /dev/null +++ b/debian/patches/old/0001-ui-vnc-avoid-floating-point-exception.patch @@ -0,0 +1,44 @@ +From 4c65fed8bdf96780735dbdb92a8bd0d6b6526cc3 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Thu, 3 Dec 2015 18:54:17 +0530 +Subject: [PATCH] ui: vnc: avoid floating point exception + +While sending 'SetPixelFormat' messages to a VNC server, +the client could set the 'red-max', 'green-max' and 'blue-max' +values to be zero. This leads to a floating point exception in +write_png_palette while doing frame buffer updates. + +Reported-by: Lian Yihan +Signed-off-by: Prasad J Pandit +Reviewed-by: Gerd Hoffmann +Signed-off-by: Peter Maydell +--- + ui/vnc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/ui/vnc.c b/ui/vnc.c +index 7538405..cbe4d33 100644 +--- a/ui/vnc.c ++++ b/ui/vnc.c +@@ -2198,15 +2198,15 @@ static void set_pixel_format(VncState *vs, + return; + } + +- vs->client_pf.rmax = red_max; ++ vs->client_pf.rmax = red_max ? red_max : 0xFF; + vs->client_pf.rbits = hweight_long(red_max); + vs->client_pf.rshift = red_shift; + vs->client_pf.rmask = red_max << red_shift; +- vs->client_pf.gmax = green_max; ++ vs->client_pf.gmax = green_max ? green_max : 0xFF; + vs->client_pf.gbits = hweight_long(green_max); + vs->client_pf.gshift = green_shift; + vs->client_pf.gmask = green_max << green_shift; +- vs->client_pf.bmax = blue_max; ++ vs->client_pf.bmax = blue_max ? blue_max : 0xFF; + vs->client_pf.bbits = hweight_long(blue_max); + vs->client_pf.bshift = blue_shift; + vs->client_pf.bmask = blue_max << blue_shift; +-- +2.1.4 + diff --git a/debian/patches/old/0002-add-basic-backup-support-to-block-driver.patch b/debian/patches/old/0002-add-basic-backup-support-to-block-driver.patch new file mode 100644 index 0000000..a178c59 --- /dev/null +++ b/debian/patches/old/0002-add-basic-backup-support-to-block-driver.patch @@ -0,0 +1,626 @@ +From 1d0c6dfc9616c0dd17986cab6744c10cb748de1e Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 13 Nov 2012 10:03:52 +0100 +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(). + +We call backup_do_cow() for each write during backup. That function +reads the original data and pass it to backup_dump_cb(). + +The tracked_request infrastructure is used to serialize access. + +Currently backup cluster size is hardcoded to 65536 bytes. + +Signed-off-by: Dietmar Maurer +--- + Makefile.objs | 1 + + backup.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++ + backup.h | 30 ++++ + block.c | 71 +++++++++- + include/block/block.h | 2 + + include/block/blockjob.h | 10 ++ + 6 files changed, 463 insertions(+), 6 deletions(-) + create mode 100644 backup.c + create mode 100644 backup.h + +diff --git a/Makefile.objs b/Makefile.objs +index a68cdac..df64f70 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -13,6 +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 += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o + block-obj-y += qemu-coroutine-sleep.o +diff --git a/backup.c b/backup.c +new file mode 100644 +index 0000000..8955e1a +--- /dev/null ++++ b/backup.c +@@ -0,0 +1,355 @@ ++/* ++ * QEMU backup ++ * ++ * 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 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#include "block/block.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "qemu/ratelimit.h" ++#include "backup.h" ++ ++#define DEBUG_BACKUP 0 ++ ++#define USE_ALLOCATION_CHECK 0 ++ ++#define DPRINTF(fmt, ...) \ ++ do { if (DEBUG_BACKUP) { printf("backup: " fmt, ## __VA_ARGS__); } } \ ++ while (0) ++ ++ ++#define SLICE_TIME 100000000ULL /* ns */ ++ ++typedef struct BackupBlockJob { ++ BlockJob common; ++ RateLimit limit; ++ CoRwlock rwlock; ++ uint64_t sectors_read; ++ unsigned long *bitmap; ++ int bitmap_size; ++ BackupDumpFunc *backup_dump_cb; ++ BlockDriverCompletionFunc *backup_complete_cb; ++ void *opaque; ++} BackupBlockJob; ++ ++static bool backup_get_bitmap(BackupBlockJob *job, int64_t cluster_num) ++{ ++ assert(job); ++ assert(job->bitmap); ++ ++ unsigned long val, idx, bit; ++ ++ idx = cluster_num / BITS_PER_LONG; ++ ++ assert(job->bitmap_size > idx); ++ ++ bit = cluster_num % BITS_PER_LONG; ++ val = job->bitmap[idx]; ++ ++ return !!(val & (1UL << bit)); ++} ++ ++static void backup_set_bitmap(BackupBlockJob *job, int64_t cluster_num, ++ bool dirty) ++{ ++ assert(job); ++ assert(job->bitmap); ++ ++ unsigned long val, idx, bit; ++ ++ idx = cluster_num / BITS_PER_LONG; ++ ++ assert(job->bitmap_size > idx); ++ ++ bit = cluster_num % BITS_PER_LONG; ++ val = job->bitmap[idx]; ++ if (dirty) { ++ val |= 1UL << bit; ++ } else { ++ val &= ~(1UL << bit); ++ } ++ job->bitmap[idx] = val; ++} ++ ++static int coroutine_fn backup_do_cow(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors) ++{ ++ assert(bs); ++ BackupBlockJob *job = (BackupBlockJob *)bs->job; ++ assert(job); ++ ++ BlockDriver *drv = bs->drv; ++ struct iovec iov; ++ QEMUIOVector bounce_qiov; ++ void *bounce_buffer = NULL; ++ int ret = 0; ++ ++ qemu_co_rwlock_rdlock(&job->rwlock); ++ ++ int64_t start, end; ++ ++ start = sector_num / BACKUP_BLOCKS_PER_CLUSTER; ++ end = (sector_num + nb_sectors + BACKUP_BLOCKS_PER_CLUSTER - 1) / ++ BACKUP_BLOCKS_PER_CLUSTER; ++ ++ 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%" PRId64 "\n", start); ++ continue; /* already copied */ ++ } ++ ++ /* immediately set bitmap (avoid coroutine race) */ ++ backup_set_bitmap(job, start, 1); ++ ++ DPRINTF("brdv_co_backup_cow C%" PRId64 "\n", start); ++ ++ if (!bounce_buffer) { ++ iov.iov_len = BACKUP_CLUSTER_SIZE; ++ iov.iov_base = bounce_buffer = qemu_blockalign(bs, iov.iov_len); ++ qemu_iovec_init_external(&bounce_qiov, &iov, 1); ++ } ++ ++#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 is_allocated C%" PRId64 " failed\n", ++ start); ++ goto out; ++ } ++ ++ 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%" PRId64 " failed\n", ++ start); ++ goto out; ++ } ++ ++ DPRINTF("brdv_co_backup_cow done C%" PRId64 "\n", start); ++ } ++ ++out: ++ if (bounce_buffer) { ++ qemu_vfree(bounce_buffer); ++ } ++ ++ qemu_co_rwlock_unlock(&job->rwlock); ++ ++ return ret; ++} ++ ++static int coroutine_fn backup_before_read(BlockDriverState *bs, ++ int64_t sector_num, ++ int nb_sectors, QEMUIOVector *qiov) ++{ ++ return backup_do_cow(bs, sector_num, nb_sectors); ++} ++ ++static int coroutine_fn backup_before_write(BlockDriverState *bs, ++ int64_t sector_num, ++ int nb_sectors, QEMUIOVector *qiov) ++{ ++ return backup_do_cow(bs, sector_num, nb_sectors); ++} ++ ++static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) ++{ ++ BackupBlockJob *s = container_of(job, BackupBlockJob, common); ++ ++ if (speed < 0) { ++ error_set(errp, QERR_INVALID_PARAMETER, "speed"); ++ return; ++ } ++ ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); ++} ++ ++static BlockJobType backup_job_type = { ++ .instance_size = sizeof(BackupBlockJob), ++ .before_read = backup_before_read, ++ .before_write = backup_before_write, ++ .job_type = "backup", ++ .set_speed = backup_set_speed, ++}; ++ ++static void coroutine_fn backup_run(void *opaque) ++{ ++ BackupBlockJob *job = opaque; ++ BlockDriverState *bs = job->common.bs; ++ assert(bs); ++ ++ int64_t start, end; ++ ++ start = 0; ++ end = (bs->total_sectors + BACKUP_BLOCKS_PER_CLUSTER - 1) / ++ BACKUP_BLOCKS_PER_CLUSTER; ++ ++ DPRINTF("backup_run start %s %" PRId64 " %" PRId64 "\n", ++ bdrv_get_device_name(bs), start, end); ++ ++ int ret = 0; ++ ++ for (; start < end; start++) { ++ if (block_job_is_cancelled(&job->common)) { ++ ret = -1; ++ break; ++ } ++ ++ /* 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) ++ */ ++ if (job->common.speed) { ++ uint64_t delay_ns = ratelimit_calculate_delay( ++ &job->limit, job->sectors_read); ++ job->sectors_read = 0; ++ block_job_sleep_ns(&job->common, rt_clock, delay_ns); ++ } else { ++ block_job_sleep_ns(&job->common, rt_clock, 1000); ++ } ++ ++ if (block_job_is_cancelled(&job->common)) { ++ ret = -1; ++ break; ++ } ++ ++ if (backup_get_bitmap(job, start)) { ++ continue; /* already copied */ ++ } ++ ++ DPRINTF("backup_run loop C%" PRId64 "\n", start); ++ ++ /** ++ * This triggers a cluster copy ++ * Note: avoid direct call to brdv_co_backup_cow, because ++ * this does not call tracked_request_begin() ++ */ ++ ret = bdrv_co_backup(bs, start*BACKUP_BLOCKS_PER_CLUSTER, 1); ++ if (ret < 0) { ++ break; ++ } ++ /* Publish progress */ ++ job->common.offset += BACKUP_CLUSTER_SIZE; ++ } ++ ++ /* 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); ++} ++ ++static void backup_job_cleanup_cb(void *opaque, int ret) ++{ ++ BlockDriverState *bs = opaque; ++ assert(bs); ++ BackupBlockJob *job = (BackupBlockJob *)bs->job; ++ assert(job); ++ ++ DPRINTF("backup_job_cleanup_cb start %d\n", ret); ++ ++ job->backup_complete_cb(job->opaque, ret); ++ ++ DPRINTF("backup_job_cleanup_cb end\n"); ++ ++ g_free(job->bitmap); ++} ++ ++void ++backup_job_start(BlockDriverState *bs, bool cancel) ++{ ++ assert(bs); ++ assert(bs->job); ++ assert(bs->job->co == NULL); ++ ++ if (cancel) { ++ block_job_cancel(bs->job); /* set cancel flag */ ++ } ++ ++ bs->job->co = qemu_coroutine_create(backup_run); ++ qemu_coroutine_enter(bs->job->co, bs->job); ++} ++ ++int ++backup_job_create(BlockDriverState *bs, BackupDumpFunc *backup_dump_cb, ++ BlockDriverCompletionFunc *backup_complete_cb, ++ void *opaque, int64_t speed) ++{ ++ assert(bs); ++ assert(backup_dump_cb); ++ assert(backup_complete_cb); ++ ++ if (bs->job) { ++ DPRINTF("bdrv_backup_init failed - running job on %s\n", ++ bdrv_get_device_name(bs)); ++ return -1; ++ } ++ ++ int64_t bitmap_size; ++ const char *devname = bdrv_get_device_name(bs); ++ ++ if (!devname || !devname[0]) { ++ return -1; ++ } ++ ++ DPRINTF("bdrv_backup_init %s\n", bdrv_get_device_name(bs)); ++ ++ Error *errp; ++ 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 + ++ BACKUP_BLOCKS_PER_CLUSTER * BITS_PER_LONG - 1; ++ bitmap_size /= BACKUP_BLOCKS_PER_CLUSTER * BITS_PER_LONG; ++ ++ job->backup_dump_cb = backup_dump_cb; ++ job->backup_complete_cb = backup_complete_cb; ++ job->opaque = opaque; ++ job->bitmap_size = bitmap_size; ++ job->bitmap = g_new0(unsigned long, bitmap_size); ++ ++ job->common.len = bs->total_sectors*BDRV_SECTOR_SIZE; ++ ++ return 0; ++} +diff --git a/backup.h b/backup.h +new file mode 100644 +index 0000000..9b1ea1c +--- /dev/null ++++ b/backup.h +@@ -0,0 +1,30 @@ ++/* ++ * QEMU backup related definitions ++ * ++ * 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 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_BACKUP_H ++#define QEMU_BACKUP_H ++ ++#define BACKUP_CLUSTER_BITS 16 ++#define BACKUP_CLUSTER_SIZE (1<backing_hd) { + return -ENOTSUP; + } +@@ -1691,6 +1692,22 @@ void bdrv_round_to_clusters(BlockDriverState *bs, + } + } + ++/** ++ * Round a region to job cluster boundaries ++ */ ++static void round_to_job_clusters(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors, ++ int job_cluster_size, ++ int64_t *cluster_sector_num, ++ int *cluster_nb_sectors) ++{ ++ int64_t c = job_cluster_size/BDRV_SECTOR_SIZE; ++ ++ *cluster_sector_num = QEMU_ALIGN_DOWN(sector_num, c); ++ *cluster_nb_sectors = QEMU_ALIGN_UP(sector_num - *cluster_sector_num + ++ nb_sectors, c); ++} ++ + static bool tracked_request_overlaps(BdrvTrackedRequest *req, + int64_t sector_num, int nb_sectors) { + /* aaaa bbbb */ +@@ -1705,7 +1722,9 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req, + } + + static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs, +- int64_t sector_num, int nb_sectors) ++ int64_t sector_num, ++ int nb_sectors, ++ int job_cluster_size) + { + BdrvTrackedRequest *req; + int64_t cluster_sector_num; +@@ -1721,6 +1740,11 @@ static void coroutine_fn wait_for_overlapping_requests(BlockDriverState *bs, + bdrv_round_to_clusters(bs, sector_num, nb_sectors, + &cluster_sector_num, &cluster_nb_sectors); + ++ if (job_cluster_size) { ++ round_to_job_clusters(bs, sector_num, nb_sectors, job_cluster_size, ++ &cluster_sector_num, &cluster_nb_sectors); ++ } ++ + do { + retry = false; + QLIST_FOREACH(req, &bs->tracked_requests, list) { +@@ -2260,12 +2284,24 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs, + bs->copy_on_read_in_flight++; + } + +- if (bs->copy_on_read_in_flight) { +- wait_for_overlapping_requests(bs, sector_num, nb_sectors); ++ int job_cluster_size = bs->job && bs->job->cluster_size ? ++ bs->job->cluster_size : 0; ++ ++ if (bs->copy_on_read_in_flight || job_cluster_size) { ++ wait_for_overlapping_requests(bs, sector_num, nb_sectors, ++ job_cluster_size); + } + + tracked_request_begin(&req, bs, sector_num, nb_sectors, false); + ++ if (bs->job && bs->job->job_type->before_read) { ++ ret = bs->job->job_type->before_read(bs, sector_num, nb_sectors, qiov); ++ if ((ret < 0) || (flags & BDRV_REQ_BACKUP_ONLY)) { ++ /* Note: We do not return any data to the caller */ ++ goto out; ++ } ++ } ++ + if (flags & BDRV_REQ_COPY_ON_READ) { + int pnum; + +@@ -2309,6 +2345,17 @@ int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, + BDRV_REQ_COPY_ON_READ); + } + ++int coroutine_fn bdrv_co_backup(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors) ++{ ++ if (!bs->job) { ++ return -ENOTSUP; ++ } ++ ++ return bdrv_co_do_readv(bs, sector_num, nb_sectors, NULL, ++ BDRV_REQ_BACKUP_ONLY); ++} ++ + static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, + int64_t sector_num, int nb_sectors) + { +@@ -2366,12 +2413,23 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, + bdrv_io_limits_intercept(bs, true, nb_sectors); + } + +- if (bs->copy_on_read_in_flight) { +- wait_for_overlapping_requests(bs, sector_num, nb_sectors); ++ int job_cluster_size = bs->job && bs->job->cluster_size ? ++ bs->job->cluster_size : 0; ++ ++ if (bs->copy_on_read_in_flight || job_cluster_size) { ++ wait_for_overlapping_requests(bs, sector_num, nb_sectors, ++ job_cluster_size); + } + + tracked_request_begin(&req, bs, sector_num, nb_sectors, true); + ++ if (bs->job && bs->job->job_type->before_write) { ++ ret = bs->job->job_type->before_write(bs, sector_num, nb_sectors, qiov); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ + if (flags & BDRV_REQ_ZERO_WRITE) { + ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors); + } else { +@@ -2390,6 +2448,7 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, + bs->wr_highest_sector = sector_num + nb_sectors - 1; + } + ++out: + tracked_request_end(&req); + + return ret; +diff --git a/include/block/block.h b/include/block/block.h +index 5c3b911..b6144be 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -172,6 +172,8 @@ int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); + int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); ++int coroutine_fn bdrv_co_backup(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors); + int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov); + /* +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +index c290d07..6f42495 100644 +--- a/include/block/blockjob.h ++++ b/include/block/blockjob.h +@@ -50,6 +50,13 @@ typedef struct BlockJobType { + * manually. + */ + void (*complete)(BlockJob *job, Error **errp); ++ ++ /** tracked requests */ ++ int coroutine_fn (*before_read)(BlockDriverState *bs, int64_t sector_num, ++ int nb_sectors, QEMUIOVector *qiov); ++ int coroutine_fn (*before_write)(BlockDriverState *bs, int64_t sector_num, ++ int nb_sectors, QEMUIOVector *qiov); ++ + } BlockJobType; + + /** +@@ -103,6 +110,9 @@ struct BlockJob { + /** Speed that was set with @block_job_set_speed. */ + int64_t speed; + ++ /** tracked requests */ ++ int cluster_size; ++ + /** The completion function that will be called when the job completes. */ + BlockDriverCompletionFunc *cb; + +-- +1.7.2.5 + diff --git a/debian/patches/old/0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch b/debian/patches/old/0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch new file mode 100644 index 0000000..72c8d25 --- /dev/null +++ b/debian/patches/old/0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch @@ -0,0 +1,36 @@ +From 59fb70f22143eccdf74639871e862df0c2f570fc Mon Sep 17 00:00:00 2001 +From: Jason Wang +Date: Mon, 30 Nov 2015 15:38:23 +0800 +Subject: [PATCH 2/2] pcnet: fix rx buffer overflow(CVE-2015-7512) + +Backends could provide a packet whose length is greater than buffer +size. Check for this and truncate the packet to avoid rx buffer +overflow in this case. + +Cc: Prasad J Pandit +Cc: qemu-stable@nongnu.org +Signed-off-by: Jason Wang +--- + hw/net/pcnet.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c +index 309c40b..1f4a3db 100644 +--- a/hw/net/pcnet.c ++++ b/hw/net/pcnet.c +@@ -1064,6 +1064,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) + int pktcount = 0; + + if (!s->looptest) { ++ if (size > 4092) { ++#ifdef PCNET_DEBUG_RMD ++ fprintf(stderr, "pcnet: truncates rx packet.\n"); ++#endif ++ size = 4092; ++ } + memcpy(src, buf, size); + /* no need to compute the CRC */ + src[size] = 0; +-- +2.1.4 + diff --git a/debian/patches/old/0003-add-backup-related-monitor-commands.patch b/debian/patches/old/0003-add-backup-related-monitor-commands.patch new file mode 100644 index 0000000..0a23a45 --- /dev/null +++ b/debian/patches/old/0003-add-backup-related-monitor-commands.patch @@ -0,0 +1,826 @@ +From cf9cc8878d3246069da3fdf7ec865b7b983487fe Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 13 Nov 2012 11:27:56 +0100 +Subject: [PATCH v5 3/6] add backup related monitor commands + +We use a generic BackupDriver struct to encapsulate all archive format +related function. + +Another option would be to simply dump to +the output fh (pipe), and an external binary saves the data. That way we +could move the whole archive format related code out of qemu. + +Signed-off-by: Dietmar Maurer +--- + backup.h | 15 ++ + blockdev.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + hmp-commands.hx | 31 ++++ + hmp.c | 63 ++++++++ + hmp.h | 3 + + monitor.c | 7 + + qapi-schema.json | 95 ++++++++++++ + qmp-commands.hx | 27 ++++ + 8 files changed, 664 insertions(+), 0 deletions(-) + +diff --git a/backup.h b/backup.h +index 9b1ea1c..22598a6 100644 +--- a/backup.h ++++ b/backup.h +@@ -14,6 +14,9 @@ + #ifndef QEMU_BACKUP_H + #define QEMU_BACKUP_H + ++#include ++#include "block/block.h" ++ + #define BACKUP_CLUSTER_BITS 16 + #define BACKUP_CLUSTER_SIZE (1<dump); ++ ++ size_t zero_bytes = 0; ++ int bytes = backup_state.driver->dump(backup_state.writer, ++ bcb->dev_id, cluster_num, ++ buf, &zero_bytes); ++ ++ if (bytes > 0) { ++ bcb->transferred += bytes; ++ backup_state.transferred += bytes; ++ if (zero_bytes) { ++ bcb->zero_bytes += bytes; ++ backup_state.zero_bytes += zero_bytes; ++ } ++ } ++ ++ return bytes; ++} ++ ++static void backup_cleanup(void) ++{ ++ if (backup_state.writer && backup_state.driver) { ++ backup_state.end_time = time(NULL); ++ Error *local_err = NULL; ++ backup_state.driver->close(backup_state.writer, &local_err); ++ error_propagate(&backup_state.error, local_err); ++ backup_state.writer = NULL; ++ } ++ ++ if (backup_state.bcb_list) { ++ GList *l = backup_state.bcb_list; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ drive_put_ref_bh_schedule(drive_get_by_blockdev(bcb->bs)); ++ g_free(bcb); ++ } ++ g_list_free(backup_state.bcb_list); ++ backup_state.bcb_list = NULL; ++ } ++} ++ ++static void backup_complete_cb(void *opaque, int ret) ++{ ++ BackupCB *bcb = opaque; ++ ++ assert(backup_state.driver); ++ assert(backup_state.writer); ++ assert(backup_state.driver->complete); ++ assert(backup_state.driver->close); ++ ++ bcb->completed = true; ++ ++ backup_state.driver->complete(backup_state.writer, bcb->dev_id, ret); ++ ++ if (!backup_state.cancel) { ++ backup_run_next_job(); ++ } ++} ++ ++static void backup_cancel(void) ++{ ++ backup_state.cancel = true; ++ ++ if (!backup_state.error) { ++ error_setg(&backup_state.error, "backup cancelled"); ++ } ++ ++ /* drain all i/o (awake jobs waiting for aio) */ ++ bdrv_drain_all(); ++ ++ int job_count = 0; ++ GList *l = backup_state.bcb_list; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ BlockJob *job = bcb->bs->job; ++ if (job) { ++ job_count++; ++ if (!bcb->started) { ++ bcb->started = true; ++ backup_job_start(bcb->bs, true); ++ } ++ if (!bcb->completed) { ++ block_job_cancel_sync(job); ++ } ++ } ++ } ++ ++ backup_cleanup(); ++} ++ ++void qmp_backup_cancel(Error **errp) ++{ ++ backup_cancel(); ++} ++ ++static void backup_run_next_job(void) ++{ ++ GList *l = backup_state.bcb_list; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ ++ if (bcb->bs && bcb->bs->job && !bcb->completed) { ++ if (!bcb->started) { ++ bcb->started = true; ++ bool cancel = backup_state.error || backup_state.cancel; ++ backup_job_start(bcb->bs, cancel); ++ } ++ return; ++ } ++ } ++ ++ backup_cleanup(); ++} ++ ++static void backup_start_jobs(void) ++{ ++ /* create all jobs (one for each device), start first one */ ++ GList *l = backup_state.bcb_list; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ ++ if (backup_job_create(bcb->bs, backup_dump_cb, backup_complete_cb, ++ bcb, backup_state.speed) != 0) { ++ error_setg(&backup_state.error, "backup_job_create failed"); ++ backup_cancel(); ++ return; ++ } ++ } ++ ++ backup_run_next_job(); ++} ++ ++char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, ++ bool has_config_file, const char *config_file, ++ bool has_devlist, const char *devlist, ++ bool has_speed, int64_t speed, Error **errp) ++{ ++ BlockDriverState *bs; ++ Error *local_err = NULL; ++ uuid_t uuid; ++ void *writer = NULL; ++ gchar **devs = NULL; ++ GList *bcblist = NULL; ++ ++ if (backup_state.bcb_list) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "previous backup not finished"); ++ return NULL; ++ } ++ ++ /* Todo: try to auto-detect format based on file name */ ++ format = has_format ? format : BACKUP_FORMAT_VMA; ++ ++ /* fixme: find driver for specifued format */ ++ const BackupDriver *driver = NULL; ++ ++ if (!driver) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); ++ return NULL; ++ } ++ ++ if (has_devlist) { ++ devs = g_strsplit_set(devlist, ",;:", -1); ++ ++ gchar **d = devs; ++ while (d && *d) { ++ bs = bdrv_find(*d); ++ if (bs) { ++ if (bdrv_is_read_only(bs)) { ++ error_set(errp, QERR_DEVICE_IS_READ_ONLY, *d); ++ goto err; ++ } ++ if (!bdrv_is_inserted(bs)) { ++ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d); ++ goto err; ++ } ++ BackupCB *bcb = g_new0(BackupCB, 1); ++ bcb->bs = bs; ++ bcblist = g_list_append(bcblist, bcb); ++ } else { ++ error_set(errp, QERR_DEVICE_NOT_FOUND, *d); ++ goto err; ++ } ++ d++; ++ } ++ ++ } else { ++ ++ bs = NULL; ++ while ((bs = bdrv_next(bs))) { ++ ++ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { ++ continue; ++ } ++ ++ BackupCB *bcb = g_new0(BackupCB, 1); ++ bcb->bs = bs; ++ bcblist = g_list_append(bcblist, bcb); ++ } ++ } ++ ++ if (!bcblist) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list"); ++ goto err; ++ } ++ ++ GList *l = bcblist; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ if (bcb->bs->job) { ++ error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bcb->bs)); ++ goto err; ++ } ++ } ++ ++ uuid_generate(uuid); ++ ++ writer = driver->open(backup_file, uuid, &local_err); ++ if (!writer) { ++ if (error_is_set(&local_err)) { ++ error_propagate(errp, local_err); ++ } ++ goto err; ++ } ++ ++ size_t total = 0; ++ ++ /* register all devices for vma writer */ ++ l = bcblist; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ ++ int64_t size = bdrv_getlength(bcb->bs); ++ const char *devname = bdrv_get_device_name(bcb->bs); ++ bcb->dev_id = driver->register_stream(writer, devname, size); ++ if (bcb->dev_id <= 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "register_stream failed"); ++ goto err; ++ } ++ bcb->size = size; ++ total += size; ++ } ++ ++ /* add configuration file to archive */ ++ if (has_config_file) { ++ char *cdata = NULL; ++ gsize clen = 0; ++ GError *err = NULL; ++ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) { ++ error_setg(errp, "unable to read file '%s'", config_file); ++ goto err; ++ } ++ ++ const char *basename = g_path_get_basename(config_file); ++ if (driver->register_config(writer, basename, cdata, clen) < 0) { ++ error_setg(errp, "register_config failed"); ++ g_free(cdata); ++ goto err; ++ } ++ g_free(cdata); ++ } ++ ++ /* initialize global backup_state now */ ++ ++ backup_state.cancel = false; ++ ++ if (backup_state.error) { ++ error_free(backup_state.error); ++ backup_state.error = NULL; ++ } ++ ++ backup_state.driver = driver; ++ ++ backup_state.speed = (has_speed && speed > 0) ? speed : 0; ++ ++ backup_state.start_time = time(NULL); ++ backup_state.end_time = 0; ++ ++ if (backup_state.backup_file) { ++ g_free(backup_state.backup_file); ++ } ++ backup_state.backup_file = g_strdup(backup_file); ++ ++ backup_state.writer = writer; ++ ++ uuid_copy(backup_state.uuid, uuid); ++ uuid_unparse_lower(uuid, backup_state.uuid_str); ++ ++ backup_state.bcb_list = bcblist; ++ ++ backup_state.total = total; ++ backup_state.transferred = 0; ++ backup_state.zero_bytes = 0; ++ ++ /* Grab a reference so hotplug does not delete the ++ * BlockDriverState from underneath us. ++ */ ++ l = bcblist; ++ while (l) { ++ BackupCB *bcb = l->data; ++ l = g_list_next(l); ++ drive_get_ref(drive_get_by_blockdev(bcb->bs)); ++ } ++ ++ backup_start_jobs(); ++ ++ return g_strdup(backup_state.uuid_str); ++ ++err: ++ ++ l = bcblist; ++ while (l) { ++ g_free(l->data); ++ l = g_list_next(l); ++ } ++ g_list_free(bcblist); ++ ++ if (devs) { ++ g_strfreev(devs); ++ } ++ ++ if (writer) { ++ unlink(backup_file); ++ if (driver) { ++ Error *err = NULL; ++ driver->close(writer, &err); ++ } ++ } ++ ++ return NULL; ++} ++ ++BackupStatus *qmp_query_backup(Error **errp) ++{ ++ BackupStatus *info = g_malloc0(sizeof(*info)); ++ ++ if (!backup_state.start_time) { ++ /* not started, return {} */ ++ return info; ++ } ++ ++ info->has_status = true; ++ info->has_start_time = true; ++ info->start_time = backup_state.start_time; ++ ++ if (backup_state.backup_file) { ++ info->has_backup_file = true; ++ info->backup_file = g_strdup(backup_state.backup_file); ++ } ++ ++ info->has_uuid = true; ++ info->uuid = g_strdup(backup_state.uuid_str); ++ ++ if (backup_state.end_time) { ++ if (backup_state.error) { ++ info->status = g_strdup("error"); ++ info->has_errmsg = true; ++ info->errmsg = g_strdup(error_get_pretty(backup_state.error)); ++ } else { ++ info->status = g_strdup("done"); ++ } ++ info->has_end_time = true; ++ info->end_time = backup_state.end_time; ++ } else { ++ info->status = g_strdup("active"); ++ } ++ ++ info->has_total = true; ++ info->total = backup_state.total; ++ info->has_zero_bytes = true; ++ info->zero_bytes = backup_state.zero_bytes; ++ info->has_transferred = true; ++ info->transferred = backup_state.transferred; ++ ++ return info; ++} ++ + static BlockJob *find_block_job(const char *device) + { + BlockDriverState *bs; +diff --git a/hmp-commands.hx b/hmp-commands.hx +index 64008a9..0f178d8 100644 +--- a/hmp-commands.hx ++++ b/hmp-commands.hx +@@ -83,6 +83,35 @@ STEXI + Copy data from a backing file into a block device. + ETEXI + ++ { ++ .name = "backup", ++ .args_type = "backupfile:s,speed:o?,devlist:s?", ++ .params = "backupfile [speed [devlist]]", ++ .help = "create a VM Backup.", ++ .mhandler.cmd = hmp_backup, ++ }, ++ ++STEXI ++@item backup ++@findex backup ++Create a VM backup. ++ETEXI ++ ++ { ++ .name = "backup_cancel", ++ .args_type = "", ++ .params = "", ++ .help = "cancel the current VM backup", ++ .mhandler.cmd = hmp_backup_cancel, ++ }, ++ ++STEXI ++@item backup_cancel ++@findex backup_cancel ++Cancel the current VM backup. ++ ++ETEXI ++ + { + .name = "block_job_set_speed", + .args_type = "device:B,speed:o", +@@ -1630,6 +1659,8 @@ show CPU statistics + show user network stack connection states + @item info migrate + show migration status ++@item info backup ++show backup status + @item info migrate_capabilities + show current migration capabilities + @item info migrate_cache_size +diff --git a/hmp.c b/hmp.c +index 2f47a8a..b2c1f23 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -131,6 +131,38 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict) + qapi_free_MouseInfoList(mice_list); + } + ++void hmp_info_backup(Monitor *mon, const QDict *qdict) ++{ ++ BackupStatus *info; ++ ++ info = qmp_query_backup(NULL); ++ if (info->has_status) { ++ if (info->has_errmsg) { ++ monitor_printf(mon, "Backup status: %s - %s\n", ++ info->status, info->errmsg); ++ } else { ++ monitor_printf(mon, "Backup status: %s\n", info->status); ++ } ++ } ++ if (info->has_backup_file) { ++ int per = (info->has_total && info->total && ++ info->has_transferred && info->transferred) ? ++ (info->transferred * 100)/info->total : 0; ++ int zero_per = (info->has_total && info->total && ++ info->has_zero_bytes && info->zero_bytes) ? ++ (info->zero_bytes * 100)/info->total : 0; ++ monitor_printf(mon, "Backup file: %s\n", info->backup_file); ++ monitor_printf(mon, "Backup uuid: %s\n", info->uuid); ++ monitor_printf(mon, "Total size: %zd\n", info->total); ++ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n", ++ info->transferred, per); ++ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n", ++ info->zero_bytes, zero_per); ++ } ++ ++ qapi_free_BackupStatus(info); ++} ++ + void hmp_info_migrate(Monitor *mon, const QDict *qdict) + { + MigrationInfo *info; +@@ -998,6 +1030,37 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &error); + } + ++void hmp_backup_cancel(Monitor *mon, const QDict *qdict) ++{ ++ Error *errp = NULL; ++ ++ qmp_backup_cancel(&errp); ++ ++ if (error_is_set(&errp)) { ++ monitor_printf(mon, "%s\n", error_get_pretty(errp)); ++ error_free(errp); ++ return; ++ } ++} ++ ++void hmp_backup(Monitor *mon, const QDict *qdict) ++{ ++ const char *backup_file = qdict_get_str(qdict, "backup-file"); ++ const char *devlist = qdict_get_try_str(qdict, "devlist"); ++ int64_t speed = qdict_get_try_int(qdict, "speed", 0); ++ ++ Error *errp = NULL; ++ ++ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, ++ devlist, qdict_haskey(qdict, "speed"), speed, &errp); ++ ++ if (error_is_set(&errp)) { ++ monitor_printf(mon, "%s\n", error_get_pretty(errp)); ++ error_free(errp); ++ return; ++ } ++} ++ + void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) + { + Error *error = NULL; +diff --git a/hmp.h b/hmp.h +index 30b3c20..ad4cf80 100644 +--- a/hmp.h ++++ b/hmp.h +@@ -28,6 +28,7 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict); + void hmp_info_migrate(Monitor *mon, const QDict *qdict); + void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); + void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict); ++void hmp_info_backup(Monitor *mon, const QDict *qdict); + void hmp_info_cpus(Monitor *mon, const QDict *qdict); + void hmp_info_block(Monitor *mon, const QDict *qdict); + void hmp_info_blockstats(Monitor *mon, const QDict *qdict); +@@ -65,6 +66,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict); + void hmp_change(Monitor *mon, const QDict *qdict); + void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); + void hmp_block_stream(Monitor *mon, const QDict *qdict); ++void hmp_backup(Monitor *mon, const QDict *qdict); ++void hmp_backup_cancel(Monitor *mon, const QDict *qdict); + void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); + void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); + void hmp_block_job_pause(Monitor *mon, const QDict *qdict); +diff --git a/monitor.c b/monitor.c +index 6a0f257..e4a810c 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -2666,6 +2666,13 @@ static mon_cmd_t info_cmds[] = { + }, + #endif + { ++ .name = "backup", ++ .args_type = "", ++ .params = "", ++ .help = "show backup status", ++ .mhandler.cmd = hmp_info_backup, ++ }, ++ { + .name = "migrate", + .args_type = "", + .params = "", +diff --git a/qapi-schema.json b/qapi-schema.json +index 7275b5d..09ca8ef 100644 +--- a/qapi-schema.json ++++ b/qapi-schema.json +@@ -425,6 +425,39 @@ + { 'type': 'EventInfo', 'data': {'name': 'str'} } + + ## ++# @BackupStatus: ++# ++# Detailed backup status. ++# ++# @status: #optional string describing the current backup status. ++# This can be 'active', 'done', 'error'. If this field is not ++# returned, no backup process has been initiated ++# ++# @errmsg: #optional error message (only returned if status is 'error') ++# ++# @total: #optional total amount of bytes involved in the backup process ++# ++# @transferred: #optional amount of bytes already backed up. ++# ++# @zero-bytes: #optional amount of 'zero' bytes detected. ++# ++# @start-time: #optional time (epoch) when backup job started. ++# ++# @end-time: #optional time (epoch) when backup job finished. ++# ++# @backupfile: #optional backup file name ++# ++# @uuid: #optional uuid for this backup job ++# ++# Since: 1.5.0 ++## ++{ 'type': 'BackupStatus', ++ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', ++ '*transferred': 'int', '*zero-bytes': 'int', ++ '*start-time': 'int', '*end-time': 'int', ++ '*backup-file': 'str', '*uuid': 'str' } } ++ ++## + # @query-events: + # + # Return a list of supported QMP events by this server +@@ -1824,6 +1857,68 @@ + 'data': { 'path': 'str' }, + 'returns': [ 'ObjectPropertyInfo' ] } + ++ ++## ++# @BackupFormat ++# ++# An enumeration of supported backup formats. ++# ++# @vma: Proxmox vma backup format ++## ++{ 'enum': 'BackupFormat', ++ 'data': [ 'vma' ] } ++ ++## ++# @backup: ++# ++# Starts a VM backup. ++# ++# @backup-file: the backup file name ++# ++# @format: format of the backup file ++# ++# @config-filename: #optional name of a configuration file to include into ++# the backup archive. ++# ++# @speed: #optional the maximum speed, in bytes per second ++# ++# @devlist: #optional list of block device names (separated by ',', ';' ++# or ':'). By default the backup includes all writable block devices. ++# ++# Returns: the uuid of the backup job ++# ++# Since: 1.5.0 ++## ++{ 'command': 'backup', 'data': { 'backup-file': 'str', ++ '*format': 'BackupFormat', ++ '*config-file': 'str', ++ '*devlist': 'str', '*speed': 'int' }, ++ 'returns': 'str' } ++ ++## ++# @query-backup ++# ++# Returns information about current/last backup task. ++# ++# Returns: @BackupStatus ++# ++# Since: 1.5.0 ++## ++{ 'command': 'query-backup', 'returns': 'BackupStatus' } ++ ++## ++# @backup-cancel ++# ++# Cancel the current executing backup process. ++# ++# Returns: nothing on success ++# ++# Notes: This command succeeds even if there is no backup process running. ++# ++# Since: 1.5.0 ++## ++{ 'command': 'backup-cancel' } ++ + ## + # @qom-get: + # +diff --git a/qmp-commands.hx b/qmp-commands.hx +index 799adea..17e381b 100644 +--- a/qmp-commands.hx ++++ b/qmp-commands.hx +@@ -889,6 +889,18 @@ EQMP + }, + + { ++ .name = "backup", ++ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?", ++ .mhandler.cmd_new = qmp_marshal_input_backup, ++ }, ++ ++ { ++ .name = "backup-cancel", ++ .args_type = "", ++ .mhandler.cmd_new = qmp_marshal_input_backup_cancel, ++ }, ++ ++ { + .name = "block-job-set-speed", + .args_type = "device:B,speed:o", + .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed, +@@ -2566,6 +2578,21 @@ EQMP + }, + + SQMP ++ ++query-backup ++------------- ++ ++Backup status. ++ ++EQMP ++ ++ { ++ .name = "query-backup", ++ .args_type = "", ++ .mhandler.cmd_new = qmp_marshal_input_query_backup, ++ }, ++ ++SQMP + migrate-set-capabilities + ------- + +-- +1.7.2.5 + diff --git a/debian/patches/old/0004-introduce-new-vma-archive-format.patch b/debian/patches/old/0004-introduce-new-vma-archive-format.patch new file mode 100644 index 0000000..4af6d62 --- /dev/null +++ b/debian/patches/old/0004-introduce-new-vma-archive-format.patch @@ -0,0 +1,2586 @@ +From 6289a43696ca6f713a5d3bb9f95a5adb608a5e13 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 13 Nov 2012 11:11:38 +0100 +Subject: [PATCH v5 4/6] introduce new vma archive format + +This is a very simple archive format, see docs/specs/vma_spec.txt + +Signed-off-by: Dietmar Maurer +--- + Makefile | 3 +- + Makefile.objs | 2 +- + blockdev.c | 6 +- + docs/specs/vma_spec.txt | 24 ++ + vma-reader.c | 799 ++++++++++++++++++++++++++++++++++++++++ + vma-writer.c | 940 +++++++++++++++++++++++++++++++++++++++++++++++ + vma.c | 561 ++++++++++++++++++++++++++++ + vma.h | 145 ++++++++ + 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 + create mode 100644 vma.c + create mode 100644 vma.h + +diff --git a/Makefile b/Makefile +index 0d9099a..16f1c25 100644 +--- a/Makefile ++++ b/Makefile +@@ -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 +@@ -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 df64f70..91f133b 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -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 += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o + block-obj-y += qemu-coroutine-sleep.o +diff --git a/blockdev.c b/blockdev.c +index 84f598d..683f7da 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -21,6 +21,7 @@ + #include "trace.h" + #include "sysemu/arch_init.h" + #include "backup.h" ++#include "vma.h" + + static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); + +@@ -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 : BACKUP_FORMAT_VMA; + +- /* fixme: find driver for specifued format */ + const BackupDriver *driver = NULL; + +- if (!driver) { ++ if (format == BACKUP_FORMAT_VMA) { ++ driver = &backup_vma_driver; ++ } else { + 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..9b715f2 +--- /dev/null ++++ b/docs/specs/vma_spec.txt +@@ -0,0 +1,24 @@ ++=Virtual Machine Archive format (VMA)= ++ ++This format contains a header which includes the VM configuration as ++binary blobs, and a list of devices (dev_id, name). ++ ++The actual VM image data is stored inside extents. An extent contains ++up to 64 clusters, and start with a 512 byte header containing ++additional information for those clusters. ++ ++We use a cluster size of 65536, and use 8 bytes for each ++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 ++ ++We only store non-zero blocks (such block is 4096 bytes). ++ ++Each archive is marked with a uuid. The archive header and all ++extent headers includes that uuid and a MD5 checksum (over header ++data). ++ ++ +diff --git a/vma-reader.c b/vma-reader.c +new file mode 100644 +index 0000000..bc36cba +--- /dev/null ++++ b/vma-reader.c +@@ -0,0 +1,799 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) 2012 Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "qemu/timer.h" ++#include "qemu/ratelimit.h" ++#include "vma.h" ++#include "block/block.h" ++ ++#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT) ++ ++static unsigned char zero_vma_block[VMA_BLOCK_SIZE]; ++ ++typedef struct VmaRestoreState { ++ BlockDriverState *bs; ++ bool write_zeroes; ++ unsigned long *bitmap; ++ int bitmap_size; ++} VmaRestoreState; ++ ++struct VmaReader { ++ int fd; ++ GChecksum *md5csum; ++ GHashTable *blob_hash; ++ unsigned char *head_data; ++ VmaDeviceInfo devinfo[256]; ++ VmaRestoreState rstate[256]; ++ 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 ++g_int32_hash(gconstpointer v) ++{ ++ return *(const uint32_t *)v; ++} ++ ++static gboolean ++g_int32_equal(gconstpointer v1, gconstpointer v2) ++{ ++ return *((const uint32_t *)v1) == *((const uint32_t *)v2); ++} ++ ++static int vma_reader_get_bitmap(VmaRestoreState *rstate, int64_t cluster_num) ++{ ++ assert(rstate); ++ assert(rstate->bitmap); ++ ++ unsigned long val, idx, bit; ++ ++ idx = cluster_num / BITS_PER_LONG; ++ ++ assert(rstate->bitmap_size > idx); ++ ++ bit = cluster_num % BITS_PER_LONG; ++ val = rstate->bitmap[idx]; ++ ++ return !!(val & (1UL << bit)); ++} ++ ++static void vma_reader_set_bitmap(VmaRestoreState *rstate, int64_t cluster_num, ++ int dirty) ++{ ++ assert(rstate); ++ assert(rstate->bitmap); ++ ++ unsigned long val, idx, bit; ++ ++ idx = cluster_num / BITS_PER_LONG; ++ ++ assert(rstate->bitmap_size > idx); ++ ++ bit = cluster_num % BITS_PER_LONG; ++ val = rstate->bitmap[idx]; ++ if (dirty) { ++ if (!(val & (1UL << bit))) { ++ val |= 1UL << bit; ++ } ++ } else { ++ if (val & (1UL << bit)) { ++ val &= ~(1UL << bit); ++ } ++ } ++ rstate->bitmap[idx] = val; ++} ++ ++typedef struct VmaBlob { ++ uint32_t start; ++ uint32_t len; ++ void *data; ++} VmaBlob; ++ ++static const VmaBlob *get_header_blob(VmaReader *vmar, uint32_t pos) ++{ ++ assert(vmar); ++ assert(vmar->blob_hash); ++ ++ return g_hash_table_lookup(vmar->blob_hash, &pos); ++} ++ ++static const char *get_header_str(VmaReader *vmar, uint32_t pos) ++{ ++ const VmaBlob *blob = get_header_blob(vmar, pos); ++ if (!blob) { ++ return NULL; ++ } ++ const char *res = (char *)blob->data; ++ if (res[blob->len-1] != '\0') { ++ return NULL; ++ } ++ return res; ++} ++ ++static ssize_t ++safe_read(int fd, unsigned char *buf, size_t count) ++{ ++ ssize_t n; ++ ++ do { ++ n = read(fd, buf, count); ++ } while (n < 0 && errno == EINTR); ++ ++ return n; ++} ++ ++static ssize_t ++full_read(int fd, unsigned char *buf, size_t len) ++{ ++ ssize_t n; ++ size_t total; ++ ++ total = 0; ++ ++ while (len > 0) { ++ n = safe_read(fd, buf, len); ++ ++ if (n == 0) { ++ return total; ++ } ++ ++ if (n <= 0) { ++ break; ++ } ++ ++ buf += n; ++ total += n; ++ len -= n; ++ } ++ ++ if (len) { ++ return -1; ++ } ++ ++ return total; ++} ++ ++void vma_reader_destroy(VmaReader *vmar) ++{ ++ assert(vmar); ++ ++ if (vmar->fd >= 0) { ++ close(vmar->fd); ++ } ++ ++ if (vmar->cdata_list) { ++ g_list_free(vmar->cdata_list); ++ } ++ ++ int i; ++ for (i = 1; i < 256; i++) { ++ if (vmar->rstate[i].bitmap) { ++ g_free(vmar->rstate[i].bitmap); ++ } ++ } ++ ++ if (vmar->md5csum) { ++ g_checksum_free(vmar->md5csum); ++ } ++ ++ if (vmar->blob_hash) { ++ g_hash_table_destroy(vmar->blob_hash); ++ } ++ ++ if (vmar->head_data) { ++ g_free(vmar->head_data); ++ } ++ ++ g_free(vmar); ++ ++}; ++ ++static int vma_reader_read_head(VmaReader *vmar, Error **errp) ++{ ++ assert(vmar); ++ assert(errp); ++ assert(*errp == NULL); ++ ++ unsigned char md5sum[16]; ++ int i; ++ int ret = 0; ++ ++ vmar->head_data = g_malloc(sizeof(VmaHeader)); ++ ++ if (full_read(vmar->fd, vmar->head_data, sizeof(VmaHeader)) != ++ sizeof(VmaHeader)) { ++ error_setg(errp, "can't read vma header - %s", ++ errno ? g_strerror(errno) : "got EOF"); ++ return -1; ++ } ++ ++ VmaHeader *h = (VmaHeader *)vmar->head_data; ++ ++ if (h->magic != VMA_MAGIC) { ++ error_setg(errp, "not a vma file - wrong magic number"); ++ return -1; ++ } ++ ++ uint32_t header_size = GUINT32_FROM_BE(h->header_size); ++ int need = header_size - sizeof(VmaHeader); ++ if (need <= 0) { ++ error_setg(errp, "wrong vma header size %d", header_size); ++ return -1; ++ } ++ ++ vmar->head_data = g_realloc(vmar->head_data, header_size); ++ h = (VmaHeader *)vmar->head_data; ++ ++ if (full_read(vmar->fd, vmar->head_data + sizeof(VmaHeader), need) != ++ need) { ++ error_setg(errp, "can't read vma header data - %s", ++ errno ? g_strerror(errno) : "got EOF"); ++ return -1; ++ } ++ ++ memcpy(md5sum, h->md5sum, 16); ++ memset(h->md5sum, 0, 16); ++ ++ g_checksum_reset(vmar->md5csum); ++ g_checksum_update(vmar->md5csum, vmar->head_data, header_size); ++ gsize csize = 16; ++ g_checksum_get_digest(vmar->md5csum, (guint8 *)(h->md5sum), &csize); ++ ++ if (memcmp(md5sum, h->md5sum, 16) != 0) { ++ error_setg(errp, "wrong vma header chechsum"); ++ return -1; ++ } ++ ++ /* we can modify header data after checksum verify */ ++ h->header_size = header_size; ++ ++ h->version = GUINT32_FROM_BE(h->version); ++ if (h->version != 1) { ++ error_setg(errp, "wrong vma version %d", h->version); ++ return -1; ++ } ++ ++ h->ctime = GUINT64_FROM_BE(h->ctime); ++ h->blob_buffer_offset = GUINT32_FROM_BE(h->blob_buffer_offset); ++ h->blob_buffer_size = GUINT32_FROM_BE(h->blob_buffer_size); ++ ++ uint32_t bstart = h->blob_buffer_offset + 1; ++ uint32_t bend = h->blob_buffer_offset + h->blob_buffer_size; ++ ++ if (bstart <= sizeof(VmaHeader)) { ++ error_setg(errp, "wrong vma blob buffer offset %d", ++ h->blob_buffer_offset); ++ return -1; ++ } ++ ++ if (bend > header_size) { ++ error_setg(errp, "wrong vma blob buffer size %d/%d", ++ h->blob_buffer_offset, h->blob_buffer_size); ++ return -1; ++ } ++ ++ while ((bstart + 2) <= bend) { ++ uint32_t size = vmar->head_data[bstart] + ++ (vmar->head_data[bstart+1] << 8); ++ if ((bstart + size + 2) <= bend) { ++ VmaBlob *blob = g_new0(VmaBlob, 1); ++ blob->start = bstart - h->blob_buffer_offset; ++ blob->len = size; ++ blob->data = vmar->head_data + bstart + 2; ++ g_hash_table_insert(vmar->blob_hash, &blob->start, blob); ++ } ++ bstart += size + 2; ++ } ++ ++ ++ int count = 0; ++ for (i = 1; i < 256; i++) { ++ VmaDeviceInfoHeader *dih = &h->dev_info[i]; ++ uint32_t devname_ptr = GUINT32_FROM_BE(dih->devname_ptr); ++ uint64_t size = GUINT64_FROM_BE(dih->size); ++ const char *devname = get_header_str(vmar, devname_ptr); ++ ++ if (size && devname) { ++ count++; ++ vmar->devinfo[i].size = size; ++ vmar->devinfo[i].devname = devname; ++ ++ if (strcmp(devname, "vmstate") == 0) { ++ vmar->vmstate_stream = i; ++ } ++ } ++ } ++ ++ if (!count) { ++ error_setg(errp, "vma does not contain data"); ++ return -1; ++ } ++ ++ for (i = 0; i < VMA_MAX_CONFIGS; i++) { ++ uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]); ++ uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]); ++ ++ if (!(name_ptr && data_ptr)) { ++ continue; ++ } ++ const char *name = get_header_str(vmar, name_ptr); ++ const VmaBlob *blob = get_header_blob(vmar, data_ptr); ++ ++ if (!(name && blob)) { ++ error_setg(errp, "vma contains invalid data pointers"); ++ return -1; ++ } ++ ++ VmaConfigData *cdata = g_new0(VmaConfigData, 1); ++ cdata->name = name; ++ cdata->data = blob->data; ++ cdata->len = blob->len; ++ ++ vmar->cdata_list = g_list_append(vmar->cdata_list, cdata); ++ } ++ ++ return ret; ++}; ++ ++VmaReader *vma_reader_create(const char *filename, Error **errp) ++{ ++ assert(filename); ++ assert(errp); ++ ++ VmaReader *vmar = g_new0(VmaReader, 1); ++ ++ 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, ++ g_strerror(errno)); ++ goto err; ++ } ++ ++ vmar->md5csum = g_checksum_new(G_CHECKSUM_MD5); ++ if (!vmar->md5csum) { ++ error_setg(errp, "can't allocate cmsum\n"); ++ goto err; ++ } ++ ++ vmar->blob_hash = g_hash_table_new_full(g_int32_hash, g_int32_equal, ++ NULL, g_free); ++ ++ if (vma_reader_read_head(vmar, errp) < 0) { ++ goto err; ++ } ++ ++ return vmar; ++ ++err: ++ if (vmar) { ++ vma_reader_destroy(vmar); ++ } ++ ++ return NULL; ++} ++ ++VmaHeader *vma_reader_get_header(VmaReader *vmar) ++{ ++ assert(vmar); ++ assert(vmar->head_data); ++ ++ return (VmaHeader *)(vmar->head_data); ++} ++ ++GList *vma_reader_get_config_data(VmaReader *vmar) ++{ ++ assert(vmar); ++ assert(vmar->head_data); ++ ++ return vmar->cdata_list; ++} ++ ++VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id) ++{ ++ assert(vmar); ++ assert(dev_id); ++ ++ if (vmar->devinfo[dev_id].size && vmar->devinfo[dev_id].devname) { ++ return &vmar->devinfo[dev_id]; ++ } ++ ++ return NULL; ++} ++ ++int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, ++ bool write_zeroes, Error **errp) ++{ ++ assert(vmar); ++ assert(bs != NULL); ++ assert(dev_id); ++ assert(vmar->rstate[dev_id].bs == NULL); ++ ++ int64_t size = bdrv_getlength(bs); ++ if (size != vmar->devinfo[dev_id].size) { ++ error_setg(errp, "vma_reader_register_bs for stream %s failed - " ++ "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, ++ size, vmar->devinfo[dev_id].size); ++ return -1; ++ } ++ ++ vmar->rstate[dev_id].bs = bs; ++ vmar->rstate[dev_id].write_zeroes = write_zeroes; ++ ++ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + ++ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; ++ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; ++ ++ 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; ++} ++ ++static ssize_t safe_write(int fd, void *buf, size_t count) ++{ ++ ssize_t n; ++ ++ do { ++ n = write(fd, buf, count); ++ } while (n < 0 && errno == EINTR); ++ ++ return n; ++} ++ ++static size_t full_write(int fd, void *buf, size_t len) ++{ ++ ssize_t n; ++ size_t total; ++ ++ total = 0; ++ ++ while (len > 0) { ++ n = safe_write(fd, buf, len); ++ if (n < 0) { ++ return n; ++ } ++ buf += n; ++ total += n; ++ len -= n; ++ } ++ ++ if (len) { ++ /* incomplete write ? */ ++ return -1; ++ } ++ ++ return total; ++} ++ ++static int restore_write_data(VmaReader *vmar, guint8 dev_id, ++ BlockDriverState *bs, int vmstate_fd, ++ unsigned char *buf, int64_t sector_num, ++ int nb_sectors, Error **errp) ++{ ++ assert(vmar); ++ ++ if (dev_id == vmar->vmstate_stream) { ++ if (vmstate_fd >= 0) { ++ int len = nb_sectors * BDRV_SECTOR_SIZE; ++ int res = full_write(vmstate_fd, buf, len); ++ if (res < 0) { ++ error_setg(errp, "write vmstate failed %d", res); ++ return -1; ++ } ++ } ++ } else { ++ int res = bdrv_write(bs, sector_num, buf, nb_sectors); ++ if (res < 0) { ++ error_setg(errp, "bdrv_write to %s failed (%d)", ++ bdrv_get_device_name(bs), res); ++ return -1; ++ } ++ } ++ return 0; ++} ++static int restore_extent(VmaReader *vmar, unsigned char *buf, ++ int extent_size, int vmstate_fd, ++ bool verbose, Error **errp) ++{ ++ assert(vmar); ++ assert(buf); ++ ++ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; ++ int start = VMA_EXTENT_HEADER_SIZE; ++ int i; ++ ++ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { ++ uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]); ++ 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; ++ ++ if (!dev_id) { ++ continue; ++ } ++ ++ VmaRestoreState *rstate = &vmar->rstate[dev_id]; ++ BlockDriverState *bs = NULL; ++ ++ if (dev_id != vmar->vmstate_stream) { ++ bs = rstate->bs; ++ if (!bs) { ++ error_setg(errp, "got wrong dev id %d", dev_id); ++ return -1; ++ } ++ ++ if (vma_reader_get_bitmap(rstate, cluster_num)) { ++ error_setg(errp, "found duplicated cluster %zd for stream %s", ++ cluster_num, vmar->devinfo[dev_id].devname); ++ return -1; ++ } ++ vma_reader_set_bitmap(rstate, cluster_num, 1); ++ ++ max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE; ++ } else { ++ max_sector = G_MAXINT64; ++ if (cluster_num != vmar->vmstate_clusters) { ++ error_setg(errp, "found out of order vmstate data"); ++ return -1; ++ } ++ 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) { ++ error_setg(errp, "short vma extent - too many blocks"); ++ return -1; ++ } ++ int64_t sector_num = (cluster_num * VMA_CLUSTER_SIZE) / ++ BDRV_SECTOR_SIZE; ++ int64_t end_sector = sector_num + ++ VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE; ++ ++ if (end_sector > max_sector) { ++ end_sector = max_sector; ++ } ++ ++ if (end_sector <= sector_num) { ++ error_setg(errp, "got wrong block address - write bejond end"); ++ return -1; ++ } ++ ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, ++ sector_num, nb_sectors, errp) < 0) { ++ return -1; ++ } ++ ++ start += VMA_CLUSTER_SIZE; ++ } else { ++ int j; ++ int bit = 1; ++ ++ for (j = 0; j < 16; j++) { ++ int64_t sector_num = (cluster_num*VMA_CLUSTER_SIZE + ++ j*VMA_BLOCK_SIZE)/BDRV_SECTOR_SIZE; ++ ++ int64_t end_sector = sector_num + ++ VMA_BLOCK_SIZE/BDRV_SECTOR_SIZE; ++ if (end_sector > max_sector) { ++ end_sector = max_sector; ++ } ++ ++ if (mask & bit) { ++ if ((start + VMA_BLOCK_SIZE) > extent_size) { ++ error_setg(errp, "short vma extent - too many blocks"); ++ return -1; ++ } ++ ++ if (end_sector <= sector_num) { ++ error_setg(errp, "got wrong block address - " ++ "write bejond end"); ++ return -1; ++ } ++ ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ buf + start, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } ++ ++ start += VMA_BLOCK_SIZE; ++ ++ } else { ++ ++ if (rstate->write_zeroes && (end_sector > sector_num)) { ++ /* Todo: use bdrv_co_write_zeroes (but that need to ++ * be run inside coroutine?) ++ */ ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ zero_vma_block, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } ++ } ++ } ++ ++ bit = bit << 1; ++ } ++ } ++ } ++ ++ if (start != extent_size) { ++ error_setg(errp, "vma extent error - missing blocks"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, ++ Error **errp) ++{ ++ assert(vmar); ++ assert(vmar->head_data); ++ ++ int ret = 0; ++ unsigned char buf[VMA_MAX_EXTENT_SIZE]; ++ int buf_pos = 0; ++ 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); ++ if (bytes < 0) { ++ error_setg(errp, "read failed - %s", g_strerror(errno)); ++ return -1; ++ } ++ ++ buf_pos += bytes; ++ ++ if (!buf_pos) { ++ break; /* EOF */ ++ } ++ ++ if (buf_pos < VMA_EXTENT_HEADER_SIZE) { ++ error_setg(errp, "read short extent (%d bytes)", buf_pos); ++ return -1; ++ } ++ ++ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; ++ ++ /* extract md5sum */ ++ memcpy(md5sum, ehead->md5sum, sizeof(ehead->md5sum)); ++ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); ++ ++ g_checksum_reset(vmar->md5csum); ++ g_checksum_update(vmar->md5csum, buf, VMA_EXTENT_HEADER_SIZE); ++ gsize csize = 16; ++ g_checksum_get_digest(vmar->md5csum, ehead->md5sum, &csize); ++ ++ if (memcmp(md5sum, ehead->md5sum, 16) != 0) { ++ error_setg(errp, "wrong vma extent header chechsum"); ++ return -1; ++ } ++ ++ if (memcmp(h->uuid, ehead->uuid, sizeof(ehead->uuid)) != 0) { ++ error_setg(errp, "wrong vma extent uuid"); ++ return -1; ++ } ++ ++ if (ehead->magic != VMA_EXTENT_MAGIC || ehead->reserved1 != 0) { ++ error_setg(errp, "wrong vma extent header magic"); ++ return -1; ++ } ++ ++ int block_count = GUINT16_FROM_BE(ehead->block_count); ++ int extent_size = VMA_EXTENT_HEADER_SIZE + block_count*VMA_BLOCK_SIZE; ++ ++ if (buf_pos < extent_size) { ++ error_setg(errp, "short vma extent (%d < %d)", buf_pos, ++ extent_size); ++ return -1; ++ } ++ ++ if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, ++ errp) < 0) { ++ return -1; ++ } ++ ++ if (buf_pos > extent_size) { ++ memmove(buf, buf + extent_size, buf_pos - extent_size); ++ buf_pos = buf_pos - extent_size; ++ } else { ++ buf_pos = 0; ++ } ++ } ++ ++ bdrv_drain_all(); ++ ++ int i; ++ for (i = 1; i < 256; i++) { ++ VmaRestoreState *rstate = &vmar->rstate[i]; ++ if (!rstate->bs) { ++ continue; ++ } ++ ++ if (bdrv_flush(rstate->bs) < 0) { ++ error_setg(errp, "vma bdrv_flush %s failed", ++ vmar->devinfo[i].devname); ++ return -1; ++ } ++ ++ if (vmar->devinfo[i].size && ++ (strcmp(vmar->devinfo[i].devname, "vmstate") != 0)) { ++ assert(rstate->bitmap); ++ ++ int64_t cluster_num, end; ++ ++ end = (vmar->devinfo[i].size + VMA_CLUSTER_SIZE - 1) / ++ VMA_CLUSTER_SIZE; ++ ++ for (cluster_num = 0; cluster_num < end; cluster_num++) { ++ if (!vma_reader_get_bitmap(rstate, cluster_num)) { ++ error_setg(errp, "detected missing cluster %zd " ++ "for stream %s", cluster_num, ++ vmar->devinfo[i].devname); ++ return -1; ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ +diff --git a/vma-writer.c b/vma-writer.c +new file mode 100644 +index 0000000..9228ca6 +--- /dev/null ++++ b/vma-writer.c +@@ -0,0 +1,940 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) 2012 Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "vma.h" ++#include "block/block.h" ++#include "monitor/monitor.h" ++ ++#define DEBUG_VMA 0 ++ ++#define DPRINTF(fmt, ...)\ ++ do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0) ++ ++#define WRITE_BUFFERS 5 ++ ++typedef struct VmaAIOCB VmaAIOCB; ++struct VmaAIOCB { ++ unsigned char buffer[VMA_MAX_EXTENT_SIZE]; ++ VmaWriter *vmaw; ++ size_t bytes; ++ Coroutine *co; ++}; ++ ++struct VmaWriter { ++ int fd; ++ FILE *cmd; ++ int status; ++ char errmsg[8192]; ++ uuid_t uuid; ++ bool header_written; ++ bool closed; ++ ++ /* we always write extents */ ++ unsigned char outbuf[VMA_MAX_EXTENT_SIZE]; ++ int outbuf_pos; /* in bytes */ ++ int outbuf_count; /* in VMA_BLOCKS */ ++ uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; ++ ++ VmaAIOCB *aiocbs[WRITE_BUFFERS]; ++ CoQueue wqueue; ++ ++ GChecksum *md5csum; ++ CoMutex writer_lock; ++ CoMutex flush_lock; ++ Coroutine *co_writer; ++ ++ /* drive informations */ ++ VmaStreamInfo stream_info[256]; ++ guint stream_count; ++ ++ guint8 vmstate_stream; ++ uint32_t vmstate_clusters; ++ ++ /* header blob table */ ++ char *header_blob_table; ++ uint32_t header_blob_table_size; ++ uint32_t header_blob_table_pos; ++ ++ /* store for config blobs */ ++ uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ ++ uint32_t config_data[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ ++ uint32_t config_count; ++}; ++ ++void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ if (vmaw->status < 0) { ++ return; ++ } ++ ++ vmaw->status = -1; ++ ++ va_start(ap, fmt); ++ g_vsnprintf(vmaw->errmsg, sizeof(vmaw->errmsg), fmt, ap); ++ va_end(ap); ++ ++ DPRINTF("vma_writer_set_error: %s\n", vmaw->errmsg); ++} ++ ++static uint32_t allocate_header_blob(VmaWriter *vmaw, const char *data, ++ size_t len) ++{ ++ if (len > 65535) { ++ return 0; ++ } ++ ++ if (!vmaw->header_blob_table || ++ (vmaw->header_blob_table_size < ++ (vmaw->header_blob_table_pos + len + 2))) { ++ int newsize = vmaw->header_blob_table_size + ((len + 2 + 511)/512)*512; ++ ++ vmaw->header_blob_table = g_realloc(vmaw->header_blob_table, newsize); ++ memset(vmaw->header_blob_table + vmaw->header_blob_table_size, ++ 0, newsize - vmaw->header_blob_table_size); ++ vmaw->header_blob_table_size = newsize; ++ } ++ ++ uint32_t cpos = vmaw->header_blob_table_pos; ++ vmaw->header_blob_table[cpos] = len & 255; ++ vmaw->header_blob_table[cpos+1] = (len >> 8) & 255; ++ memcpy(vmaw->header_blob_table + cpos + 2, data, len); ++ vmaw->header_blob_table_pos += len + 2; ++ return cpos; ++} ++ ++static uint32_t allocate_header_string(VmaWriter *vmaw, const char *str) ++{ ++ assert(vmaw); ++ ++ size_t len = strlen(str) + 1; ++ ++ return allocate_header_blob(vmaw, str, len); ++} ++ ++int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, ++ gsize len) ++{ ++ assert(vmaw); ++ assert(!vmaw->header_written); ++ assert(vmaw->config_count < VMA_MAX_CONFIGS); ++ assert(name); ++ assert(data); ++ assert(len); ++ ++ uint32_t name_ptr = allocate_header_string(vmaw, name); ++ if (!name_ptr) { ++ return -1; ++ } ++ ++ uint32_t data_ptr = allocate_header_blob(vmaw, data, len); ++ if (!data_ptr) { ++ return -1; ++ } ++ ++ vmaw->config_names[vmaw->config_count] = name_ptr; ++ vmaw->config_data[vmaw->config_count] = data_ptr; ++ ++ vmaw->config_count++; ++ ++ return 0; ++} ++ ++int vma_writer_register_stream(VmaWriter *vmaw, const char *devname, ++ size_t size) ++{ ++ assert(vmaw); ++ assert(devname); ++ assert(!vmaw->status); ++ ++ if (vmaw->header_written) { ++ vma_writer_set_error(vmaw, "vma_writer_register_stream: header " ++ "already written"); ++ return -1; ++ } ++ ++ guint n = vmaw->stream_count + 1; ++ ++ /* we can have dev_ids form 1 to 255 (0 reserved) ++ * 255(-1) reseverd for safety ++ */ ++ if (n > 254) { ++ vma_writer_set_error(vmaw, "vma_writer_register_stream: " ++ "too many drives"); ++ return -1; ++ } ++ ++ if (size <= 0) { ++ vma_writer_set_error(vmaw, "vma_writer_register_stream: " ++ "got strange size %zd", size); ++ return -1; ++ } ++ ++ DPRINTF("vma_writer_register_stream %s %zu %d\n", devname, size, n); ++ ++ vmaw->stream_info[n].devname = g_strdup(devname); ++ vmaw->stream_info[n].size = size; ++ ++ vmaw->stream_info[n].cluster_count = (size + VMA_CLUSTER_SIZE - 1) / ++ VMA_CLUSTER_SIZE; ++ ++ vmaw->stream_count = n; ++ ++ if (strcmp(devname, "vmstate") == 0) { ++ vmaw->vmstate_stream = n; ++ } ++ ++ return n; ++} ++ ++static void vma_co_continue_write(void *opaque) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ 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) ++{ ++ size_t done = 0; ++ ssize_t ret; ++ ++ /* atomic writes (we cannot interleave writes) */ ++ qemu_co_mutex_lock(&vmaw->writer_lock); ++ ++ 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) { ++ done += ret; ++ DPRINTF("vma_co_write written %zd %zd\n", done, ret); ++ } else if (ret < 0) { ++ if (errno == EAGAIN || errno == EWOULDBLOCK) { ++ DPRINTF("vma_co_write yield %zd\n", done); ++ qemu_coroutine_yield(); ++ DPRINTF("vma_co_write restart %zd\n", done); ++ } else { ++ vma_writer_set_error(vmaw, "vma_co_write write error - %s", ++ g_strerror(errno)); ++ done = -1; /* always return failure for partial writes */ ++ break; ++ } ++ } else if (ret == 0) { ++ /* should not happen - simply try again */ ++ } ++ } ++ ++ 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); ++ return done; ++} ++ ++static void coroutine_fn vma_co_writer_task(void *opaque) ++{ ++ VmaAIOCB *cb = opaque; ++ ++ DPRINTF("vma_co_writer_task start\n"); ++ ++ int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); ++ DPRINTF("vma_co_writer_task write done %zd\n", done); ++ ++ if (done != cb->bytes) { ++ DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); ++ vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", ++ done); ++ } ++ ++ cb->bytes = 0; ++ ++ qemu_co_queue_next(&cb->vmaw->wqueue); ++ ++ DPRINTF("vma_co_writer_task end\n"); ++} ++ ++static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) ++{ ++ DPRINTF("vma_queue_flush enter\n"); ++ ++ assert(vmaw); ++ ++ while (1) { ++ int i; ++ VmaAIOCB *cb = NULL; ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ if (vmaw->aiocbs[i]->bytes) { ++ cb = vmaw->aiocbs[i]; ++ DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, ++ vmaw->aiocbs[i]->bytes); ++ break; ++ } ++ } ++ if (!cb) { ++ break; ++ } ++ qemu_co_queue_wait(&vmaw->wqueue); ++ } ++ ++ DPRINTF("vma_queue_flush leave\n"); ++} ++ ++/** ++ * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') ++ * So we need to create a coroutione to allow 'parallel' execution. ++ */ ++static ssize_t coroutine_fn ++vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) ++{ ++ DPRINTF("vma_queue_write enter %zd\n", bytes); ++ ++ assert(vmaw); ++ assert(buf); ++ assert(bytes <= VMA_MAX_EXTENT_SIZE); ++ ++ VmaAIOCB *cb = NULL; ++ while (!cb) { ++ int i; ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ if (!vmaw->aiocbs[i]->bytes) { ++ cb = vmaw->aiocbs[i]; ++ break; ++ } ++ } ++ if (!cb) { ++ qemu_co_queue_wait(&vmaw->wqueue); ++ } ++ } ++ ++ memcpy(cb->buffer, buf, bytes); ++ cb->bytes = bytes; ++ cb->vmaw = vmaw; ++ ++ DPRINTF("vma_queue_write start %zd\n", bytes); ++ cb->co = qemu_coroutine_create(vma_co_writer_task); ++ qemu_coroutine_enter(cb->co, cb); ++ ++ DPRINTF("vma_queue_write leave\n"); ++ ++ return bytes; ++} ++ ++VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp) ++{ ++ const char *p; ++ ++ assert(sizeof(VmaHeader) == (4096 + 8192)); ++ assert(sizeof(VmaExtentHeader) == 512); ++ ++ VmaWriter *vmaw = g_new0(VmaWriter, 1); ++ vmaw->fd = -1; ++ ++ vmaw->md5csum = g_checksum_new(G_CHECKSUM_MD5); ++ if (!vmaw->md5csum) { ++ error_setg(errp, "can't allocate cmsum\n"); ++ goto err; ++ } ++ ++ if (strstart(filename, "exec:", &p)) { ++ vmaw->cmd = popen(p, "w"); ++ if (vmaw->cmd == NULL) { ++ error_setg(errp, "can't popen command '%s' - %s\n", p, ++ g_strerror(errno)); ++ goto err; ++ } ++ vmaw->fd = fileno(vmaw->cmd); ++ ++ /* 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 { ++ 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, ++ g_strerror(errno)); ++ goto err; ++ } ++ } ++ ++ /* 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; ++ ++ vmaw->header_blob_table_pos = 1; /* start at pos 1 */ ++ ++ qemu_co_mutex_init(&vmaw->writer_lock); ++ qemu_co_mutex_init(&vmaw->flush_lock); ++ qemu_co_queue_init(&vmaw->wqueue); ++ ++ uuid_copy(vmaw->uuid, uuid); ++ ++ return vmaw; ++ ++err: ++ if (vmaw) { ++ if (vmaw->cmd) { ++ pclose(vmaw->cmd); ++ } else if (vmaw->fd >= 0) { ++ close(vmaw->fd); ++ } ++ ++ if (vmaw->md5csum) { ++ g_checksum_free(vmaw->md5csum); ++ } ++ ++ g_free(vmaw); ++ } ++ ++ return NULL; ++} ++ ++static int coroutine_fn vma_write_header(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ int header_clusters = 8; ++ char buf[65536*header_clusters]; ++ VmaHeader *head = (VmaHeader *)buf; ++ ++ int i; ++ ++ DPRINTF("VMA WRITE HEADER\n"); ++ ++ if (vmaw->status < 0) { ++ return vmaw->status; ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ head->magic = VMA_MAGIC; ++ head->version = GUINT32_TO_BE(1); /* v1 */ ++ memcpy(head->uuid, vmaw->uuid, 16); ++ ++ time_t ctime = time(NULL); ++ head->ctime = GUINT64_TO_BE(ctime); ++ ++ if (!vmaw->stream_count) { ++ return -1; ++ } ++ ++ for (i = 0; i < VMA_MAX_CONFIGS; i++) { ++ head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]); ++ head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]); ++ } ++ ++ /* 32 bytes per device (12 used currently) = 8192 bytes max */ ++ for (i = 1; i <= 254; i++) { ++ VmaStreamInfo *si = &vmaw->stream_info[i]; ++ if (si->size) { ++ assert(si->devname); ++ uint32_t devname_ptr = allocate_header_string(vmaw, si->devname); ++ if (!devname_ptr) { ++ return -1; ++ } ++ head->dev_info[i].devname_ptr = GUINT32_TO_BE(devname_ptr); ++ head->dev_info[i].size = GUINT64_TO_BE(si->size); ++ } ++ } ++ ++ uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size; ++ head->header_size = GUINT32_TO_BE(header_size); ++ ++ if (header_size > sizeof(buf)) { ++ return -1; /* just to be sure */ ++ } ++ ++ uint32_t blob_buffer_offset = sizeof(VmaHeader); ++ memcpy(buf + blob_buffer_offset, vmaw->header_blob_table, ++ vmaw->header_blob_table_size); ++ head->blob_buffer_offset = GUINT32_TO_BE(blob_buffer_offset); ++ head->blob_buffer_size = GUINT32_TO_BE(vmaw->header_blob_table_pos); ++ ++ g_checksum_reset(vmaw->md5csum); ++ g_checksum_update(vmaw->md5csum, (const guchar *)buf, header_size); ++ gsize csize = 16; ++ g_checksum_get_digest(vmaw->md5csum, (guint8 *)(head->md5sum), &csize); ++ ++ return vma_queue_write(vmaw, buf, header_size); ++} ++ ++static int coroutine_fn vma_writer_flush(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ ++ int ret; ++ int i; ++ ++ if (vmaw->status < 0) { ++ return vmaw->status; ++ } ++ ++ if (!vmaw->header_written) { ++ vmaw->header_written = true; ++ ret = vma_write_header(vmaw); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_flush: write header failed"); ++ return ret; ++ } ++ } ++ ++ DPRINTF("VMA WRITE FLUSH %d %d\n", vmaw->outbuf_count, vmaw->outbuf_pos); ++ ++ ++ VmaExtentHeader *ehead = (VmaExtentHeader *)vmaw->outbuf; ++ ++ ehead->magic = VMA_EXTENT_MAGIC; ++ ehead->reserved1 = 0; ++ ++ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { ++ ehead->blockinfo[i] = GUINT64_TO_BE(vmaw->outbuf_block_info[i]); ++ } ++ ++ guint16 block_count = (vmaw->outbuf_pos - VMA_EXTENT_HEADER_SIZE) / ++ VMA_BLOCK_SIZE; ++ ++ ehead->block_count = GUINT16_TO_BE(block_count); ++ ++ memcpy(ehead->uuid, vmaw->uuid, sizeof(ehead->uuid)); ++ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); ++ ++ g_checksum_reset(vmaw->md5csum); ++ g_checksum_update(vmaw->md5csum, vmaw->outbuf, VMA_EXTENT_HEADER_SIZE); ++ gsize csize = 16; ++ g_checksum_get_digest(vmaw->md5csum, ehead->md5sum, &csize); ++ ++ int bytes = vmaw->outbuf_pos; ++ ret = vma_queue_write(vmaw, vmaw->outbuf, bytes); ++ if (ret != bytes) { ++ vma_writer_set_error(vmaw, "vma_writer_flush: failed write"); ++ } ++ ++ vmaw->outbuf_count = 0; ++ vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE; ++ ++ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { ++ vmaw->outbuf_block_info[i] = 0; ++ } ++ ++ return vmaw->status; ++} ++ ++static int vma_count_open_streams(VmaWriter *vmaw) ++{ ++ g_assert(vmaw != NULL); ++ ++ int i; ++ int open_drives = 0; ++ for (i = 0; i <= 255; i++) { ++ if (vmaw->stream_info[i].size && !vmaw->stream_info[i].finished) { ++ open_drives++; ++ } ++ } ++ ++ return open_drives; ++} ++ ++/** ++ * all jobs should call this when there is no more data ++ * Returns: number of remaining stream (0 ==> finished) ++ */ ++int coroutine_fn ++vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id) ++{ ++ g_assert(vmaw != NULL); ++ ++ DPRINTF("vma_writer_set_status %d\n", dev_id); ++ if (!vmaw->stream_info[dev_id].size) { ++ vma_writer_set_error(vmaw, "vma_writer_close_stream: " ++ "no such stream %d", dev_id); ++ return -1; ++ } ++ if (vmaw->stream_info[dev_id].finished) { ++ vma_writer_set_error(vmaw, "vma_writer_close_stream: " ++ "stream already closed %d", dev_id); ++ return -1; ++ } ++ ++ vmaw->stream_info[dev_id].finished = true; ++ ++ int open_drives = vma_count_open_streams(vmaw); ++ ++ if (open_drives <= 0) { ++ DPRINTF("vma_writer_set_status all drives completed\n"); ++ qemu_co_mutex_lock(&vmaw->flush_lock); ++ int ret = vma_writer_flush(vmaw); ++ qemu_co_mutex_unlock(&vmaw->flush_lock); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed"); ++ } ++ } ++ ++ return open_drives; ++} ++ ++int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status) ++{ ++ int i; ++ ++ g_assert(vmaw != NULL); ++ ++ if (status) { ++ status->status = vmaw->status; ++ g_strlcpy(status->errmsg, vmaw->errmsg, sizeof(status->errmsg)); ++ for (i = 0; i <= 255; i++) { ++ status->stream_info[i] = vmaw->stream_info[i]; ++ } ++ ++ uuid_unparse_lower(vmaw->uuid, status->uuid_str); ++ } ++ ++ status->closed = vmaw->closed; ++ ++ return vmaw->status; ++} ++ ++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)) { ++ ret = vma_writer_flush(vmaw); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed"); ++ break; ++ } ++ } ++ ++ qemu_co_mutex_unlock(&vmaw->flush_lock); ++ ++ return ret; ++} ++ ++ ++int64_t coroutine_fn ++vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num, ++ unsigned char *buf, size_t *zero_bytes) ++{ ++ g_assert(vmaw != NULL); ++ g_assert(zero_bytes != NULL); ++ ++ *zero_bytes = 0; ++ ++ if (vmaw->status < 0) { ++ return vmaw->status; ++ } ++ ++ if (!dev_id || !vmaw->stream_info[dev_id].size) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "no such stream %d", dev_id); ++ return -1; ++ } ++ ++ if (vmaw->stream_info[dev_id].finished) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "stream already closed %d", dev_id); ++ return -1; ++ } ++ ++ ++ if (cluster_num >= (((uint64_t)1)<<32)) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "cluster number out of range"); ++ return -1; ++ } ++ ++ if (dev_id == vmaw->vmstate_stream) { ++ if (cluster_num != vmaw->vmstate_clusters) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "non sequential vmstate write"); ++ } ++ vmaw->vmstate_clusters++; ++ } else if (cluster_num >= vmaw->stream_info[dev_id].cluster_count) { ++ vma_writer_set_error(vmaw, "vma_writer_write: cluster number too big"); ++ return -1; ++ } ++ ++ /* wait until buffer is available */ ++ if (vma_writer_get_buffer(vmaw) < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "vma_writer_get_buffer failed"); ++ return -1; ++ } ++ ++ DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num); ++ ++ uint16_t mask = 0; ++ ++ 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); ++ block_info |= ((uint64_t)dev_id) << 32; ++ block_info |= (cluster_num & 0xffffffff); ++ vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info; ++ ++ DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info); ++ ++ vmaw->outbuf_count++; ++ ++ /** NOTE: We allways write whole clusters, but we correctly set ++ * transferred bytes. So transferred == size when when everything ++ * went OK. ++ */ ++ size_t transferred = VMA_CLUSTER_SIZE; ++ ++ if (dev_id != vmaw->vmstate_stream) { ++ uint64_t last = (cluster_num + 1) * VMA_CLUSTER_SIZE; ++ if (last > vmaw->stream_info[dev_id].size) { ++ uint64_t diff = last - vmaw->stream_info[dev_id].size; ++ if (diff >= VMA_CLUSTER_SIZE) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "read after last cluster"); ++ return -1; ++ } ++ transferred -= diff; ++ } ++ } ++ ++ vmaw->stream_info[dev_id].transferred += transferred; ++ ++ return transferred; ++} ++ ++int vma_writer_close(VmaWriter *vmaw, Error **errp) ++{ ++ g_assert(vmaw != NULL); ++ ++ int i; ++ ++ vma_queue_flush(vmaw); ++ ++ /* this should not happen - just to be sure */ ++ while (!qemu_co_queue_empty(&vmaw->wqueue)) { ++ DPRINTF("vma_writer_close wait\n"); ++ co_sleep_ns(rt_clock, 1000000); ++ } ++ ++ if (vmaw->cmd) { ++ if (pclose(vmaw->cmd) < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "pclose failed - %s", g_strerror(errno)); ++ } ++ } else { ++ if (close(vmaw->fd) < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "close failed - %s", g_strerror(errno)); ++ } ++ } ++ ++ for (i = 0; i <= 255; i++) { ++ VmaStreamInfo *si = &vmaw->stream_info[i]; ++ if (si->size) { ++ if (!si->finished) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "detected open stream '%s'", si->devname); ++ } else if ((si->transferred != si->size) && ++ (i != vmaw->vmstate_stream)) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "incomplete stream '%s' (%zd != %zd)", ++ si->devname, si->transferred, si->size); ++ } ++ } ++ } ++ ++ for (i = 0; i <= 255; i++) { ++ vmaw->stream_info[i].finished = 1; /* mark as closed */ ++ } ++ ++ vmaw->closed = 1; ++ ++ if (vmaw->status < 0 && *errp == NULL) { ++ error_setg(errp, "%s", vmaw->errmsg); ++ } ++ ++ return vmaw->status; ++} ++ ++void vma_writer_destroy(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ ++ int i; ++ ++ for (i = 0; i <= 255; i++) { ++ if (vmaw->stream_info[i].devname) { ++ g_free(vmaw->stream_info[i].devname); ++ } ++ } ++ ++ if (vmaw->md5csum) { ++ g_checksum_free(vmaw->md5csum); ++ } ++ ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ free(vmaw->aiocbs[i]); ++ } ++ ++ g_free(vmaw); ++} ++ ++/* backup driver plugin */ ++ ++static int vma_dump_cb(void *opaque, uint8_t dev_id, int64_t cluster_num, ++ unsigned char *buf, size_t *zero_bytes) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ return vma_writer_write(vmaw, dev_id, cluster_num, buf, zero_bytes); ++} ++ ++static int vma_close_cb(void *opaque, Error **errp) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ int res = vma_writer_close(vmaw, errp); ++ vma_writer_destroy(vmaw); ++ ++ return res; ++} ++ ++static int vma_complete_cb(void *opaque, uint8_t dev_id, int ret) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "backup_complete_cb %d", ret); ++ } ++ ++ return vma_writer_close_stream(vmaw, dev_id); ++} ++ ++static int vma_register_stream_cb(void *opaque, const char *devname, ++ size_t size) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ return vma_writer_register_stream(vmaw, devname, size); ++} ++ ++static int vma_register_config_cb(void *opaque, const char *name, ++ gpointer data, size_t data_len) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ return vma_writer_add_config(vmaw, name, data, data_len); ++} ++ ++static void *vma_open_cb(const char *filename, uuid_t uuid, Error **errp) ++{ ++ return vma_writer_create(filename, uuid, errp); ++} ++ ++const BackupDriver backup_vma_driver = { ++ .format = "vma", ++ .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..6633aa5 +--- /dev/null ++++ b/vma.c +@@ -0,0 +1,561 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) 2012 Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "qemu/error-report.h" ++#include "vma.h" ++#include "block/block.h" ++ ++static void help(void) ++{ ++ const char *help_msg = ++ "usage: vma command [command options]\n" ++ "\n" ++ "vma list \n" ++ "vma create [-c config] pathname ...\n" ++ "vma extract [-r] \n" ++ ; ++ ++ printf("%s", help_msg); ++ exit(1); ++} ++ ++static const char *extract_devname(const char *path, char **devname, int index) ++{ ++ assert(path); ++ ++ const char *sep = strchr(path, '='); ++ ++ if (sep) { ++ *devname = g_strndup(path, sep - path); ++ path = sep + 1; ++ } else { ++ if (index >= 0) { ++ *devname = g_strdup_printf("disk%d", index); ++ } else { ++ *devname = NULL; ++ } ++ } ++ ++ return path; ++} ++ ++static void print_content(VmaReader *vmar) ++{ ++ assert(vmar); ++ ++ VmaHeader *head = vma_reader_get_header(vmar); ++ ++ GList *l = vma_reader_get_config_data(vmar); ++ while (l && l->data) { ++ VmaConfigData *cdata = (VmaConfigData *)l->data; ++ l = g_list_next(l); ++ printf("CFG: size: %d name: %s\n", cdata->len, cdata->name); ++ } ++ ++ int i; ++ VmaDeviceInfo *di; ++ for (i = 1; i < 255; i++) { ++ di = vma_reader_get_device_info(vmar, i); ++ if (di) { ++ if (strcmp(di->devname, "vmstate") == 0) { ++ printf("VMSTATE: dev_id=%d memory: %zd\n", i, di->size); ++ } else { ++ printf("DEV: dev_id=%d size: %zd devname: %s\n", ++ i, di->size, di->devname); ++ } ++ } ++ } ++ /* ctime is the last entry we print */ ++ printf("CTIME: %s", ctime(&head->ctime)); ++ fflush(stdout); ++} ++ ++static int list_content(int argc, char **argv) ++{ ++ int c, ret = 0; ++ const char *filename; ++ ++ for (;;) { ++ c = getopt(argc, argv, "h"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 1) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ print_content(vmar); ++ ++ vma_reader_destroy(vmar); ++ ++ return ret; ++} ++ ++typedef struct RestoreMap { ++ char *devname; ++ char *path; ++ bool write_zero; ++} RestoreMap; ++ ++static int extract_content(int argc, char **argv) ++{ ++ int c, ret = 0; ++ int verbose = 0; ++ const char *filename; ++ const char *dirname; ++ const char *readmap = NULL; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hvr:"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'r': ++ readmap = optarg; ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ default: ++ help(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 2) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ dirname = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (mkdir(dirname, 0777) < 0) { ++ g_error("unable to create target directory %s - %s", ++ dirname, g_strerror(errno)); ++ } ++ ++ GList *l = vma_reader_get_config_data(vmar); ++ while (l && l->data) { ++ VmaConfigData *cdata = (VmaConfigData *)l->data; ++ l = g_list_next(l); ++ char *cfgfn = g_strdup_printf("%s/%s", dirname, cdata->name); ++ GError *err = NULL; ++ if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len, ++ &err)) { ++ g_error("unable to write file: %s", err->message); ++ } ++ } ++ ++ GHashTable *devmap = g_hash_table_new(g_str_hash, g_str_equal); ++ ++ if (readmap) { ++ print_content(vmar); ++ ++ FILE *map = fopen(readmap, "r"); ++ if (!map) { ++ g_error("unable to open fifo %s - %s", readmap, g_strerror(errno)); ++ } ++ ++ while (1) { ++ char inbuf[8192]; ++ char *line = fgets(inbuf, sizeof(inbuf), map); ++ if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { ++ break; ++ } ++ int len = strlen(line); ++ if (line[len - 1] == '\n') { ++ line[len - 1] = '\0'; ++ if (len == 1) { ++ break; ++ } ++ } ++ ++ const char *path; ++ bool write_zero; ++ if (line[0] == '0' && line[1] == ':') { ++ path = inbuf + 2; ++ write_zero = false; ++ } else if (line[0] == '1' && line[1] == ':') { ++ path = inbuf + 2; ++ write_zero = true; ++ } else { ++ g_error("read map failed - parse error ('%s')", inbuf); ++ } ++ ++ char *devname = NULL; ++ path = extract_devname(path, &devname, -1); ++ if (!devname) { ++ g_error("read map failed - no dev name specified ('%s')", ++ inbuf); ++ } ++ ++ RestoreMap *map = g_new0(RestoreMap, 1); ++ map->devname = g_strdup(devname); ++ map->path = g_strdup(path); ++ map->write_zero = write_zero; ++ ++ g_hash_table_insert(devmap, map->devname, map); ++ ++ }; ++ } ++ ++ int i; ++ int vmstate_fd = -1; ++ guint8 vmstate_stream = 0; ++ ++ for (i = 1; i < 255; i++) { ++ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); ++ if (di && (strcmp(di->devname, "vmstate") == 0)) { ++ vmstate_stream = i; ++ char *statefn = g_strdup_printf("%s/vmstate.bin", dirname); ++ vmstate_fd = open(statefn, O_WRONLY|O_CREAT|O_EXCL, 0644); ++ if (vmstate_fd < 0) { ++ g_error("create vmstate file '%s' failed - %s", statefn, ++ g_strerror(errno)); ++ } ++ g_free(statefn); ++ } else if (di) { ++ char *devfn = NULL; ++ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB; ++ bool write_zero = true; ++ ++ if (readmap) { ++ RestoreMap *map; ++ map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname); ++ if (map == NULL) { ++ g_error("no device name mapping for %s", di->devname); ++ } ++ devfn = map->path; ++ write_zero = map->write_zero; ++ } else { ++ devfn = g_strdup_printf("%s/tmp-disk-%s.raw", ++ dirname, di->devname); ++ printf("DEVINFO %s %zd\n", devfn, di->size); ++ ++ 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 ++ * need to write zeroes (so we generate a sparse file) ++ */ ++ write_zero = false; ++ } ++ ++ 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) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (!readmap) { ++ g_free(devfn); ++ } ++ } ++ } ++ ++ if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) { ++ g_error("restore failed - %s", error_get_pretty(errp)); ++ } ++ ++ if (!readmap) { ++ for (i = 1; i < 255; i++) { ++ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); ++ if (di && (i != vmstate_stream)) { ++ char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw", ++ dirname, di->devname); ++ char *fn = g_strdup_printf("%s/disk-%s.raw", ++ dirname, di->devname); ++ if (rename(tmpfn, fn) != 0) { ++ g_error("rename %s to %s failed - %s", ++ tmpfn, fn, g_strerror(errno)); ++ } ++ } ++ } ++ } ++ ++ vma_reader_destroy(vmar); ++ ++ bdrv_close_all(); ++ ++ return ret; ++} ++ ++typedef struct BackupCB { ++ VmaWriter *vmaw; ++ uint8_t dev_id; ++} BackupCB; ++ ++static int backup_dump_cb(void *opaque, BlockDriverState *bs, ++ int64_t cluster_num, unsigned char *buf) ++{ ++ BackupCB *bcb = opaque; ++ size_t zb = 0; ++ if (vma_writer_write(bcb->vmaw, bcb->dev_id, cluster_num, buf, &zb) < 0) { ++ g_warning("backup_dump_cb vma_writer_write failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void backup_complete_cb(void *opaque, int ret) ++{ ++ BackupCB *bcb = opaque; ++ ++ if (ret < 0) { ++ vma_writer_set_error(bcb->vmaw, "backup_complete_cb %d", ret); ++ } ++ ++ if (vma_writer_close_stream(bcb->vmaw, bcb->dev_id) <= 0) { ++ Error *err = NULL; ++ if (vma_writer_close(bcb->vmaw, &err) != 0) { ++ g_warning("vma_writer_close failed %s", error_get_pretty(err)); ++ } ++ } ++} ++ ++static int create_archive(int argc, char **argv) ++{ ++ int i, c, res; ++ int verbose = 0; ++ const char *archivename; ++ GList *config_files = NULL; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hvc:"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'c': ++ config_files = g_list_append(config_files, optarg); ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ ++ /* make sure we have archive name and at least one path */ ++ if ((optind + 2) > argc) { ++ help(); ++ } ++ ++ archivename = argv[optind++]; ++ ++ uuid_t uuid; ++ uuid_generate(uuid); ++ ++ Error *local_err = NULL; ++ VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err); ++ ++ if (vmaw == NULL) { ++ g_error("%s", error_get_pretty(local_err)); ++ } ++ ++ GList *l = config_files; ++ while (l && l->data) { ++ char *name = l->data; ++ char *cdata = NULL; ++ gsize clen = 0; ++ GError *err = NULL; ++ if (!g_file_get_contents(name, &cdata, &clen, &err)) { ++ unlink(archivename); ++ g_error("Unable to read file: %s", err->message); ++ } ++ ++ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) { ++ unlink(archivename); ++ g_error("Unable to append config data %s (len = %zd)", ++ name, clen); ++ } ++ l = g_list_next(l); ++ } ++ ++ int ind = 0; ++ while (optind < argc) { ++ const char *path = argv[optind++]; ++ char *devname = NULL; ++ path = extract_devname(path, &devname, ind++); ++ ++ BlockDriver *drv = NULL; ++ BlockDriverState *bs = bdrv_new(devname); ++ ++ res = bdrv_open(bs, path, BDRV_O_CACHE_WB , drv); ++ if (res < 0) { ++ unlink(archivename); ++ g_error("bdrv_open '%s' failed", path); ++ } ++ int64_t size = bdrv_getlength(bs); ++ int dev_id = vma_writer_register_stream(vmaw, devname, size); ++ if (dev_id <= 0) { ++ unlink(archivename); ++ g_error("vma_writer_register_stream '%s' failed", devname); ++ } ++ ++ BackupCB *bcb = g_new0(BackupCB, 1); ++ bcb->vmaw = vmaw; ++ bcb->dev_id = dev_id; ++ ++ 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); ++ } ++ } ++ ++ VmaStatus vmastat; ++ int percent = 0; ++ int last_percent = -1; ++ ++ while (1) { ++ main_loop_wait(false); ++ vma_writer_get_status(vmaw, &vmastat); ++ ++ if (verbose) { ++ ++ uint64_t total = 0; ++ uint64_t transferred = 0; ++ uint64_t zero_bytes = 0; ++ ++ int i; ++ for (i = 0; i < 256; i++) { ++ if (vmastat.stream_info[i].size) { ++ total += vmastat.stream_info[i].size; ++ transferred += vmastat.stream_info[i].transferred; ++ zero_bytes += vmastat.stream_info[i].zero_bytes; ++ } ++ } ++ percent = (transferred*100)/total; ++ if (percent != last_percent) { ++ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, ++ transferred, total, zero_bytes); ++ fflush(stderr); ++ ++ last_percent = percent; ++ } ++ } ++ ++ if (vmastat.closed) { ++ break; ++ } ++ } ++ ++ bdrv_drain_all(); ++ ++ vma_writer_get_status(vmaw, &vmastat); ++ ++ if (verbose) { ++ for (i = 0; i < 256; i++) { ++ VmaStreamInfo *si = &vmastat.stream_info[i]; ++ if (si->size) { ++ fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n", ++ si->devname, si->size, si->zero_bytes, ++ si->size - si->zero_bytes); ++ } ++ } ++ } ++ ++ if (vmastat.status < 0) { ++ unlink(archivename); ++ g_error("creating vma archive failed"); ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char **argv) ++{ ++ const char *cmdname; ++ ++ error_set_progname(argv[0]); ++ ++ qemu_init_main_loop(); ++ ++ bdrv_init(); ++ ++ if (argc < 2) { ++ help(); ++ } ++ ++ cmdname = argv[1]; ++ argc--; argv++; ++ ++ ++ if (!strcmp(cmdname, "list")) { ++ return list_content(argc, argv); ++ } else if (!strcmp(cmdname, "create")) { ++ return create_archive(argc, argv); ++ } else if (!strcmp(cmdname, "extract")) { ++ return extract_content(argc, argv); ++ } ++ ++ help(); ++ return 0; ++} +diff --git a/vma.h b/vma.h +new file mode 100644 +index 0000000..76d0dc8 +--- /dev/null ++++ b/vma.h +@@ -0,0 +1,145 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef BACKUP_VMA_H ++#define BACKUP_VMA_H ++ ++#include "backup.h" ++#include "error.h" ++ ++#define VMA_BLOCK_BITS 12 ++#define VMA_BLOCK_SIZE (1< +Date: Wed, 14 Nov 2012 09:57:04 +0100 +Subject: [PATCH v5 5/6] add regression tests for backup + +Simple regression tests using vma-reader and vma-writer. + +Signed-off-by: Dietmar Maurer +--- + tests/Makefile | 11 +- + 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 +index 567e36e..136be84 100644 +--- a/tests/Makefile ++++ b/tests/Makefile +@@ -59,6 +59,8 @@ gcov-files-test-mul64-y = util/host-utils.c + + check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh + ++check-backup-y = tests/backup-test$(EXESUF) ++ + # All QTests for now are POSIX-only, but the dependencies are + # really in libqtest, not in the testcases themselves. + check-qtest-i386-y = tests/fdc-test$(EXESUF) +@@ -102,6 +104,7 @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil + tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a + tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a + tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a ++tests/backup-test$(EXESUF): tests/backup-test.o vma-reader.o $(block-obj-y) libqemuutil.a libqemustub.a + tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a + tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o + tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuutil.a +@@ -213,10 +216,14 @@ check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) + + # Consolidated targets + +-.PHONY: check-qtest check-unit check ++.PHONY: check-backup check-qtest check-unit check + check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) + check-unit: $(patsubst %,check-%, $(check-unit-y)) + check-block: $(patsubst %,check-%, $(check-block-y)) +-check: check-unit check-qtest ++ ++check-backup: tests/backup-test$(EXESUF) ++ $< ++ ++check: check-unit check-qtest check-backup + + -include $(wildcard tests/*.d) +diff --git a/tests/backup-test.c b/tests/backup-test.c +new file mode 100644 +index 0000000..47a9664 +--- /dev/null ++++ b/tests/backup-test.c +@@ -0,0 +1,529 @@ ++/* ++ * QEMU backup test suit ++ * ++ * 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. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "block/block.h" ++ ++#include "vma.h" ++ ++static int opt_debug; ++static int opt_loop; ++ ++#define DPRINTF(fmt, ...) \ ++ do { if (opt_debug) { printf(fmt, ## __VA_ARGS__); } } while (0) ++ ++#define CLUSTER(x) (x*BACKUP_CLUSTER_SIZE) ++ ++#define RUN_TEST(testfunc, speed) \ ++ backup_test(#testfunc " speed " #speed, speed, testfunc); ++ ++ ++static unsigned char buf_sec_pattern_cd[BDRV_SECTOR_SIZE]; ++static unsigned char buf_sec_pattern_32[BDRV_SECTOR_SIZE]; ++ ++#define TEST_IMG_SIZE (6*1024*1024+BDRV_SECTOR_SIZE) ++#define TEST_IMG_NAME "backuptest.raw" ++#define TEST_IMG_RESTORE_NAME "backuptest.raw.restore" ++#define TEST_VMA_NAME "backuptest.vma" ++ ++typedef struct BackupCB { ++ VmaWriter *vmaw; ++ uint8_t dev_id; ++} BackupCB; ++ ++static int backup_dump_cb(void *opaque, BlockDriverState *bs, ++ int64_t cluster_num, unsigned char *buf) ++{ ++ BackupCB *bcb = opaque; ++ ++ 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) { ++ printf("backup_dump_cb vma_writer_write failed\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void backup_complete_cb(void *opaque, int ret) ++{ ++ BackupCB *bcb = opaque; ++ ++ DPRINTF("backup_complete_cb %d %d\n", bcb->dev_id, ret); ++ ++ if (ret < 0) { ++ vma_writer_set_error(bcb->vmaw, "backup_complete_cb %d", ret); ++ } ++ ++ if (vma_writer_close_stream(bcb->vmaw, bcb->dev_id) <= 0) { ++ Error *err = NULL; ++ if (vma_writer_close(bcb->vmaw, &err) != 0) { ++ g_error("vma_writer_close failed %s", error_get_pretty(err)); ++ } ++ } ++ DPRINTF("backup_complete_cb finish\n"); ++} ++ ++static void write_sec_pattern_cd(BlockDriverState *bs, int64_t offset) ++{ ++ int ret; ++ ++ DPRINTF("write_sec_pattern_cd %" PRId64 "\n", offset); ++ ++ if (offset & 0x1ff) { ++ 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 %" PRId64 " failed", offset); ++ } ++ ++} ++ ++static void read_sec(BlockDriverState *bs, int64_t offset, unsigned char *buf) ++{ ++ DPRINTF("read_sec C%" PRId64 " start %" PRId64 "\n", ++ offset>>VMA_CLUSTER_BITS, offset); ++ ++ if (offset & 0x1ff) { ++ g_error("read_sec offset %" PRId64 " is not sector aligned\n", offset); ++ } ++ ++ if (bdrv_read(bs, offset >> 9, buf, 1) < 0) { ++ g_error("bdrv_read failed"); ++ } ++} ++ ++static bool request_term; ++ ++typedef struct TestCB { ++ Coroutine *co; ++ BlockDriverState *bs; ++ bool finished; ++} TestCB; ++ ++static TestCB *enter_test_co(BlockDriverState *bs, CoroutineEntry *entry) ++{ ++ TestCB *cb = g_new0(TestCB, 1); ++ cb->bs = bs; ++ cb->co = qemu_coroutine_create(entry); ++ qemu_coroutine_enter(cb->co, cb); ++ return cb; ++} ++ ++static void test_co_sleep(double sec) ++{ ++ co_sleep_ns(rt_clock, (int64_t)(sec*1000000000)); ++}; ++ ++static void test_co_yield(void) ++{ ++ co_sleep_ns(rt_clock, (int64_t)(1000)); ++}; ++ ++static void coroutine_fn run_co_test1(void *opaque) ++{ ++ assert(opaque); ++ TestCB *cb = (TestCB *)opaque; ++ ++ test_co_sleep(0.2); ++ write_sec_pattern_cd(cb->bs, 5*BACKUP_CLUSTER_SIZE); ++ test_co_sleep(0.2); ++ write_sec_pattern_cd(cb->bs, 10*BACKUP_CLUSTER_SIZE); ++ test_co_sleep(0.2); ++ write_sec_pattern_cd(cb->bs, 10*BACKUP_CLUSTER_SIZE); ++ ++ cb->finished = true; ++} ++ ++static void coroutine_fn run_co_test2(void *opaque) ++{ ++ assert(opaque); ++ TestCB *cb = (TestCB *)opaque; ++ unsigned char buf[512]; ++ ++ test_co_sleep(0.2); ++ read_sec(cb->bs, 5*BACKUP_CLUSTER_SIZE, buf); ++ write_sec_pattern_cd(cb->bs, 6*BACKUP_CLUSTER_SIZE); ++ ++ cb->finished = true; ++} ++ ++static void coroutine_fn run_co_random_read(void *opaque) ++{ ++ assert(opaque); ++ TestCB *cb = (TestCB *)opaque; ++ int64_t sectors = bdrv_getlength(cb->bs)/BDRV_SECTOR_SIZE - 1; ++ unsigned char buf[512]; ++ ++ while (1) { ++ test_co_yield(); ++ if (request_term) { ++ DPRINTF("finish run_co_random_read\n"); ++ break; ++ } ++ int64_t s = (rand()*sectors)/RAND_MAX; ++ read_sec(cb->bs, s*BDRV_SECTOR_SIZE, buf); ++ } ++ ++ cb->finished = true; ++} ++ ++static void coroutine_fn run_co_random_write(void *opaque) ++{ ++ assert(opaque); ++ TestCB *cb = (TestCB *)opaque; ++ int64_t sectors = bdrv_getlength(cb->bs)/BDRV_SECTOR_SIZE; ++ ++ while (1) { ++ test_co_yield(); ++ if (request_term) { ++ DPRINTF("finish run_co_random_write\n"); ++ break; ++ } ++ int64_t s = (rand()*sectors)/RAND_MAX; ++ write_sec_pattern_cd(cb->bs, s*BDRV_SECTOR_SIZE); ++ } ++ ++ cb->finished = true; ++} ++ ++static void fill_test_sector(void *buf, size_t sector_num) ++{ ++ int64_t *i64buf = (int64_t *)buf; ++ int i; ++ ++ int data = sector_num; ++ if (sector_num >= 8 && sector_num < 8*(2*16+2)) { ++ 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; ++ } ++} ++ ++static void verify_archive(const char *archive, size_t size) ++{ ++ Error *errp = NULL; ++ ++ VmaReader *vmar = vma_reader_create(archive, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, 1); ++ if (!di || strcmp((char *)di->devname, "hda") || di->size != size) { ++ g_error("got wrong device info"); ++ } ++ ++ unlink(TEST_IMG_RESTORE_NAME); ++ ++ int flags = BDRV_O_NATIVE_AIO|BDRV_O_RDWR|BDRV_O_CACHE_WB; ++ ++ bdrv_img_create(TEST_IMG_RESTORE_NAME, "raw", NULL, NULL, NULL, ++ size, flags, &errp); ++ if (error_is_set(&errp)) { ++ g_error("can't create file %s: %s", TEST_IMG_RESTORE_NAME, ++ error_get_pretty(errp)); ++ } ++ ++ BlockDriverState *bs = NULL; ++ if (bdrv_file_open(&bs, TEST_IMG_RESTORE_NAME, flags)) { ++ g_error("can't open file %s", TEST_IMG_RESTORE_NAME); ++ } ++ if (vma_reader_register_bs(vmar, 1, bs, false, &errp) < 0) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (vma_reader_restore(vmar, -1, false, &errp) < 0) { ++ g_error("restore failed - %s", error_get_pretty(errp)); ++ } ++ ++ size_t i; ++ size_t sectors = size/BDRV_SECTOR_SIZE; ++ int64_t buf[512/sizeof(int64_t)]; ++ int64_t buf2[512/sizeof(int64_t)]; ++ ++ for (i = 0; i < sectors; i++) { ++ if (bdrv_read(bs, i, (uint8_t *)buf, 1) != 0) { ++ g_error("bdrv_read failed"); ++ } ++ fill_test_sector(buf2, i); ++ if (bcmp(buf, buf2, sizeof(buf))) { ++ g_error("data is different at sector %" PRId64, i); ++ } ++ } ++ ++ vma_reader_destroy(vmar); ++ ++ unlink(TEST_IMG_RESTORE_NAME); ++} ++ ++static void prepare_vm_image(const char *filename, size_t sectors) ++{ ++ int fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); ++ if (fd < 0) { ++ g_error("can't open file %s\n", filename); ++ } ++ ++ size_t 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; ++ while (1) { ++ res = pwrite(fd, buf, sizeof(buf), i*512); ++ if (!(res < 0 && errno == EINTR)) { ++ break; ++ } ++ } ++ if (res != sizeof(buf)) { ++ g_error("can't initialize file %s - %s %d\n", ++ filename, g_strerror(errno), res); ++ } ++ } ++ ++ if (close(fd) != 0) { ++ g_error("close failed"); ++ } ++} ++ ++static GList *simple_test(BlockDriverState *bs) ++{ ++ GList *cb_list = NULL; ++ ++ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_test1)); ++ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_test2)); ++ ++ return cb_list; ++} ++ ++static GList *random_read_write_test(BlockDriverState *bs) ++{ ++ GList *cb_list = NULL; ++ ++ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_read)); ++ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_read)); ++ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_write)); ++ cb_list = g_list_append(cb_list, enter_test_co(bs, run_co_random_write)); ++ ++ return cb_list; ++} ++ ++static void backup_test(const char *testname, int64_t speed, ++ GList *(*start_test_cb)(BlockDriverState *bs)) ++{ ++ BlockDriverState *bs = bdrv_new("hda"); ++ ++ static int test_counter; ++ ++ test_counter++; ++ ++ printf("starting test #%d '%s'\n", test_counter, testname); ++ ++ const char *filename = TEST_IMG_NAME; ++ ++ prepare_vm_image(TEST_IMG_NAME, TEST_IMG_SIZE/BDRV_SECTOR_SIZE); ++ ++ int flags = BDRV_O_NATIVE_AIO|BDRV_O_RDWR|BDRV_O_CACHE_WB; ++ ++ if (bdrv_open(bs, filename, flags, NULL) < 0) { ++ g_error("can't open device %s\n", filename); ++ } ++ ++ Error *err = NULL; ++ uuid_t uuid; ++ uuid_generate(uuid); ++ ++ unlink(TEST_VMA_NAME); ++ ++ VmaWriter *vmaw = vma_writer_create(TEST_VMA_NAME, uuid, &err); ++ if (!vmaw) { ++ g_error("%s", error_get_pretty(err)); ++ } ++ ++ BackupCB bcb; ++ bcb.vmaw = vmaw; ++ bcb.dev_id = vma_writer_register_stream(vmaw, bdrv_get_device_name(bs), ++ bdrv_getlength(bs)); ++ if (backup_job_create(bs, backup_dump_cb, backup_complete_cb, &bcb, ++ speed) < 0) { ++ g_error("backup_job_create failed"); ++ } else { ++ backup_job_start(bs, false); ++ } ++ ++ request_term = false; ++ ++ GList *cb_list = start_test_cb(bs); ++ ++ while (1) { ++ main_loop_wait(false); ++ ++ VmaStatus vmastat; ++ vma_writer_get_status(vmaw, &vmastat); ++ if (vmastat.closed) { ++ break; ++ } ++ } ++ ++ request_term = true; ++ ++ while (1) { ++ GList *l = cb_list; ++ bool active = 0; ++ while (l && l->data) { ++ TestCB *cb = (TestCB *)l->data; ++ l = g_list_next(l); ++ if (!cb->finished) { ++ active = true; ++ break; ++ } ++ } ++ if (!active) { ++ DPRINTF("All test coroutines finished\n"); ++ break; ++ } ++ main_loop_wait(false); ++ } ++ ++ /* Make sure all outstanding requests complete */ ++ bdrv_drain_all(); ++ ++ VmaStatus vmastat; ++ vma_writer_get_status(vmaw, &vmastat); ++ 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); ++ ++ vma_writer_destroy(vmaw); ++ ++ bdrv_delete(bs); ++ ++ /* start verification */ ++ verify_archive(TEST_VMA_NAME, TEST_IMG_SIZE); ++ ++ bdrv_close_all(); ++ ++ unlink(TEST_IMG_NAME); ++ unlink(TEST_VMA_NAME); ++ ++ printf("finish test #%d '%s' OK\n", test_counter, testname); ++} ++ ++static void help(void) ++{ ++ const char *help_msg = ++ "usage: backup-test [options]\n" ++ "\n" ++ "backup-test run default regression test (fast)\n" ++ "backup-test -l run long running test loop (endless)\n" ++ "\n" ++ "use option -d to turn on verbose debug output\n" ++ ; ++ ++ printf("%s", help_msg); ++ exit(1); ++} ++ ++int main(int argc, char **argv) ++{ ++ 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 (;;) { ++ c = getopt(argc, argv, "hdl"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'd': ++ opt_debug = 1; ++ break; ++ case 'l': ++ opt_loop = 1; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ memset(buf_sec_pattern_cd, 0xcd, sizeof(buf_sec_pattern_cd)); ++ memset(buf_sec_pattern_32, 0x32, sizeof(buf_sec_pattern_32)); ++ ++ srand(1234); ++ ++ /* ignore SIGPIPE */ ++ struct sigaction act; ++ sigfillset(&act.sa_mask); ++ act.sa_flags = 0; ++ act.sa_handler = SIG_IGN; ++ sigaction(SIGPIPE, &act, NULL); ++ ++ qemu_init_main_loop(); ++ ++ bdrv_init(); ++ ++ if (opt_loop) { /* endless test loop */ ++ while (1) { ++ RUN_TEST(random_read_write_test, 0); ++ } ++ return 0; ++ } ++ ++ if (opt_debug) { /* run simple test (rate limited) */ ++ RUN_TEST(simple_test, 1024*1024); ++ return 0; ++ } ++ ++ /* run default regression tests at full speed */ ++ ++ RUN_TEST(simple_test, 0); ++ RUN_TEST(random_read_write_test, 0); ++ ++ return 0; ++} +-- +1.7.2.5 + diff --git a/debian/patches/old/0006-add-vm-state-to-backups.patch b/debian/patches/old/0006-add-vm-state-to-backups.patch new file mode 100644 index 0000000..d68b427 --- /dev/null +++ b/debian/patches/old/0006-add-vm-state-to-backups.patch @@ -0,0 +1,305 @@ +From c209554171466e2aa8b1eca92d952429faca5fc6 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Thu, 29 Nov 2012 10:46:49 +0100 +Subject: [PATCH v5 6/6] add vm state to backups + + +Signed-off-by: Dietmar Maurer +--- + blockdev.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++- + hmp.c | 3 +- + qapi-schema.json | 5 +- + 3 files changed, 200 insertions(+), 4 deletions(-) + +diff --git a/blockdev.c b/blockdev.c +index 683f7da..dd20631 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -22,6 +22,8 @@ + #include "sysemu/arch_init.h" + #include "backup.h" + #include "vma.h" ++#include "migration/qemu-file.h" ++#include "migration/migration.h" + + static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); + +@@ -1355,6 +1357,10 @@ static struct GenericBackupState { + size_t total; + size_t transferred; + size_t zero_bytes; ++ unsigned char buf[BACKUP_CLUSTER_SIZE]; ++ int buf_index; ++ size_t buf_cluster_num; ++ guint8 vmstate_dev_id; + } backup_state; + + typedef struct BackupCB { +@@ -1510,10 +1516,170 @@ static void backup_start_jobs(void) + backup_run_next_job(); + } + ++static int backup_state_close(void *opaque) ++{ ++ if (!backup_state.buf_index) { ++ return 0; ++ } ++ ++ size_t zero_bytes = 0; ++ if (backup_state.buf_index < BACKUP_CLUSTER_SIZE) { ++ memset(backup_state.buf + backup_state.buf_index, 0, ++ BACKUP_CLUSTER_SIZE - backup_state.buf_index); ++ } ++ 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_state.buf_index = 0; ++ ++ return bytes < 0 ? -1 : 0; ++} ++ ++static int backup_state_put_buffer(void *opaque, const uint8_t *buf, ++ int64_t pos, int size) ++{ ++ assert(backup_state.driver); ++ assert(backup_state.writer); ++ assert(backup_state.driver->dump); ++ ++ /* Note: our backup driver expects to get whole clusters (64KB) */ ++ ++ int ret = size; ++ ++ while (size > 0) { ++ int l = BACKUP_CLUSTER_SIZE - backup_state.buf_index; ++ l = l > size ? size : l; ++ memcpy(backup_state.buf + backup_state.buf_index, buf, l); ++ backup_state.buf_index += l; ++ buf += l; ++ size -= l; ++ if (backup_state.buf_index == BACKUP_CLUSTER_SIZE) { ++ size_t zero_bytes = 0; ++ 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_state.buf_index = 0; ++ if (bytes < 0) { ++ return -1; ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++static const QEMUFileOps backup_file_ops = { ++ .put_buffer = backup_state_put_buffer, ++ .close = backup_state_close, ++}; ++ ++static void coroutine_fn backup_start_savevm(void *opaque) ++{ ++ assert(backup_state.driver); ++ assert(backup_state.writer); ++ int ret; ++ char *err = NULL; ++ uint64_t remaining; ++ int64_t maxlen; ++ MigrationParams params = { ++ .blk = 0, ++ .shared = 0 ++ }; ++ ++ int restart = 0; ++ ++ QEMUFile *file = qemu_fopen_ops(NULL, &backup_file_ops); ++ ++ ret = qemu_savevm_state_begin(file, ¶ms); ++ if (ret < 0) { ++ qemu_fclose(file); ++ err = g_strdup("qemu_savevm_state_begin failed"); ++ goto abort; ++ } ++ ++ while (1) { ++ ret = qemu_savevm_state_iterate(file); ++ remaining = ram_bytes_remaining(); ++ ++ if (ret < 0) { ++ qemu_fclose(file); ++ err = g_strdup_printf("qemu_savevm_state_iterate error %d", ret); ++ goto abort; ++ } ++ ++ /* stop the VM if we use too much space, ++ * or if remaining is just a few MB ++ */ ++ maxlen = ram_bytes_total(); ++ size_t cpos = backup_state.buf_cluster_num * BACKUP_CLUSTER_SIZE; ++ if ((remaining < 100000) || ((cpos + remaining) >= maxlen)) { ++ if (runstate_is_running()) { ++ restart = 1; ++ vm_stop(RUN_STATE_SAVE_VM); ++ } ++ } ++ ++ if (ret == 1) { /* finished */ ++ if (runstate_is_running()) { ++ restart = 1; ++ vm_stop(RUN_STATE_SAVE_VM); ++ } ++ ++ ret = qemu_savevm_state_complete(file); ++ if (ret < 0) { ++ qemu_fclose(file); ++ err = g_strdup("qemu_savevm_state_complete error"); ++ goto abort; ++ ++ } else { ++ if (qemu_fclose(file) < 0) { ++ error_setg(&backup_state.error, ++ "backup_start_savevm: qemu_fclose failed"); ++ goto abort; ++ } ++ if (backup_state.driver->complete(backup_state.writer, ++ backup_state.vmstate_dev_id, 0) < 0) { ++ err = g_strdup("backup_start_savevm: complete failed"); ++ goto abort; ++ } ++ backup_run_next_job(); ++ goto out; ++ } ++ } ++ } ++ ++out: ++ if (restart) { ++ vm_start(); ++ } ++ return; ++ ++abort: ++ backup_state.end_time = time(NULL); ++ ++ Error *local_err = NULL; ++ backup_state.driver->close(backup_state.writer, &local_err); ++ backup_state.writer = NULL; ++ ++ error_propagate(&backup_state.error, local_err); ++ ++ if (err) { ++ if (!backup_state.error) { ++ error_setg(&backup_state.error, "%s", err); ++ } ++ g_free(err); ++ } ++ ++ goto out; ++} ++ + char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, + bool has_config_file, const char *config_file, + bool has_devlist, const char *devlist, +- bool has_speed, int64_t speed, Error **errp) ++ bool has_speed, int64_t speed, ++ bool has_state, bool state, Error **errp) + { + BlockDriverState *bs; + Error *local_err = NULL; +@@ -1528,6 +1694,8 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, + return NULL; + } + ++ bool save_state = has_state ? state : false; ++ + /* Todo: try to auto-detect format based on file name */ + format = has_format ? format : BACKUP_FORMAT_VMA; + +@@ -1608,6 +1776,22 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, + size_t total = 0; + + /* register all devices for vma writer */ ++ ++ guint8 vmstate_dev_id = 0; ++ if (save_state) { ++ /* Note: we pass ram_bytes_total() for vmstate size ++ * The backup driver needs to be aware of the fact ++ * that the real stream size can be different (we do ++ * not know that size in advance). ++ */ ++ size_t ramsize = ram_bytes_total(); ++ vmstate_dev_id = driver->register_stream(writer, "vmstate", ramsize); ++ if (vmstate_dev_id <= 0) { ++ error_setg(errp, "register vmstate stream failed"); ++ goto err; ++ } ++ } ++ + l = bcblist; + while (l) { + BackupCB *bcb = l->data; +@@ -1675,6 +1859,9 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, + backup_state.total = total; + backup_state.transferred = 0; + backup_state.zero_bytes = 0; ++ backup_state.buf_index = 0; ++ backup_state.buf_cluster_num = 0; ++ backup_state.vmstate_dev_id = vmstate_dev_id; + + /* Grab a reference so hotplug does not delete the + * BlockDriverState from underneath us. +@@ -1686,7 +1873,12 @@ char *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, + drive_get_ref(drive_get_by_blockdev(bcb->bs)); + } + +- backup_start_jobs(); ++ if (save_state) { ++ Coroutine *co = qemu_coroutine_create(backup_start_savevm); ++ qemu_coroutine_enter(co, NULL); ++ } else { ++ backup_start_jobs(); ++ } + + return g_strdup(backup_state.uuid_str); + +diff --git a/hmp.c b/hmp.c +index b2c1f23..370cdf8 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -1052,7 +1052,8 @@ void hmp_backup(Monitor *mon, const QDict *qdict) + Error *errp = NULL; + + qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, +- devlist, qdict_haskey(qdict, "speed"), speed, &errp); ++ devlist, qdict_haskey(qdict, "speed"), speed, false, false, ++ &errp); + + if (error_is_set(&errp)) { + monitor_printf(mon, "%s\n", error_get_pretty(errp)); +diff --git a/qapi-schema.json b/qapi-schema.json +index 09ca8ef..1fabb67 100644 +--- a/qapi-schema.json ++++ b/qapi-schema.json +@@ -1885,6 +1885,8 @@ + # @devlist: #optional list of block device names (separated by ',', ';' + # or ':'). By default the backup includes all writable block devices. + # ++# @state: #optional flag to include vm state ++# + # Returns: the uuid of the backup job + # + # Since: 1.5.0 +@@ -1892,7 +1894,8 @@ + { 'command': 'backup', 'data': { 'backup-file': 'str', + '*format': 'BackupFormat', + '*config-file': 'str', +- '*devlist': 'str', '*speed': 'int' }, ++ '*devlist': 'str', '*speed': 'int', ++ '*state': 'bool' }, + 'returns': 'str' } + + ## +-- +1.7.2.5 + diff --git a/debian/patches/old/0007-use-extra-thread-for-vma-writer.patch b/debian/patches/old/0007-use-extra-thread-for-vma-writer.patch new file mode 100644 index 0000000..d6d1f5f --- /dev/null +++ b/debian/patches/old/0007-use-extra-thread-for-vma-writer.patch @@ -0,0 +1,471 @@ +From a6f324d47b810809de2a6106849527c6a9590175 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Mon, 14 Jan 2013 08:05:40 +0100 +Subject: [PATCH v3 7/7] use extra thread for vma writer + +The previous AIO approach has problem with bdrv_drain_all(), because writer +coroutines are not considered there. Those coroutines are not restarted, so +bdrv_drain_all() can fail (tracked_requests list not empty). + +We now use a thread, so we could also add compression here. + +Signed-off-by: Dietmar Maurer +--- + vma-writer.c | 296 +++++++++++++++++++++++++++++++++++----------------------- + 1 files changed, 180 insertions(+), 116 deletions(-) + +Index: new/vma-writer.c +=================================================================== +--- new.orig/vma-writer.c 2013-01-23 07:35:12.000000000 +0100 ++++ new/vma-writer.c 2013-01-23 09:24:19.000000000 +0100 +@@ -37,13 +37,21 @@ + + #define WRITE_BUFFERS 5 + +-typedef struct VmaAIOCB VmaAIOCB; +-struct VmaAIOCB { +- VmaWriter *vmaw; ++typedef struct WriteBuffer { + unsigned char buffer[VMA_MAX_EXTENT_SIZE]; + size_t bytes; +- Coroutine *co; +-}; ++} WriteBuffer; ++ ++typedef struct WriterThread { ++ int fd; ++ int error; ++ bool cancel; ++ GThread *thread; ++ GMutex *mutex; ++ GCond *change_cond; ++ WriteBuffer wbuf[WRITE_BUFFERS]; ++ CoQueue wqueue; ++} WriterThread; + + struct VmaWriter { + int fd; +@@ -60,8 +68,7 @@ + int outbuf_count; /* in VMA_BLOCKS */ + uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; + +- VmaAIOCB aiocbs[WRITE_BUFFERS]; +- CoQueue wqueue; ++ WriterThread wt; + + GChecksum *md5csum; + CoMutex writer_lock; +@@ -86,6 +93,107 @@ + uint32_t config_count; + }; + ++static gpointer vma_writer_thread(gpointer data) ++{ ++ WriterThread *wt = (WriterThread *)data; ++ ++ while (1) { ++ WriteBuffer *b = NULL; ++ ++ qemu_mutex_lock_iothread(); ++ int i; ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ if (wt->wbuf[i].bytes) { ++ b = &wt->wbuf[i]; ++ break; ++ } ++ } ++ qemu_mutex_unlock_iothread(); ++ ++ if (b) { ++ size_t done = 0; ++ while (done < b->bytes) { ++ int ret = write(wt->fd, b->buffer + done, b->bytes - done); ++ if (ret > 0) { ++ done += ret; ++ } else if (ret < 0) { ++ if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { ++ qemu_mutex_lock_iothread(); ++ wt->error = errno; ++ qemu_mutex_unlock_iothread(); ++ break; ++ } ++ } else if (ret == 0) { ++ /* should not happen - simply try again */ ++ } ++ } ++ qemu_mutex_lock_iothread(); ++ b->bytes = 0; ++ DPRINTF("AWAKE JOB %d\n", wt->error); ++ if (wt->error) { ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ wt->wbuf[i].bytes = 0; ++ } ++ qemu_co_queue_restart_all(&wt->wqueue); ++ } else { ++ qemu_co_queue_next(&wt->wqueue); ++ } ++ qemu_mutex_unlock_iothread(); ++ DPRINTF("AWAKE JOB END\n"); ++ } ++ ++ if (wt->error) { ++ DPRINTF("WRITER THREAD ERROR %d - exit thread\n", wt->error); ++ g_thread_exit(NULL); ++ } ++ ++ g_mutex_lock(wt->mutex); ++ bool cancel = wt->cancel; ++ if (!b && !cancel) { ++ DPRINTF("WRITER THREAD WAIT FOR DATA\n"); ++ g_cond_wait(wt->change_cond, wt->mutex); ++ cancel = wt->cancel; ++ } ++ g_mutex_unlock(wt->mutex); ++ ++ if (cancel) { ++ qemu_mutex_lock_iothread(); ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ wt->wbuf[i].bytes = 0; ++ } ++ qemu_co_queue_restart_all(&wt->wqueue); ++ qemu_mutex_unlock_iothread(); ++ DPRINTF("END WRITER THREAD\n"); ++ g_thread_exit(NULL); ++ } ++ } ++ ++ return NULL; ++} ++ ++static void vma_stop_writer_thread(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ ++ DPRINTF("vma_stop_writer_thread start\n"); ++ ++ if (vmaw->wt.thread) { ++ DPRINTF("vma_stop_writer_thread 1\n"); ++ g_mutex_lock(vmaw->wt.mutex); ++ DPRINTF("vma_stop_writer_thread 2\n"); ++ vmaw->wt.cancel = true; ++ g_cond_signal(vmaw->wt.change_cond); ++ g_mutex_unlock(vmaw->wt.mutex); ++ DPRINTF("vma_stop_writer_thread 3\n"); ++ qemu_mutex_unlock_iothread(); ++ g_thread_join(vmaw->wt.thread); ++ qemu_mutex_lock_iothread(); ++ DPRINTF("vma_stop_writer_thread 4\n"); ++ vmaw->wt.thread = NULL; ++ } ++ DPRINTF("vma_stop_writer_thread end\n"); ++} ++ + void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) + { + va_list ap; +@@ -213,111 +321,45 @@ + return n; + } + +-static void vma_co_continue_write(void *opaque) +-{ +- 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 ssize_t coroutine_fn +-vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes) +-{ +- size_t done = 0; +- ssize_t ret; +- +- /* atomic writes (we cannot interleave writes) */ +- qemu_co_mutex_lock(&vmaw->writer_lock); +- +- DPRINTF("vma_co_write enter %zd\n", bytes); +- +- while (done < bytes) { +- ret = write(vmaw->fd, buf + done, bytes - done); +- if (ret > 0) { +- done += ret; +- DPRINTF("vma_co_write written %zd %zd\n", done, ret); +- } 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 { +- vma_writer_set_error(vmaw, "vma_co_write write error - %s", +- strerror(errno)); +- done = -1; /* always return failure for partial writes */ +- break; +- } +- } else if (ret == 0) { +- /* should not happen - simply try again */ +- } +- } +- +- qemu_co_mutex_unlock(&vmaw->writer_lock); +- +- DPRINTF("vma_co_write leave %zd\n", done); +- return done; +-} +- +-static void coroutine_fn vma_co_writer_task(void *opaque) +-{ +- VmaAIOCB *cb = opaque; +- +- DPRINTF("vma_co_writer_task start\n"); +- +- int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); +- DPRINTF("vma_co_writer_task write done %zd\n", done); +- +- if (done != cb->bytes) { +- DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); +- vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", +- done); +- } +- +- cb->bytes = 0; +- +- qemu_co_queue_next(&cb->vmaw->wqueue); +- +- DPRINTF("vma_co_writer_task end\n"); +-} +- + static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) + { + DPRINTF("vma_queue_flush enter\n"); + + assert(vmaw); + ++ int error; ++ + while (1) { + int i; +- VmaAIOCB *cb = NULL; ++ WriteBuffer *b = NULL; ++ ++ error = vmaw->wt.error; ++ + for (i = 0; i < WRITE_BUFFERS; i++) { +- if (vmaw->aiocbs[i].bytes) { +- cb = &vmaw->aiocbs[i]; +- DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, +- vmaw->aiocbs[i].bytes); ++ if (vmaw->wt.wbuf[i].bytes) { ++ b = &vmaw->wt.wbuf[i]; ++ DPRINTF("FOUND USED WRITE BUFFER %d %zd\n", i, ++ vmaw->wt.wbuf[i].bytes); + break; + } + } +- if (!cb) { ++ ++ if (!b || error) { + break; + } +- qemu_co_queue_wait(&vmaw->wqueue); ++ DPRINTF("WAIT FOR BUFFER FLUSH\n"); ++ qemu_co_queue_wait(&vmaw->wt.wqueue); ++ DPRINTF("WAIT FOR BUFFER FLUSH END\n"); ++ } ++ ++ if (error) { ++ vma_writer_set_error(vmaw, "vma_queue_flush write error - %s", ++ strerror(error)); + } + + DPRINTF("vma_queue_flush leave\n"); + } + +-/** +- * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') +- * So we need to create a coroutione to allow 'parallel' execution. +- */ + static ssize_t coroutine_fn + vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) + { +@@ -327,29 +369,42 @@ + assert(buf); + assert(bytes <= VMA_MAX_EXTENT_SIZE); + +- VmaAIOCB *cb = NULL; +- while (!cb) { ++ int error = 0; ++ ++ /* wait for a free output buffer */ ++ WriteBuffer *b = NULL; ++ while (!b) { ++ error = vmaw->wt.error; ++ if (error) { ++ vma_writer_set_error(vmaw, "vma_queue_write error - %s", ++ strerror(error)); ++ return -1; ++ } ++ + int i; + for (i = 0; i < WRITE_BUFFERS; i++) { +- if (!vmaw->aiocbs[i].bytes) { +- cb = &vmaw->aiocbs[i]; ++ if (!vmaw->wt.wbuf[i].bytes) { ++ b = &vmaw->wt.wbuf[i]; + break; + } + } +- if (!cb) { +- qemu_co_queue_wait(&vmaw->wqueue); ++ if (!b) { ++ DPRINTF("WAIT FOR BUFFER\n"); ++ qemu_co_queue_wait(&vmaw->wt.wqueue); ++ DPRINTF("WAIT FOR BUFFER DONE\n"); + } + } + +- memcpy(cb->buffer, buf, bytes); +- cb->bytes = bytes; +- cb->vmaw = vmaw; ++ /* copy data to output buffer */ ++ memcpy(b->buffer, buf, bytes); ++ b->bytes = bytes; + +- DPRINTF("vma_queue_write start %zd\n", bytes); +- cb->co = qemu_coroutine_create(vma_co_writer_task); +- qemu_coroutine_enter(cb->co, cb); ++ g_mutex_lock(vmaw->wt.mutex); ++ /* signal writer thread that we have new data */ ++ g_cond_signal(vmaw->wt.change_cond); ++ g_mutex_unlock(vmaw->wt.mutex); + +- DPRINTF("vma_queue_write leave\n"); ++ DPRINTF("vma_queue_write queued %zd\n", bytes); + + return bytes; + } +@@ -386,10 +441,10 @@ + const char *tmp_id_str; + + if ((stat(filename, &st) == 0) && S_ISFIFO(st.st_mode)) { +- oflags = O_NONBLOCK|O_WRONLY; ++ oflags = O_WRONLY; + vmaw->fd = qemu_open(filename, oflags, 0644); + } else if (strstart(filename, "/dev/fdset/", &tmp_id_str)) { +- oflags = O_NONBLOCK|O_WRONLY; ++ oflags = 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); +@@ -397,7 +452,7 @@ + goto err; + } + } else { +- oflags = O_NONBLOCK|O_WRONLY|O_CREAT|O_EXCL; ++ oflags = O_WRONLY|O_CREAT|O_EXCL; + vmaw->fd = qemu_open(filename, oflags, 0644); + } + +@@ -415,10 +470,19 @@ + + qemu_co_mutex_init(&vmaw->writer_lock); + qemu_co_mutex_init(&vmaw->flush_lock); +- qemu_co_queue_init(&vmaw->wqueue); ++ qemu_co_queue_init(&vmaw->wt.wqueue); + + uuid_copy(vmaw->uuid, uuid); + ++ vmaw->wt.mutex = g_mutex_new(); ++ vmaw->wt.change_cond = g_cond_new(); ++ vmaw->wt.fd = vmaw->fd; ++ vmaw->wt.thread = g_thread_create(vma_writer_thread, &vmaw->wt, true, NULL); ++ if (vmaw->wt.thread == NULL) { ++ error_setg(errp, "can't allocate writer thread\n"); ++ goto err; ++ } ++ + return vmaw; + + err: +@@ -433,6 +497,14 @@ + g_checksum_free(vmaw->md5csum); + } + ++ if (vmaw->wt.mutex) { ++ g_mutex_free(vmaw->wt.mutex); ++ } ++ ++ if (vmaw->wt.change_cond) { ++ g_cond_free(vmaw->wt.change_cond); ++ } ++ + g_free(vmaw); + } + +@@ -672,6 +744,14 @@ + + *zero_bytes = 0; + ++ int error = vmaw->wt.error; ++ ++ if (error) { ++ vma_writer_set_error(vmaw, "vma_writer_get_buffer write error - %s", ++ strerror(error)); ++ return -1; ++ } ++ + if (vmaw->status < 0) { + return vmaw->status; + } +@@ -783,14 +863,17 @@ + + int i; + ++ DPRINTF("vma_writer_close start\n"); + vma_queue_flush(vmaw); + + /* this should not happen - just to be sure */ +- while (!qemu_co_queue_empty(&vmaw->wqueue)) { ++ while (!qemu_co_queue_empty(&vmaw->wt.wqueue)) { + DPRINTF("vma_writer_close wait\n"); + co_sleep_ns(rt_clock, 1000000); + } + ++ vma_stop_writer_thread(vmaw); ++ + if (vmaw->cmd) { + if (pclose(vmaw->cmd) < 0) { + vma_writer_set_error(vmaw, "vma_writer_close: " +@@ -835,8 +918,9 @@ + { + assert(vmaw); + +- int i; ++ vma_stop_writer_thread(vmaw); + ++ int i; + for (i = 0; i <= 255; i++) { + if (vmaw->stream_info[i].devname) { + g_free(vmaw->stream_info[i].devname); +@@ -847,6 +931,14 @@ + g_checksum_free(vmaw->md5csum); + } + ++ if (vmaw->wt.mutex) { ++ g_mutex_free(vmaw->wt.mutex); ++ } ++ ++ if (vmaw->wt.change_cond) { ++ g_cond_free(vmaw->wt.change_cond); ++ } ++ + g_free(vmaw); + } + diff --git a/debian/patches/old/0007-vma-add-verify-command.patch b/debian/patches/old/0007-vma-add-verify-command.patch new file mode 100644 index 0000000..a6aff85 --- /dev/null +++ b/debian/patches/old/0007-vma-add-verify-command.patch @@ -0,0 +1,320 @@ +From acdcd483ac6977e096ef7cde746d22bbf82e04d3 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Mon, 11 Mar 2013 07:07:46 +0100 +Subject: [PATCH v5 7/7] vma: add verify command + +Users wants to verify the archive after backup. + +Examples: + + # vma verify -v test.vma + + # lzop -d -c test.vma.lzo |vma verify - + +Signed-off-by: Dietmar Maurer +--- + vma-reader.c | 118 +++++++++++++++++++++++++++++++++++++++++++--------------- + vma.c | 57 +++++++++++++++++++++++++++- + vma.h | 1 + + 3 files changed, 145 insertions(+), 31 deletions(-) + +diff --git a/vma-reader.c b/vma-reader.c +index bc36cba..a740f04 100644 +--- a/vma-reader.c ++++ b/vma-reader.c +@@ -53,6 +53,8 @@ struct VmaReader { + time_t start_time; + int64_t cluster_count; + int64_t clusters_read; ++ int64_t zero_cluster_data; ++ int64_t partial_zero_cluster_data; + int clusters_read_per; + }; + +@@ -433,6 +435,27 @@ VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id) + return NULL; + } + ++static void allocate_rstate(VmaReader *vmar, guint8 dev_id, ++ BlockDriverState *bs, bool write_zeroes) ++{ ++ assert(vmar); ++ assert(dev_id); ++ ++ vmar->rstate[dev_id].bs = bs; ++ vmar->rstate[dev_id].write_zeroes = write_zeroes; ++ ++ int64_t size = vmar->devinfo[dev_id].size; ++ ++ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + ++ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; ++ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; ++ ++ 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; ++} ++ + int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, + bool write_zeroes, Error **errp) + { +@@ -449,17 +472,7 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, + return -1; + } + +- vmar->rstate[dev_id].bs = bs; +- vmar->rstate[dev_id].write_zeroes = write_zeroes; +- +- int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + +- (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; +- bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; +- +- 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; ++ allocate_rstate(vmar, dev_id, bs, write_zeroes); + + return 0; + } +@@ -526,9 +539,10 @@ static int restore_write_data(VmaReader *vmar, guint8 dev_id, + } + return 0; + } ++ + static int restore_extent(VmaReader *vmar, unsigned char *buf, + int extent_size, int vmstate_fd, +- bool verbose, Error **errp) ++ bool verbose, bool verify, Error **errp) + { + assert(vmar); + assert(buf); +@@ -553,7 +567,7 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, + + if (dev_id != vmar->vmstate_stream) { + bs = rstate->bs; +- if (!bs) { ++ if (!verify && !bs) { + error_setg(errp, "got wrong dev id %d", dev_id); + return -1; + } +@@ -609,10 +623,13 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, + return -1; + } + +- int nb_sectors = end_sector - sector_num; +- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, +- sector_num, nb_sectors, errp) < 0) { +- return -1; ++ if (!verify) { ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ buf + start, sector_num, nb_sectors, ++ errp) < 0) { ++ return -1; ++ } + } + + start += VMA_CLUSTER_SIZE; +@@ -642,26 +659,37 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, + return -1; + } + +- int nb_sectors = end_sector - sector_num; +- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, +- buf + start, sector_num, +- nb_sectors, errp) < 0) { +- return -1; ++ if (!verify) { ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ buf + start, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } + } + + start += VMA_BLOCK_SIZE; + + } else { + +- if (rstate->write_zeroes && (end_sector > sector_num)) { ++ ++ if (end_sector > sector_num) { + /* Todo: use bdrv_co_write_zeroes (but that need to + * be run inside coroutine?) + */ + int nb_sectors = end_sector - sector_num; +- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, +- zero_vma_block, sector_num, +- nb_sectors, errp) < 0) { +- return -1; ++ int zero_size = BDRV_SECTOR_SIZE*nb_sectors; ++ vmar->zero_cluster_data += zero_size; ++ if (mask != 0) { ++ vmar->partial_zero_cluster_data += zero_size; ++ } ++ ++ if (rstate->write_zeroes && !verify) { ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ zero_vma_block, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } + } + } + } +@@ -679,8 +707,9 @@ static int restore_extent(VmaReader *vmar, unsigned char *buf, + return 0; + } + +-int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, +- Error **errp) ++static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd, ++ bool verbose, bool verify, ++ Error **errp) + { + assert(vmar); + assert(vmar->head_data); +@@ -747,7 +776,7 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, + } + + if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, +- errp) < 0) { ++ verify, errp) < 0) { + return -1; + } + +@@ -794,6 +823,35 @@ int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, + } + } + ++ if (verbose) { ++ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", ++ vmar->clusters_read*VMA_CLUSTER_SIZE, ++ vmar->zero_cluster_data, ++ (double)(100.0*vmar->zero_cluster_data)/ ++ (vmar->clusters_read*VMA_CLUSTER_SIZE)); ++ printf("space reduction due to 4K zero blocks %.3g%%\n", ++ (double)(100.0*vmar->partial_zero_cluster_data) / ++ (vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data)); ++ } + return ret; + } + ++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, ++ Error **errp) ++{ ++ return vma_reader_restore_full(vmar, vmstate_fd, verbose, false, errp); ++} ++ ++int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp) ++{ ++ guint8 dev_id; ++ ++ for (dev_id = 1; dev_id < 255; dev_id++) { ++ if (vma_reader_get_device_info(vmar, dev_id)) { ++ allocate_rstate(vmar, dev_id, NULL, false); ++ } ++ } ++ ++ return vma_reader_restore_full(vmar, -1, verbose, true, errp); ++} ++ +diff --git a/vma.c b/vma.c +index 6633aa5..bcde379 100644 +--- a/vma.c ++++ b/vma.c +@@ -33,7 +33,8 @@ static void help(void) + "\n" + "vma list \n" + "vma create [-c config] pathname ...\n" +- "vma extract [-r] \n" ++ "vma extract [-v] [-r] \n" ++ "vma verify [-v]\n" + ; + + printf("%s", help_msg); +@@ -337,6 +338,58 @@ static int extract_content(int argc, char **argv) + return ret; + } + ++static int verify_content(int argc, char **argv) ++{ ++ int c, ret = 0; ++ int verbose = 0; ++ const char *filename; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hv"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ default: ++ help(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 1) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (verbose) { ++ print_content(vmar); ++ } ++ ++ if (vma_reader_verify(vmar, verbose, &errp) < 0) { ++ g_error("verify failed - %s", error_get_pretty(errp)); ++ } ++ ++ vma_reader_destroy(vmar); ++ ++ bdrv_close_all(); ++ ++ return ret; ++} ++ + typedef struct BackupCB { + VmaWriter *vmaw; + uint8_t dev_id; +@@ -554,6 +607,8 @@ int main(int argc, char **argv) + return create_archive(argc, argv); + } else if (!strcmp(cmdname, "extract")) { + return extract_content(argc, argv); ++ } else if (!strcmp(cmdname, "verify")) { ++ return verify_content(argc, argv); + } + + help(); +diff --git a/vma.h b/vma.h +index 76d0dc8..ebd96c1 100644 +--- a/vma.h ++++ b/vma.h +@@ -141,5 +141,6 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, + Error **errp); + int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, + Error **errp); ++int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp); + + #endif /* BACKUP_VMA_H */ +-- +1.7.2.5 + diff --git a/debian/patches/old/0008-vma-restore-tolerate-a-size-difference-up-to-4M.patch b/debian/patches/old/0008-vma-restore-tolerate-a-size-difference-up-to-4M.patch new file mode 100644 index 0000000..2e76dae --- /dev/null +++ b/debian/patches/old/0008-vma-restore-tolerate-a-size-difference-up-to-4M.patch @@ -0,0 +1,33 @@ +From 4ca68d0ccfd64f2fdc63ba44813bbafab8d90c81 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 26 Mar 2013 06:21:16 +0100 +Subject: [PATCH v5 8/8] vma restore: tolerate a size difference up to 4M + + +Signed-off-by: Dietmar Maurer +--- + vma-reader.c | 8 +++++++- + 1 files changed, 7 insertions(+), 1 deletions(-) + +diff --git a/vma-reader.c b/vma-reader.c +index a740f04..69e7578 100644 +--- a/vma-reader.c ++++ b/vma-reader.c +@@ -465,7 +465,13 @@ int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, + assert(vmar->rstate[dev_id].bs == NULL); + + int64_t size = bdrv_getlength(bs); +- if (size != vmar->devinfo[dev_id].size) { ++ int64_t size_diff = size - vmar->devinfo[dev_id].size; ++ ++ /* storage types can have different size restrictions, so it ++ * is not always possible to create an image with exact size. ++ * So we tolerate a size difference up to 4MB. ++ */ ++ if ((size_diff < 0) || (size_diff > 4*1024*1024)) { + error_setg(errp, "vma_reader_register_bs for stream %s failed - " + "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, + size, vmar->devinfo[dev_id].size); +-- +1.7.2.5 + diff --git a/debian/patches/old/0009-vma-only-store-the-basename-of-a-configuration-file.patch b/debian/patches/old/0009-vma-only-store-the-basename-of-a-configuration-file.patch new file mode 100644 index 0000000..f6b3a66 --- /dev/null +++ b/debian/patches/old/0009-vma-only-store-the-basename-of-a-configuration-file.patch @@ -0,0 +1,45 @@ +From 41811d473df9a1cb09bc4f44eba3147dcec231b1 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Wed, 8 May 2013 10:01:29 +0200 +Subject: [PATCH v5 9/9] vma: only store the basename of a configuration file + +It makes no sense to store the whole filesystem path here. + +Signed-off-by: Dietmar Maurer +--- + vma-writer.c | 5 ++++- + vma.c | 2 +- + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/vma-writer.c b/vma-writer.c +index 9228ca6..3cc9cf2 100644 +--- a/vma-writer.c ++++ b/vma-writer.c +@@ -145,7 +145,10 @@ int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, + assert(data); + assert(len); + +- uint32_t name_ptr = allocate_header_string(vmaw, name); ++ gchar *basename = g_path_get_basename(name); ++ uint32_t name_ptr = allocate_header_string(vmaw, basename); ++ g_free(basename); ++ + if (!name_ptr) { + return -1; + } +diff --git a/vma.c b/vma.c +index bcde379..f4a8218 100644 +--- a/vma.c ++++ b/vma.c +@@ -33,7 +33,7 @@ static void help(void) + "\n" + "vma list \n" + "vma create [-c config] pathname ...\n" +- "vma extract [-v] [-r] \n" ++ "vma extract [-v] [-r ] \n" + "vma verify [-v]\n" + ; + +-- +1.7.10.4 + diff --git a/debian/patches/old/CVE-2015-7549-msix-pba-write-ro.patch b/debian/patches/old/CVE-2015-7549-msix-pba-write-ro.patch new file mode 100644 index 0000000..631ae37 --- /dev/null +++ b/debian/patches/old/CVE-2015-7549-msix-pba-write-ro.patch @@ -0,0 +1,52 @@ +commit 43b11a91dd861a946b231b89b7542856ade23d1b +Author: Marc-André Lureau +Date: Fri Jun 26 14:25:29 2015 +0200 + + msix: implement pba write (but read-only) + + qpci_msix_pending() writes on pba region, causing qemu to SEGV: + + Program received signal SIGSEGV, Segmentation fault. + [Switching to Thread 0x7ffff7fba8c0 (LWP 25882)] + 0x0000000000000000 in ?? () + (gdb) bt + #0 0x0000000000000000 in () + #1 0x00005555556556c5 in memory_region_oldmmio_write_accessor (mr=0x5555579f3f80, addr=0, value=0x7fffffffbf68, size=4, shift=0, mask=4294967295, attrs=...) at /home/elmarco/src/qemu/memory.c:434 + #2 0x00005555556558e1 in access_with_adjusted_size (addr=0, value=0x7fffffffbf68, size=4, access_size_min=1, access_size_max=4, access=0x55555565563e , mr=0x5555579f3f80, attrs=...) at /home/elmarco/src/qemu/memory.c:506 + #3 0x00005555556581eb in memory_region_dispatch_write (mr=0x5555579f3f80, addr=0, data=0, size=4, attrs=...) at /home/elmarco/src/qemu/memory.c:1176 + #4 0x000055555560b6f9 in address_space_rw (as=0x555555eff4e0 , addr=3759147008, attrs=..., buf=0x7fffffffc1b0 "", len=4, is_write=true) at /home/elmarco/src/qemu/exec.c:2439 + #5 0x000055555560baa2 in cpu_physical_memory_rw (addr=3759147008, buf=0x7fffffffc1b0 "", len=4, is_write=1) at /home/elmarco/src/qemu/exec.c:2534 + #6 0x000055555564c005 in cpu_physical_memory_write (addr=3759147008, buf=0x7fffffffc1b0, len=4) at /home/elmarco/src/qemu/include/exec/cpu-common.h:80 + #7 0x000055555564cd9c in qtest_process_command (chr=0x55555642b890, words=0x5555578de4b0) at /home/elmarco/src/qemu/qtest.c:378 + #8 0x000055555564db77 in qtest_process_inbuf (chr=0x55555642b890, inbuf=0x55555641b340) at /home/elmarco/src/qemu/qtest.c:569 + #9 0x000055555564dc07 in qtest_read (opaque=0x55555642b890, buf=0x7fffffffc2e0 "writel 0xe0100800 0x0\n", size=22) at /home/elmarco/src/qemu/qtest.c:581 + #10 0x000055555574ce3e in qemu_chr_be_write (s=0x55555642b890, buf=0x7fffffffc2e0 "writel 0xe0100800 0x0\n", len=22) at qemu-char.c:306 + #11 0x0000555555751263 in tcp_chr_read (chan=0x55555642bcf0, cond=G_IO_IN, opaque=0x55555642b890) at qemu-char.c:2876 + #12 0x00007ffff64c9a8a in g_main_context_dispatch (context=0x55555641c400) at gmain.c:3122 + + (without this patch, this can be reproduced with the ivshmem qtest) + + Implement an empty mmio write to avoid the crash. + + Signed-off-by: Marc-André Lureau + Reviewed-by: Paolo Bonzini + +diff --git a/hw/pci/msix.c b/hw/pci/msix.c +index 2fdada4..64c93d8 100644 +--- a/hw/pci/msix.c ++++ b/hw/pci/msix.c +@@ -200,8 +200,14 @@ static uint64_t msix_pba_mmio_read(void *opaque, hwaddr addr, + return pci_get_long(dev->msix_pba + addr); + } + ++static void msix_pba_mmio_write(void *opaque, hwaddr addr, ++ uint64_t val, unsigned size) ++{ ++} ++ + static const MemoryRegionOps msix_pba_mmio_ops = { + .read = msix_pba_mmio_read, ++ .write = msix_pba_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, diff --git a/debian/patches/old/CVE-2015-8558-ehci_make_idt_processing_more_robust.patch b/debian/patches/old/CVE-2015-8558-ehci_make_idt_processing_more_robust.patch new file mode 100644 index 0000000..743b68d --- /dev/null +++ b/debian/patches/old/CVE-2015-8558-ehci_make_idt_processing_more_robust.patch @@ -0,0 +1,49 @@ +From: Gerd Hoffmann +Subject: [Qemu-devel] [PULL 5/5] ehci: make idt processing more robust + +Make ehci_process_itd return an error in case we didn't do any actual +iso transfer because we've found no active transaction. That'll avoid +ehci happily run in circles forever if the guest builds a loop out of +idts. + +This is CVE-2015-8558. + +Cc: qemu-stable@nongnu.org +Reported-by: Qinghao Tang +Tested-by: P J P +Signed-off-by: Gerd Hoffmann +--- + hw/usb/hcd-ehci.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c +index 4e2161b..d07f228 100644 +--- a/hw/usb/hcd-ehci.c ++++ b/hw/usb/hcd-ehci.c +@@ -1389,7 +1389,7 @@ static int ehci_process_itd(EHCIState *ehci, + { + USBDevice *dev; + USBEndpoint *ep; +- uint32_t i, len, pid, dir, devaddr, endp; ++ uint32_t i, len, pid, dir, devaddr, endp, xfers = 0; + uint32_t pg, off, ptr1, ptr2, max, mult; + + ehci->periodic_sched_active = PERIODIC_ACTIVE; +@@ -1479,9 +1479,10 @@ static int ehci_process_itd(EHCIState *ehci, + ehci_raise_irq(ehci, USBSTS_INT); + } + itd->transact[i] &= ~ITD_XACT_ACTIVE; ++ xfers++; + } + } +- return 0; ++ return xfers ? 0 : -1; + } + + +-- +1.8.3.1 + + + + diff --git a/debian/patches/old/CVE-2015-8613-scsi-initialize-info-object.patch b/debian/patches/old/CVE-2015-8613-scsi-initialize-info-object.patch new file mode 100644 index 0000000..ee0f36d --- /dev/null +++ b/debian/patches/old/CVE-2015-8613-scsi-initialize-info-object.patch @@ -0,0 +1,31 @@ +From 5823b4a214ede884f4ba597fdd629862620e0f92 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Mon, 21 Dec 2015 14:48:18 +0530 +Subject: [PATCH] scsi: initialise info object with appropriate size + +While processing controller 'CTRL_GET_INFO' command, the routine +'megasas_ctrl_get_info' overflows the '&info' object size. Use its +appropriate size to null initialise it. + +Reported-by: Qinghao Tang +Signed-off-by: Prasad J Pandit +--- + hw/scsi/megasas.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c +index d7dc667..576f56c 100644 +--- a/hw/scsi/megasas.c ++++ b/hw/scsi/megasas.c +@@ -718,7 +718,7 @@ static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd) + BusChild *kid; + int num_pd_disks = 0; + +- memset(&info, 0x0, cmd->iov_size); ++ memset(&info, 0x0, dcmd_size); + if (cmd->iov_size < dcmd_size) { + trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, + dcmd_size); +-- +2.4.3 +=== diff --git a/debian/patches/old/CVE-2015-8619-hmp-sendkey-oob-fix.patch b/debian/patches/old/CVE-2015-8619-hmp-sendkey-oob-fix.patch new file mode 100644 index 0000000..78aea0c --- /dev/null +++ b/debian/patches/old/CVE-2015-8619-hmp-sendkey-oob-fix.patch @@ -0,0 +1,44 @@ +From 8da4a3bf8fb076314f986a0d58cb94f5458e3659 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +Date: Mon, 11 Jan 2016 08:21:25 +0100 +Subject: [PATCH] hmp: fix sendkey out of bounds write (CVE-2015-8619) + +When processing 'sendkey' command, hmp_sendkey routine null +terminates the 'keyname_buf' array. This results in an OOB +write issue, if 'keyname_len' was to fall outside of +'keyname_buf' array. + +Now checking the length against the buffer size before using +it. + +Reported-by: Ling Liu +Signed-off-by: Wolfgang Bumiller +--- + hmp.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/hmp.c b/hmp.c +index c2b2c16..0c7a04c 100644 +--- a/hmp.c ++++ b/hmp.c +@@ -1749,6 +1749,8 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) + while (1) { + separator = strchr(keys, '-'); + keyname_len = separator ? separator - keys : strlen(keys); ++ if (keyname_len >= sizeof(keyname_buf)) ++ goto err_out; + pstrcpy(keyname_buf, sizeof(keyname_buf), keys); + + /* Be compatible with old interface, convert user inputted "<" */ +@@ -1800,7 +1802,7 @@ out: + return; + + err_out: +- monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); ++ monitor_printf(mon, "invalid parameter: %s\n", keys); + goto out; + } + +-- +2.1.4 + diff --git a/debian/patches/old/CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch b/debian/patches/old/CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch new file mode 100644 index 0000000..b46ab48 --- /dev/null +++ b/debian/patches/old/CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch @@ -0,0 +1,45 @@ +From d9a3b33d2c9f996537b7f1d0246dee2d0120cefb Mon Sep 17 00:00:00 2001 +From: "Michael S. Tsirkin" +Date: Thu, 19 Nov 2015 15:14:07 +0200 +Subject: [PATCH] acpi: fix buffer overrun on migration + +ich calls acpi_gpe_init with length ICH9_PMIO_GPE0_LEN so +ICH9_PMIO_GPE0_LEN/2 bytes are allocated, but then the full +ICH9_PMIO_GPE0_LEN bytes are migrated. + +As a quick work-around, allocate twice the memory. +We'll probably want to tweak code to avoid +migrating the extra ICH9_PMIO_GPE0_LEN/2 bytes, +but that is a bit trickier to do without breaking +migration compatibility. + +Tested-by: "Dr. David Alan Gilbert" +Reported-by: "Dr. David Alan Gilbert" +Cc: qemu-stable@nongnu.org +Signed-off-by: Michael S. Tsirkin +--- + hw/acpi/core.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/hw/acpi/core.c b/hw/acpi/core.c +index fe6215a..21e113d 100644 +--- a/hw/acpi/core.c ++++ b/hw/acpi/core.c +@@ -625,8 +625,12 @@ void acpi_pm1_cnt_reset(ACPIREGS *ar) + void acpi_gpe_init(ACPIREGS *ar, uint8_t len) + { + ar->gpe.len = len; +- ar->gpe.sts = g_malloc0(len / 2); +- ar->gpe.en = g_malloc0(len / 2); ++ /* Only first len / 2 bytes are ever used, ++ * but the caller in ich9.c migrates full len bytes. ++ * TODO: fix ich9.c and drop the extra allocation. ++ */ ++ ar->gpe.sts = g_malloc0(len); ++ ar->gpe.en = g_malloc0(len); + } + + void acpi_gpe_reset(ACPIREGS *ar) +-- +2.1.4 + diff --git a/debian/patches/old/CVE-2015-8701-net-rocker-off-by-one.patch b/debian/patches/old/CVE-2015-8701-net-rocker-off-by-one.patch new file mode 100644 index 0000000..7b17355 --- /dev/null +++ b/debian/patches/old/CVE-2015-8701-net-rocker-off-by-one.patch @@ -0,0 +1,47 @@ +From 60e8fd72b0faaf940e220a0514001b86b7149e09 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Mon, 28 Dec 2015 16:24:08 +0530 +Subject: [PATCH] net: rocker: fix an incorrect array bounds check + +While processing transmit(tx) descriptors in 'tx_consume' routine +the switch emulator suffers from an off-by-one error, if a +descriptor was to have more than allowed(ROCKER_TX_FRAGS_MAX=16) +fragments. Fix an incorrect bounds check to avoid it. + +Reported-by: Qinghao Tang +Cc: qemu-stable@nongnu.org +Signed-off-by: Prasad J Pandit +Signed-off-by: Jason Wang +--- + hw/net/rocker/rocker.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c +index c57f1a6..2e77e50 100644 +--- a/hw/net/rocker/rocker.c ++++ b/hw/net/rocker/rocker.c +@@ -232,6 +232,9 @@ static int tx_consume(Rocker *r, DescInfo *info) + frag_addr = rocker_tlv_get_le64(tlvs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]); + frag_len = rocker_tlv_get_le16(tlvs[ROCKER_TLV_TX_FRAG_ATTR_LEN]); + ++ if (iovcnt >= ROCKER_TX_FRAGS_MAX) { ++ goto err_too_many_frags; ++ } + iov[iovcnt].iov_len = frag_len; + iov[iovcnt].iov_base = g_malloc(frag_len); + if (!iov[iovcnt].iov_base) { +@@ -244,10 +247,7 @@ static int tx_consume(Rocker *r, DescInfo *info) + err = -ROCKER_ENXIO; + goto err_bad_io; + } +- +- if (++iovcnt > ROCKER_TX_FRAGS_MAX) { +- goto err_too_many_frags; +- } ++ iovcnt++; + } + + if (iovcnt) { +-- +2.1.4 + diff --git a/debian/patches/old/CVE-2015-8743-ne2000-ioport-bounds-check.patch b/debian/patches/old/CVE-2015-8743-ne2000-ioport-bounds-check.patch new file mode 100644 index 0000000..5b34ecf --- /dev/null +++ b/debian/patches/old/CVE-2015-8743-ne2000-ioport-bounds-check.patch @@ -0,0 +1,48 @@ +From ab216355b6d509dce42fda4391f61b49df2ddc93 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Thu, 31 Dec 2015 17:05:27 +0530 +Subject: [PATCH] net: ne2000: fix bounds check in ioport operations + +While doing ioport r/w operations, ne2000 device emulation suffers +from OOB r/w errors. Update respective array bounds check to avoid +OOB access. + +Reported-by: Ling Liu +Cc: qemu-stable@nongnu.org +Signed-off-by: Prasad J Pandit +Signed-off-by: Jason Wang +--- + hw/net/ne2000.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c +index 010f9ef..a3dffff 100644 +--- a/hw/net/ne2000.c ++++ b/hw/net/ne2000.c +@@ -467,8 +467,9 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, + uint32_t val) + { + addr &= ~1; /* XXX: check exact behaviour if not even */ +- if (addr < 32 || +- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { ++ if (addr < 32 ++ || (addr >= NE2000_PMEM_START ++ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { + stl_le_p(s->mem + addr, val); + } + } +@@ -497,8 +498,9 @@ static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) + static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) + { + addr &= ~1; /* XXX: check exact behaviour if not even */ +- if (addr < 32 || +- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { ++ if (addr < 32 ++ || (addr >= NE2000_PMEM_START ++ && addr + sizeof(uint32_t) <= NE2000_MEM_SIZE)) { + return ldl_le_p(s->mem + addr); + } else { + return 0xffffffff; +-- +2.1.4 + diff --git a/debian/patches/old/CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch b/debian/patches/old/CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch new file mode 100644 index 0000000..fbb9f54 --- /dev/null +++ b/debian/patches/old/CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch @@ -0,0 +1,76 @@ +From a7278b36fcab9af469563bd7b9dadebe2ae25e48 Mon Sep 17 00:00:00 2001 +From: Dana Rubin +Date: Tue, 18 Aug 2015 12:45:55 +0300 +Subject: [PATCH] net/vmxnet3: Refine l2 header validation + +Validation of l2 header length assumed minimal packet size as +eth_header + 2 * vlan_header regardless of the actual protocol. + +This caused crash for valid non-IP packets shorter than 22 bytes, as +'tx_pkt->packet_type' hasn't been assigned for such packets, and +'vmxnet3_on_tx_done_update_stats()' expects it to be properly set. + +Refine header length validation in 'vmxnet_tx_pkt_parse_headers'. +Check its return value during packet processing flow. + +As a side effect, in case IPv4 and IPv6 header validation failure, +corrupt packets will be dropped. + +Signed-off-by: Dana Rubin +Signed-off-by: Shmulik Ladkani +Signed-off-by: Jason Wang +--- + hw/net/vmxnet3.c | 4 +--- + hw/net/vmxnet_tx_pkt.c | 19 ++++++++++++++++--- + 2 files changed, 17 insertions(+), 6 deletions(-) + +diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c +index 04159c8..48ced71 100644 +--- a/hw/net/vmxnet3.c ++++ b/hw/net/vmxnet3.c +@@ -729,9 +729,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx) + } + + if (txd.eop) { +- if (!s->skip_current_tx_pkt) { +- vmxnet_tx_pkt_parse(s->tx_pkt); +- ++ if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) { + if (s->needs_vlan) { + vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci); + } +diff --git a/hw/net/vmxnet_tx_pkt.c b/hw/net/vmxnet_tx_pkt.c +index f7344c4..eb88ddf 100644 +--- a/hw/net/vmxnet_tx_pkt.c ++++ b/hw/net/vmxnet_tx_pkt.c +@@ -142,11 +142,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt) + + bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base, + ETH_MAX_L2_HDR_LEN); +- if (bytes_read < ETH_MAX_L2_HDR_LEN) { ++ if (bytes_read < sizeof(struct eth_header)) { ++ l2_hdr->iov_len = 0; ++ return false; ++ } ++ ++ l2_hdr->iov_len = sizeof(struct eth_header); ++ switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) { ++ case ETH_P_VLAN: ++ l2_hdr->iov_len += sizeof(struct vlan_header); ++ break; ++ case ETH_P_DVLAN: ++ l2_hdr->iov_len += 2 * sizeof(struct vlan_header); ++ break; ++ } ++ ++ if (bytes_read < l2_hdr->iov_len) { + l2_hdr->iov_len = 0; + return false; +- } else { +- l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base); + } + + l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len); +-- +2.1.4 + diff --git a/debian/patches/old/CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch b/debian/patches/old/CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch new file mode 100644 index 0000000..deb755f --- /dev/null +++ b/debian/patches/old/CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch @@ -0,0 +1,37 @@ +From c6048f849c7e3f009786df76206e895a69de032c Mon Sep 17 00:00:00 2001 +From: Shmulik Ladkani +Date: Mon, 21 Sep 2015 17:09:02 +0300 +Subject: [PATCH] vmxnet3: Support reading IMR registers on bar0 + +Instead of asserting, return the actual IMR register value. +This is aligned with what's returned on ESXi. + +Signed-off-by: Shmulik Ladkani +Tested-by: Dana Rubin +Signed-off-by: Jason Wang +--- + hw/net/vmxnet3.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c +index 48ced71..057f0dc 100644 +--- a/hw/net/vmxnet3.c ++++ b/hw/net/vmxnet3.c +@@ -1163,9 +1163,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr, + static uint64_t + vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size) + { ++ VMXNET3State *s = opaque; ++ + if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR, + VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) { +- g_assert_not_reached(); ++ int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR, ++ VMXNET3_REG_ALIGN); ++ return s->interrupt_states[l].is_masked; + } + + VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size); +-- +2.1.4 + diff --git a/debian/patches/old/CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch b/debian/patches/old/CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch new file mode 100644 index 0000000..e7d5ebc --- /dev/null +++ b/debian/patches/old/CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch @@ -0,0 +1,37 @@ +From b9a60d97fac671f31df599e7e48fd28fc203c0af Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Sat, 9 Jan 2016 01:18:31 +0530 +Subject: [PATCH] ide: ahci: reset ncq object to unused on error + +When processing NCQ commands, ACHI device emulation prepares a +NCQ transfer object; To which an aio control block(aiocb) object +is assigned in 'execute_ncq_command'. In case, when the NCQ +command is invalid, the 'aiocb' object is not assigned, and NCQ +transfer object is left as 'used'. This leads to a use after +free kind of error in 'bdrv_aio_cancel_async' via 'ahci_reset_port'. +Reset NCQ transfer object to 'unused' to avoid it. + +Reported-by: Qinghao Tang +Signed-off-by: Prasad J Pandit +Reviewed-by: John Snow +Message-id: 1452282511-4116-1-git-send-email-ppandit@redhat.com +Signed-off-by: John Snow +--- + hw/ide/ahci.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c +index dd1912e..17f1cbd 100644 +--- a/hw/ide/ahci.c ++++ b/hw/ide/ahci.c +@@ -910,6 +910,7 @@ static void ncq_err(NCQTransferState *ncq_tfs) + ide_state->error = ABRT_ERR; + ide_state->status = READY_STAT | ERR_STAT; + ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); ++ ncq_tfs->used = 0; + } + + static void ncq_finish(NCQTransferState *ncq_tfs) +-- +2.1.4 + diff --git a/debian/patches/old/add-qmp-get-link-status.patch b/debian/patches/old/add-qmp-get-link-status.patch new file mode 100644 index 0000000..83df213 --- /dev/null +++ b/debian/patches/old/add-qmp-get-link-status.patch @@ -0,0 +1,111 @@ +Index: new/scripts/qapi.py +=================================================================== +--- new.orig/scripts/qapi.py 2015-07-13 10:00:00.000000000 +0100 ++++ new/scripts/qapi.py 2015-07-13 15:50:00.000000000 +0100 +@@ -37,6 +37,8 @@ + + # Whitelist of commands allowed to return a non-dictionary + returns_whitelist = [ ++ 'get_link_status', ++ + # From QMP: + 'human-monitor-command', + 'query-migrate-cache-size', +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-12-10 09:15:50.890262765 +0100 ++++ new/qapi-schema.json 2014-12-11 09:20:31.072561486 +0100 +@@ -1366,6 +1366,21 @@ + ## + { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } + ++## ++# @get_link_status ++# ++# Get the current link state of the nics or nic. ++# ++# @name: name of the nic you get the state of ++# ++# Return: If link is up 1 ++# If link is down 0 ++# If an error occure an empty string. ++# ++# Notes: this is an Proxmox VE extension and not offical part of Qemu. ++## ++{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'} ++ + ## + # @balloon: + # +Index: new/net/net.c +=================================================================== +--- new.orig/net/net.c 2014-12-10 10:24:39.790496356 +0100 ++++ new/net/net.c 2014-12-11 09:37:55.971321170 +0100 +@@ -1141,6 +1141,33 @@ + } + } + ++int64_t qmp_get_link_status(const char *name, Error **errp) ++{ ++ NetClientState *ncs[MAX_QUEUE_NUM]; ++ NetClientState *nc; ++ int queues; ++ bool ret; ++ ++ queues = qemu_find_net_clients_except(name, ncs, ++ NET_CLIENT_OPTIONS_KIND_MAX, ++ MAX_QUEUE_NUM); ++ ++ if (queues == 0) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", name); ++ return (int64_t) -1; ++ } ++ ++ nc = ncs[0]; ++ ret = ncs[0]->link_down; ++ ++ if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { ++ ret = ncs[0]->peer->link_down; ++ } ++ ++ return (int64_t) ret ? 0 : 1; ++} ++ + void qmp_set_link(const char *name, bool up, Error **errp) + { + NetClientState *ncs[MAX_QUEUE_NUM]; +Index: new/qmp-commands.hx +=================================================================== +--- new.orig/qmp-commands.hx 2014-12-10 09:15:50.891262737 +0100 ++++ new/qmp-commands.hx 2014-12-11 08:36:26.583532314 +0100 +@@ -1473,6 +1473,29 @@ + EQMP + + { ++ .name = "get_link_status", ++ .args_type = "name:s", ++ .mhandler.cmd_new = qmp_marshal_input_get_link_status, ++ }, ++ ++SQMP ++get_link_status ++-------- ++ ++Get the link status of a network adapter. ++ ++Arguments: ++ ++- "name": network device name (json-string) ++ ++Example: ++ ++-> { "execute": "get_link_status", "arguments": { "name": "e1000.0" } } ++<- { "return": {1} } ++ ++EQMP ++ ++ { + .name = "getfd", + .args_type = "fdname:s", + .params = "getfd name", diff --git a/debian/patches/old/add_firewall_to_vma.patch b/debian/patches/old/add_firewall_to_vma.patch new file mode 100644 index 0000000..0ab20a0 --- /dev/null +++ b/debian/patches/old/add_firewall_to_vma.patch @@ -0,0 +1,143 @@ +Index: qemu-kvm-dev/qapi-schema.json +=================================================================== +--- qemu-kvm-dev.orig/qapi-schema.json ++++ qemu-kvm-dev/qapi-schema.json +@@ -416,6 +416,7 @@ + { 'command': 'backup', 'data': { 'backup-file': 'str', + '*format': 'BackupFormat', + '*config-file': 'str', ++ '*firewall-file': 'str', + '*devlist': 'str', '*speed': 'int' }, + 'returns': 'UuidInfo' } + +Index: qemu-kvm-dev/qmp-commands.hx +=================================================================== +--- qemu-kvm-dev.orig/qmp-commands.hx ++++ qemu-kvm-dev/qmp-commands.hx +@@ -1204,7 +1204,7 @@ EQMP + + { + .name = "backup", +- .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?", ++ .args_type = "backup-file:s,format:s?,config-file:F?,firewall-file:F?,speed:o?,devlist:s?", + .mhandler.cmd_new = qmp_marshal_input_backup, + }, + +Index: qemu-kvm-dev/blockdev.c +=================================================================== +--- qemu-kvm-dev.orig/blockdev.c ++++ qemu-kvm-dev/blockdev.c +@@ -2457,6 +2457,44 @@ void qmp_backup_cancel(Error **errp) + } + } + ++static int config_to_vma(const char *file, BackupFormat format, ++ const char *backup_dir, VmaWriter *vmaw, ++ Error **errp) ++{ ++ char *cdata = NULL; ++ gsize clen = 0; ++ GError *err = NULL; ++ if (!g_file_get_contents(file, &cdata, &clen, &err)) { ++ error_setg(errp, "unable to read file '%s'", file); ++ return 1; ++ } ++ ++ char *basename = g_path_get_basename(file); ++ ++ if (format == BACKUP_FORMAT_VMA) { ++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { ++ error_setg(errp, "unable to add %s config data to vma archive", file); ++ g_free(cdata); ++ g_free(basename); ++ return 1; ++ } ++ } else if (format == BACKUP_FORMAT_DIR) { ++ char config_path[PATH_MAX]; ++ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); ++ if (!g_file_set_contents(config_path, cdata, clen, &err)) { ++ error_setg(errp, "unable to write config file '%s'", config_path); ++ g_free(cdata); ++ g_free(basename); ++ return 1; ++ } ++ } ++ ++ g_free(basename); ++ g_free(cdata); ++ ++ return 0; ++} ++ + static void pvebackup_run_next_job(void) + { + GList *l = backup_state.di_list; +@@ -2483,6 +2521,7 @@ static void pvebackup_run_next_job(void) + UuidInfo *qmp_backup(const char *backup_file, bool has_format, + BackupFormat format, + bool has_config_file, const char *config_file, ++ bool has_firewall_file, const char *firewall_file, + bool has_devlist, const char *devlist, + bool has_speed, int64_t speed, Error **errp) + { +@@ -2635,38 +2674,17 @@ UuidInfo *qmp_backup(const char *backup_ + + /* add configuration file to archive */ + if (has_config_file) { +- char *cdata = NULL; +- gsize clen = 0; +- GError *err = NULL; +- if (!g_file_get_contents(config_file, &cdata, &clen, &err)) { +- error_setg(errp, "unable to read file '%s'", config_file); +- goto err; +- } +- +- char *basename = g_path_get_basename(config_file); +- +- if (format == BACKUP_FORMAT_VMA) { +- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { +- error_setg(errp, "unable to add config data to vma archive"); +- g_free(cdata); +- g_free(basename); +- goto err; +- } +- } else if (format == BACKUP_FORMAT_DIR) { +- char config_path[PATH_MAX]; +- snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); +- if (!g_file_set_contents(config_path, cdata, clen, &err)) { +- error_setg(errp, "unable to write config file '%s'", config_path); +- g_free(cdata); +- g_free(basename); +- goto err; +- } +- } +- +- g_free(basename); +- g_free(cdata); ++ if(config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) { ++ goto err; ++ } + } + ++ /* add firewall file to archive */ ++ if (has_firewall_file) { ++ if(config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) { ++ goto err; ++ } ++ } + /* initialize global backup_state now */ + + backup_state.cancel = false; +Index: qemu-kvm-dev/hmp.c +=================================================================== +--- qemu-kvm-dev.orig/hmp.c ++++ qemu-kvm-dev/hmp.c +@@ -1465,7 +1465,7 @@ void hmp_backup(Monitor *mon, const QDic + int64_t speed = qdict_get_try_int(qdict, "speed", 0); + + qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, +- false, NULL, !!devlist, ++ false, NULL, false, NULL, !!devlist, + devlist, qdict_haskey(qdict, "speed"), speed, &error); + + hmp_handle_error(mon, &error); diff --git a/debian/patches/old/adjust-path.diff b/debian/patches/old/adjust-path.diff new file mode 100644 index 0000000..ea72b60 --- /dev/null +++ b/debian/patches/old/adjust-path.diff @@ -0,0 +1,16 @@ +Index: new/include/net/net.h +=================================================================== +--- new.orig/include/net/net.h 2014-11-20 06:45:06.000000000 +0100 ++++ new/include/net/net.h 2014-11-20 06:47:47.000000000 +0100 +@@ -195,8 +195,9 @@ + int net_hub_id_for_client(NetClientState *nc, int *id); + NetClientState *net_hub_port_find(int hub_id); + +-#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" +-#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" ++#define DEFAULT_NETWORK_SCRIPT "/etc/kvm/kvm-ifup" ++#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/kvm/kvm-ifdown" ++ + #define DEFAULT_BRIDGE_HELPER CONFIG_QEMU_HELPERDIR "/qemu-bridge-helper" + #define DEFAULT_BRIDGE_INTERFACE "br0" + diff --git a/debian/patches/old/ahci-add_migration-support.patch b/debian/patches/old/ahci-add_migration-support.patch new file mode 100644 index 0000000..a5791f7 --- /dev/null +++ b/debian/patches/old/ahci-add_migration-support.patch @@ -0,0 +1,154 @@ +From: Jason Baron +Subject: [Qemu-devel] [PATCH] ahci: add migration support +Date: Thu, 30 Aug 2012 14:00:04 -0400 + +Add support for ahci migration. This patch builds upon the patches posted +previously by Andreas Faerber: + +http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg01538.html + +(I hope I am giving Andreas proper credit for his work.) + +I've tested these patches by migrating Windows 7 and Fedora 16 guests on +both piix with ahci attached and on q35 (which has a built-in ahci controller). + +Signed-off-by: Jason Baron +--- + hw/ide/ahci.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- + hw/ide/ahci.h | 10 +++++++++ + hw/ide/ich.c | 11 +++++++-- + 3 files changed, 81 insertions(+), 4 deletions(-) + +Index: new/hw/ide/ahci.c +=================================================================== +--- new.orig/hw/ide/ahci.c 2012-09-24 07:15:00.000000000 +0200 ++++ new/hw/ide/ahci.c 2012-09-24 07:15:28.000000000 +0200 +@@ -1203,6 +1203,65 @@ + } + } + ++static const VMStateDescription vmstate_ahci_device = { ++ .name = "ahci port", ++ .version_id = 1, ++ .fields = (VMStateField []) { ++ VMSTATE_IDE_BUS(port, AHCIDevice), ++ VMSTATE_UINT32(port_state, AHCIDevice), ++ VMSTATE_UINT32(finished, AHCIDevice), ++ VMSTATE_UINT32(port_regs.lst_addr, AHCIDevice), ++ VMSTATE_UINT32(port_regs.lst_addr_hi, AHCIDevice), ++ VMSTATE_UINT32(port_regs.fis_addr, AHCIDevice), ++ VMSTATE_UINT32(port_regs.fis_addr_hi, AHCIDevice), ++ VMSTATE_UINT32(port_regs.irq_stat, AHCIDevice), ++ VMSTATE_UINT32(port_regs.irq_mask, AHCIDevice), ++ VMSTATE_UINT32(port_regs.cmd, AHCIDevice), ++ VMSTATE_UINT32(port_regs.tfdata, AHCIDevice), ++ VMSTATE_UINT32(port_regs.sig, AHCIDevice), ++ VMSTATE_UINT32(port_regs.scr_stat, AHCIDevice), ++ VMSTATE_UINT32(port_regs.scr_ctl, AHCIDevice), ++ VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), ++ VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), ++ VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), ++ VMSTATE_END_OF_LIST() ++ }, ++}; ++ ++static int ahci_state_post_load(void *opaque, int version_id) ++{ ++ int i; ++ AHCIState *s = opaque; ++ ++ for (i = 0; i < s->ports; i++) { ++ AHCIPortRegs *pr = &s->dev[i].port_regs; ++ ++ map_page(&s->dev[i].lst, ++ ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); ++ map_page(&s->dev[i].res_fis, ++ ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); ++ } ++ ++ return 0; ++} ++ ++const VMStateDescription vmstate_ahci = { ++ .name = "ahci", ++ .version_id = 1, ++ .post_load = ahci_state_post_load, ++ .fields = (VMStateField []) { ++ VMSTATE_STRUCT_VARRAY_POINTER_INT32(dev, AHCIState, ports, ++ vmstate_ahci_device, AHCIDevice), ++ VMSTATE_UINT32(control_regs.cap, AHCIState), ++ VMSTATE_UINT32(control_regs.ghc, AHCIState), ++ VMSTATE_UINT32(control_regs.irqstatus, AHCIState), ++ VMSTATE_UINT32(control_regs.impl, AHCIState), ++ VMSTATE_UINT32(control_regs.version, AHCIState), ++ VMSTATE_UINT32(idp_index, AHCIState), ++ VMSTATE_END_OF_LIST() ++ }, ++}; ++ + typedef struct SysbusAHCIState { + SysBusDevice busdev; + AHCIState ahci; +@@ -1211,7 +1270,10 @@ + + static const VMStateDescription vmstate_sysbus_ahci = { + .name = "sysbus-ahci", +- .unmigratable = 1, ++ .fields = (VMStateField []) { ++ VMSTATE_AHCI(ahci, AHCIPCIState), ++ VMSTATE_END_OF_LIST() ++ }, + }; + + static void sysbus_ahci_reset(DeviceState *dev) +Index: new/hw/ide/ahci.h +=================================================================== +--- new.orig/hw/ide/ahci.h 2012-09-24 07:15:00.000000000 +0200 ++++ new/hw/ide/ahci.h 2012-09-24 07:15:28.000000000 +0200 +@@ -307,6 +307,16 @@ + AHCIState ahci; + } AHCIPCIState; + ++extern const VMStateDescription vmstate_ahci; ++ ++#define VMSTATE_AHCI(_field, _state) { \ ++ .name = (stringify(_field)), \ ++ .size = sizeof(AHCIState), \ ++ .vmsd = &vmstate_ahci, \ ++ .flags = VMS_STRUCT, \ ++ .offset = vmstate_offset_value(_state, _field, AHCIState), \ ++} ++ + typedef struct NCQFrame { + uint8_t fis_type; + uint8_t c; +Index: new/hw/ide/ich.c +=================================================================== +--- new.orig/hw/ide/ich.c 2012-09-24 07:15:00.000000000 +0200 ++++ new/hw/ide/ich.c 2012-09-24 07:15:28.000000000 +0200 +@@ -79,9 +79,14 @@ + #define ICH9_IDP_INDEX 0x10 + #define ICH9_IDP_INDEX_LOG2 0x04 + +-static const VMStateDescription vmstate_ahci = { ++static const VMStateDescription vmstate_ich9_ahci = { + .name = "ahci", +- .unmigratable = 1, ++ .version_id = 1, ++ .fields = (VMStateField []) { ++ VMSTATE_PCI_DEVICE(card, AHCIPCIState), ++ VMSTATE_AHCI(ahci, AHCIPCIState), ++ VMSTATE_END_OF_LIST() ++ }, + }; + + static void pci_ich9_reset(DeviceState *dev) +@@ -152,7 +157,7 @@ + k->device_id = PCI_DEVICE_ID_INTEL_82801IR; + k->revision = 0x02; + k->class_id = PCI_CLASS_STORAGE_SATA; +- dc->vmsd = &vmstate_ahci; ++ dc->vmsd = &vmstate_ich9_ahci; + dc->reset = pci_ich9_reset; + } + diff --git a/debian/patches/old/backup-add-dir-format.patch b/debian/patches/old/backup-add-dir-format.patch new file mode 100644 index 0000000..0d32af5 --- /dev/null +++ b/debian/patches/old/backup-add-dir-format.patch @@ -0,0 +1,262 @@ +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-11-20 08:59:29.000000000 +0100 ++++ new/qapi-schema.json 2014-11-20 09:03:23.000000000 +0100 +@@ -391,7 +391,7 @@ + # @vma: Proxmox vma backup format + ## + { 'enum': 'BackupFormat', +- 'data': [ 'vma' ] } ++ 'data': [ 'vma', 'dir' ] } + + ## + # @backup: +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 08:59:29.000000000 +0100 ++++ new/blockdev.c 2014-11-20 09:04:05.000000000 +0100 +@@ -2301,6 +2301,8 @@ + uint8_t dev_id; + //bool started; + bool completed; ++ char targetfile[PATH_MAX]; ++ BlockDriverState *target; + } PVEBackupDevInfo; + + static void pvebackup_run_next_job(void); +@@ -2369,8 +2371,6 @@ + { + PVEBackupDevInfo *di = opaque; + +- assert(backup_state.vmaw); +- + di->completed = true; + + if (ret < 0 && !backup_state.error) { +@@ -2381,8 +2381,11 @@ + BlockDriverState *bs = di->bs; + + di->bs = NULL; ++ di->target = NULL; + +- vma_writer_close_stream(backup_state.vmaw, di->dev_id); ++ if (backup_state.vmaw) { ++ vma_writer_close_stream(backup_state.vmaw, di->dev_id); ++ } + + block_job_cb(bs, ret); + +@@ -2461,6 +2464,7 @@ + { + BlockBackend *blk; + BlockDriverState *bs = NULL; ++ const char *backup_dir = NULL; + Error *local_err = NULL; + uuid_t uuid; + VmaWriter *vmaw = NULL; +@@ -2478,11 +2482,6 @@ + /* Todo: try to auto-detect format based on file name */ + format = has_format ? format : BACKUP_FORMAT_VMA; + +- if (format != BACKUP_FORMAT_VMA) { +- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); +- return NULL; +- } +- + if (has_devlist) { + devs = g_strsplit_set(devlist, ",;:", -1); + +@@ -2551,27 +2550,63 @@ + + uuid_generate(uuid); + +- vmaw = vma_writer_create(backup_file, uuid, &local_err); +- if (!vmaw) { +- if (local_err) { +- error_propagate(errp, local_err); ++ if (format == BACKUP_FORMAT_VMA) { ++ vmaw = vma_writer_create(backup_file, uuid, &local_err); ++ if (!vmaw) { ++ if (local_err) { ++ error_propagate(errp, local_err); ++ } ++ goto err; + } +- goto err; +- } + +- /* register all devices for vma writer */ +- l = di_list; +- while (l) { +- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; +- l = g_list_next(l); ++ /* register all devices for vma writer */ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); + +- const char *devname = bdrv_get_device_name(di->bs); +- di->dev_id = vma_writer_register_stream(vmaw, devname, di->size); +- if (di->dev_id <= 0) { +- error_set(errp, ERROR_CLASS_GENERIC_ERROR, +- "register_stream failed"); ++ const char *devname = bdrv_get_device_name(di->bs); ++ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size); ++ if (di->dev_id <= 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "register_stream failed"); ++ goto err; ++ } ++ } ++ } else if (format == BACKUP_FORMAT_DIR) { ++ if (mkdir(backup_file, 0640) != 0) { ++ error_setg_errno(errp, errno, "can't create directory '%s'\n", ++ backup_file); + goto err; + } ++ backup_dir = backup_file; ++ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ ++ const char *devname = bdrv_get_device_name(di->bs); ++ snprintf(di->targetfile, PATH_MAX, "%s/%s.raw", backup_dir, devname); ++ ++ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB; ++ bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL, ++ di->size, flags, &local_err, false); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto err; ++ } ++ ++ di->target = bdrv_new(); ++ if (bdrv_open(&di->target, di->targetfile, NULL, NULL, flags, NULL, &local_err) < 0) { ++ bdrv_unref(di->target); ++ error_propagate(errp, local_err); ++ goto err; ++ } ++ } ++ } else { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); ++ goto err; + } + + /* add configuration file to archive */ +@@ -2584,12 +2619,27 @@ + goto err; + } + +- const char *basename = g_path_get_basename(config_file); +- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { +- error_setg(errp, "unable to add config data to vma archive"); +- g_free(cdata); +- goto err; ++ char *basename = g_path_get_basename(config_file); ++ ++ if (format == BACKUP_FORMAT_VMA) { ++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { ++ error_setg(errp, "unable to add config data to vma archive"); ++ g_free(cdata); ++ g_free(basename); ++ goto err; ++ } ++ } else if (format == BACKUP_FORMAT_DIR) { ++ char config_path[PATH_MAX]; ++ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); ++ if (!g_file_set_contents(config_path, cdata, clen, &err)) { ++ error_setg(errp, "unable to write config file '%s'", config_path); ++ g_free(cdata); ++ g_free(basename); ++ goto err; ++ } + } ++ ++ g_free(basename); + g_free(cdata); + } + +@@ -2629,10 +2679,11 @@ + PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; + l = g_list_next(l); + +- backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL, ++ backup_start(di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL, + BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, + pvebackup_dump_cb, pvebackup_complete_cb, di, + 1, &local_err); ++ + if (local_err != NULL) { + error_setg(&backup_state.error, "backup_job_create failed"); + pvebackup_cancel(NULL); +@@ -2651,8 +2702,17 @@ + + l = di_list; + while (l) { +- g_free(l->data); ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; + l = g_list_next(l); ++ ++ if (di->target) { ++ bdrv_unref(di->target); ++ } ++ ++ if (di->targetfile[0]) { ++ unlink(di->targetfile); ++ } ++ g_free(di); + } + g_list_free(di_list); + +@@ -2666,6 +2726,10 @@ + unlink(backup_file); + } + ++ if (backup_dir) { ++ rmdir(backup_dir); ++ } ++ + return NULL; + } + +Index: new/hmp-commands.hx +=================================================================== +--- new.orig/hmp-commands.hx 2014-11-20 08:59:29.000000000 +0100 ++++ new/hmp-commands.hx 2014-11-20 09:03:23.000000000 +0100 +@@ -89,9 +89,11 @@ + + { + .name = "backup", +- .args_type = "backupfile:s,speed:o?,devlist:s?", +- .params = "backupfile [speed [devlist]]", +- .help = "create a VM Backup.", ++ .args_type = "directory:-d,backupfile:s,speed:o?,devlist:s?", ++ .params = "[-d] backupfile [speed [devlist]]", ++ .help = "create a VM Backup." ++ "\n\t\t\t Use -d to dump data into a directory instead" ++ "\n\t\t\t of using VMA format.", + .mhandler.cmd = hmp_backup, + }, + +Index: new/hmp.c +=================================================================== +--- new.orig/hmp.c 2014-11-20 08:59:29.000000000 +0100 ++++ new/hmp.c 2014-11-20 09:03:23.000000000 +0100 +@@ -1459,11 +1459,13 @@ + { + Error *error = NULL; + ++ int dir = qdict_get_try_bool(qdict, "directory", 0); + const char *backup_file = qdict_get_str(qdict, "backupfile"); + const char *devlist = qdict_get_try_str(qdict, "devlist"); + int64_t speed = qdict_get_try_int(qdict, "speed", 0); + +- qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, ++ qmp_backup(backup_file, true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, ++ false, NULL, !!devlist, + devlist, qdict_haskey(qdict, "speed"), speed, &error); + + hmp_handle_error(mon, &error); diff --git a/debian/patches/old/backup-add-pve-monitor-commands.patch b/debian/patches/old/backup-add-pve-monitor-commands.patch new file mode 100644 index 0000000..2b45b2c --- /dev/null +++ b/debian/patches/old/backup-add-pve-monitor-commands.patch @@ -0,0 +1,752 @@ +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 07:36:12.000000000 +0100 ++++ new/blockdev.c 2014-11-20 07:47:31.000000000 +0100 +@@ -49,6 +49,7 @@ + #include "qmp-commands.h" + #include "trace.h" + #include "sysemu/arch_init.h" ++#include "vma.h" + + static const char *const if_name[IF_COUNT] = { + [IF_NONE] = "none", +@@ -2276,6 +2277,443 @@ + bdrv_put_ref_bh_schedule(bs); + } + ++/* PVE backup related function */ ++ ++static struct PVEBackupState { ++ Error *error; ++ bool cancel; ++ uuid_t uuid; ++ char uuid_str[37]; ++ int64_t speed; ++ time_t start_time; ++ time_t end_time; ++ char *backup_file; ++ VmaWriter *vmaw; ++ GList *di_list; ++ size_t total; ++ size_t transferred; ++ size_t zero_bytes; ++} backup_state; ++ ++typedef struct PVEBackupDevInfo { ++ BlockDriverState *bs; ++ size_t size; ++ uint8_t dev_id; ++ //bool started; ++ bool completed; ++} PVEBackupDevInfo; ++ ++static void pvebackup_run_next_job(void); ++ ++static int pvebackup_dump_cb(void *opaque, BlockDriverState *target, ++ int64_t sector_num, int n_sectors, ++ unsigned char *buf) ++{ ++ PVEBackupDevInfo *di = opaque; ++ ++ if (sector_num & 0x7f) { ++ if (!backup_state.error) { ++ error_setg(&backup_state.error, ++ "got unaligned write inside backup dump " ++ "callback (sector %ld)", sector_num); ++ } ++ return -1; // not aligned to cluster size ++ } ++ ++ int64_t cluster_num = sector_num >> 7; ++ int size = n_sectors * BDRV_SECTOR_SIZE; ++ ++ int ret = -1; ++ ++ if (backup_state.vmaw) { ++ size_t zero_bytes = 0; ++ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, ++ buf, &zero_bytes); ++ backup_state.zero_bytes += zero_bytes; ++ } else { ++ ret = size; ++ if (!buf) { ++ backup_state.zero_bytes += size; ++ } ++ } ++ ++ backup_state.transferred += size; ++ ++ return ret; ++} ++ ++static void pvebackup_cleanup(void) ++{ ++ backup_state.end_time = time(NULL); ++ ++ if (backup_state.vmaw) { ++ Error *local_err = NULL; ++ vma_writer_close(backup_state.vmaw, &local_err); ++ error_propagate(&backup_state.error, local_err); ++ backup_state.vmaw = NULL; ++ } ++ ++ if (backup_state.di_list) { ++ GList *l = backup_state.di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ g_free(di); ++ } ++ g_list_free(backup_state.di_list); ++ backup_state.di_list = NULL; ++ } ++} ++ ++static void pvebackup_complete_cb(void *opaque, int ret) ++{ ++ PVEBackupDevInfo *di = opaque; ++ ++ assert(backup_state.vmaw); ++ ++ di->completed = true; ++ ++ if (ret < 0 && !backup_state.error) { ++ error_setg(&backup_state.error, "job failed with err %d - %s", ++ ret, strerror(-ret)); ++ } ++ ++ BlockDriverState *bs = di->bs; ++ ++ di->bs = NULL; ++ ++ vma_writer_close_stream(backup_state.vmaw, di->dev_id); ++ ++ block_job_cb(bs, ret); ++ ++ if (!backup_state.cancel) { ++ pvebackup_run_next_job(); ++ } ++} ++ ++static void pvebackup_cancel(void *opaque) ++{ ++ backup_state.cancel = true; ++ ++ if (!backup_state.error) { ++ error_setg(&backup_state.error, "backup cancelled"); ++ } ++ ++ /* drain all i/o (awake jobs waiting for aio) */ ++ bdrv_drain_all(); ++ ++ GList *l = backup_state.di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ if (!di->completed && di->bs) { ++ BlockJob *job = di->bs->job; ++ if (job) { ++ if (!di->completed) { ++ block_job_cancel_sync(job); ++ } ++ } ++ } ++ } ++ ++ pvebackup_cleanup(); ++} ++ ++void qmp_backup_cancel(Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_create(pvebackup_cancel); ++ qemu_coroutine_enter(co, NULL); ++ ++ while (backup_state.vmaw) { ++ /* vma writer use main aio context */ ++ aio_poll(qemu_get_aio_context(), true); ++ } ++} ++ ++static void pvebackup_run_next_job(void) ++{ ++ GList *l = backup_state.di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ if (!di->completed && di->bs && di->bs->job) { ++ BlockJob *job = di->bs->job; ++ if (block_job_is_paused(job)) { ++ bool cancel = backup_state.error || backup_state.cancel; ++ if (cancel) { ++ block_job_cancel(job); ++ } else { ++ block_job_resume(job); ++ } ++ } ++ return; ++ } ++ } ++ ++ pvebackup_cleanup(); ++} ++ ++UuidInfo *qmp_backup(const char *backup_file, bool has_format, ++ BackupFormat format, ++ bool has_config_file, const char *config_file, ++ bool has_devlist, const char *devlist, ++ bool has_speed, int64_t speed, Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs = NULL; ++ Error *local_err = NULL; ++ uuid_t uuid; ++ VmaWriter *vmaw = NULL; ++ gchar **devs = NULL; ++ GList *di_list = NULL; ++ GList *l; ++ UuidInfo *uuid_info; ++ ++ if (backup_state.di_list) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "previous backup not finished"); ++ return NULL; ++ } ++ ++ /* Todo: try to auto-detect format based on file name */ ++ format = has_format ? format : BACKUP_FORMAT_VMA; ++ ++ if (format != BACKUP_FORMAT_VMA) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); ++ return NULL; ++ } ++ ++ if (has_devlist) { ++ devs = g_strsplit_set(devlist, ",;:", -1); ++ ++ gchar **d = devs; ++ while (d && *d) { ++ blk = blk_by_name(*d); ++ if (blk) { ++ bs = blk_bs(blk); ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Node '%s' is read only", *d); ++ goto err; ++ } ++ if (!bdrv_is_inserted(bs)) { ++ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d); ++ goto err; ++ } ++ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1); ++ di->bs = bs; ++ di_list = g_list_append(di_list, di); ++ } else { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", *d); ++ goto err; ++ } ++ d++; ++ } ++ ++ } else { ++ ++ bs = NULL; ++ while ((bs = bdrv_next(bs))) { ++ ++ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { ++ continue; ++ } ++ ++ PVEBackupDevInfo *di = g_new0(PVEBackupDevInfo, 1); ++ di->bs = bs; ++ di_list = g_list_append(di_list, di); ++ } ++ } ++ ++ if (!di_list) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list"); ++ goto err; ++ } ++ ++ size_t total = 0; ++ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { ++ goto err; ++ } ++ ++ ssize_t size = bdrv_getlength(di->bs); ++ if (size < 0) { ++ error_setg_errno(errp, -di->size, "bdrv_getlength failed"); ++ goto err; ++ } ++ di->size = size; ++ total += size; ++ } ++ ++ uuid_generate(uuid); ++ ++ vmaw = vma_writer_create(backup_file, uuid, &local_err); ++ if (!vmaw) { ++ if (local_err) { ++ error_propagate(errp, local_err); ++ } ++ goto err; ++ } ++ ++ /* register all devices for vma writer */ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ ++ const char *devname = bdrv_get_device_name(di->bs); ++ di->dev_id = vma_writer_register_stream(vmaw, devname, di->size); ++ if (di->dev_id <= 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "register_stream failed"); ++ goto err; ++ } ++ } ++ ++ /* add configuration file to archive */ ++ if (has_config_file) { ++ char *cdata = NULL; ++ gsize clen = 0; ++ GError *err = NULL; ++ if (!g_file_get_contents(config_file, &cdata, &clen, &err)) { ++ error_setg(errp, "unable to read file '%s'", config_file); ++ goto err; ++ } ++ ++ const char *basename = g_path_get_basename(config_file); ++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { ++ error_setg(errp, "unable to add config data to vma archive"); ++ g_free(cdata); ++ goto err; ++ } ++ g_free(cdata); ++ } ++ ++ /* initialize global backup_state now */ ++ ++ backup_state.cancel = false; ++ ++ if (backup_state.error) { ++ error_free(backup_state.error); ++ backup_state.error = NULL; ++ } ++ ++ backup_state.speed = (has_speed && speed > 0) ? speed : 0; ++ ++ backup_state.start_time = time(NULL); ++ backup_state.end_time = 0; ++ ++ if (backup_state.backup_file) { ++ g_free(backup_state.backup_file); ++ } ++ backup_state.backup_file = g_strdup(backup_file); ++ ++ backup_state.vmaw = vmaw; ++ ++ uuid_copy(backup_state.uuid, uuid); ++ uuid_unparse_lower(uuid, backup_state.uuid_str); ++ ++ backup_state.di_list = di_list; ++ ++ backup_state.total = total; ++ backup_state.transferred = 0; ++ backup_state.zero_bytes = 0; ++ ++ /* start all jobs (paused state) */ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ ++ backup_start(di->bs, NULL, speed, MIRROR_SYNC_MODE_FULL, NULL, ++ BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, ++ pvebackup_dump_cb, pvebackup_complete_cb, di, ++ 1, &local_err); ++ if (local_err != NULL) { ++ error_setg(&backup_state.error, "backup_job_create failed"); ++ pvebackup_cancel(NULL); ++ } ++ } ++ ++ if (!backup_state.error) { ++ pvebackup_run_next_job(); // run one job ++ } ++ ++ uuid_info = g_malloc0(sizeof(*uuid_info)); ++ uuid_info->UUID = g_strdup(backup_state.uuid_str); ++ return uuid_info; ++ ++err: ++ ++ l = di_list; ++ while (l) { ++ g_free(l->data); ++ l = g_list_next(l); ++ } ++ g_list_free(di_list); ++ ++ if (devs) { ++ g_strfreev(devs); ++ } ++ ++ if (vmaw) { ++ Error *err = NULL; ++ vma_writer_close(vmaw, &err); ++ unlink(backup_file); ++ } ++ ++ return NULL; ++} ++ ++BackupStatus *qmp_query_backup(Error **errp) ++{ ++ BackupStatus *info = g_malloc0(sizeof(*info)); ++ ++ if (!backup_state.start_time) { ++ /* not started, return {} */ ++ return info; ++ } ++ ++ info->has_status = true; ++ info->has_start_time = true; ++ info->start_time = backup_state.start_time; ++ ++ if (backup_state.backup_file) { ++ info->has_backup_file = true; ++ info->backup_file = g_strdup(backup_state.backup_file); ++ } ++ ++ info->has_uuid = true; ++ info->uuid = g_strdup(backup_state.uuid_str); ++ ++ if (backup_state.end_time) { ++ if (backup_state.error) { ++ info->status = g_strdup("error"); ++ info->has_errmsg = true; ++ info->errmsg = g_strdup(error_get_pretty(backup_state.error)); ++ } else { ++ info->status = g_strdup("done"); ++ } ++ info->has_end_time = true; ++ info->end_time = backup_state.end_time; ++ } else { ++ info->status = g_strdup("active"); ++ } ++ ++ info->has_total = true; ++ info->total = backup_state.total; ++ info->has_zero_bytes = true; ++ info->zero_bytes = backup_state.zero_bytes; ++ info->has_transferred = true; ++ info->transferred = backup_state.transferred; ++ ++ return info; ++} ++ + void qmp_block_stream(const char *device, + bool has_base, const char *base, + bool has_backing_file, const char *backing_file, +Index: new/hmp-commands.hx +=================================================================== +--- new.orig/hmp-commands.hx 2014-11-20 06:45:05.000000000 +0100 ++++ new/hmp-commands.hx 2014-11-20 07:47:31.000000000 +0100 +@@ -87,6 +87,35 @@ + Copy data from a backing file into a block device. + ETEXI + ++ { ++ .name = "backup", ++ .args_type = "backupfile:s,speed:o?,devlist:s?", ++ .params = "backupfile [speed [devlist]]", ++ .help = "create a VM Backup.", ++ .mhandler.cmd = hmp_backup, ++ }, ++ ++STEXI ++@item backup ++@findex backup ++Create a VM backup. ++ETEXI ++ ++ { ++ .name = "backup_cancel", ++ .args_type = "", ++ .params = "", ++ .help = "cancel the current VM backup", ++ .mhandler.cmd = hmp_backup_cancel, ++ }, ++ ++STEXI ++@item backup_cancel ++@findex backup_cancel ++Cancel the current VM backup. ++ ++ETEXI ++ + { + .name = "block_job_set_speed", + .args_type = "device:B,speed:o", +@@ -1768,6 +1797,8 @@ + show CPU statistics + @item info usernet + show user network stack connection states ++@item info backup ++show backup status + @item info migrate + show migration status + @item info migrate_capabilities +Index: new/hmp.c +=================================================================== +--- new.orig/hmp.c 2014-11-20 07:26:23.000000000 +0100 ++++ new/hmp.c 2014-11-20 07:47:31.000000000 +0100 +@@ -145,6 +145,44 @@ + qapi_free_MouseInfoList(mice_list); + } + ++void hmp_info_backup(Monitor *mon, const QDict *qdict) ++{ ++ BackupStatus *info; ++ ++ info = qmp_query_backup(NULL); ++ if (info->has_status) { ++ if (info->has_errmsg) { ++ monitor_printf(mon, "Backup status: %s - %s\n", ++ info->status, info->errmsg); ++ } else { ++ monitor_printf(mon, "Backup status: %s\n", info->status); ++ } ++ } ++ ++ if (info->has_backup_file) { ++ monitor_printf(mon, "Start time: %s", ctime(&info->start_time)); ++ if (info->end_time) { ++ monitor_printf(mon, "End time: %s", ctime(&info->end_time)); ++ } ++ ++ int per = (info->has_total && info->total && ++ info->has_transferred && info->transferred) ? ++ (info->transferred * 100)/info->total : 0; ++ int zero_per = (info->has_total && info->total && ++ info->has_zero_bytes && info->zero_bytes) ? ++ (info->zero_bytes * 100)/info->total : 0; ++ monitor_printf(mon, "Backup file: %s\n", info->backup_file); ++ monitor_printf(mon, "Backup uuid: %s\n", info->uuid); ++ monitor_printf(mon, "Total size: %zd\n", info->total); ++ monitor_printf(mon, "Transferred bytes: %zd (%d%%)\n", ++ info->transferred, per); ++ monitor_printf(mon, "Zero bytes: %zd (%d%%)\n", ++ info->zero_bytes, zero_per); ++ } ++ ++ qapi_free_BackupStatus(info); ++} ++ + void hmp_info_migrate(Monitor *mon, const QDict *qdict) + { + MigrationInfo *info; +@@ -1407,6 +1445,29 @@ + + hmp_handle_error(mon, &error); + } ++ ++void hmp_backup_cancel(Monitor *mon, const QDict *qdict) ++{ ++ Error *error = NULL; ++ ++ qmp_backup_cancel(&error); ++ ++ hmp_handle_error(mon, &error); ++} ++ ++void hmp_backup(Monitor *mon, const QDict *qdict) ++{ ++ Error *error = NULL; ++ ++ const char *backup_file = qdict_get_str(qdict, "backupfile"); ++ const char *devlist = qdict_get_try_str(qdict, "devlist"); ++ int64_t speed = qdict_get_try_int(qdict, "speed", 0); ++ ++ qmp_backup(backup_file, true, BACKUP_FORMAT_VMA, false, NULL, !!devlist, ++ devlist, qdict_haskey(qdict, "speed"), speed, &error); ++ ++ hmp_handle_error(mon, &error); ++} + + void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict) + { +Index: new/hmp.h +=================================================================== +--- new.orig/hmp.h 2014-11-20 06:45:05.000000000 +0100 ++++ new/hmp.h 2014-11-20 07:47:31.000000000 +0100 +@@ -30,6 +30,7 @@ + void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); + void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict); + void hmp_info_migrate_cache_size(Monitor *mon, const QDict *qdict); ++void hmp_info_backup(Monitor *mon, const QDict *qdict); + void hmp_info_cpus(Monitor *mon, const QDict *qdict); + void hmp_info_block(Monitor *mon, const QDict *qdict); + void hmp_info_blockstats(Monitor *mon, const QDict *qdict); +@@ -74,6 +75,8 @@ + void hmp_change(Monitor *mon, const QDict *qdict); + void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); + void hmp_block_stream(Monitor *mon, const QDict *qdict); ++void hmp_backup(Monitor *mon, const QDict *qdict); ++void hmp_backup_cancel(Monitor *mon, const QDict *qdict); + void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); + void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); + void hmp_block_job_pause(Monitor *mon, const QDict *qdict); +Index: new/monitor.c +=================================================================== +--- new.orig/monitor.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/monitor.c 2014-11-20 07:47:31.000000000 +0100 +@@ -2759,6 +2759,13 @@ + }, + #endif + { ++ .name = "backup", ++ .args_type = "", ++ .params = "", ++ .help = "show backup status", ++ .mhandler.cmd = hmp_info_backup, ++ }, ++ { + .name = "migrate", + .args_type = "", + .params = "", +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-11-20 07:26:43.000000000 +0100 ++++ new/qapi-schema.json 2014-11-20 07:47:31.000000000 +0100 +@@ -352,6 +352,95 @@ + ## + { 'command': 'query-events', 'returns': ['EventInfo'] } + ++# @BackupStatus: ++# ++# Detailed backup status. ++# ++# @status: #optional string describing the current backup status. ++# This can be 'active', 'done', 'error'. If this field is not ++# returned, no backup process has been initiated ++# ++# @errmsg: #optional error message (only returned if status is 'error') ++# ++# @total: #optional total amount of bytes involved in the backup process ++# ++# @transferred: #optional amount of bytes already backed up. ++# ++# @zero-bytes: #optional amount of 'zero' bytes detected. ++# ++# @start-time: #optional time (epoch) when backup job started. ++# ++# @end-time: #optional time (epoch) when backup job finished. ++# ++# @backupfile: #optional backup file name ++# ++# @uuid: #optional uuid for this backup job ++# ++## ++{ 'struct': 'BackupStatus', ++ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', ++ '*transferred': 'int', '*zero-bytes': 'int', ++ '*start-time': 'int', '*end-time': 'int', ++ '*backup-file': 'str', '*uuid': 'str' } } ++ ++## ++# @BackupFormat ++# ++# An enumeration of supported backup formats. ++# ++# @vma: Proxmox vma backup format ++## ++{ 'enum': 'BackupFormat', ++ 'data': [ 'vma' ] } ++ ++## ++# @backup: ++# ++# Starts a VM backup. ++# ++# @backup-file: the backup file name ++# ++# @format: format of the backup file ++# ++# @config-filename: #optional name of a configuration file to include into ++# the backup archive. ++# ++# @speed: #optional the maximum speed, in bytes per second ++# ++# @devlist: #optional list of block device names (separated by ',', ';' ++# or ':'). By default the backup includes all writable block devices. ++# ++# Returns: the uuid of the backup job ++# ++## ++{ 'command': 'backup', 'data': { 'backup-file': 'str', ++ '*format': 'BackupFormat', ++ '*config-file': 'str', ++ '*devlist': 'str', '*speed': 'int' }, ++ 'returns': 'UuidInfo' } ++ ++## ++# @query-backup ++# ++# Returns information about current/last backup task. ++# ++# Returns: @BackupStatus ++# ++## ++{ 'command': 'query-backup', 'returns': 'BackupStatus' } ++ ++## ++# @backup-cancel ++# ++# Cancel the current executing backup process. ++# ++# Returns: nothing on success ++# ++# Notes: This command succeeds even if there is no backup process running. ++# ++## ++{ 'command': 'backup-cancel' } ++ + ## + # @MigrationStats + # +Index: new/qmp-commands.hx +=================================================================== +--- new.orig/qmp-commands.hx 2014-11-20 07:26:23.000000000 +0100 ++++ new/qmp-commands.hx 2014-11-20 07:47:31.000000000 +0100 +@@ -1203,6 +1203,24 @@ + EQMP + + { ++ .name = "backup", ++ .args_type = "backup-file:s,format:s?,config-file:F?,speed:o?,devlist:s?", ++ .mhandler.cmd_new = qmp_marshal_input_backup, ++ }, ++ ++ { ++ .name = "backup-cancel", ++ .args_type = "", ++ .mhandler.cmd_new = qmp_marshal_input_backup_cancel, ++ }, ++ ++ { ++ .name = "query-backup", ++ .args_type = "", ++ .mhandler.cmd_new = qmp_marshal_input_query_backup, ++ }, ++ ++ { + .name = "block-job-set-speed", + .args_type = "device:B,speed:o", + .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed, diff --git a/debian/patches/old/backup-add-vma-binary.patch b/debian/patches/old/backup-add-vma-binary.patch new file mode 100644 index 0000000..6fbd7ad --- /dev/null +++ b/debian/patches/old/backup-add-vma-binary.patch @@ -0,0 +1,2480 @@ +From 6289a43696ca6f713a5d3bb9f95a5adb608a5e13 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 13 Nov 2012 11:11:38 +0100 +Subject: [PATCH v5 4/6] introduce new vma archive format + +This is a very simple archive format, see docs/specs/vma_spec.txt + +Signed-off-by: Dietmar Maurer +--- + Makefile | 3 +- + Makefile.objs | 2 +- + blockdev.c | 6 +- + docs/specs/vma_spec.txt | 24 ++ + vma-reader.c | 799 ++++++++++++++++++++++++++++++++++++++++ + vma-writer.c | 940 +++++++++++++++++++++++++++++++++++++++++++++++ + vma.c | 561 ++++++++++++++++++++++++++++ + vma.h | 145 ++++++++ + 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 + create mode 100644 vma.c + create mode 100644 vma.h + +Index: new/Makefile +=================================================================== +--- new.orig/Makefile 2014-11-20 08:12:54.000000000 +0100 ++++ new/Makefile 2014-11-20 08:15:12.000000000 +0100 +@@ -156,7 +156,7 @@ + include $(SRC_PATH)/libcacard/Makefile + endif + +-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules ++all: $(DOCS) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules + + config-host.h: config-host.h-timestamp + config-host.h-timestamp: config-host.mak +@@ -224,6 +224,7 @@ + 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 $(block-obj-y) libqemuutil.a libqemustub.a ++vma$(EXESUF): vma.o vma-reader.o $(block-obj-y) libqemuutil.a libqemustub.a + + qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o + +Index: new/vma-reader.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ new/vma-reader.c 2014-11-20 08:15:12.000000000 +0100 +@@ -0,0 +1,799 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) 2012 Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "qemu/timer.h" ++#include "qemu/ratelimit.h" ++#include "vma.h" ++#include "block/block.h" ++ ++#define BITS_PER_LONG (sizeof(unsigned long) * CHAR_BIT) ++ ++static unsigned char zero_vma_block[VMA_BLOCK_SIZE]; ++ ++typedef struct VmaRestoreState { ++ BlockDriverState *bs; ++ bool write_zeroes; ++ unsigned long *bitmap; ++ int bitmap_size; ++} VmaRestoreState; ++ ++struct VmaReader { ++ int fd; ++ GChecksum *md5csum; ++ GHashTable *blob_hash; ++ unsigned char *head_data; ++ VmaDeviceInfo devinfo[256]; ++ VmaRestoreState rstate[256]; ++ 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 ++g_int32_hash(gconstpointer v) ++{ ++ return *(const uint32_t *)v; ++} ++ ++static gboolean ++g_int32_equal(gconstpointer v1, gconstpointer v2) ++{ ++ return *((const uint32_t *)v1) == *((const uint32_t *)v2); ++} ++ ++static int vma_reader_get_bitmap(VmaRestoreState *rstate, int64_t cluster_num) ++{ ++ assert(rstate); ++ assert(rstate->bitmap); ++ ++ unsigned long val, idx, bit; ++ ++ idx = cluster_num / BITS_PER_LONG; ++ ++ assert(rstate->bitmap_size > idx); ++ ++ bit = cluster_num % BITS_PER_LONG; ++ val = rstate->bitmap[idx]; ++ ++ return !!(val & (1UL << bit)); ++} ++ ++static void vma_reader_set_bitmap(VmaRestoreState *rstate, int64_t cluster_num, ++ int dirty) ++{ ++ assert(rstate); ++ assert(rstate->bitmap); ++ ++ unsigned long val, idx, bit; ++ ++ idx = cluster_num / BITS_PER_LONG; ++ ++ assert(rstate->bitmap_size > idx); ++ ++ bit = cluster_num % BITS_PER_LONG; ++ val = rstate->bitmap[idx]; ++ if (dirty) { ++ if (!(val & (1UL << bit))) { ++ val |= 1UL << bit; ++ } ++ } else { ++ if (val & (1UL << bit)) { ++ val &= ~(1UL << bit); ++ } ++ } ++ rstate->bitmap[idx] = val; ++} ++ ++typedef struct VmaBlob { ++ uint32_t start; ++ uint32_t len; ++ void *data; ++} VmaBlob; ++ ++static const VmaBlob *get_header_blob(VmaReader *vmar, uint32_t pos) ++{ ++ assert(vmar); ++ assert(vmar->blob_hash); ++ ++ return g_hash_table_lookup(vmar->blob_hash, &pos); ++} ++ ++static const char *get_header_str(VmaReader *vmar, uint32_t pos) ++{ ++ const VmaBlob *blob = get_header_blob(vmar, pos); ++ if (!blob) { ++ return NULL; ++ } ++ const char *res = (char *)blob->data; ++ if (res[blob->len-1] != '\0') { ++ return NULL; ++ } ++ return res; ++} ++ ++static ssize_t ++safe_read(int fd, unsigned char *buf, size_t count) ++{ ++ ssize_t n; ++ ++ do { ++ n = read(fd, buf, count); ++ } while (n < 0 && errno == EINTR); ++ ++ return n; ++} ++ ++static ssize_t ++full_read(int fd, unsigned char *buf, size_t len) ++{ ++ ssize_t n; ++ size_t total; ++ ++ total = 0; ++ ++ while (len > 0) { ++ n = safe_read(fd, buf, len); ++ ++ if (n == 0) { ++ return total; ++ } ++ ++ if (n <= 0) { ++ break; ++ } ++ ++ buf += n; ++ total += n; ++ len -= n; ++ } ++ ++ if (len) { ++ return -1; ++ } ++ ++ return total; ++} ++ ++void vma_reader_destroy(VmaReader *vmar) ++{ ++ assert(vmar); ++ ++ if (vmar->fd >= 0) { ++ close(vmar->fd); ++ } ++ ++ if (vmar->cdata_list) { ++ g_list_free(vmar->cdata_list); ++ } ++ ++ int i; ++ for (i = 1; i < 256; i++) { ++ if (vmar->rstate[i].bitmap) { ++ g_free(vmar->rstate[i].bitmap); ++ } ++ } ++ ++ if (vmar->md5csum) { ++ g_checksum_free(vmar->md5csum); ++ } ++ ++ if (vmar->blob_hash) { ++ g_hash_table_destroy(vmar->blob_hash); ++ } ++ ++ if (vmar->head_data) { ++ g_free(vmar->head_data); ++ } ++ ++ g_free(vmar); ++ ++}; ++ ++static int vma_reader_read_head(VmaReader *vmar, Error **errp) ++{ ++ assert(vmar); ++ assert(errp); ++ assert(*errp == NULL); ++ ++ unsigned char md5sum[16]; ++ int i; ++ int ret = 0; ++ ++ vmar->head_data = g_malloc(sizeof(VmaHeader)); ++ ++ if (full_read(vmar->fd, vmar->head_data, sizeof(VmaHeader)) != ++ sizeof(VmaHeader)) { ++ error_setg(errp, "can't read vma header - %s", ++ errno ? g_strerror(errno) : "got EOF"); ++ return -1; ++ } ++ ++ VmaHeader *h = (VmaHeader *)vmar->head_data; ++ ++ if (h->magic != VMA_MAGIC) { ++ error_setg(errp, "not a vma file - wrong magic number"); ++ return -1; ++ } ++ ++ uint32_t header_size = GUINT32_FROM_BE(h->header_size); ++ int need = header_size - sizeof(VmaHeader); ++ if (need <= 0) { ++ error_setg(errp, "wrong vma header size %d", header_size); ++ return -1; ++ } ++ ++ vmar->head_data = g_realloc(vmar->head_data, header_size); ++ h = (VmaHeader *)vmar->head_data; ++ ++ if (full_read(vmar->fd, vmar->head_data + sizeof(VmaHeader), need) != ++ need) { ++ error_setg(errp, "can't read vma header data - %s", ++ errno ? g_strerror(errno) : "got EOF"); ++ return -1; ++ } ++ ++ memcpy(md5sum, h->md5sum, 16); ++ memset(h->md5sum, 0, 16); ++ ++ g_checksum_reset(vmar->md5csum); ++ g_checksum_update(vmar->md5csum, vmar->head_data, header_size); ++ gsize csize = 16; ++ g_checksum_get_digest(vmar->md5csum, (guint8 *)(h->md5sum), &csize); ++ ++ if (memcmp(md5sum, h->md5sum, 16) != 0) { ++ error_setg(errp, "wrong vma header chechsum"); ++ return -1; ++ } ++ ++ /* we can modify header data after checksum verify */ ++ h->header_size = header_size; ++ ++ h->version = GUINT32_FROM_BE(h->version); ++ if (h->version != 1) { ++ error_setg(errp, "wrong vma version %d", h->version); ++ return -1; ++ } ++ ++ h->ctime = GUINT64_FROM_BE(h->ctime); ++ h->blob_buffer_offset = GUINT32_FROM_BE(h->blob_buffer_offset); ++ h->blob_buffer_size = GUINT32_FROM_BE(h->blob_buffer_size); ++ ++ uint32_t bstart = h->blob_buffer_offset + 1; ++ uint32_t bend = h->blob_buffer_offset + h->blob_buffer_size; ++ ++ if (bstart <= sizeof(VmaHeader)) { ++ error_setg(errp, "wrong vma blob buffer offset %d", ++ h->blob_buffer_offset); ++ return -1; ++ } ++ ++ if (bend > header_size) { ++ error_setg(errp, "wrong vma blob buffer size %d/%d", ++ h->blob_buffer_offset, h->blob_buffer_size); ++ return -1; ++ } ++ ++ while ((bstart + 2) <= bend) { ++ uint32_t size = vmar->head_data[bstart] + ++ (vmar->head_data[bstart+1] << 8); ++ if ((bstart + size + 2) <= bend) { ++ VmaBlob *blob = g_new0(VmaBlob, 1); ++ blob->start = bstart - h->blob_buffer_offset; ++ blob->len = size; ++ blob->data = vmar->head_data + bstart + 2; ++ g_hash_table_insert(vmar->blob_hash, &blob->start, blob); ++ } ++ bstart += size + 2; ++ } ++ ++ ++ int count = 0; ++ for (i = 1; i < 256; i++) { ++ VmaDeviceInfoHeader *dih = &h->dev_info[i]; ++ uint32_t devname_ptr = GUINT32_FROM_BE(dih->devname_ptr); ++ uint64_t size = GUINT64_FROM_BE(dih->size); ++ const char *devname = get_header_str(vmar, devname_ptr); ++ ++ if (size && devname) { ++ count++; ++ vmar->devinfo[i].size = size; ++ vmar->devinfo[i].devname = devname; ++ ++ if (strcmp(devname, "vmstate") == 0) { ++ vmar->vmstate_stream = i; ++ } ++ } ++ } ++ ++ if (!count) { ++ error_setg(errp, "vma does not contain data"); ++ return -1; ++ } ++ ++ for (i = 0; i < VMA_MAX_CONFIGS; i++) { ++ uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]); ++ uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]); ++ ++ if (!(name_ptr && data_ptr)) { ++ continue; ++ } ++ const char *name = get_header_str(vmar, name_ptr); ++ const VmaBlob *blob = get_header_blob(vmar, data_ptr); ++ ++ if (!(name && blob)) { ++ error_setg(errp, "vma contains invalid data pointers"); ++ return -1; ++ } ++ ++ VmaConfigData *cdata = g_new0(VmaConfigData, 1); ++ cdata->name = name; ++ cdata->data = blob->data; ++ cdata->len = blob->len; ++ ++ vmar->cdata_list = g_list_append(vmar->cdata_list, cdata); ++ } ++ ++ return ret; ++}; ++ ++VmaReader *vma_reader_create(const char *filename, Error **errp) ++{ ++ assert(filename); ++ assert(errp); ++ ++ VmaReader *vmar = g_new0(VmaReader, 1); ++ ++ 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, ++ g_strerror(errno)); ++ goto err; ++ } ++ ++ vmar->md5csum = g_checksum_new(G_CHECKSUM_MD5); ++ if (!vmar->md5csum) { ++ error_setg(errp, "can't allocate cmsum\n"); ++ goto err; ++ } ++ ++ vmar->blob_hash = g_hash_table_new_full(g_int32_hash, g_int32_equal, ++ NULL, g_free); ++ ++ if (vma_reader_read_head(vmar, errp) < 0) { ++ goto err; ++ } ++ ++ return vmar; ++ ++err: ++ if (vmar) { ++ vma_reader_destroy(vmar); ++ } ++ ++ return NULL; ++} ++ ++VmaHeader *vma_reader_get_header(VmaReader *vmar) ++{ ++ assert(vmar); ++ assert(vmar->head_data); ++ ++ return (VmaHeader *)(vmar->head_data); ++} ++ ++GList *vma_reader_get_config_data(VmaReader *vmar) ++{ ++ assert(vmar); ++ assert(vmar->head_data); ++ ++ return vmar->cdata_list; ++} ++ ++VmaDeviceInfo *vma_reader_get_device_info(VmaReader *vmar, guint8 dev_id) ++{ ++ assert(vmar); ++ assert(dev_id); ++ ++ if (vmar->devinfo[dev_id].size && vmar->devinfo[dev_id].devname) { ++ return &vmar->devinfo[dev_id]; ++ } ++ ++ return NULL; ++} ++ ++int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, ++ bool write_zeroes, Error **errp) ++{ ++ assert(vmar); ++ assert(bs != NULL); ++ assert(dev_id); ++ assert(vmar->rstate[dev_id].bs == NULL); ++ ++ int64_t size = bdrv_getlength(bs); ++ if (size != vmar->devinfo[dev_id].size) { ++ error_setg(errp, "vma_reader_register_bs for stream %s failed - " ++ "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, ++ size, vmar->devinfo[dev_id].size); ++ return -1; ++ } ++ ++ vmar->rstate[dev_id].bs = bs; ++ vmar->rstate[dev_id].write_zeroes = write_zeroes; ++ ++ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + ++ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; ++ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; ++ ++ 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; ++} ++ ++static ssize_t safe_write(int fd, void *buf, size_t count) ++{ ++ ssize_t n; ++ ++ do { ++ n = write(fd, buf, count); ++ } while (n < 0 && errno == EINTR); ++ ++ return n; ++} ++ ++static size_t full_write(int fd, void *buf, size_t len) ++{ ++ ssize_t n; ++ size_t total; ++ ++ total = 0; ++ ++ while (len > 0) { ++ n = safe_write(fd, buf, len); ++ if (n < 0) { ++ return n; ++ } ++ buf += n; ++ total += n; ++ len -= n; ++ } ++ ++ if (len) { ++ /* incomplete write ? */ ++ return -1; ++ } ++ ++ return total; ++} ++ ++static int restore_write_data(VmaReader *vmar, guint8 dev_id, ++ BlockDriverState *bs, int vmstate_fd, ++ unsigned char *buf, int64_t sector_num, ++ int nb_sectors, Error **errp) ++{ ++ assert(vmar); ++ ++ if (dev_id == vmar->vmstate_stream) { ++ if (vmstate_fd >= 0) { ++ int len = nb_sectors * BDRV_SECTOR_SIZE; ++ int res = full_write(vmstate_fd, buf, len); ++ if (res < 0) { ++ error_setg(errp, "write vmstate failed %d", res); ++ return -1; ++ } ++ } ++ } else { ++ int res = bdrv_write(bs, sector_num, buf, nb_sectors); ++ if (res < 0) { ++ error_setg(errp, "bdrv_write to %s failed (%d)", ++ bdrv_get_device_name(bs), res); ++ return -1; ++ } ++ } ++ return 0; ++} ++static int restore_extent(VmaReader *vmar, unsigned char *buf, ++ int extent_size, int vmstate_fd, ++ bool verbose, Error **errp) ++{ ++ assert(vmar); ++ assert(buf); ++ ++ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; ++ int start = VMA_EXTENT_HEADER_SIZE; ++ int i; ++ ++ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { ++ uint64_t block_info = GUINT64_FROM_BE(ehead->blockinfo[i]); ++ 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; ++ ++ if (!dev_id) { ++ continue; ++ } ++ ++ VmaRestoreState *rstate = &vmar->rstate[dev_id]; ++ BlockDriverState *bs = NULL; ++ ++ if (dev_id != vmar->vmstate_stream) { ++ bs = rstate->bs; ++ if (!bs) { ++ error_setg(errp, "got wrong dev id %d", dev_id); ++ return -1; ++ } ++ ++ if (vma_reader_get_bitmap(rstate, cluster_num)) { ++ error_setg(errp, "found duplicated cluster %zd for stream %s", ++ cluster_num, vmar->devinfo[dev_id].devname); ++ return -1; ++ } ++ vma_reader_set_bitmap(rstate, cluster_num, 1); ++ ++ max_sector = vmar->devinfo[dev_id].size/BDRV_SECTOR_SIZE; ++ } else { ++ max_sector = G_MAXINT64; ++ if (cluster_num != vmar->vmstate_clusters) { ++ error_setg(errp, "found out of order vmstate data"); ++ return -1; ++ } ++ 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) { ++ error_setg(errp, "short vma extent - too many blocks"); ++ return -1; ++ } ++ int64_t sector_num = (cluster_num * VMA_CLUSTER_SIZE) / ++ BDRV_SECTOR_SIZE; ++ int64_t end_sector = sector_num + ++ VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE; ++ ++ if (end_sector > max_sector) { ++ end_sector = max_sector; ++ } ++ ++ if (end_sector <= sector_num) { ++ error_setg(errp, "got wrong block address - write bejond end"); ++ return -1; ++ } ++ ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, ++ sector_num, nb_sectors, errp) < 0) { ++ return -1; ++ } ++ ++ start += VMA_CLUSTER_SIZE; ++ } else { ++ int j; ++ int bit = 1; ++ ++ for (j = 0; j < 16; j++) { ++ int64_t sector_num = (cluster_num*VMA_CLUSTER_SIZE + ++ j*VMA_BLOCK_SIZE)/BDRV_SECTOR_SIZE; ++ ++ int64_t end_sector = sector_num + ++ VMA_BLOCK_SIZE/BDRV_SECTOR_SIZE; ++ if (end_sector > max_sector) { ++ end_sector = max_sector; ++ } ++ ++ if (mask & bit) { ++ if ((start + VMA_BLOCK_SIZE) > extent_size) { ++ error_setg(errp, "short vma extent - too many blocks"); ++ return -1; ++ } ++ ++ if (end_sector <= sector_num) { ++ error_setg(errp, "got wrong block address - " ++ "write bejond end"); ++ return -1; ++ } ++ ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ buf + start, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } ++ ++ start += VMA_BLOCK_SIZE; ++ ++ } else { ++ ++ if (rstate->write_zeroes && (end_sector > sector_num)) { ++ /* Todo: use bdrv_co_write_zeroes (but that need to ++ * be run inside coroutine?) ++ */ ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ zero_vma_block, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } ++ } ++ } ++ ++ bit = bit << 1; ++ } ++ } ++ } ++ ++ if (start != extent_size) { ++ error_setg(errp, "vma extent error - missing blocks"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, ++ Error **errp) ++{ ++ assert(vmar); ++ assert(vmar->head_data); ++ ++ int ret = 0; ++ unsigned char buf[VMA_MAX_EXTENT_SIZE]; ++ int buf_pos = 0; ++ 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); ++ if (bytes < 0) { ++ error_setg(errp, "read failed - %s", g_strerror(errno)); ++ return -1; ++ } ++ ++ buf_pos += bytes; ++ ++ if (!buf_pos) { ++ break; /* EOF */ ++ } ++ ++ if (buf_pos < VMA_EXTENT_HEADER_SIZE) { ++ error_setg(errp, "read short extent (%d bytes)", buf_pos); ++ return -1; ++ } ++ ++ VmaExtentHeader *ehead = (VmaExtentHeader *)buf; ++ ++ /* extract md5sum */ ++ memcpy(md5sum, ehead->md5sum, sizeof(ehead->md5sum)); ++ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); ++ ++ g_checksum_reset(vmar->md5csum); ++ g_checksum_update(vmar->md5csum, buf, VMA_EXTENT_HEADER_SIZE); ++ gsize csize = 16; ++ g_checksum_get_digest(vmar->md5csum, ehead->md5sum, &csize); ++ ++ if (memcmp(md5sum, ehead->md5sum, 16) != 0) { ++ error_setg(errp, "wrong vma extent header chechsum"); ++ return -1; ++ } ++ ++ if (memcmp(h->uuid, ehead->uuid, sizeof(ehead->uuid)) != 0) { ++ error_setg(errp, "wrong vma extent uuid"); ++ return -1; ++ } ++ ++ if (ehead->magic != VMA_EXTENT_MAGIC || ehead->reserved1 != 0) { ++ error_setg(errp, "wrong vma extent header magic"); ++ return -1; ++ } ++ ++ int block_count = GUINT16_FROM_BE(ehead->block_count); ++ int extent_size = VMA_EXTENT_HEADER_SIZE + block_count*VMA_BLOCK_SIZE; ++ ++ if (buf_pos < extent_size) { ++ error_setg(errp, "short vma extent (%d < %d)", buf_pos, ++ extent_size); ++ return -1; ++ } ++ ++ if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, ++ errp) < 0) { ++ return -1; ++ } ++ ++ if (buf_pos > extent_size) { ++ memmove(buf, buf + extent_size, buf_pos - extent_size); ++ buf_pos = buf_pos - extent_size; ++ } else { ++ buf_pos = 0; ++ } ++ } ++ ++ bdrv_drain_all(); ++ ++ int i; ++ for (i = 1; i < 256; i++) { ++ VmaRestoreState *rstate = &vmar->rstate[i]; ++ if (!rstate->bs) { ++ continue; ++ } ++ ++ if (bdrv_flush(rstate->bs) < 0) { ++ error_setg(errp, "vma bdrv_flush %s failed", ++ vmar->devinfo[i].devname); ++ return -1; ++ } ++ ++ if (vmar->devinfo[i].size && ++ (strcmp(vmar->devinfo[i].devname, "vmstate") != 0)) { ++ assert(rstate->bitmap); ++ ++ int64_t cluster_num, end; ++ ++ end = (vmar->devinfo[i].size + VMA_CLUSTER_SIZE - 1) / ++ VMA_CLUSTER_SIZE; ++ ++ for (cluster_num = 0; cluster_num < end; cluster_num++) { ++ if (!vma_reader_get_bitmap(rstate, cluster_num)) { ++ error_setg(errp, "detected missing cluster %zd " ++ "for stream %s", cluster_num, ++ vmar->devinfo[i].devname); ++ return -1; ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ +Index: new/vma-writer.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ new/vma-writer.c 2014-11-20 08:31:06.000000000 +0100 +@@ -0,0 +1,876 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) 2012 Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "vma.h" ++#include "block/block.h" ++#include "monitor/monitor.h" ++#include "qemu/main-loop.h" ++ ++#define DEBUG_VMA 0 ++ ++#define DPRINTF(fmt, ...)\ ++ do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0) ++ ++#define WRITE_BUFFERS 5 ++ ++typedef struct VmaAIOCB VmaAIOCB; ++struct VmaAIOCB { ++ unsigned char buffer[VMA_MAX_EXTENT_SIZE]; ++ VmaWriter *vmaw; ++ size_t bytes; ++ Coroutine *co; ++}; ++ ++struct VmaWriter { ++ int fd; ++ FILE *cmd; ++ int status; ++ char errmsg[8192]; ++ uuid_t uuid; ++ bool header_written; ++ bool closed; ++ ++ /* we always write extents */ ++ unsigned char outbuf[VMA_MAX_EXTENT_SIZE]; ++ int outbuf_pos; /* in bytes */ ++ int outbuf_count; /* in VMA_BLOCKS */ ++ uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; ++ ++ VmaAIOCB *aiocbs[WRITE_BUFFERS]; ++ CoQueue wqueue; ++ ++ GChecksum *md5csum; ++ CoMutex writer_lock; ++ CoMutex flush_lock; ++ Coroutine *co_writer; ++ ++ /* drive informations */ ++ VmaStreamInfo stream_info[256]; ++ guint stream_count; ++ ++ guint8 vmstate_stream; ++ uint32_t vmstate_clusters; ++ ++ /* header blob table */ ++ char *header_blob_table; ++ uint32_t header_blob_table_size; ++ uint32_t header_blob_table_pos; ++ ++ /* store for config blobs */ ++ uint32_t config_names[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ ++ uint32_t config_data[VMA_MAX_CONFIGS]; /* offset into blob_buffer table */ ++ uint32_t config_count; ++}; ++ ++void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ if (vmaw->status < 0) { ++ return; ++ } ++ ++ vmaw->status = -1; ++ ++ va_start(ap, fmt); ++ g_vsnprintf(vmaw->errmsg, sizeof(vmaw->errmsg), fmt, ap); ++ va_end(ap); ++ ++ DPRINTF("vma_writer_set_error: %s\n", vmaw->errmsg); ++} ++ ++static uint32_t allocate_header_blob(VmaWriter *vmaw, const char *data, ++ size_t len) ++{ ++ if (len > 65535) { ++ return 0; ++ } ++ ++ if (!vmaw->header_blob_table || ++ (vmaw->header_blob_table_size < ++ (vmaw->header_blob_table_pos + len + 2))) { ++ int newsize = vmaw->header_blob_table_size + ((len + 2 + 511)/512)*512; ++ ++ vmaw->header_blob_table = g_realloc(vmaw->header_blob_table, newsize); ++ memset(vmaw->header_blob_table + vmaw->header_blob_table_size, ++ 0, newsize - vmaw->header_blob_table_size); ++ vmaw->header_blob_table_size = newsize; ++ } ++ ++ uint32_t cpos = vmaw->header_blob_table_pos; ++ vmaw->header_blob_table[cpos] = len & 255; ++ vmaw->header_blob_table[cpos+1] = (len >> 8) & 255; ++ memcpy(vmaw->header_blob_table + cpos + 2, data, len); ++ vmaw->header_blob_table_pos += len + 2; ++ return cpos; ++} ++ ++static uint32_t allocate_header_string(VmaWriter *vmaw, const char *str) ++{ ++ assert(vmaw); ++ ++ size_t len = strlen(str) + 1; ++ ++ return allocate_header_blob(vmaw, str, len); ++} ++ ++int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, ++ gsize len) ++{ ++ assert(vmaw); ++ assert(!vmaw->header_written); ++ assert(vmaw->config_count < VMA_MAX_CONFIGS); ++ assert(name); ++ assert(data); ++ assert(len); ++ ++ gchar *basename = g_path_get_basename(name); ++ uint32_t name_ptr = allocate_header_string(vmaw, basename); ++ g_free(basename); ++ ++ if (!name_ptr) { ++ return -1; ++ } ++ ++ uint32_t data_ptr = allocate_header_blob(vmaw, data, len); ++ if (!data_ptr) { ++ return -1; ++ } ++ ++ vmaw->config_names[vmaw->config_count] = name_ptr; ++ vmaw->config_data[vmaw->config_count] = data_ptr; ++ ++ vmaw->config_count++; ++ ++ return 0; ++} ++ ++int vma_writer_register_stream(VmaWriter *vmaw, const char *devname, ++ size_t size) ++{ ++ assert(vmaw); ++ assert(devname); ++ assert(!vmaw->status); ++ ++ if (vmaw->header_written) { ++ vma_writer_set_error(vmaw, "vma_writer_register_stream: header " ++ "already written"); ++ return -1; ++ } ++ ++ guint n = vmaw->stream_count + 1; ++ ++ /* we can have dev_ids form 1 to 255 (0 reserved) ++ * 255(-1) reseverd for safety ++ */ ++ if (n > 254) { ++ vma_writer_set_error(vmaw, "vma_writer_register_stream: " ++ "too many drives"); ++ return -1; ++ } ++ ++ if (size <= 0) { ++ vma_writer_set_error(vmaw, "vma_writer_register_stream: " ++ "got strange size %zd", size); ++ return -1; ++ } ++ ++ DPRINTF("vma_writer_register_stream %s %zu %d\n", devname, size, n); ++ ++ vmaw->stream_info[n].devname = g_strdup(devname); ++ vmaw->stream_info[n].size = size; ++ ++ vmaw->stream_info[n].cluster_count = (size + VMA_CLUSTER_SIZE - 1) / ++ VMA_CLUSTER_SIZE; ++ ++ vmaw->stream_count = n; ++ ++ if (strcmp(devname, "vmstate") == 0) { ++ vmaw->vmstate_stream = n; ++ } ++ ++ return n; ++} ++ ++static void vma_co_continue_write(void *opaque) ++{ ++ VmaWriter *vmaw = opaque; ++ ++ DPRINTF("vma_co_continue_write\n"); ++ qemu_coroutine_enter(vmaw->co_writer, NULL); ++} ++ ++static ssize_t coroutine_fn ++vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes) ++{ ++ size_t done = 0; ++ ssize_t ret; ++ ++ /* atomic writes (we cannot interleave writes) */ ++ qemu_co_mutex_lock(&vmaw->writer_lock); ++ ++ DPRINTF("vma_co_write enter %zd\n", bytes); ++ ++ assert(vmaw->co_writer == NULL); ++ ++ vmaw->co_writer = qemu_coroutine_self(); ++ ++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, 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) { ++ done += ret; ++ DPRINTF("vma_co_write written %zd %zd\n", done, ret); ++ } else if (ret < 0) { ++ if (errno == EAGAIN || errno == EWOULDBLOCK) { ++ DPRINTF("vma_co_write yield %zd\n", done); ++ qemu_coroutine_yield(); ++ DPRINTF("vma_co_write restart %zd\n", done); ++ } else { ++ vma_writer_set_error(vmaw, "vma_co_write write error - %s", ++ g_strerror(errno)); ++ done = -1; /* always return failure for partial writes */ ++ break; ++ } ++ } else if (ret == 0) { ++ /* should not happen - simply try again */ ++ } ++ } ++ ++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL); ++ ++ vmaw->co_writer = NULL; ++ ++ qemu_co_mutex_unlock(&vmaw->writer_lock); ++ ++ DPRINTF("vma_co_write leave %zd\n", done); ++ return done; ++} ++ ++static void coroutine_fn vma_co_writer_task(void *opaque) ++{ ++ VmaAIOCB *cb = opaque; ++ ++ DPRINTF("vma_co_writer_task start\n"); ++ ++ int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); ++ DPRINTF("vma_co_writer_task write done %zd\n", done); ++ ++ if (done != cb->bytes) { ++ DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); ++ vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", ++ done); ++ } ++ ++ cb->bytes = 0; ++ ++ qemu_co_queue_next(&cb->vmaw->wqueue); ++ ++ DPRINTF("vma_co_writer_task end\n"); ++} ++ ++static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) ++{ ++ DPRINTF("vma_queue_flush enter\n"); ++ ++ assert(vmaw); ++ ++ while (1) { ++ int i; ++ VmaAIOCB *cb = NULL; ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ if (vmaw->aiocbs[i]->bytes) { ++ cb = vmaw->aiocbs[i]; ++ DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, ++ vmaw->aiocbs[i]->bytes); ++ break; ++ } ++ } ++ if (!cb) { ++ break; ++ } ++ qemu_co_queue_wait(&vmaw->wqueue); ++ } ++ ++ DPRINTF("vma_queue_flush leave\n"); ++} ++ ++/** ++ * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') ++ * So we need to create a coroutione to allow 'parallel' execution. ++ */ ++static ssize_t coroutine_fn ++vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) ++{ ++ DPRINTF("vma_queue_write enter %zd\n", bytes); ++ ++ assert(vmaw); ++ assert(buf); ++ assert(bytes <= VMA_MAX_EXTENT_SIZE); ++ ++ VmaAIOCB *cb = NULL; ++ while (!cb) { ++ int i; ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ if (!vmaw->aiocbs[i]->bytes) { ++ cb = vmaw->aiocbs[i]; ++ break; ++ } ++ } ++ if (!cb) { ++ qemu_co_queue_wait(&vmaw->wqueue); ++ } ++ } ++ ++ memcpy(cb->buffer, buf, bytes); ++ cb->bytes = bytes; ++ cb->vmaw = vmaw; ++ ++ DPRINTF("vma_queue_write start %zd\n", bytes); ++ cb->co = qemu_coroutine_create(vma_co_writer_task); ++ qemu_coroutine_enter(cb->co, cb); ++ ++ DPRINTF("vma_queue_write leave\n"); ++ ++ return bytes; ++} ++ ++VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp) ++{ ++ const char *p; ++ ++ assert(sizeof(VmaHeader) == (4096 + 8192)); ++ assert(G_STRUCT_OFFSET(VmaHeader, config_names) == 2044); ++ assert(G_STRUCT_OFFSET(VmaHeader, config_data) == 3068); ++ assert(G_STRUCT_OFFSET(VmaHeader, dev_info) == 4096); ++ assert(sizeof(VmaExtentHeader) == 512); ++ ++ VmaWriter *vmaw = g_new0(VmaWriter, 1); ++ vmaw->fd = -1; ++ ++ vmaw->md5csum = g_checksum_new(G_CHECKSUM_MD5); ++ if (!vmaw->md5csum) { ++ error_setg(errp, "can't allocate cmsum\n"); ++ goto err; ++ } ++ ++ if (strstart(filename, "exec:", &p)) { ++ vmaw->cmd = popen(p, "w"); ++ if (vmaw->cmd == NULL) { ++ error_setg(errp, "can't popen command '%s' - %s\n", p, ++ g_strerror(errno)); ++ goto err; ++ } ++ vmaw->fd = fileno(vmaw->cmd); ++ ++ /* 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 { ++ 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, ++ g_strerror(errno)); ++ goto err; ++ } ++ } ++ ++ /* 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; ++ ++ vmaw->header_blob_table_pos = 1; /* start at pos 1 */ ++ ++ qemu_co_mutex_init(&vmaw->writer_lock); ++ qemu_co_mutex_init(&vmaw->flush_lock); ++ qemu_co_queue_init(&vmaw->wqueue); ++ ++ uuid_copy(vmaw->uuid, uuid); ++ ++ return vmaw; ++ ++err: ++ if (vmaw) { ++ if (vmaw->cmd) { ++ pclose(vmaw->cmd); ++ } else if (vmaw->fd >= 0) { ++ close(vmaw->fd); ++ } ++ ++ if (vmaw->md5csum) { ++ g_checksum_free(vmaw->md5csum); ++ } ++ ++ g_free(vmaw); ++ } ++ ++ return NULL; ++} ++ ++static int coroutine_fn vma_write_header(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ int header_clusters = 8; ++ char buf[65536*header_clusters]; ++ VmaHeader *head = (VmaHeader *)buf; ++ ++ int i; ++ ++ DPRINTF("VMA WRITE HEADER\n"); ++ ++ if (vmaw->status < 0) { ++ return vmaw->status; ++ } ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ head->magic = VMA_MAGIC; ++ head->version = GUINT32_TO_BE(1); /* v1 */ ++ memcpy(head->uuid, vmaw->uuid, 16); ++ ++ time_t ctime = time(NULL); ++ head->ctime = GUINT64_TO_BE(ctime); ++ ++ if (!vmaw->stream_count) { ++ return -1; ++ } ++ ++ for (i = 0; i < VMA_MAX_CONFIGS; i++) { ++ head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]); ++ head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]); ++ } ++ ++ /* 32 bytes per device (12 used currently) = 8192 bytes max */ ++ for (i = 1; i <= 254; i++) { ++ VmaStreamInfo *si = &vmaw->stream_info[i]; ++ if (si->size) { ++ assert(si->devname); ++ uint32_t devname_ptr = allocate_header_string(vmaw, si->devname); ++ if (!devname_ptr) { ++ return -1; ++ } ++ head->dev_info[i].devname_ptr = GUINT32_TO_BE(devname_ptr); ++ head->dev_info[i].size = GUINT64_TO_BE(si->size); ++ } ++ } ++ ++ uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size; ++ head->header_size = GUINT32_TO_BE(header_size); ++ ++ if (header_size > sizeof(buf)) { ++ return -1; /* just to be sure */ ++ } ++ ++ uint32_t blob_buffer_offset = sizeof(VmaHeader); ++ memcpy(buf + blob_buffer_offset, vmaw->header_blob_table, ++ vmaw->header_blob_table_size); ++ head->blob_buffer_offset = GUINT32_TO_BE(blob_buffer_offset); ++ head->blob_buffer_size = GUINT32_TO_BE(vmaw->header_blob_table_pos); ++ ++ g_checksum_reset(vmaw->md5csum); ++ g_checksum_update(vmaw->md5csum, (const guchar *)buf, header_size); ++ gsize csize = 16; ++ g_checksum_get_digest(vmaw->md5csum, (guint8 *)(head->md5sum), &csize); ++ ++ return vma_queue_write(vmaw, buf, header_size); ++} ++ ++static int coroutine_fn vma_writer_flush(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ ++ int ret; ++ int i; ++ ++ if (vmaw->status < 0) { ++ return vmaw->status; ++ } ++ ++ if (!vmaw->header_written) { ++ vmaw->header_written = true; ++ ret = vma_write_header(vmaw); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_flush: write header failed"); ++ return ret; ++ } ++ } ++ ++ DPRINTF("VMA WRITE FLUSH %d %d\n", vmaw->outbuf_count, vmaw->outbuf_pos); ++ ++ ++ VmaExtentHeader *ehead = (VmaExtentHeader *)vmaw->outbuf; ++ ++ ehead->magic = VMA_EXTENT_MAGIC; ++ ehead->reserved1 = 0; ++ ++ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { ++ ehead->blockinfo[i] = GUINT64_TO_BE(vmaw->outbuf_block_info[i]); ++ } ++ ++ guint16 block_count = (vmaw->outbuf_pos - VMA_EXTENT_HEADER_SIZE) / ++ VMA_BLOCK_SIZE; ++ ++ ehead->block_count = GUINT16_TO_BE(block_count); ++ ++ memcpy(ehead->uuid, vmaw->uuid, sizeof(ehead->uuid)); ++ memset(ehead->md5sum, 0, sizeof(ehead->md5sum)); ++ ++ g_checksum_reset(vmaw->md5csum); ++ g_checksum_update(vmaw->md5csum, vmaw->outbuf, VMA_EXTENT_HEADER_SIZE); ++ gsize csize = 16; ++ g_checksum_get_digest(vmaw->md5csum, ehead->md5sum, &csize); ++ ++ int bytes = vmaw->outbuf_pos; ++ ret = vma_queue_write(vmaw, vmaw->outbuf, bytes); ++ if (ret != bytes) { ++ vma_writer_set_error(vmaw, "vma_writer_flush: failed write"); ++ } ++ ++ vmaw->outbuf_count = 0; ++ vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE; ++ ++ for (i = 0; i < VMA_BLOCKS_PER_EXTENT; i++) { ++ vmaw->outbuf_block_info[i] = 0; ++ } ++ ++ return vmaw->status; ++} ++ ++static int vma_count_open_streams(VmaWriter *vmaw) ++{ ++ g_assert(vmaw != NULL); ++ ++ int i; ++ int open_drives = 0; ++ for (i = 0; i <= 255; i++) { ++ if (vmaw->stream_info[i].size && !vmaw->stream_info[i].finished) { ++ open_drives++; ++ } ++ } ++ ++ return open_drives; ++} ++ ++/** ++ * all jobs should call this when there is no more data ++ * Returns: number of remaining stream (0 ==> finished) ++ */ ++int coroutine_fn ++vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id) ++{ ++ g_assert(vmaw != NULL); ++ ++ DPRINTF("vma_writer_set_status %d\n", dev_id); ++ if (!vmaw->stream_info[dev_id].size) { ++ vma_writer_set_error(vmaw, "vma_writer_close_stream: " ++ "no such stream %d", dev_id); ++ return -1; ++ } ++ if (vmaw->stream_info[dev_id].finished) { ++ vma_writer_set_error(vmaw, "vma_writer_close_stream: " ++ "stream already closed %d", dev_id); ++ return -1; ++ } ++ ++ vmaw->stream_info[dev_id].finished = true; ++ ++ int open_drives = vma_count_open_streams(vmaw); ++ ++ if (open_drives <= 0) { ++ DPRINTF("vma_writer_set_status all drives completed\n"); ++ qemu_co_mutex_lock(&vmaw->flush_lock); ++ int ret = vma_writer_flush(vmaw); ++ qemu_co_mutex_unlock(&vmaw->flush_lock); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed"); ++ } ++ } ++ ++ return open_drives; ++} ++ ++int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status) ++{ ++ int i; ++ ++ g_assert(vmaw != NULL); ++ ++ if (status) { ++ status->status = vmaw->status; ++ g_strlcpy(status->errmsg, vmaw->errmsg, sizeof(status->errmsg)); ++ for (i = 0; i <= 255; i++) { ++ status->stream_info[i] = vmaw->stream_info[i]; ++ } ++ ++ uuid_unparse_lower(vmaw->uuid, status->uuid_str); ++ } ++ ++ status->closed = vmaw->closed; ++ ++ return vmaw->status; ++} ++ ++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)) { ++ ret = vma_writer_flush(vmaw); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_get_buffer: flush failed"); ++ break; ++ } ++ } ++ ++ qemu_co_mutex_unlock(&vmaw->flush_lock); ++ ++ return ret; ++} ++ ++ ++int64_t coroutine_fn ++vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num, ++ unsigned char *buf, size_t *zero_bytes) ++{ ++ g_assert(vmaw != NULL); ++ g_assert(zero_bytes != NULL); ++ ++ *zero_bytes = 0; ++ ++ if (vmaw->status < 0) { ++ return vmaw->status; ++ } ++ ++ if (!dev_id || !vmaw->stream_info[dev_id].size) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "no such stream %d", dev_id); ++ return -1; ++ } ++ ++ if (vmaw->stream_info[dev_id].finished) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "stream already closed %d", dev_id); ++ return -1; ++ } ++ ++ ++ if (cluster_num >= (((uint64_t)1)<<32)) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "cluster number out of range"); ++ return -1; ++ } ++ ++ if (dev_id == vmaw->vmstate_stream) { ++ if (cluster_num != vmaw->vmstate_clusters) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "non sequential vmstate write"); ++ } ++ vmaw->vmstate_clusters++; ++ } else if (cluster_num >= vmaw->stream_info[dev_id].cluster_count) { ++ vma_writer_set_error(vmaw, "vma_writer_write: cluster number too big"); ++ return -1; ++ } ++ ++ /* wait until buffer is available */ ++ if (vma_writer_get_buffer(vmaw) < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "vma_writer_get_buffer failed"); ++ return -1; ++ } ++ ++ DPRINTF("VMA WRITE %d %zd\n", dev_id, cluster_num); ++ ++ uint16_t mask = 0; ++ ++ 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); ++ block_info |= ((uint64_t)dev_id) << 32; ++ block_info |= (cluster_num & 0xffffffff); ++ vmaw->outbuf_block_info[vmaw->outbuf_count] = block_info; ++ ++ DPRINTF("VMA WRITE MASK %zd %zx\n", cluster_num, block_info); ++ ++ vmaw->outbuf_count++; ++ ++ /** NOTE: We allways write whole clusters, but we correctly set ++ * transferred bytes. So transferred == size when when everything ++ * went OK. ++ */ ++ size_t transferred = VMA_CLUSTER_SIZE; ++ ++ if (dev_id != vmaw->vmstate_stream) { ++ uint64_t last = (cluster_num + 1) * VMA_CLUSTER_SIZE; ++ if (last > vmaw->stream_info[dev_id].size) { ++ uint64_t diff = last - vmaw->stream_info[dev_id].size; ++ if (diff >= VMA_CLUSTER_SIZE) { ++ vma_writer_set_error(vmaw, "vma_writer_write: " ++ "read after last cluster"); ++ return -1; ++ } ++ transferred -= diff; ++ } ++ } ++ ++ vmaw->stream_info[dev_id].transferred += transferred; ++ ++ return transferred; ++} ++ ++int vma_writer_close(VmaWriter *vmaw, Error **errp) ++{ ++ g_assert(vmaw != NULL); ++ ++ int i; ++ ++ vma_queue_flush(vmaw); ++ ++ /* this should not happen - just to be sure */ ++ while (!qemu_co_queue_empty(&vmaw->wqueue)) { ++ DPRINTF("vma_writer_close wait\n"); ++ co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 1000000); ++ } ++ ++ if (vmaw->cmd) { ++ if (pclose(vmaw->cmd) < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "pclose failed - %s", g_strerror(errno)); ++ } ++ } else { ++ if (close(vmaw->fd) < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "close failed - %s", g_strerror(errno)); ++ } ++ } ++ ++ for (i = 0; i <= 255; i++) { ++ VmaStreamInfo *si = &vmaw->stream_info[i]; ++ if (si->size) { ++ if (!si->finished) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "detected open stream '%s'", si->devname); ++ } else if ((si->transferred != si->size) && ++ (i != vmaw->vmstate_stream)) { ++ vma_writer_set_error(vmaw, "vma_writer_close: " ++ "incomplete stream '%s' (%zd != %zd)", ++ si->devname, si->transferred, si->size); ++ } ++ } ++ } ++ ++ for (i = 0; i <= 255; i++) { ++ vmaw->stream_info[i].finished = 1; /* mark as closed */ ++ } ++ ++ vmaw->closed = 1; ++ ++ if (vmaw->status < 0 && *errp == NULL) { ++ error_setg(errp, "%s", vmaw->errmsg); ++ } ++ ++ return vmaw->status; ++} ++ ++void vma_writer_destroy(VmaWriter *vmaw) ++{ ++ assert(vmaw); ++ ++ int i; ++ ++ for (i = 0; i <= 255; i++) { ++ if (vmaw->stream_info[i].devname) { ++ g_free(vmaw->stream_info[i].devname); ++ } ++ } ++ ++ if (vmaw->md5csum) { ++ g_checksum_free(vmaw->md5csum); ++ } ++ ++ for (i = 0; i < WRITE_BUFFERS; i++) { ++ free(vmaw->aiocbs[i]); ++ } ++ ++ g_free(vmaw); ++} +Index: new/vma.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ new/vma.c 2014-11-20 08:47:23.000000000 +0100 +@@ -0,0 +1,582 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) 2012-2013 Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vma.h" ++#include "qemu-common.h" ++#include "qemu/error-report.h" ++#include "qemu/main-loop.h" ++ ++static void help(void) ++{ ++ const char *help_msg = ++ "usage: vma command [command options]\n" ++ "\n" ++ "vma list \n" ++ "vma create [-c config] pathname ...\n" ++ "vma extract [-r ] \n" ++ ; ++ ++ printf("%s", help_msg); ++ exit(1); ++} ++ ++static const char *extract_devname(const char *path, char **devname, int index) ++{ ++ assert(path); ++ ++ const char *sep = strchr(path, '='); ++ ++ if (sep) { ++ *devname = g_strndup(path, sep - path); ++ path = sep + 1; ++ } else { ++ if (index >= 0) { ++ *devname = g_strdup_printf("disk%d", index); ++ } else { ++ *devname = NULL; ++ } ++ } ++ ++ return path; ++} ++ ++static void print_content(VmaReader *vmar) ++{ ++ assert(vmar); ++ ++ VmaHeader *head = vma_reader_get_header(vmar); ++ ++ GList *l = vma_reader_get_config_data(vmar); ++ while (l && l->data) { ++ VmaConfigData *cdata = (VmaConfigData *)l->data; ++ l = g_list_next(l); ++ printf("CFG: size: %d name: %s\n", cdata->len, cdata->name); ++ } ++ ++ int i; ++ VmaDeviceInfo *di; ++ for (i = 1; i < 255; i++) { ++ di = vma_reader_get_device_info(vmar, i); ++ if (di) { ++ if (strcmp(di->devname, "vmstate") == 0) { ++ printf("VMSTATE: dev_id=%d memory: %zd\n", i, di->size); ++ } else { ++ printf("DEV: dev_id=%d size: %zd devname: %s\n", ++ i, di->size, di->devname); ++ } ++ } ++ } ++ /* ctime is the last entry we print */ ++ printf("CTIME: %s", ctime(&head->ctime)); ++ fflush(stdout); ++} ++ ++static int list_content(int argc, char **argv) ++{ ++ int c, ret = 0; ++ const char *filename; ++ ++ for (;;) { ++ c = getopt(argc, argv, "h"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 1) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ print_content(vmar); ++ ++ vma_reader_destroy(vmar); ++ ++ return ret; ++} ++ ++typedef struct RestoreMap { ++ char *devname; ++ char *path; ++ bool write_zero; ++} RestoreMap; ++ ++static int extract_content(int argc, char **argv) ++{ ++ int c, ret = 0; ++ int verbose = 0; ++ const char *filename; ++ const char *dirname; ++ const char *readmap = NULL; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hvr:"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'r': ++ readmap = optarg; ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ default: ++ help(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 2) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ dirname = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (mkdir(dirname, 0777) < 0) { ++ g_error("unable to create target directory %s - %s", ++ dirname, g_strerror(errno)); ++ } ++ ++ GList *l = vma_reader_get_config_data(vmar); ++ while (l && l->data) { ++ VmaConfigData *cdata = (VmaConfigData *)l->data; ++ l = g_list_next(l); ++ char *cfgfn = g_strdup_printf("%s/%s", dirname, cdata->name); ++ GError *err = NULL; ++ if (!g_file_set_contents(cfgfn, (gchar *)cdata->data, cdata->len, ++ &err)) { ++ g_error("unable to write file: %s", err->message); ++ } ++ } ++ ++ GHashTable *devmap = g_hash_table_new(g_str_hash, g_str_equal); ++ ++ if (readmap) { ++ print_content(vmar); ++ ++ FILE *map = fopen(readmap, "r"); ++ if (!map) { ++ g_error("unable to open fifo %s - %s", readmap, g_strerror(errno)); ++ } ++ ++ while (1) { ++ char inbuf[8192]; ++ char *line = fgets(inbuf, sizeof(inbuf), map); ++ if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { ++ break; ++ } ++ int len = strlen(line); ++ if (line[len - 1] == '\n') { ++ line[len - 1] = '\0'; ++ if (len == 1) { ++ break; ++ } ++ } ++ ++ const char *path; ++ bool write_zero; ++ if (line[0] == '0' && line[1] == ':') { ++ path = inbuf + 2; ++ write_zero = false; ++ } else if (line[0] == '1' && line[1] == ':') { ++ path = inbuf + 2; ++ write_zero = true; ++ } else { ++ g_error("read map failed - parse error ('%s')", inbuf); ++ } ++ ++ char *devname = NULL; ++ path = extract_devname(path, &devname, -1); ++ if (!devname) { ++ g_error("read map failed - no dev name specified ('%s')", ++ inbuf); ++ } ++ ++ RestoreMap *map = g_new0(RestoreMap, 1); ++ map->devname = g_strdup(devname); ++ map->path = g_strdup(path); ++ map->write_zero = write_zero; ++ ++ g_hash_table_insert(devmap, map->devname, map); ++ ++ }; ++ } ++ ++ int i; ++ int vmstate_fd = -1; ++ guint8 vmstate_stream = 0; ++ ++ for (i = 1; i < 255; i++) { ++ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); ++ if (di && (strcmp(di->devname, "vmstate") == 0)) { ++ vmstate_stream = i; ++ char *statefn = g_strdup_printf("%s/vmstate.bin", dirname); ++ vmstate_fd = open(statefn, O_WRONLY|O_CREAT|O_EXCL, 0644); ++ if (vmstate_fd < 0) { ++ g_error("create vmstate file '%s' failed - %s", statefn, ++ g_strerror(errno)); ++ } ++ g_free(statefn); ++ } else if (di) { ++ char *devfn = NULL; ++ int flags = BDRV_O_RDWR|BDRV_O_CACHE_WB; ++ bool write_zero = true; ++ ++ if (readmap) { ++ RestoreMap *map; ++ map = (RestoreMap *)g_hash_table_lookup(devmap, di->devname); ++ if (map == NULL) { ++ g_error("no device name mapping for %s", di->devname); ++ } ++ devfn = map->path; ++ write_zero = map->write_zero; ++ } else { ++ devfn = g_strdup_printf("%s/tmp-disk-%s.raw", ++ dirname, di->devname); ++ printf("DEVINFO %s %zd\n", devfn, di->size); ++ ++ bdrv_img_create(devfn, "raw", NULL, NULL, NULL, di->size, ++ flags, &errp, 0); ++ if (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 ++ * need to write zeroes (so we generate a sparse file) ++ */ ++ write_zero = false; ++ } ++ ++ BlockDriverState *bs = bdrv_new(); ++ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, NULL, &errp)) { ++ g_error("can't open file %s - %s", devfn, ++ error_get_pretty(errp)); ++ } ++ if (vma_reader_register_bs(vmar, i, bs, write_zero, &errp) < 0) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (!readmap) { ++ g_free(devfn); ++ } ++ } ++ } ++ ++ if (vma_reader_restore(vmar, vmstate_fd, verbose, &errp) < 0) { ++ g_error("restore failed - %s", error_get_pretty(errp)); ++ } ++ ++ if (!readmap) { ++ for (i = 1; i < 255; i++) { ++ VmaDeviceInfo *di = vma_reader_get_device_info(vmar, i); ++ if (di && (i != vmstate_stream)) { ++ char *tmpfn = g_strdup_printf("%s/tmp-disk-%s.raw", ++ dirname, di->devname); ++ char *fn = g_strdup_printf("%s/disk-%s.raw", ++ dirname, di->devname); ++ if (rename(tmpfn, fn) != 0) { ++ g_error("rename %s to %s failed - %s", ++ tmpfn, fn, g_strerror(errno)); ++ } ++ } ++ } ++ } ++ ++ vma_reader_destroy(vmar); ++ ++ bdrv_close_all(); ++ ++ return ret; ++} ++ ++typedef struct BackupJob { ++ BlockDriverState *bs; ++ int64_t len; ++ VmaWriter *vmaw; ++ uint8_t dev_id; ++} BackupJob; ++ ++#define BACKUP_SECTORS_PER_CLUSTER (VMA_CLUSTER_SIZE / BDRV_SECTOR_SIZE) ++ ++static void coroutine_fn backup_run(void *opaque) ++{ ++ BackupJob *job = (BackupJob *)opaque; ++ struct iovec iov; ++ QEMUIOVector qiov; ++ ++ int64_t start, end; ++ int ret = 0; ++ ++ unsigned char *buf = qemu_blockalign(job->bs, VMA_CLUSTER_SIZE); ++ ++ start = 0; ++ end = DIV_ROUND_UP(job->len / BDRV_SECTOR_SIZE, ++ BACKUP_SECTORS_PER_CLUSTER); ++ ++ for (; start < end; start++) { ++ iov.iov_base = buf; ++ iov.iov_len = VMA_CLUSTER_SIZE; ++ qemu_iovec_init_external(&qiov, &iov, 1); ++ ++ ret = bdrv_co_readv(job->bs, start * BACKUP_SECTORS_PER_CLUSTER, ++ BACKUP_SECTORS_PER_CLUSTER, &qiov); ++ if (ret < 0) { ++ vma_writer_set_error(job->vmaw, "read error", -1); ++ goto out; ++ } ++ ++ size_t zb = 0; ++ if (vma_writer_write(job->vmaw, job->dev_id, start, buf, &zb) < 0) { ++ vma_writer_set_error(job->vmaw, "backup_dump_cb vma_writer_write failed", -1); ++ goto out; ++ } ++ } ++ ++ ++out: ++ if (vma_writer_close_stream(job->vmaw, job->dev_id) <= 0) { ++ Error *err = NULL; ++ if (vma_writer_close(job->vmaw, &err) != 0) { ++ g_warning("vma_writer_close failed %s", error_get_pretty(err)); ++ } ++ } ++} ++ ++static int create_archive(int argc, char **argv) ++{ ++ int i, c, res; ++ int verbose = 0; ++ const char *archivename; ++ GList *config_files = NULL; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hvc:"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'c': ++ config_files = g_list_append(config_files, optarg); ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ ++ /* make sure we have archive name and at least one path */ ++ if ((optind + 2) > argc) { ++ help(); ++ } ++ ++ archivename = argv[optind++]; ++ ++ uuid_t uuid; ++ uuid_generate(uuid); ++ ++ Error *local_err = NULL; ++ VmaWriter *vmaw = vma_writer_create(archivename, uuid, &local_err); ++ ++ if (vmaw == NULL) { ++ g_error("%s", error_get_pretty(local_err)); ++ } ++ ++ GList *l = config_files; ++ while (l && l->data) { ++ char *name = l->data; ++ char *cdata = NULL; ++ gsize clen = 0; ++ GError *err = NULL; ++ if (!g_file_get_contents(name, &cdata, &clen, &err)) { ++ unlink(archivename); ++ g_error("Unable to read file: %s", err->message); ++ } ++ ++ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) { ++ unlink(archivename); ++ g_error("Unable to append config data %s (len = %zd)", ++ name, clen); ++ } ++ l = g_list_next(l); ++ } ++ ++ int ind = 0; ++ while (optind < argc) { ++ const char *path = argv[optind++]; ++ char *devname = NULL; ++ path = extract_devname(path, &devname, ind++); ++ ++ BlockDriver *drv = NULL; ++ Error *errp = NULL; ++ BlockDriverState *bs = bdrv_new(); ++ ++ res = bdrv_open(&bs, path, NULL, NULL, BDRV_O_CACHE_WB , drv, &errp); ++ if (res < 0) { ++ unlink(archivename); ++ g_error("bdrv_open '%s' failed - %s", path, error_get_pretty(errp)); ++ } ++ int64_t size = bdrv_getlength(bs); ++ int dev_id = vma_writer_register_stream(vmaw, devname, size); ++ if (dev_id <= 0) { ++ unlink(archivename); ++ g_error("vma_writer_register_stream '%s' failed", devname); ++ } ++ ++ BackupJob *job = g_new0(BackupJob, 1); ++ job->len = size; ++ job->bs = bs; ++ job->vmaw = vmaw; ++ job->dev_id = dev_id; ++ ++ Coroutine *co = qemu_coroutine_create(backup_run); ++ qemu_coroutine_enter(co, job); ++ } ++ ++ VmaStatus vmastat; ++ int percent = 0; ++ int last_percent = -1; ++ ++ while (1) { ++ main_loop_wait(false); ++ vma_writer_get_status(vmaw, &vmastat); ++ ++ if (verbose) { ++ ++ uint64_t total = 0; ++ uint64_t transferred = 0; ++ uint64_t zero_bytes = 0; ++ ++ int i; ++ for (i = 0; i < 256; i++) { ++ if (vmastat.stream_info[i].size) { ++ total += vmastat.stream_info[i].size; ++ transferred += vmastat.stream_info[i].transferred; ++ zero_bytes += vmastat.stream_info[i].zero_bytes; ++ } ++ } ++ percent = (transferred*100)/total; ++ if (percent != last_percent) { ++ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, ++ transferred, total, zero_bytes); ++ fflush(stderr); ++ ++ last_percent = percent; ++ } ++ } ++ ++ if (vmastat.closed) { ++ break; ++ } ++ } ++ ++ bdrv_drain_all(); ++ ++ vma_writer_get_status(vmaw, &vmastat); ++ ++ if (verbose) { ++ for (i = 0; i < 256; i++) { ++ VmaStreamInfo *si = &vmastat.stream_info[i]; ++ if (si->size) { ++ fprintf(stderr, "image %s: size=%zd zeros=%zd saved=%zd\n", ++ si->devname, si->size, si->zero_bytes, ++ si->size - si->zero_bytes); ++ } ++ } ++ } ++ ++ if (vmastat.status < 0) { ++ unlink(archivename); ++ g_error("creating vma archive failed"); ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char **argv) ++{ ++ const char *cmdname; ++ Error *main_loop_err = NULL; ++ ++ error_set_progname(argv[0]); ++ ++ if (qemu_init_main_loop(&main_loop_err)) { ++ g_error("%s", error_get_pretty(main_loop_err)); ++ } ++ ++ bdrv_init(); ++ ++ if (argc < 2) { ++ help(); ++ } ++ ++ cmdname = argv[1]; ++ argc--; argv++; ++ ++ ++ if (!strcmp(cmdname, "list")) { ++ return list_content(argc, argv); ++ } else if (!strcmp(cmdname, "create")) { ++ return create_archive(argc, argv); ++ } else if (!strcmp(cmdname, "extract")) { ++ return extract_content(argc, argv); ++ } ++ ++ help(); ++ return 0; ++} +Index: new/vma.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ new/vma.h 2014-11-20 08:15:12.000000000 +0100 +@@ -0,0 +1,146 @@ ++/* ++ * VMA: Virtual Machine Archive ++ * ++ * Copyright (C) Proxmox Server Solutions ++ * ++ * Authors: ++ * Dietmar Maurer (dietmar@proxmox.com) ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef BACKUP_VMA_H ++#define BACKUP_VMA_H ++ ++#include ++#include "qapi/error.h" ++#include "block/block.h" ++ ++#define VMA_BLOCK_BITS 12 ++#define VMA_BLOCK_SIZE (1< +Date: Mon, 11 Mar 2013 07:07:46 +0100 +Subject: [PATCH v5 7/7] vma: add verify command + +Users wants to verify the archive after backup. + +Examples: + + # vma verify -v test.vma + + # lzop -d -c test.vma.lzo |vma verify - + +Signed-off-by: Dietmar Maurer +--- + vma-reader.c | 118 +++++++++++++++++++++++++++++++++++++++++++--------------- + vma.c | 57 +++++++++++++++++++++++++++- + vma.h | 1 + + 3 files changed, 145 insertions(+), 31 deletions(-) + +Index: new/vma-reader.c +=================================================================== +--- new.orig/vma-reader.c 2014-11-20 08:15:12.000000000 +0100 ++++ new/vma-reader.c 2014-11-20 08:47:30.000000000 +0100 +@@ -53,6 +53,8 @@ + time_t start_time; + int64_t cluster_count; + int64_t clusters_read; ++ int64_t zero_cluster_data; ++ int64_t partial_zero_cluster_data; + int clusters_read_per; + }; + +@@ -433,6 +435,27 @@ + return NULL; + } + ++static void allocate_rstate(VmaReader *vmar, guint8 dev_id, ++ BlockDriverState *bs, bool write_zeroes) ++{ ++ assert(vmar); ++ assert(dev_id); ++ ++ vmar->rstate[dev_id].bs = bs; ++ vmar->rstate[dev_id].write_zeroes = write_zeroes; ++ ++ int64_t size = vmar->devinfo[dev_id].size; ++ ++ int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + ++ (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; ++ bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; ++ ++ 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; ++} ++ + int vma_reader_register_bs(VmaReader *vmar, guint8 dev_id, BlockDriverState *bs, + bool write_zeroes, Error **errp) + { +@@ -449,17 +472,7 @@ + return -1; + } + +- vmar->rstate[dev_id].bs = bs; +- vmar->rstate[dev_id].write_zeroes = write_zeroes; +- +- int64_t bitmap_size = (size/BDRV_SECTOR_SIZE) + +- (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG - 1; +- bitmap_size /= (VMA_CLUSTER_SIZE/BDRV_SECTOR_SIZE) * BITS_PER_LONG; +- +- 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; ++ allocate_rstate(vmar, dev_id, bs, write_zeroes); + + return 0; + } +@@ -526,9 +539,10 @@ + } + return 0; + } ++ + static int restore_extent(VmaReader *vmar, unsigned char *buf, + int extent_size, int vmstate_fd, +- bool verbose, Error **errp) ++ bool verbose, bool verify, Error **errp) + { + assert(vmar); + assert(buf); +@@ -553,7 +567,7 @@ + + if (dev_id != vmar->vmstate_stream) { + bs = rstate->bs; +- if (!bs) { ++ if (!verify && !bs) { + error_setg(errp, "got wrong dev id %d", dev_id); + return -1; + } +@@ -609,10 +623,13 @@ + return -1; + } + +- int nb_sectors = end_sector - sector_num; +- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, buf + start, +- sector_num, nb_sectors, errp) < 0) { +- return -1; ++ if (!verify) { ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ buf + start, sector_num, nb_sectors, ++ errp) < 0) { ++ return -1; ++ } + } + + start += VMA_CLUSTER_SIZE; +@@ -642,26 +659,37 @@ + return -1; + } + +- int nb_sectors = end_sector - sector_num; +- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, +- buf + start, sector_num, +- nb_sectors, errp) < 0) { +- return -1; ++ if (!verify) { ++ int nb_sectors = end_sector - sector_num; ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ buf + start, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } + } + + start += VMA_BLOCK_SIZE; + + } else { + +- if (rstate->write_zeroes && (end_sector > sector_num)) { ++ ++ if (end_sector > sector_num) { + /* Todo: use bdrv_co_write_zeroes (but that need to + * be run inside coroutine?) + */ + int nb_sectors = end_sector - sector_num; +- if (restore_write_data(vmar, dev_id, bs, vmstate_fd, +- zero_vma_block, sector_num, +- nb_sectors, errp) < 0) { +- return -1; ++ int zero_size = BDRV_SECTOR_SIZE*nb_sectors; ++ vmar->zero_cluster_data += zero_size; ++ if (mask != 0) { ++ vmar->partial_zero_cluster_data += zero_size; ++ } ++ ++ if (rstate->write_zeroes && !verify) { ++ if (restore_write_data(vmar, dev_id, bs, vmstate_fd, ++ zero_vma_block, sector_num, ++ nb_sectors, errp) < 0) { ++ return -1; ++ } + } + } + } +@@ -679,8 +707,9 @@ + return 0; + } + +-int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, +- Error **errp) ++static int vma_reader_restore_full(VmaReader *vmar, int vmstate_fd, ++ bool verbose, bool verify, ++ Error **errp) + { + assert(vmar); + assert(vmar->head_data); +@@ -747,7 +776,7 @@ + } + + if (restore_extent(vmar, buf, extent_size, vmstate_fd, verbose, +- errp) < 0) { ++ verify, errp) < 0) { + return -1; + } + +@@ -794,6 +823,38 @@ + } + } + ++ if (verbose) { ++ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", ++ vmar->clusters_read*VMA_CLUSTER_SIZE, ++ vmar->zero_cluster_data, ++ (double)(100.0*vmar->zero_cluster_data)/ ++ (vmar->clusters_read*VMA_CLUSTER_SIZE)); ++ ++ int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data; ++ if (datasize) { // this does not make sense for empty files ++ printf("space reduction due to 4K zero blocks %.3g%%\n", ++ (double)(100.0*vmar->partial_zero_cluster_data) / datasize); ++ } ++ } + return ret; + } + ++int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, ++ Error **errp) ++{ ++ return vma_reader_restore_full(vmar, vmstate_fd, verbose, false, errp); ++} ++ ++int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp) ++{ ++ guint8 dev_id; ++ ++ for (dev_id = 1; dev_id < 255; dev_id++) { ++ if (vma_reader_get_device_info(vmar, dev_id)) { ++ allocate_rstate(vmar, dev_id, NULL, false); ++ } ++ } ++ ++ return vma_reader_restore_full(vmar, -1, verbose, true, errp); ++} ++ +Index: new/vma.c +=================================================================== +--- new.orig/vma.c 2014-11-20 08:47:23.000000000 +0100 ++++ new/vma.c 2014-11-20 08:47:30.000000000 +0100 +@@ -34,6 +34,7 @@ + "vma list \n" + "vma create [-c config] pathname ...\n" + "vma extract [-r ] \n" ++ "vma verify [-v]\n" + ; + + printf("%s", help_msg); +@@ -338,6 +339,58 @@ + return ret; + } + ++static int verify_content(int argc, char **argv) ++{ ++ int c, ret = 0; ++ int verbose = 0; ++ const char *filename; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hv"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'v': ++ verbose = 1; ++ break; ++ default: ++ help(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 1) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ if (verbose) { ++ print_content(vmar); ++ } ++ ++ if (vma_reader_verify(vmar, verbose, &errp) < 0) { ++ g_error("verify failed - %s", error_get_pretty(errp)); ++ } ++ ++ vma_reader_destroy(vmar); ++ ++ bdrv_close_all(); ++ ++ return ret; ++} ++ + typedef struct BackupJob { + BlockDriverState *bs; + int64_t len; +@@ -575,6 +628,8 @@ + return create_archive(argc, argv); + } else if (!strcmp(cmdname, "extract")) { + return extract_content(argc, argv); ++ } else if (!strcmp(cmdname, "verify")) { ++ return verify_content(argc, argv); + } + + help(); +Index: new/vma.h +=================================================================== +--- new.orig/vma.h 2014-11-20 08:15:12.000000000 +0100 ++++ new/vma.h 2014-11-20 08:47:30.000000000 +0100 +@@ -142,5 +142,6 @@ + Error **errp); + int vma_reader_restore(VmaReader *vmar, int vmstate_fd, bool verbose, + Error **errp); ++int vma_reader_verify(VmaReader *vmar, bool verbose, Error **errp); + + #endif /* BACKUP_VMA_H */ diff --git a/debian/patches/old/backup-do-not-return-errors-in-dump-callback.patch b/debian/patches/old/backup-do-not-return-errors-in-dump-callback.patch new file mode 100644 index 0000000..d217968 --- /dev/null +++ b/debian/patches/old/backup-do-not-return-errors-in-dump-callback.patch @@ -0,0 +1,71 @@ +The backup dump_cb should always return success. Else the original write +does not succeed and the VM gets notified. + +We simply report success and cancel the backup job instead. + + +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 07:49:05.000000000 +0100 ++++ new/blockdev.c 2014-11-20 07:49:14.000000000 +0100 +@@ -1991,6 +1991,11 @@ + { + PVEBackupDevInfo *di = opaque; + ++ int size = n_sectors * BDRV_SECTOR_SIZE; ++ if (backup_state.cancel) { ++ return size; // return success ++ } ++ + if (sector_num & 0x7f) { + if (!backup_state.error) { + error_setg(&backup_state.error, +@@ -2001,7 +2006,6 @@ + } + + int64_t cluster_num = sector_num >> 7; +- int size = n_sectors * BDRV_SECTOR_SIZE; + + int ret = -1; + +@@ -2009,17 +2013,27 @@ + size_t zero_bytes = 0; + ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, + buf, &zero_bytes); +- backup_state.zero_bytes += zero_bytes; ++ if (ret < 0) { ++ if (!backup_state.error) { ++ error_setg(&backup_state.error, "vma_writer_write error %d", ret); ++ } ++ if (di->bs && di->bs->job) { ++ block_job_cancel(di->bs->job); ++ } ++ } else { ++ backup_state.zero_bytes += zero_bytes; ++ backup_state.transferred += size; ++ } + } else { +- ret = size; + if (!buf) { + backup_state.zero_bytes += size; + } ++ backup_state.transferred += size; + } + +- backup_state.transferred += size; ++ // Note: always return success, because we want that writes succeed anyways. + +- return ret; ++ return size; + } + + static void pvebackup_cleanup(void) +@@ -2091,7 +2105,7 @@ + BlockJob *job = di->bs->job; + if (job) { + if (!di->completed) { +- block_job_cancel_sync(job); ++ block_job_cancel_sync(job); + } + } + } diff --git a/debian/patches/old/backup-do-not-use-bdrv-drain-all.patch b/debian/patches/old/backup-do-not-use-bdrv-drain-all.patch new file mode 100644 index 0000000..c0c8e6d --- /dev/null +++ b/debian/patches/old/backup-do-not-use-bdrv-drain-all.patch @@ -0,0 +1,28 @@ +do not use bdrv_drain_all() + +This seems to hang now (qemu 2.2), causing timeouts. + +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-12-29 06:27:15.000000000 +0100 ++++ new/blockdev.c 2014-12-29 07:11:53.000000000 +0100 +@@ -2107,9 +2107,6 @@ + vma_writer_set_error(backup_state.vmaw, "backup cancelled"); + } + +- /* drain all i/o (awake jobs waiting for aio) */ +- bdrv_drain_all(); +- + GList *l = backup_state.di_list; + while (l) { + PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; +@@ -2118,8 +2115,7 @@ + BlockJob *job = di->bs->job; + if (job) { + if (!di->completed) { +- block_job_cancel_sync(job); +- bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */ ++ block_job_cancel_sync(job); + } + } + } diff --git a/debian/patches/old/backup-modify-job-api.patch b/debian/patches/old/backup-modify-job-api.patch new file mode 100644 index 0000000..2f330e2 --- /dev/null +++ b/debian/patches/old/backup-modify-job-api.patch @@ -0,0 +1,194 @@ +Index: new/block/backup.c +=================================================================== +--- new.orig/block/backup.c 2014-11-20 07:55:31.000000000 +0100 ++++ new/block/backup.c 2014-11-20 08:56:23.000000000 +0100 +@@ -42,6 +42,7 @@ + BdrvDirtyBitmap *sync_bitmap; + MirrorSyncMode sync_mode; + RateLimit limit; ++ BackupDumpFunc *dump_cb; + BlockdevOnError on_source_error; + BlockdevOnError on_target_error; + CoRwlock flush_rwlock; +@@ -139,14 +140,21 @@ + goto out; + } + ++ int64_t start_sec = start * BACKUP_SECTORS_PER_CLUSTER; + if (buffer_is_zero(iov.iov_base, iov.iov_len)) { +- ret = bdrv_co_write_zeroes(job->target, +- start * BACKUP_SECTORS_PER_CLUSTER, +- n, BDRV_REQ_MAY_UNMAP); ++ if (job->dump_cb) { ++ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, NULL); ++ } ++ if (job->target) { ++ ret = bdrv_co_write_zeroes(job->target, start_sec, n, BDRV_REQ_MAY_UNMAP); ++ } + } else { +- ret = bdrv_co_writev(job->target, +- start * BACKUP_SECTORS_PER_CLUSTER, n, +- &bounce_qiov); ++ if (job->dump_cb) { ++ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, bounce_buffer); ++ } ++ if (job->target) { ++ ret = bdrv_co_writev(job->target, start_sec, n, &bounce_qiov); ++ } + } + if (ret < 0) { + trace_backup_do_cow_write_fail(job, start, ret); +@@ -208,7 +216,9 @@ + { + BackupBlockJob *s = container_of(job, BackupBlockJob, common); + +- bdrv_iostatus_reset(s->target); ++ if (s->target) { ++ bdrv_iostatus_reset(s->target); ++ } + } + + static const BlockJobDriver backup_job_driver = { +@@ -224,9 +234,11 @@ + if (read) { + return block_job_error_action(&job->common, job->common.bs, + job->on_source_error, true, error); +- } else { ++ } else if (job->target) { + return block_job_error_action(&job->common, job->target, + job->on_target_error, false, error); ++ } else { ++ return BLOCK_ERROR_ACTION_REPORT; + } + } + +@@ -351,9 +363,11 @@ + + job->bitmap = hbitmap_alloc(end, 0); + +- bdrv_set_enable_write_cache(target, true); +- bdrv_set_on_error(target, on_target_error, on_target_error); +- bdrv_iostatus_enable(target); ++ if (target) { ++ bdrv_set_enable_write_cache(target, true); ++ bdrv_set_on_error(target, on_target_error, on_target_error); ++ bdrv_iostatus_enable(target); ++ } + + bdrv_add_before_write_notifier(bs, &before_write); + +@@ -443,8 +461,10 @@ + } + hbitmap_free(job->bitmap); + +- bdrv_iostatus_disable(target); +- bdrv_op_unblock_all(target, job->common.blocker); ++ if (target) { ++ bdrv_iostatus_disable(target); ++ bdrv_op_unblock_all(target, job->common.blocker); ++ } + + data = g_malloc(sizeof(*data)); + data->ret = ret; +@@ -456,13 +472,15 @@ for backup_start + BdrvDirtyBitmap *sync_bitmap, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, ++ BackupDumpFunc *dump_cb, + BlockCompletionFunc *cb, void *opaque, ++ int pause_count, + Error **errp) + { + int64_t len; + + assert(bs); +- assert(target); ++ assert(target || dump_cb); + assert(cb); + + if (bs == target) { +@@ -483,7 +501,7 @@ in backup_start + return; + } + +- if (!bdrv_is_inserted(target)) { ++ if (target && !bdrv_is_inserted(target)) { + error_setg(errp, "Device is not inserted: %s", + bdrv_get_device_name(target)); + return; +@@ -493,7 +511,7 @@ in backup_start + return; + } + +- if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { ++ if (target && bdrv_op_is_blocked(target, BLOCK_OP_TYPE_BACKUP_TARGET, errp)) { + return; + } + +@@ -529,14 +547,17 @@ in backup_start + goto error; + } + +- bdrv_op_block_all(target, job->common.blocker); ++ if (target) ++ bdrv_op_block_all(target, job->common.blocker); + ++ job->dump_cb = dump_cb; + job->on_source_error = on_source_error; + job->on_target_error = on_target_error; + job->target = target; + job->sync_mode = sync_mode; + job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ? + sync_bitmap : NULL; ++ job->common.pause_count = pause_count; + job->common.len = len; + job->common.co = qemu_coroutine_create(backup_run); + qemu_coroutine_enter(job->common.co, job); +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 07:55:31.000000000 +0100 ++++ new/blockdev.c 2014-11-20 08:48:02.000000000 +0100 +@@ -2571,8 +2571,8 @@ qmp_drive_backup + } + + backup_start(bs, target_bs, speed, sync, bmap, +- on_source_error, on_target_error, +- block_job_cb, bs, &local_err); ++ on_source_error, on_target_error, NULL, ++ block_job_cb, bs, 0, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); +@@ -2633,7 +2633,7 @@ qmp_blockdev_backup + bdrv_ref(target_bs); + bdrv_set_aio_context(target_bs, aio_context); + backup_start(bs, target_bs, speed, sync, NULL, on_source_error, +- on_target_error, block_job_cb, bs, &local_err); ++ on_target_error, NULL, block_job_cb, bs, 0, &local_err); + if (local_err != NULL) { + bdrv_unref(target_bs); + error_propagate(errp, local_err); +Index: new/include/block/block_int.h +=================================================================== +--- new.orig/include/block/block_int.h 2014-11-20 07:55:31.000000000 +0100 ++++ new/include/block/block_int.h 2014-11-20 08:52:59.000000000 +0100 +@@ -59,6 +59,9 @@ + + #define BLOCK_PROBE_BUF_SIZE 512 + ++typedef int BackupDumpFunc(void *opaque, BlockDriverState *bs, ++ int64_t sector_num, int n_sectors, unsigned char *buf); ++ + typedef struct BdrvTrackedRequest { + BlockDriverState *bs; + int64_t offset; +@@ -651,7 +654,9 @@ + BdrvDirtyBitmap *sync_bitmap, + BlockdevOnError on_source_error, + BlockdevOnError on_target_error, ++ BackupDumpFunc *dump_cb, + BlockCompletionFunc *cb, void *opaque, ++ int pause_count, + Error **errp); + + void blk_dev_change_media_cb(BlockBackend *blk, bool load); diff --git a/debian/patches/old/backup-run-flush-inside-coroutine.patch b/debian/patches/old/backup-run-flush-inside-coroutine.patch new file mode 100644 index 0000000..913eb08 --- /dev/null +++ b/debian/patches/old/backup-run-flush-inside-coroutine.patch @@ -0,0 +1,50 @@ +run vma_writer_close_stream inside coroutine + +Newer qemu (> 2.2.0-rc2) runs pvebackup_complete_cb from main loop, + +But vma_writer_close_stream triggers an async write (flush), which uses qemu_coroutine_yield(). This only works if called from a coroutine. + + +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 12:17:17.000000000 +0100 ++++ new/blockdev.c 2014-11-20 12:17:23.000000000 +0100 +@@ -2059,6 +2059,13 @@ + } + } + ++static void coroutine_fn backup_close_vma_stream(void *opaque) ++{ ++ PVEBackupDevInfo *di = opaque; ++ ++ vma_writer_close_stream(backup_state.vmaw, di->dev_id); ++} ++ + static void pvebackup_complete_cb(void *opaque, int ret) + { + PVEBackupDevInfo *di = opaque; +@@ -2076,7 +2083,8 @@ + di->target = NULL; + + if (backup_state.vmaw) { +- vma_writer_close_stream(backup_state.vmaw, di->dev_id); ++ Coroutine *co = qemu_coroutine_create(backup_close_vma_stream); ++ qemu_coroutine_enter(co, di); + } + + block_job_cb(bs, ret); +Index: new/vma-writer.c +=================================================================== +--- new.orig/vma-writer.c 2014-11-20 12:17:17.000000000 +0100 ++++ new/vma-writer.c 2014-11-20 12:17:23.000000000 +0100 +@@ -706,6 +706,10 @@ + + int i; + ++ while (vmaw->co_writer) { ++ aio_poll(qemu_get_aio_context(), true); ++ } ++ + assert(vmaw->co_writer == NULL); + + if (vmaw->cmd) { diff --git a/debian/patches/old/backup-vma-add-dump-config.patch b/debian/patches/old/backup-vma-add-dump-config.patch new file mode 100644 index 0000000..6febcc4 --- /dev/null +++ b/debian/patches/old/backup-vma-add-dump-config.patch @@ -0,0 +1,89 @@ +Index: new/vma.c +=================================================================== +--- new.orig/vma.c 2014-11-20 08:47:30.000000000 +0100 ++++ new/vma.c 2014-11-20 08:47:47.000000000 +0100 +@@ -32,6 +32,7 @@ + "usage: vma command [command options]\n" + "\n" + "vma list \n" ++ "vma config [-c config]\n" + "vma create [-c config] pathname ...\n" + "vma extract [-r ] \n" + "vma verify [-v]\n" +@@ -601,6 +602,67 @@ + return 0; + } + ++static int dump_config(int argc, char **argv) ++{ ++ int c, ret = 0; ++ const char *filename; ++ const char *config_name = "qemu-server.conf"; ++ ++ for (;;) { ++ c = getopt(argc, argv, "hc:"); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'c': ++ config_name = optarg; ++ break; ++ default: ++ help(); ++ } ++ } ++ ++ /* Get the filename */ ++ if ((optind + 1) != argc) { ++ help(); ++ } ++ filename = argv[optind++]; ++ ++ Error *errp = NULL; ++ VmaReader *vmar = vma_reader_create(filename, &errp); ++ ++ if (!vmar) { ++ g_error("%s", error_get_pretty(errp)); ++ } ++ ++ int found = 0; ++ GList *l = vma_reader_get_config_data(vmar); ++ while (l && l->data) { ++ VmaConfigData *cdata = (VmaConfigData *)l->data; ++ l = g_list_next(l); ++ if (strcmp(cdata->name, config_name) == 0) { ++ found = 1; ++ fwrite(cdata->data, cdata->len, 1, stdout); ++ break; ++ } ++ } ++ ++ vma_reader_destroy(vmar); ++ ++ bdrv_close_all(); ++ ++ if (!found) { ++ fprintf(stderr, "unable to find configuration data '%s'\n", config_name); ++ return -1; ++ } ++ ++ return ret; ++} ++ + int main(int argc, char **argv) + { + const char *cmdname; +@@ -630,6 +692,8 @@ + return extract_content(argc, argv); + } else if (!strcmp(cmdname, "verify")) { + return verify_content(argc, argv); ++ } else if (!strcmp(cmdname, "config")) { ++ return dump_config(argc, argv); + } + + help(); diff --git a/debian/patches/old/backup-vma-allow-empty-backups.patch b/debian/patches/old/backup-vma-allow-empty-backups.patch new file mode 100644 index 0000000..0fa4eef --- /dev/null +++ b/debian/patches/old/backup-vma-allow-empty-backups.patch @@ -0,0 +1,259 @@ +This patch allows the creation of vma files without data streams. + +Such files only contain configuration data. This is useful if a +user set backup=no to all VM disks. + +Index: new/vma-reader.c +=================================================================== +--- new.orig/vma-reader.c ++++ new/vma-reader.c +@@ -334,11 +334,6 @@ static int vma_reader_read_head(VmaReade + } + } + +- if (!count) { +- error_setg(errp, "vma does not contain data"); +- return -1; +- } +- + for (i = 0; i < VMA_MAX_CONFIGS; i++) { + uint32_t name_ptr = GUINT32_FROM_BE(h->config_names[i]); + uint32_t data_ptr = GUINT32_FROM_BE(h->config_data[i]); +@@ -830,16 +825,20 @@ static int vma_reader_restore_full(VmaRe + } + + if (verbose) { +- printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", +- vmar->clusters_read*VMA_CLUSTER_SIZE, +- vmar->zero_cluster_data, +- (double)(100.0*vmar->zero_cluster_data)/ +- (vmar->clusters_read*VMA_CLUSTER_SIZE)); +- +- int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data; +- if (datasize) { // this does not make sense for empty files +- printf("space reduction due to 4K zero blocks %.3g%%\n", +- (double)(100.0*vmar->partial_zero_cluster_data) / datasize); ++ if (vmar->clusters_read) { ++ printf("total bytes read %zd, sparse bytes %zd (%.3g%%)\n", ++ vmar->clusters_read*VMA_CLUSTER_SIZE, ++ vmar->zero_cluster_data, ++ (double)(100.0*vmar->zero_cluster_data)/ ++ (vmar->clusters_read*VMA_CLUSTER_SIZE)); ++ ++ int64_t datasize = vmar->clusters_read*VMA_CLUSTER_SIZE-vmar->zero_cluster_data; ++ if (datasize) { // this does not make sense for empty files ++ printf("space reduction due to 4K zero blocks %.3g%%\n", ++ (double)(100.0*vmar->partial_zero_cluster_data) / datasize); ++ } ++ } else { ++ printf("vma archive contains no image data\n"); + } + } + return ret; +Index: new/vma-writer.c +=================================================================== +--- new.orig/vma-writer.c ++++ new/vma-writer.c +@@ -258,7 +258,7 @@ vma_queue_write(VmaWriter *vmaw, const v + } + + vmaw->co_writer = NULL; +- ++ + return (done == bytes) ? bytes : -1; + } + +@@ -382,10 +382,6 @@ static int coroutine_fn vma_write_header + time_t ctime = time(NULL); + head->ctime = GUINT64_TO_BE(ctime); + +- if (!vmaw->stream_count) { +- return -1; +- } +- + for (i = 0; i < VMA_MAX_CONFIGS; i++) { + head->config_names[i] = GUINT32_TO_BE(vmaw->config_names[i]); + head->config_data[i] = GUINT32_TO_BE(vmaw->config_data[i]); +@@ -502,6 +498,23 @@ static int vma_count_open_streams(VmaWri + return open_drives; + } + ++ ++/** ++ * You need to call this if the vma archive does not contain ++ * any data stream. ++ */ ++int coroutine_fn ++vma_writer_flush_output(VmaWriter *vmaw) ++{ ++ qemu_co_mutex_lock(&vmaw->flush_lock); ++ int ret = vma_writer_flush(vmaw); ++ qemu_co_mutex_unlock(&vmaw->flush_lock); ++ if (ret < 0) { ++ vma_writer_set_error(vmaw, "vma_writer_flush_header failed"); ++ } ++ return ret; ++} ++ + /** + * all jobs should call this when there is no more data + * Returns: number of remaining stream (0 ==> finished) +@@ -529,12 +542,7 @@ vma_writer_close_stream(VmaWriter *vmaw, + + if (open_drives <= 0) { + DPRINTF("vma_writer_set_status all drives completed\n"); +- qemu_co_mutex_lock(&vmaw->flush_lock); +- int ret = vma_writer_flush(vmaw); +- qemu_co_mutex_unlock(&vmaw->flush_lock); +- if (ret < 0) { +- vma_writer_set_error(vmaw, "vma_writer_close_stream: flush failed"); +- } ++ vma_writer_flush_output(vmaw); + } + + return open_drives; +Index: new/vma.c +=================================================================== +--- new.orig/vma.c ++++ new/vma.c +@@ -33,7 +33,7 @@ static void help(void) + "\n" + "vma list \n" + "vma config [-c config]\n" +- "vma create [-c config] pathname ...\n" ++ "vma create [-c config] pathname ...\n" + "vma extract [-r ] \n" + "vma verify [-v]\n" + ; +@@ -401,6 +401,18 @@ typedef struct BackupJob { + + #define BACKUP_SECTORS_PER_CLUSTER (VMA_CLUSTER_SIZE / BDRV_SECTOR_SIZE) + ++static void coroutine_fn backup_run_empty(void *opaque) ++{ ++ VmaWriter *vmaw = (VmaWriter *)opaque; ++ ++ vma_writer_flush_output(vmaw); ++ ++ Error *err = NULL; ++ if (vma_writer_close(vmaw, &err) != 0) { ++ g_warning("vma_writer_close failed %s", error_get_pretty(err)); ++ } ++} ++ + static void coroutine_fn backup_run(void *opaque) + { + BackupJob *job = (BackupJob *)opaque; +@@ -474,8 +486,8 @@ static int create_archive(int argc, char + } + + +- /* make sure we have archive name and at least one path */ +- if ((optind + 2) > argc) { ++ /* make sure we an archive name */ ++ if ((optind + 1) > argc) { + help(); + } + +@@ -510,11 +522,11 @@ static int create_archive(int argc, char + l = g_list_next(l); + } + +- int ind = 0; ++ int devcount = 0; + while (optind < argc) { + const char *path = argv[optind++]; + char *devname = NULL; +- path = extract_devname(path, &devname, ind++); ++ path = extract_devname(path, &devname, devcount++); + + BlockDriver *drv = NULL; + Error *errp = NULL; +@@ -546,37 +558,49 @@ static int create_archive(int argc, char + int percent = 0; + int last_percent = -1; + +- while (1) { +- main_loop_wait(false); +- vma_writer_get_status(vmaw, &vmastat); +- +- if (verbose) { +- +- uint64_t total = 0; +- uint64_t transferred = 0; +- uint64_t zero_bytes = 0; +- +- int i; +- for (i = 0; i < 256; i++) { +- if (vmastat.stream_info[i].size) { +- total += vmastat.stream_info[i].size; +- transferred += vmastat.stream_info[i].transferred; +- zero_bytes += vmastat.stream_info[i].zero_bytes; ++ if (devcount) { ++ while (1) { ++ main_loop_wait(false); ++ vma_writer_get_status(vmaw, &vmastat); ++ ++ if (verbose) { ++ ++ uint64_t total = 0; ++ uint64_t transferred = 0; ++ uint64_t zero_bytes = 0; ++ ++ int i; ++ for (i = 0; i < 256; i++) { ++ if (vmastat.stream_info[i].size) { ++ total += vmastat.stream_info[i].size; ++ transferred += vmastat.stream_info[i].transferred; ++ zero_bytes += vmastat.stream_info[i].zero_bytes; ++ } + } +- } +- percent = (transferred*100)/total; +- if (percent != last_percent) { +- fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, +- transferred, total, zero_bytes); +- fflush(stderr); ++ percent = (transferred*100)/total; ++ if (percent != last_percent) { ++ fprintf(stderr, "progress %d%% %zd/%zd %zd\n", percent, ++ transferred, total, zero_bytes); ++ fflush(stderr); + +- last_percent = percent; ++ last_percent = percent; ++ } + } +- } + +- if (vmastat.closed) { +- break; ++ if (vmastat.closed) { ++ break; ++ } + } ++ } else { ++ Coroutine *co = qemu_coroutine_create(backup_run_empty); ++ qemu_coroutine_enter(co, vmaw); ++ while (1) { ++ main_loop_wait(false); ++ vma_writer_get_status(vmaw, &vmastat); ++ if (vmastat.closed) { ++ break; ++ } ++ } + } + + bdrv_drain_all(); +Index: new/vma.h +=================================================================== +--- new.orig/vma.h ++++ new/vma.h +@@ -128,6 +128,7 @@ int64_t coroutine_fn vma_writer_write(Vm + size_t *zero_bytes); + + int coroutine_fn vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id); ++int coroutine_fn vma_writer_flush_output(VmaWriter *vmaw); + + int vma_writer_get_status(VmaWriter *vmaw, VmaStatus *status); + void vma_writer_set_error(VmaWriter *vmaw, const char *fmt, ...); diff --git a/debian/patches/old/backup-vma-correctly-propagate-error.patch b/debian/patches/old/backup-vma-correctly-propagate-error.patch new file mode 100644 index 0000000..b351947 --- /dev/null +++ b/debian/patches/old/backup-vma-correctly-propagate-error.patch @@ -0,0 +1,43 @@ +Index: new/vma-writer.c +=================================================================== +--- new.orig/vma-writer.c 2014-11-20 07:34:19.000000000 +0100 ++++ new/vma-writer.c 2014-11-20 07:49:17.000000000 +0100 +@@ -798,6 +798,13 @@ + return transferred; + } + ++void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp) ++{ ++ if (vmaw->status < 0 && *errp == NULL) { ++ error_setg(errp, "%s", vmaw->errmsg); ++ } ++} ++ + int vma_writer_close(VmaWriter *vmaw, Error **errp) + { + g_assert(vmaw != NULL); +Index: new/vma.h +=================================================================== +--- new.orig/vma.h 2014-11-20 07:34:27.000000000 +0100 ++++ new/vma.h 2014-11-20 07:49:17.000000000 +0100 +@@ -116,6 +116,7 @@ + + VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp); + int vma_writer_close(VmaWriter *vmaw, Error **errp); ++void vma_writer_error_propagate(VmaWriter *vmaw, Error **errp); + void vma_writer_destroy(VmaWriter *vmaw); + int vma_writer_add_config(VmaWriter *vmaw, const char *name, gpointer data, + size_t len); +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 07:49:14.000000000 +0100 ++++ new/blockdev.c 2014-11-20 07:49:17.000000000 +0100 +@@ -2015,7 +2015,7 @@ + buf, &zero_bytes); + if (ret < 0) { + if (!backup_state.error) { +- error_setg(&backup_state.error, "vma_writer_write error %d", ret); ++ vma_writer_error_propagate(backup_state.vmaw, &backup_state.error); + } + if (di->bs && di->bs->job) { + block_job_cancel(di->bs->job); diff --git a/debian/patches/old/backup-vma-extract-add-block-driver-type.patch b/debian/patches/old/backup-vma-extract-add-block-driver-type.patch new file mode 100644 index 0000000..6f6dabb --- /dev/null +++ b/debian/patches/old/backup-vma-extract-add-block-driver-type.patch @@ -0,0 +1,19 @@ +Index: new/vma.c +=================================================================== +--- new.orig/vma.c ++++ new/vma.c +@@ -299,7 +299,13 @@ static int extract_content(int argc, cha + } + + BlockDriverState *bs = bdrv_new(); +- if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, NULL, &errp)) { ++ ++ const char *tmp = g_strrstr(devfn, "."); ++ const char *format = (tmp == NULL) ? "raw" : ++tmp; ++ ++ BlockDriver *drv = bdrv_find_format(format); ++ ++ if (errp || bdrv_open(&bs, devfn, NULL, NULL, flags, drv, &errp)) { + g_error("can't open file %s - %s", devfn, + error_get_pretty(errp)); + } diff --git a/debian/patches/old/backup-vma-remove-async-queue.patch b/debian/patches/old/backup-vma-remove-async-queue.patch new file mode 100644 index 0000000..73f2c96 --- /dev/null +++ b/debian/patches/old/backup-vma-remove-async-queue.patch @@ -0,0 +1,309 @@ +We do not gain much speed here, so I removed the whole queue code +to make things simpler. + +Also, previous code produced segmentation faults in qemu_co_mutex_lock(). + +Index: new/vma-writer.c +=================================================================== +--- new.orig/vma-writer.c 2014-11-20 09:08:33.000000000 +0100 ++++ new/vma-writer.c 2014-11-20 09:10:14.000000000 +0100 +@@ -34,14 +34,8 @@ + do { if (DEBUG_VMA) { printf("vma: " fmt, ## __VA_ARGS__); } } while (0) + + #define WRITE_BUFFERS 5 +- +-typedef struct VmaAIOCB VmaAIOCB; +-struct VmaAIOCB { +- unsigned char buffer[VMA_MAX_EXTENT_SIZE]; +- VmaWriter *vmaw; +- size_t bytes; +- Coroutine *co; +-}; ++#define HEADER_CLUSTERS 8 ++#define HEADERBUF_SIZE (VMA_CLUSTER_SIZE*HEADER_CLUSTERS) + + struct VmaWriter { + int fd; +@@ -53,16 +47,14 @@ + bool closed; + + /* we always write extents */ +- unsigned char outbuf[VMA_MAX_EXTENT_SIZE]; ++ unsigned char *outbuf; + int outbuf_pos; /* in bytes */ + int outbuf_count; /* in VMA_BLOCKS */ + uint64_t outbuf_block_info[VMA_BLOCKS_PER_EXTENT]; + +- VmaAIOCB *aiocbs[WRITE_BUFFERS]; +- CoQueue wqueue; ++ unsigned char *headerbuf; + + GChecksum *md5csum; +- CoMutex writer_lock; + CoMutex flush_lock; + Coroutine *co_writer; + +@@ -223,38 +215,39 @@ + } + + static ssize_t coroutine_fn +-vma_co_write(VmaWriter *vmaw, const void *buf, size_t bytes) ++vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) + { +- size_t done = 0; +- ssize_t ret; ++ DPRINTF("vma_queue_write enter %zd\n", bytes); + +- /* atomic writes (we cannot interleave writes) */ +- qemu_co_mutex_lock(&vmaw->writer_lock); ++ assert(vmaw); ++ assert(buf); ++ assert(bytes <= VMA_MAX_EXTENT_SIZE); + +- DPRINTF("vma_co_write enter %zd\n", bytes); ++ size_t done = 0; ++ ssize_t ret; + + assert(vmaw->co_writer == NULL); + + vmaw->co_writer = qemu_coroutine_self(); + +- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw); +- +- DPRINTF("vma_co_write wait until writable\n"); +- qemu_coroutine_yield(); +- DPRINTF("vma_co_write starting %zd\n", bytes); +- + while (done < bytes) { ++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, vma_co_continue_write, vmaw); ++ qemu_coroutine_yield(); ++ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL); ++ if (vmaw->status < 0) { ++ DPRINTF("vma_queue_write detected canceled backup\n"); ++ done = -1; ++ break; ++ } + ret = write(vmaw->fd, buf + done, bytes - done); + if (ret > 0) { + done += ret; +- DPRINTF("vma_co_write written %zd %zd\n", done, ret); ++ DPRINTF("vma_queue_write written %zd %zd\n", done, ret); + } else if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { +- DPRINTF("vma_co_write yield %zd\n", done); +- qemu_coroutine_yield(); +- DPRINTF("vma_co_write restart %zd\n", done); +- } else { +- vma_writer_set_error(vmaw, "vma_co_write write error - %s", ++ /* try again */ ++ } else { ++ vma_writer_set_error(vmaw, "vma_queue_write: write error - %s", + g_strerror(errno)); + done = -1; /* always return failure for partial writes */ + break; +@@ -264,102 +257,9 @@ + } + } + +- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, NULL, NULL, NULL); +- + vmaw->co_writer = NULL; +- +- qemu_co_mutex_unlock(&vmaw->writer_lock); +- +- DPRINTF("vma_co_write leave %zd\n", done); +- return done; +-} +- +-static void coroutine_fn vma_co_writer_task(void *opaque) +-{ +- VmaAIOCB *cb = opaque; +- +- DPRINTF("vma_co_writer_task start\n"); +- +- int64_t done = vma_co_write(cb->vmaw, cb->buffer, cb->bytes); +- DPRINTF("vma_co_writer_task write done %zd\n", done); +- +- if (done != cb->bytes) { +- DPRINTF("vma_co_writer_task failed write %zd %zd", cb->bytes, done); +- vma_writer_set_error(cb->vmaw, "vma_co_writer_task failed write %zd", +- done); +- } +- +- cb->bytes = 0; +- +- qemu_co_queue_next(&cb->vmaw->wqueue); +- +- DPRINTF("vma_co_writer_task end\n"); +-} +- +-static void coroutine_fn vma_queue_flush(VmaWriter *vmaw) +-{ +- DPRINTF("vma_queue_flush enter\n"); +- +- assert(vmaw); +- +- while (1) { +- int i; +- VmaAIOCB *cb = NULL; +- for (i = 0; i < WRITE_BUFFERS; i++) { +- if (vmaw->aiocbs[i]->bytes) { +- cb = vmaw->aiocbs[i]; +- DPRINTF("FOUND USED AIO BUFFER %d %zd\n", i, +- vmaw->aiocbs[i]->bytes); +- break; +- } +- } +- if (!cb) { +- break; +- } +- qemu_co_queue_wait(&vmaw->wqueue); +- } +- +- DPRINTF("vma_queue_flush leave\n"); +-} +- +-/** +- * NOTE: pipe buffer size in only 4096 bytes on linux (see 'ulimit -a') +- * So we need to create a coroutione to allow 'parallel' execution. +- */ +-static ssize_t coroutine_fn +-vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) +-{ +- DPRINTF("vma_queue_write enter %zd\n", bytes); +- +- assert(vmaw); +- assert(buf); +- assert(bytes <= VMA_MAX_EXTENT_SIZE); +- +- VmaAIOCB *cb = NULL; +- while (!cb) { +- int i; +- for (i = 0; i < WRITE_BUFFERS; i++) { +- if (!vmaw->aiocbs[i]->bytes) { +- cb = vmaw->aiocbs[i]; +- break; +- } +- } +- if (!cb) { +- qemu_co_queue_wait(&vmaw->wqueue); +- } +- } +- +- memcpy(cb->buffer, buf, bytes); +- cb->bytes = bytes; +- cb->vmaw = vmaw; +- +- DPRINTF("vma_queue_write start %zd\n", bytes); +- cb->co = qemu_coroutine_create(vma_co_writer_task); +- qemu_coroutine_enter(cb->co, cb); +- +- DPRINTF("vma_queue_write leave\n"); +- +- return bytes; ++ ++ return (done == bytes) ? bytes : -1; + } + + VmaWriter *vma_writer_create(const char *filename, uuid_t uuid, Error **errp) +@@ -426,20 +326,16 @@ + } + + /* 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 = qemu_memalign(512, VMA_MAX_EXTENT_SIZE); ++ vmaw->headerbuf = qemu_memalign(512, HEADERBUF_SIZE); + + vmaw->outbuf_count = 0; + vmaw->outbuf_pos = VMA_EXTENT_HEADER_SIZE; + + vmaw->header_blob_table_pos = 1; /* start at pos 1 */ + +- qemu_co_mutex_init(&vmaw->writer_lock); + qemu_co_mutex_init(&vmaw->flush_lock); +- qemu_co_queue_init(&vmaw->wqueue); + + uuid_copy(vmaw->uuid, uuid); + +@@ -466,8 +362,7 @@ + static int coroutine_fn vma_write_header(VmaWriter *vmaw) + { + assert(vmaw); +- int header_clusters = 8; +- char buf[65536*header_clusters]; ++ unsigned char *buf = vmaw->headerbuf; + VmaHeader *head = (VmaHeader *)buf; + + int i; +@@ -478,7 +373,7 @@ + return vmaw->status; + } + +- memset(buf, 0, sizeof(buf)); ++ memset(buf, 0, HEADERBUF_SIZE); + + head->magic = VMA_MAGIC; + head->version = GUINT32_TO_BE(1); /* v1 */ +@@ -513,7 +408,7 @@ + uint32_t header_size = sizeof(VmaHeader) + vmaw->header_blob_table_size; + head->header_size = GUINT32_TO_BE(header_size); + +- if (header_size > sizeof(buf)) { ++ if (header_size > HEADERBUF_SIZE) { + return -1; /* just to be sure */ + } + +@@ -811,13 +706,7 @@ + + int i; + +- vma_queue_flush(vmaw); +- +- /* this should not happen - just to be sure */ +- while (!qemu_co_queue_empty(&vmaw->wqueue)) { +- DPRINTF("vma_writer_close wait\n"); +- co_aio_sleep_ns(qemu_get_aio_context(), QEMU_CLOCK_REALTIME, 1000000); +- } ++ assert(vmaw->co_writer == NULL); + + if (vmaw->cmd) { + if (pclose(vmaw->cmd) < 0) { +@@ -875,9 +764,5 @@ + g_checksum_free(vmaw->md5csum); + } + +- for (i = 0; i < WRITE_BUFFERS; i++) { +- free(vmaw->aiocbs[i]); +- } +- + g_free(vmaw); + } +Index: new/blockdev.c +=================================================================== +--- new.orig/blockdev.c 2014-11-20 09:08:33.000000000 +0100 ++++ new/blockdev.c 2014-11-20 09:08:49.000000000 +0100 +@@ -2094,6 +2094,11 @@ + error_setg(&backup_state.error, "backup cancelled"); + } + ++ if (backup_state.vmaw) { ++ /* make sure vma writer does not block anymore */ ++ vma_writer_set_error(backup_state.vmaw, "backup cancelled"); ++ } ++ + /* drain all i/o (awake jobs waiting for aio) */ + bdrv_drain_all(); + +@@ -2106,6 +2111,7 @@ + if (job) { + if (!di->completed) { + block_job_cancel_sync(job); ++ bdrv_drain_all(); /* drain all i/o (awake jobs waiting for aio) */ + } + } + } diff --git a/debian/patches/old/backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch b/debian/patches/old/backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch new file mode 100644 index 0000000..779d4c6 --- /dev/null +++ b/debian/patches/old/backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch @@ -0,0 +1,30 @@ +From 4ca68d0ccfd64f2fdc63ba44813bbafab8d90c81 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 26 Mar 2013 06:21:16 +0100 +Subject: [PATCH v5 8/8] vma restore: tolerate a size difference up to 4M + + +Signed-off-by: Dietmar Maurer +--- + vma-reader.c | 8 +++++++- + 1 files changed, 7 insertions(+), 1 deletions(-) + +Index: new/vma-reader.c +=================================================================== +--- new.orig/vma-reader.c 2014-07-16 12:01:32.000000000 +0200 ++++ new/vma-reader.c 2014-07-16 12:01:33.000000000 +0200 +@@ -465,7 +465,13 @@ + assert(vmar->rstate[dev_id].bs == NULL); + + int64_t size = bdrv_getlength(bs); +- if (size != vmar->devinfo[dev_id].size) { ++ int64_t size_diff = size - vmar->devinfo[dev_id].size; ++ ++ /* storage types can have different size restrictions, so it ++ * is not always possible to create an image with exact size. ++ * So we tolerate a size difference up to 4MB. ++ */ ++ if ((size_diff < 0) || (size_diff > 4*1024*1024)) { + error_setg(errp, "vma_reader_register_bs for stream %s failed - " + "unexpected size %zd != %zd", vmar->devinfo[dev_id].devname, + size, vmar->devinfo[dev_id].size); diff --git a/debian/patches/old/cpuid-fix.diff b/debian/patches/old/cpuid-fix.diff new file mode 100644 index 0000000..1052ad5 --- /dev/null +++ b/debian/patches/old/cpuid-fix.diff @@ -0,0 +1,18 @@ +see http://git.kernel.org/?p=virt/kvm/qemu-kvm.git;a=commitdiff;h=8fa3b3ce6e#patch1 + +KVM_GET_SUPPORTED_CPUID has been known to fail to return -E2BIG +when it runs out of entries. Detect this by always trying again +with a bigger table if the ioctl() fills the table. + +--- a/kvm/libkvm/libkvm-x86.c ++++ b/kvm/libkvm/libkvm-x86.c +@@ -575,6 +575,8 @@ static struct kvm_cpuid2 *try_get_cpuid(kvm_context_t kvm, int max) + r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, cpuid); + if (r == -1) + r = -errno; ++ else if (r == 0 && cpuid->nent >= max) ++ r = -E2BIG; + if (r < 0) { + if (r == -E2BIG) { + free(cpuid); + diff --git a/debian/patches/old/disable-efi-enable-pxe-roms.patch b/debian/patches/old/disable-efi-enable-pxe-roms.patch new file mode 100644 index 0000000..16c69ce --- /dev/null +++ b/debian/patches/old/disable-efi-enable-pxe-roms.patch @@ -0,0 +1,65 @@ +Index: new/hw/net/e1000.c +=================================================================== +--- new.orig/hw/net/e1000.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/net/e1000.c 2014-11-20 07:54:14.000000000 +0100 +@@ -1606,7 +1606,7 @@ + + k->realize = pci_e1000_realize; + k->exit = pci_e1000_uninit; +- k->romfile = "efi-e1000.rom"; ++ k->romfile = "pxe-e1000.rom"; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = info->device_id; + k->revision = info->revision; +Index: new/hw/net/ne2000.c +=================================================================== +--- new.orig/hw/net/ne2000.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/net/ne2000.c 2014-11-20 07:54:14.000000000 +0100 +@@ -773,7 +773,7 @@ + + k->realize = pci_ne2000_realize; + k->exit = pci_ne2000_exit; +- k->romfile = "efi-ne2k_pci.rom", ++ k->romfile = "pxe-ne2k_pci.rom", + k->vendor_id = PCI_VENDOR_ID_REALTEK; + k->device_id = PCI_DEVICE_ID_REALTEK_8029; + k->class_id = PCI_CLASS_NETWORK_ETHERNET; +Index: new/hw/net/pcnet-pci.c +=================================================================== +--- new.orig/hw/net/pcnet-pci.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/net/pcnet-pci.c 2014-11-20 07:54:14.000000000 +0100 +@@ -367,7 +367,7 @@ + + k->realize = pci_pcnet_realize; + k->exit = pci_pcnet_uninit; +- k->romfile = "efi-pcnet.rom", ++ k->romfile = "pxe-pcnet.rom", + k->vendor_id = PCI_VENDOR_ID_AMD; + k->device_id = PCI_DEVICE_ID_AMD_LANCE; + k->revision = 0x10; +Index: new/hw/net/rtl8139.c +=================================================================== +--- new.orig/hw/net/rtl8139.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/net/rtl8139.c 2014-11-20 07:54:14.000000000 +0100 +@@ -3562,7 +3562,7 @@ + + k->realize = pci_rtl8139_realize; + k->exit = pci_rtl8139_uninit; +- k->romfile = "efi-rtl8139.rom"; ++ k->romfile = "pxe-rtl8139.rom"; + k->vendor_id = PCI_VENDOR_ID_REALTEK; + k->device_id = PCI_DEVICE_ID_REALTEK_8139; + k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ +Index: new/hw/virtio/virtio-pci.c +=================================================================== +--- new.orig/hw/virtio/virtio-pci.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/virtio/virtio-pci.c 2014-11-20 07:54:14.000000000 +0100 +@@ -1445,7 +1445,7 @@ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + +- k->romfile = "efi-virtio.rom"; ++ k->romfile = "pxe-virtio.rom"; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VIRTIO_NET; + k->revision = VIRTIO_PCI_ABI_VERSION; diff --git a/debian/patches/old/enable-kvm-by-default.patch b/debian/patches/old/enable-kvm-by-default.patch new file mode 100644 index 0000000..329170f --- /dev/null +++ b/debian/patches/old/enable-kvm-by-default.patch @@ -0,0 +1,15 @@ +Index: new/accel.c +=================================================================== +--- new.orig/accel.c 2014-11-20 06:45:05.000000000 +0100 ++++ new/accel.c 2014-11-20 07:25:45.000000000 +0100 +@@ -87,8 +87,8 @@ + + p = qemu_opt_get(qemu_get_machine_opts(), "accel"); + if (p == NULL) { +- /* Use the default "accelerator", tcg */ +- p = "tcg"; ++ /* Use the default "accelerator", kvm */ ++ p = "kvm"; + } + + while (!accel_initialised && *p != '\0') { diff --git a/debian/patches/old/fix-emulator-version-string.patch b/debian/patches/old/fix-emulator-version-string.patch new file mode 100644 index 0000000..6cf849b --- /dev/null +++ b/debian/patches/old/fix-emulator-version-string.patch @@ -0,0 +1,18 @@ +use whith spave between VERSION and QEMU_PKGVERSION + +Our kvm version parser expects a white space ot comma after version, +see PVE::QemuServer::kvm_user_version() + +Index: new/vl.c +=================================================================== +--- new.orig/vl.c ++++ new/vl.c +@@ -1913,7 +1913,7 @@ static void main_loop(void) + + static void version(void) + { +- printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); ++ printf("QEMU emulator version " QEMU_VERSION " " QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); + } + + static void help(int exitcode) diff --git a/debian/patches/old/fix-qemu-img-snapshot-removal.patch b/debian/patches/old/fix-qemu-img-snapshot-removal.patch new file mode 100644 index 0000000..58d9ecc --- /dev/null +++ b/debian/patches/old/fix-qemu-img-snapshot-removal.patch @@ -0,0 +1,14 @@ +Index: new/qemu-img.c +=================================================================== +--- new.orig/qemu-img.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/qemu-img.c 2014-11-20 06:51:05.000000000 +0100 +@@ -2080,7 +2080,8 @@ + + list = collect_image_info_list(filename, fmt, chain); + if (!list) { +- return 1; ++ // return success if snapshot does not exists ++ return 0; + } + + switch (output_format) { diff --git a/debian/patches/old/fr-ca-keymap-corrections.diff b/debian/patches/old/fr-ca-keymap-corrections.diff new file mode 100644 index 0000000..a1a3941 --- /dev/null +++ b/debian/patches/old/fr-ca-keymap-corrections.diff @@ -0,0 +1,36 @@ +Index: new/pc-bios/keymaps/fr-ca +=================================================================== +--- new.orig/pc-bios/keymaps/fr-ca 2014-07-16 11:53:56.000000000 +0200 ++++ new/pc-bios/keymaps/fr-ca 2014-07-16 12:01:20.000000000 +0200 +@@ -14,22 +14,31 @@ + twosuperior 0x9 altgr + threesuperior 0xa altgr + onequarter 0xb altgr ++minus 0x0c + onehalf 0xc altgr ++equal 0xd + threequarters 0xd altgr + section 0x18 altgr + paragraph 0x19 altgr + bracketleft 0x1a altgr + bracketright 0x1b altgr ++semicolon 0x27 ++colon 0x27 shift + asciitilde 0x27 altgr + braceleft 0x28 altgr ++numbersign 0x29 + braceright 0x2b altgr + less 0x2b + greater 0x2b shift + guillemotleft 0x56 + guillemotright 0x56 shift + degree 0x56 altgr ++comma 0x33 + mu 0x32 altgr ++apostrophe 0x33 shift ++period 0x34 shift + eacute 0x35 ++Eacute 0x35 shift + dead_acute 0x35 altgr + dead_grave 0x28 + dead_circumflex 0x1a diff --git a/debian/patches/old/gluster-backupserver.patch b/debian/patches/old/gluster-backupserver.patch new file mode 100644 index 0000000..ef96541 --- /dev/null +++ b/debian/patches/old/gluster-backupserver.patch @@ -0,0 +1,131 @@ +Allow to specify a backup volfile server + +Unfortunately, it dos now work as expected: + + # qemu-img info gluster:///myvolume/test.raw + +works, but (1.2.3.4 is a non-existent server) + + # qemu-img info gluster://1.2.3.4/myvolume/test.raw?s2= + +does not work. Also + + # qemu-img info gluster:///myvolume/test.raw?s2= + +simply hangs forever. + +Index: new/block/gluster.c +=================================================================== +--- new.orig/block/gluster.c 2014-08-26 11:48:49.000000000 +0200 ++++ new/block/gluster.c 2014-08-26 12:51:53.000000000 +0200 +@@ -26,6 +26,7 @@ + + typedef struct GlusterConf { + char *server; ++ char *backupserver; + int port; + char *volname; + char *image; +@@ -36,6 +37,7 @@ + { + if (gconf) { + g_free(gconf->server); ++ g_free(gconf->backupserver); + g_free(gconf->volname); + g_free(gconf->image); + g_free(gconf->transport); +@@ -69,7 +71,7 @@ + } + + /* +- * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...] ++ * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...|?s2=...] + * + * 'gluster' is the protocol. + * +@@ -85,6 +87,8 @@ + * The 'socket' field needs to be populated with the path to unix domain + * socket. + * ++ * 's2' can be used to specifies a second volfile server. ++ * + * 'port' is the port number on which glusterd is listening. This is optional + * and if not specified, QEMU will send 0 which will make gluster to use the + * default port. If the transport type is unix, then 'port' should not be +@@ -97,6 +101,7 @@ + * Examples: + * + * file=gluster://1.2.3.4/testvol/a.img ++ * file=gluster://1.2.3.4/testvol/a.img?s2=1.2.3.5 + * file=gluster+tcp://1.2.3.4/testvol/a.img + * file=gluster+tcp://1.2.3.4:24007/testvol/dir/a.img + * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img +@@ -111,6 +116,8 @@ + QueryParams *qp = NULL; + bool is_unix = false; + int ret = 0; ++ int i; ++ char *socket = NULL; + + uri = uri_parse(filename); + if (!uri) { +@@ -138,21 +145,28 @@ + } + + qp = query_params_parse(uri->query); +- if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { ++ for (i = 0; i < qp->n; i++) { ++ if (!is_unix && strcmp(qp->p[i].name, "s2") == 0) { ++ gconf->backupserver = g_strdup(qp->p[i].value); ++ } else if (is_unix && strcmp(qp->p[i].name, "socket") == 0) { ++ socket = qp->p[i].value; ++ } else { ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ ++ if (is_unix && !socket) { + ret = -EINVAL; + goto out; + } + + if (is_unix) { +- if (uri->server || uri->port) { ++ if (!socket || uri->server || uri->port) { + ret = -EINVAL; + goto out; + } +- if (strcmp(qp->p[0].name, "socket")) { +- ret = -EINVAL; +- goto out; +- } +- gconf->server = g_strdup(qp->p[0].value); ++ gconf->server = g_strdup(socket); + } else { + gconf->server = g_strdup(uri->server ? uri->server : "localhost"); + gconf->port = uri->port; +@@ -176,7 +190,7 @@ + ret = qemu_gluster_parseuri(gconf, filename); + if (ret < 0) { + error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/" +- "volname/image[?socket=...]"); ++ "volname/image[?socket=...|?s2=...]"); + errno = -ret; + goto out; + } +@@ -192,6 +206,14 @@ + goto out; + } + ++ if (gconf->backupserver) { ++ ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->backupserver, ++ gconf->port); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ + /* + * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when + * GlusterFS makes GF_LOG_* macros available to libgfapi users. diff --git a/debian/patches/old/glusterfs-daemonize.patch b/debian/patches/old/glusterfs-daemonize.patch new file mode 100644 index 0000000..e657dcc --- /dev/null +++ b/debian/patches/old/glusterfs-daemonize.patch @@ -0,0 +1,23 @@ +glusterfs: do not log to stdout if daemonized + +Else stdout is not closed correctly. + +Index: new/block/gluster.c +=================================================================== +--- new.orig/block/gluster.c 2014-08-22 13:21:39.000000000 +0200 ++++ new/block/gluster.c 2014-08-22 13:25:18.000000000 +0200 +@@ -196,9 +196,11 @@ + * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when + * GlusterFS makes GF_LOG_* macros available to libgfapi users. + */ +- ret = glfs_set_logging(glfs, "-", 4); +- if (ret < 0) { +- goto out; ++ if (!is_daemonized()) { ++ ret = glfs_set_logging(glfs, "-", 4); ++ if (ret < 0) { ++ goto out; ++ } + } + + ret = glfs_init(glfs); diff --git a/debian/patches/old/internal-snapshot-async.patch b/debian/patches/old/internal-snapshot-async.patch new file mode 100644 index 0000000..c66dfb8 --- /dev/null +++ b/debian/patches/old/internal-snapshot-async.patch @@ -0,0 +1,986 @@ +From 46fd4bb673a91d40352c95e9d3f62f63b5021053 Mon Sep 17 00:00:00 2001 +From: Stefan Priebe +Date: Fri, 29 Nov 2013 22:17:03 +0100 +Subject: [PATCH] internal-snapshot-async-qemu1.7.patch + +--- + Makefile.objs | 1 + + block.c | 2 +- + hmp-commands.hx | 34 ++++ + hmp.c | 57 ++++++ + hmp.h | 5 + + include/block/block.h | 1 + + include/sysemu/sysemu.h | 5 +- + monitor.c | 7 + + qapi-schema.json | 46 +++++ + qemu-options.hx | 13 ++ + qmp-commands.hx | 31 +++ + savevm-async.c | 478 +++++++++++++++++++++++++++++++++++++++++++++++ + savevm.c | 10 +- + vl.c | 9 + + 14 files changed, 692 insertions(+), 7 deletions(-) + create mode 100644 savevm-async.c + +Index: new/Makefile.objs +=================================================================== +--- new.orig/Makefile.objs 2014-11-20 09:13:01.000000000 +0100 ++++ new/Makefile.objs 2014-11-20 09:16:47.000000000 +0100 +@@ -53,6 +53,7 @@ + common-obj-y += qemu-char.o #aio.o + common-obj-y += page_cache.o + common-obj-y += qjson.o ++common-obj-y += savevm-async.o + + common-obj-$(CONFIG_SPICE) += spice-qemu-char.o + +Index: new/block.c +=================================================================== +--- new.orig/block.c 2014-11-20 09:13:01.000000000 +0100 ++++ new/block.c 2014-11-20 09:16:47.000000000 +0100 +@@ -2202,7 +2202,7 @@ + bdrv_attach_child(bs_top, bs_new, &child_backing); + } + +-static void bdrv_delete(BlockDriverState *bs) ++void bdrv_delete(BlockDriverState *bs) + { + assert(!bs->job); + assert(bdrv_op_blocker_is_empty(bs)); +Index: new/hmp-commands.hx +=================================================================== +--- new.orig/hmp-commands.hx 2014-11-20 09:13:01.000000000 +0100 ++++ new/hmp-commands.hx 2014-11-20 09:16:47.000000000 +0100 +@@ -1809,6 +1809,8 @@ + show current migration parameters + @item info migrate_cache_size + show current migration XBZRLE cache size ++@item info savevm ++show savevm status + @item info balloon + show balloon information + @item info qtree +@@ -1857,3 +1859,35 @@ + STEXI + @end table + ETEXI ++ ++ { ++ .name = "savevm-start", ++ .args_type = "statefile:s?", ++ .params = "[statefile]", ++ .help = "Prepare for snapshot and halt VM. Save VM state to statefile.", ++ .mhandler.cmd = hmp_savevm_start, ++ }, ++ ++ { ++ .name = "snapshot-drive", ++ .args_type = "device:s,name:s", ++ .params = "device name", ++ .help = "Create internal snapshot.", ++ .mhandler.cmd = hmp_snapshot_drive, ++ }, ++ ++ { ++ .name = "delete-drive-snapshot", ++ .args_type = "device:s,name:s", ++ .params = "device name", ++ .help = "Delete internal snapshot.", ++ .mhandler.cmd = hmp_delete_drive_snapshot, ++ }, ++ ++ { ++ .name = "savevm-end", ++ .args_type = "", ++ .params = "", ++ .help = "Resume VM after snaphot.", ++ .mhandler.cmd = hmp_savevm_end, ++ }, +Index: new/hmp.c +=================================================================== +--- new.orig/hmp.c 2014-11-20 09:13:01.000000000 +0100 ++++ new/hmp.c 2014-11-20 09:16:47.000000000 +0100 +@@ -1849,6 +1849,63 @@ + + qapi_free_MemoryDeviceInfoList(info_list); + } ++ ++void hmp_savevm_start(Monitor *mon, const QDict *qdict) ++{ ++ Error *errp = NULL; ++ const char *statefile = qdict_get_try_str(qdict, "statefile"); ++ ++ qmp_savevm_start(statefile != NULL, statefile, &errp); ++ hmp_handle_error(mon, &errp); ++} ++ ++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict) ++{ ++ Error *errp = NULL; ++ const char *name = qdict_get_str(qdict, "name"); ++ const char *device = qdict_get_str(qdict, "device"); ++ ++ qmp_snapshot_drive(device, name, &errp); ++ hmp_handle_error(mon, &errp); ++} ++ ++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict) ++{ ++ Error *errp = NULL; ++ const char *name = qdict_get_str(qdict, "name"); ++ const char *device = qdict_get_str(qdict, "device"); ++ ++ qmp_delete_drive_snapshot(device, name, &errp); ++ hmp_handle_error(mon, &errp); ++} ++ ++void hmp_savevm_end(Monitor *mon, const QDict *qdict) ++{ ++ Error *errp = NULL; ++ ++ qmp_savevm_end(&errp); ++ hmp_handle_error(mon, &errp); ++} ++ ++void hmp_info_savevm(Monitor *mon, const QDict *qdict) ++{ ++ SaveVMInfo *info; ++ info = qmp_query_savevm(NULL); ++ ++ if (info->has_status) { ++ monitor_printf(mon, "savevm status: %s\n", info->status); ++ monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n", ++ info->total_time); ++ } else { ++ monitor_printf(mon, "savevm status: not running\n"); ++ } ++ if (info->has_bytes) { ++ monitor_printf(mon, "Bytes saved: %"PRIu64"\n", info->bytes); ++ } ++ if (info->has_error) { ++ monitor_printf(mon, "Error: %s\n", info->error); ++ } ++} + + void hmp_qom_list(Monitor *mon, const QDict *qdict) + { +Index: new/hmp.h +=================================================================== +--- new.orig/hmp.h 2014-11-20 09:13:01.000000000 +0100 ++++ new/hmp.h 2014-11-20 09:16:47.000000000 +0100 +@@ -26,6 +26,7 @@ + void hmp_info_uuid(Monitor *mon, const QDict *qdict); + void hmp_info_chardev(Monitor *mon, const QDict *qdict); + void hmp_info_mice(Monitor *mon, const QDict *qdict); ++void hmp_info_savevm(Monitor *mon, const QDict *qdict); + void hmp_info_migrate(Monitor *mon, const QDict *qdict); + void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); + void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict); +@@ -85,6 +86,10 @@ + void hmp_netdev_del(Monitor *mon, const QDict *qdict); + void hmp_getfd(Monitor *mon, const QDict *qdict); + void hmp_closefd(Monitor *mon, const QDict *qdict); ++void hmp_savevm_start(Monitor *mon, const QDict *qdict); ++void hmp_snapshot_drive(Monitor *mon, const QDict *qdict); ++void hmp_delete_drive_snapshot(Monitor *mon, const QDict *qdict); ++void hmp_savevm_end(Monitor *mon, const QDict *qdict); + void hmp_sendkey(Monitor *mon, const QDict *qdict); + void hmp_screendump(Monitor *mon, const QDict *qdict); + void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); +Index: new/include/block/block.h +=================================================================== +--- new.orig/include/block/block.h 2014-11-20 09:13:01.000000000 +0100 ++++ new/include/block/block.h 2014-11-20 09:16:47.000000000 +0100 +@@ -235,6 +235,7 @@ + int bdrv_get_backing_file_depth(BlockDriverState *bs); + void bdrv_refresh_filename(BlockDriverState *bs); + int bdrv_truncate(BlockDriverState *bs, int64_t offset); ++void bdrv_delete(BlockDriverState *bs); + int64_t bdrv_nb_sectors(BlockDriverState *bs); + int64_t bdrv_getlength(BlockDriverState *bs); + int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); +Index: new/include/sysemu/sysemu.h +=================================================================== +--- new.orig/include/sysemu/sysemu.h 2014-11-20 09:13:01.000000000 +0100 ++++ new/include/sysemu/sysemu.h 2014-11-20 09:16:47.000000000 +0100 +@@ -76,17 +76,18 @@ + + void hmp_savevm(Monitor *mon, const QDict *qdict); + int load_vmstate(const char *name); ++int load_state_from_blockdev(const char *filename); + void hmp_delvm(Monitor *mon, const QDict *qdict); + void hmp_info_snapshots(Monitor *mon, const QDict *qdict); + + void qemu_announce_self(void); + + bool qemu_savevm_state_blocked(Error **errp); +-void qemu_savevm_state_begin(QEMUFile *f, ++int qemu_savevm_state_begin(QEMUFile *f, + const MigrationParams *params); + void qemu_savevm_state_header(QEMUFile *f); + int qemu_savevm_state_iterate(QEMUFile *f); +-void qemu_savevm_state_complete(QEMUFile *f); ++int qemu_savevm_state_complete(QEMUFile *f); + void qemu_savevm_state_cancel(void); + uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size); + int qemu_loadvm_state(QEMUFile *f); +Index: new/monitor.c +=================================================================== +--- new.orig/monitor.c 2014-11-20 09:13:01.000000000 +0100 ++++ new/monitor.c 2014-11-20 09:16:47.000000000 +0100 +@@ -2876,6 +2876,13 @@ + .mhandler.cmd = hmp_info_migrate_cache_size, + }, + { ++ .name = "savevm", ++ .args_type = "", ++ .params = "", ++ .help = "show savevm status", ++ .mhandler.cmd = hmp_info_savevm, ++ }, ++ { + .name = "balloon", + .args_type = "", + .params = "", +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-11-20 09:13:01.000000000 +0100 ++++ new/qapi-schema.json 2014-11-20 09:16:47.000000000 +0100 +@@ -550,6 +550,42 @@ + '*downtime': 'int', + '*setup-time': 'int'} } + ++ ++# @SaveVMInfo ++# ++# Information about current migration process. ++# ++# @status: #optional string describing the current savevm status. ++# This can be 'active', 'completed', 'failed'. ++# If this field is not returned, no savevm process ++# has been initiated ++# ++# @error: #optional string containing error message is status is failed. ++# ++# @total-time: #optional total amount of milliseconds since savevm started. ++# If savevm has ended, it returns the total save time ++# ++# @bytes: #optional total amount of data transfered ++# ++# Since: 1.3 ++## ++{ 'struct': 'SaveVMInfo', ++ 'data': {'*status': 'str', '*error': 'str', ++ '*total-time': 'int', '*bytes': 'int'} } ++ ++## ++# @query-savevm ++# ++# Returns information about current savevm process. ++# ++# Returns: @SaveVMInfo ++# ++# Since: 1.3 ++## ++{ 'command': 'query-savevm', 'returns': 'SaveVMInfo' } ++ ++## ++ + ## + # @query-migrate + # +@@ -2649,8 +2685,18 @@ + # + # Since: 1.2.0 + ## ++ + { 'command': 'query-target', 'returns': 'TargetInfo' } + ++{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } } ++ ++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } } ++ ++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } } ++ ++{ 'command': 'savevm-end' } ++ ++ + ## + # @QKeyCode: + # +Index: new/qemu-options.hx +=================================================================== +--- new.orig/qemu-options.hx 2014-11-20 09:13:01.000000000 +0100 ++++ new/qemu-options.hx 2014-11-20 09:16:47.000000000 +0100 +@@ -2975,6 +2975,19 @@ + Start right away with a saved state (@code{loadvm} in monitor) + ETEXI + ++DEF("loadstate", HAS_ARG, QEMU_OPTION_loadstate, \ ++ "-loadstate file\n" \ ++ " start right away with a saved state\n", ++ QEMU_ARCH_ALL) ++STEXI ++@item -loadstate @var{file} ++@findex -loadstate ++Start right away with a saved state. This option does not rollback ++disk state like @code{loadvm}, so user must make sure that disk ++have correct state. @var{file} can be any valid device URL. See the section ++for "Device URL Syntax" for more information. ++ETEXI ++ + #ifndef _WIN32 + DEF("daemonize", 0, QEMU_OPTION_daemonize, \ + "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) +Index: new/qmp-commands.hx +=================================================================== +--- new.orig/qmp-commands.hx 2014-11-20 09:13:01.000000000 +0100 ++++ new/qmp-commands.hx 2014-11-20 09:16:47.000000000 +0100 +@@ -4200,6 +4200,36 @@ + <- { "return": {} } + + EQMP ++ ++ { ++ .name = "savevm-start", ++ .args_type = "statefile:s?", ++ .mhandler.cmd_new = qmp_marshal_input_savevm_start, ++ }, ++ ++ { ++ .name = "snapshot-drive", ++ .args_type = "device:s,name:s", ++ .mhandler.cmd_new = qmp_marshal_input_snapshot_drive, ++ }, ++ ++ { ++ .name = "delete-drive-snapshot", ++ .args_type = "device:s,name:s", ++ .mhandler.cmd_new = qmp_marshal_input_delete_drive_snapshot, ++ }, ++ ++ { ++ .name = "savevm-end", ++ .args_type = "", ++ .mhandler.cmd_new = qmp_marshal_input_savevm_end, ++ }, ++ ++ { ++ .name = "query-savevm", ++ .args_type = "", ++ .mhandler.cmd_new = qmp_marshal_input_query_savevm, ++ }, + + { + .name = "query-rocker", +Index: new/savevm-async.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ new/savevm-async.c 2014-11-20 09:17:48.000000000 +0100 +@@ -0,0 +1,522 @@ ++#include "qemu-common.h" ++#include "qapi/qmp/qerror.h" ++#include "qemu/error-report.h" ++#include "sysemu/sysemu.h" ++#include "qmp-commands.h" ++#include "qemu-options.h" ++#include "migration/qemu-file.h" ++#include "qom/qom-qobject.h" ++#include "migration/migration.h" ++#include "block/snapshot.h" ++#include "block/qapi.h" ++#include "block/block.h" ++#include "qemu/timer.h" ++#include "sysemu/block-backend.h" ++#include "qapi/qmp/qstring.h" ++ ++/* #define DEBUG_SAVEVM_STATE */ ++ ++#ifdef DEBUG_SAVEVM_STATE ++#define DPRINTF(fmt, ...) \ ++ do { printf("savevm-async: " fmt, ## __VA_ARGS__); } while (0) ++#else ++#define DPRINTF(fmt, ...) \ ++ do { } while (0) ++#endif ++ ++enum { ++ SAVE_STATE_DONE, ++ SAVE_STATE_ERROR, ++ SAVE_STATE_ACTIVE, ++ SAVE_STATE_COMPLETED, ++ SAVE_STATE_CANCELLED ++}; ++ ++ ++static struct SnapshotState { ++ BlockDriverState *bs; ++ size_t bs_pos; ++ int state; ++ Error *error; ++ Error *blocker; ++ int saved_vm_running; ++ QEMUFile *file; ++ int64_t total_time; ++} snap_state; ++ ++SaveVMInfo *qmp_query_savevm(Error **errp) ++{ ++ SaveVMInfo *info = g_malloc0(sizeof(*info)); ++ struct SnapshotState *s = &snap_state; ++ ++ if (s->state != SAVE_STATE_DONE) { ++ info->has_bytes = true; ++ info->bytes = s->bs_pos; ++ switch (s->state) { ++ case SAVE_STATE_ERROR: ++ info->has_status = true; ++ info->status = g_strdup("failed"); ++ info->has_total_time = true; ++ info->total_time = s->total_time; ++ if (s->error) { ++ info->has_error = true; ++ info->error = g_strdup(error_get_pretty(s->error)); ++ } ++ break; ++ case SAVE_STATE_ACTIVE: ++ info->has_status = true; ++ info->status = g_strdup("active"); ++ info->has_total_time = true; ++ info->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) ++ - s->total_time; ++ break; ++ case SAVE_STATE_COMPLETED: ++ info->has_status = true; ++ info->status = g_strdup("completed"); ++ info->has_total_time = true; ++ info->total_time = s->total_time; ++ break; ++ } ++ } ++ ++ return info; ++} ++ ++static int save_snapshot_cleanup(void) ++{ ++ int ret = 0; ++ ++ DPRINTF("save_snapshot_cleanup\n"); ++ ++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - ++ snap_state.total_time; ++ ++ if (snap_state.file) { ++ ret = qemu_fclose(snap_state.file); ++ } ++ ++ if (snap_state.bs) { ++ /* try to truncate, but ignore errors (will fail on block devices). ++ * note: bdrv_read() need whole blocks, so we round up ++ */ ++ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK; ++ bdrv_truncate(snap_state.bs, size); ++ bdrv_op_unblock_all(snap_state.bs, snap_state.blocker); ++ error_free(snap_state.blocker); ++ snap_state.blocker = NULL; ++ bdrv_unref(snap_state.bs); ++ snap_state.bs = NULL; ++ } ++ ++ return ret; ++} ++ ++static void save_snapshot_error(const char *fmt, ...) ++{ ++ va_list ap; ++ char *msg; ++ ++ va_start(ap, fmt); ++ msg = g_strdup_vprintf(fmt, ap); ++ va_end(ap); ++ ++ DPRINTF("save_snapshot_error: %s\n", msg); ++ ++ if (!snap_state.error) { ++ error_set(&snap_state.error, ERROR_CLASS_GENERIC_ERROR, "%s", msg); ++ } ++ ++ g_free (msg); ++ ++ snap_state.state = SAVE_STATE_ERROR; ++ ++ save_snapshot_cleanup(); ++} ++ ++static void save_snapshot_completed(void) ++{ ++ DPRINTF("save_snapshot_completed\n"); ++ ++ if (save_snapshot_cleanup() < 0) { ++ snap_state.state = SAVE_STATE_ERROR; ++ } else { ++ snap_state.state = SAVE_STATE_COMPLETED; ++ } ++} ++ ++static int block_state_close(void *opaque) ++{ ++ snap_state.file = NULL; ++ return bdrv_flush(snap_state.bs); ++} ++ ++static int block_state_put_buffer(void *opaque, const uint8_t *buf, ++ int64_t pos, int size) ++{ ++ int ret; ++ ++ assert(pos == snap_state.bs_pos); ++ ++ if ((ret = bdrv_pwrite(snap_state.bs, snap_state.bs_pos, buf, size)) > 0) { ++ snap_state.bs_pos += ret; ++ } ++ ++ return ret; ++} ++ ++static int store_and_stop(void) { ++ if (global_state_store()) { ++ save_snapshot_error("Error saving global state"); ++ return 1; ++ } ++ if (runstate_is_running()) { ++ vm_stop(RUN_STATE_SAVE_VM); ++ } ++ return 0; ++} ++ ++static void process_savevm_co(void *opaque) ++{ ++ int ret; ++ int64_t maxlen; ++ MigrationParams params = { ++ .blk = 0, ++ .shared = 0 ++ }; ++ ++ snap_state.state = SAVE_STATE_ACTIVE; ++ ++ qemu_mutex_unlock_iothread(); ++ qemu_savevm_state_header(snap_state.file); ++ ret = qemu_savevm_state_begin(snap_state.file, ¶ms); ++ qemu_mutex_lock_iothread(); ++ ++ if (ret < 0) { ++ save_snapshot_error("qemu_savevm_state_begin failed"); ++ return; ++ } ++ ++ while (snap_state.state == SAVE_STATE_ACTIVE) { ++ uint64_t pending_size; ++ ++ pending_size = qemu_savevm_state_pending(snap_state.file, 0); ++ ++ if (pending_size) { ++ ret = qemu_savevm_state_iterate(snap_state.file); ++ if (ret < 0) { ++ save_snapshot_error("qemu_savevm_state_iterate error %d", ret); ++ break; ++ } ++ DPRINTF("savevm inerate pending size %lu ret %d\n", pending_size, ret); ++ } else { ++ DPRINTF("done iterating\n"); ++ if (store_and_stop()) ++ break; ++ DPRINTF("savevm inerate finished\n"); ++ qemu_savevm_state_complete(snap_state.file); ++ DPRINTF("save complete\n"); ++ save_snapshot_completed(); ++ break; ++ } ++ ++ /* stop the VM if we get to the end of available space, ++ * or if pending_size is just a few MB ++ */ ++ maxlen = bdrv_getlength(snap_state.bs) - 30*1024*1024; ++ if ((pending_size < 100000) || ++ ((snap_state.bs_pos + pending_size) >= maxlen)) { ++ if (store_and_stop()) ++ break; ++ } ++ } ++ ++ if(snap_state.state == SAVE_STATE_CANCELLED) { ++ save_snapshot_completed(); ++ Error *errp = NULL; ++ qmp_savevm_end(&errp); ++ } ++ ++} ++ ++static const QEMUFileOps block_file_ops = { ++ .put_buffer = block_state_put_buffer, ++ .close = block_state_close, ++}; ++ ++ ++void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp) ++{ ++ BlockDriver *drv = NULL; ++ Error *local_err = NULL; ++ ++ int bdrv_oflags = BDRV_O_CACHE_WB | BDRV_O_RDWR; ++ int ret; ++ ++ if (snap_state.state != SAVE_STATE_DONE) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "VM snapshot already started\n"); ++ return; ++ } ++ ++ /* initialize snapshot info */ ++ snap_state.saved_vm_running = runstate_is_running(); ++ snap_state.bs_pos = 0; ++ snap_state.total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); ++ snap_state.blocker = NULL; ++ ++ if (snap_state.error) { ++ error_free(snap_state.error); ++ snap_state.error = NULL; ++ } ++ ++ if (!has_statefile) { ++ vm_stop(RUN_STATE_SAVE_VM); ++ snap_state.state = SAVE_STATE_COMPLETED; ++ return; ++ } ++ ++ if (qemu_savevm_state_blocked(errp)) { ++ return; ++ } ++ ++ /* Open the image */ ++ snap_state.bs = bdrv_new(); ++ ++ QDict *options = NULL; ++ options = qdict_new(); ++ qdict_put(options, "driver", qstring_from_str("raw")); ++ ret = bdrv_open(&snap_state.bs, statefile, NULL, options, bdrv_oflags, drv, &local_err); ++ if (ret < 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile); ++ goto restart; ++ } ++ ++ snap_state.file = qemu_fopen_ops(&snap_state, &block_file_ops); ++ ++ if (!snap_state.file) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "failed to open '%s'", statefile); ++ goto restart; ++ } ++ ++ ++ error_setg(&snap_state.blocker, "block device is in use by savevm"); ++ bdrv_op_block_all(snap_state.bs, snap_state.blocker); ++ ++ Coroutine *co = qemu_coroutine_create(process_savevm_co); ++ qemu_coroutine_enter(co, NULL); ++ ++ return; ++ ++restart: ++ ++ save_snapshot_error("setup failed"); ++ ++ if (snap_state.saved_vm_running) { ++ vm_start(); ++ } ++} ++ ++void qmp_savevm_end(Error **errp) ++{ ++ if (snap_state.state == SAVE_STATE_DONE) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "VM snapshot not started\n"); ++ return; ++ } ++ ++ if (snap_state.state == SAVE_STATE_ACTIVE) { ++ snap_state.state = SAVE_STATE_CANCELLED; ++ return; ++ } ++ ++ if (snap_state.saved_vm_running) { ++ vm_start(); ++ } ++ ++ snap_state.state = SAVE_STATE_DONE; ++} ++ ++void qmp_snapshot_drive(const char *device, const char *name, Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn1, *sn = &sn1; ++ int ret; ++#ifdef _WIN32 ++ struct _timeb tb; ++#else ++ struct timeval tv; ++#endif ++ ++ if (snap_state.state != SAVE_STATE_COMPLETED) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "VM snapshot not ready/started\n"); ++ return; ++ } ++ ++ blk = blk_by_name(device); ++ if (!blk) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", device); ++ return; ++ } ++ ++ bs = blk_bs(blk); ++ if (!bdrv_is_inserted(bs)) { ++ error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); ++ return; ++ } ++ ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Node '%s' is read only", device); ++ return; ++ } ++ ++ if (!bdrv_can_snapshot(bs)) { ++ error_setg(errp, QERR_UNSUPPORTED); ++ return; ++ } ++ ++ if (bdrv_snapshot_find(bs, sn, name) >= 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "snapshot '%s' already exists", name); ++ return; ++ } ++ ++ sn = &sn1; ++ memset(sn, 0, sizeof(*sn)); ++ ++#ifdef _WIN32 ++ _ftime(&tb); ++ sn->date_sec = tb.time; ++ sn->date_nsec = tb.millitm * 1000000; ++#else ++ gettimeofday(&tv, NULL); ++ sn->date_sec = tv.tv_sec; ++ sn->date_nsec = tv.tv_usec * 1000; ++#endif ++ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ ++ pstrcpy(sn->name, sizeof(sn->name), name); ++ ++ sn->vm_state_size = 0; /* do not save state */ ++ ++ ret = bdrv_snapshot_create(bs, sn); ++ if (ret < 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "Error while creating snapshot on '%s'\n", device); ++ return; ++ } ++} ++ ++void qmp_delete_drive_snapshot(const char *device, const char *name, ++ Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn1, *sn = &sn1; ++ Error *local_err = NULL; ++ ++ int ret; ++ ++ blk = blk_by_name(device); ++ if (!blk) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", device); ++ return; ++ } ++ ++ bs = blk_bs(blk); ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Node '%s' is read only", device); ++ return; ++ } ++ ++ if (!bdrv_can_snapshot(bs)) { ++ error_setg(errp, QERR_UNSUPPORTED); ++ return; ++ } ++ ++ if (bdrv_snapshot_find(bs, sn, name) < 0) { ++ /* return success if snapshot does not exists */ ++ return; ++ } ++ ++ ret = bdrv_snapshot_delete(bs, NULL, name, &local_err); ++ if (ret < 0) { ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, ++ "Error while deleting snapshot on '%s'\n", device); ++ return; ++ } ++} ++ ++static int loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos, ++ int size) ++{ ++ BlockDriverState *bs = (BlockDriverState *)opaque; ++ int64_t maxlen = bdrv_getlength(bs); ++ if (pos > maxlen) { ++ return -EIO; ++ } ++ if ((pos + size) > maxlen) { ++ size = maxlen - pos - 1; ++ } ++ if (size == 0) { ++ return 0; ++ } ++ return bdrv_pread(bs, pos, buf, size); ++} ++ ++static const QEMUFileOps loadstate_file_ops = { ++ .get_buffer = loadstate_get_buffer, ++}; ++ ++int load_state_from_blockdev(const char *filename) ++{ ++ BlockDriverState *bs = NULL; ++ BlockDriver *drv = NULL; ++ Error *local_err = NULL; ++ Error *blocker = NULL; ++ ++ QEMUFile *f; ++ int ret; ++ ++ bs = bdrv_new(); ++ ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_CACHE_WB, drv, &local_err); ++ error_setg(&blocker, "block device is in use by load state"); ++ bdrv_op_block_all(bs, blocker); ++ ++ if (ret < 0) { ++ error_report("Could not open VM state file"); ++ goto the_end; ++ } ++ ++ /* restore the VM state */ ++ f = qemu_fopen_ops(bs, &loadstate_file_ops); ++ if (!f) { ++ error_report("Could not open VM state file"); ++ ret = -EINVAL; ++ goto the_end; ++ } ++ ++ qemu_system_reset(VMRESET_SILENT); ++ migration_incoming_state_new(f); ++ ret = qemu_loadvm_state(f); ++ ++ qemu_fclose(f); ++ migration_incoming_state_destroy(); ++ if (ret < 0) { ++ error_report("Error %d while loading VM state", ret); ++ goto the_end; ++ } ++ ++ ret = 0; ++ ++ the_end: ++ if (bs) { ++ bdrv_op_unblock_all(bs, blocker); ++ error_free(blocker); ++ bdrv_unref(bs); ++ } ++ return ret; ++} +Index: new/savevm.c +=================================================================== +--- new.orig/migration/savevm.c 2014-11-20 09:13:01.000000000 +0100 ++++ new/migration/savevm.c 2014-11-20 09:16:47.000000000 +0100 +@@ -718,11 +718,11 @@ + qemu_put_be32(f, QEMU_VM_FILE_VERSION); + } + +-void qemu_savevm_state_begin(QEMUFile *f, ++int qemu_savevm_state_begin(QEMUFile *f, + const MigrationParams *params) + { + SaveStateEntry *se; +- int ret; ++ int ret = 0; + + trace_savevm_state_begin(); + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { +@@ -642,6 +642,7 @@ + break; + } + } ++ return ret; + } + + /* +@@ -690,7 +691,7 @@ + return !machine->suppress_vmdesc; + } + +-void qemu_savevm_state_complete(QEMUFile *f) ++int qemu_savevm_state_complete(QEMUFile *f) + { + QJSON *vmdesc; + int vmdesc_len; +@@ -838,7 +839,7 @@ + save_section_footer(f, se); + if (ret < 0) { + qemu_file_set_error(f, ret); +- return; ++ return ret; + } + } + +@@ -746,6 +747,7 @@ + object_unref(OBJECT(vmdesc)); + + qemu_fflush(f); ++ return qemu_file_get_error(f); + } + + uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) +Index: new/vl.c +=================================================================== +--- new.orig/vl.c 2014-11-20 09:13:01.000000000 +0100 ++++ new/vl.c 2014-11-20 09:16:47.000000000 +0100 +@@ -2760,6 +2760,7 @@ + int optind; + const char *optarg; + const char *loadvm = NULL; ++ const char *loadstate = NULL; + MachineClass *machine_class; + const char *cpu_model; + const char *vga_model = NULL; +@@ -3457,6 +3458,9 @@ + case QEMU_OPTION_loadvm: + loadvm = optarg; + break; ++ case QEMU_OPTION_loadstate: ++ loadstate = optarg; ++ break; + case QEMU_OPTION_full_screen: + full_screen = 1; + break; +@@ -4428,6 +4432,10 @@ + if (load_vmstate(loadvm) < 0) { + autostart = 0; + } ++ } else if (loadstate) { ++ if (load_state_from_blockdev(loadstate) < 0) { ++ autostart = 0; ++ } + } + + qdev_prop_check_globals(); diff --git a/debian/patches/old/jemalloc.patch b/debian/patches/old/jemalloc.patch new file mode 100644 index 0000000..839689f --- /dev/null +++ b/debian/patches/old/jemalloc.patch @@ -0,0 +1,143 @@ +From patchwork Fri Jun 19 10:56:58 2015 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: configure: Add support for jemalloc +From: Alexandre DERUMIER +X-Patchwork-Id: 486671 +Message-Id: <1434711418-20429-1-git-send-email-aderumier@odiso.com> +To: qemu-devel@nongnu.org +Cc: Alexandre Derumier +Date: Fri, 19 Jun 2015 12:56:58 +0200 + +This adds "--enable-jemalloc" and "--disable-jemalloc" to allow linking +to jemalloc memory allocator. + +We have already tcmalloc support, +but it seem to not working well with a lot of iothreads/disks. + +The main problem is that tcmalloc use a shared thread cache of 16MB +by default. +With more threads, this cache is shared, and some bad garbage collections +can occur if the cache is too low. + +It's possible to tcmalloc cache increase it with a env var: +TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES=256MB + +With default 16MB, performances are really bad with more than 2 disks. +Increasing to 256MB, it's helping but still have problem with 16 disks/iothreads. + +Jemalloc don't have performance problem with default configuration. + +Here the benchmark results in iops of 1 qemu vm randread 4K iodepth=32, +with rbd block backend (librbd is doing a lot of memory allocation), +1 iothread by disk + +glibc malloc +------------ + +1 disk 29052 +2 disks 55878 +4 disks 127899 +8 disks 240566 +15 disks 269976 + +jemalloc +-------- + +1 disk 41278 +2 disks 75781 +4 disks 195351 +8 disks 294241 +15 disks 298199 + +tcmalloc 2.2.1 default 16M cache +-------------------------------- + +1 disk 37911 +2 disks 67698 +4 disks 41076 +8 disks 43312 +15 disks 37569 + +tcmalloc : 256M cache +--------------------------- + +1 disk 33914 +2 disks 58839 +4 disks 148205 +8 disks 213298 +15 disks 218383 + +Signed-off-by: Alexandre Derumier +Reviewed-by: Fam Zheng +--- + configure | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/configure b/configure +index 222694f..2fe1e05 100755 +--- a/configure ++++ b/configure +@@ -338,6 +338,7 @@ vhdx="" + vhdx="" + numa="" + tcmalloc="no" ++jemalloc="no" + + # parse CC options first + for opt do +@@ -1149,6 +1150,10 @@ for opt do + ;; + --enable-tcmalloc) tcmalloc="yes" + ;; ++ --disable-jemalloc) jemalloc="no" ++ ;; ++ --enable-jemalloc) jemalloc="yes" ++ ;; + *) + echo "ERROR: unknown option $opt" + echo "Try '$0 --help' for more information" +@@ -3345,6 +3352,11 @@ EOF + fi + fi + ++if test "$tcmalloc" = "yes" && test "$jemalloc" = "yes" ; then ++ echo "ERROR: tcmalloc && jemalloc can't be used at the same time" ++ exit 1 ++fi ++ + ########################################## + # tcmalloc probe + +@@ -3362,6 +3374,22 @@ EOF + fi + + ########################################## ++# jemalloc probe ++ ++if test "$jemalloc" = "yes" ; then ++ cat > $TMPC << EOF ++#include ++int main(void) { malloc(1); return 0; } ++EOF ++ ++ if compile_prog "" "-ljemalloc" ; then ++ LIBS="-ljemalloc $LIBS" ++ else ++ feature_not_found "jemalloc" "install jemalloc devel" ++ fi ++fi ++ ++########################################## + # signalfd probe + signalfd="no" + cat > $TMPC << EOF +@@ -4573,6 +4591,7 @@ echo "snappy support $snappy" + echo "bzip2 support $bzip2" + echo "NUMA host support $numa" + echo "tcmalloc support $tcmalloc" ++echo "jemalloc support $jemalloc" + + if test "$sdl_too_old" = "yes"; then + echo "-> Your SDL version is too old - please upgrade to have SDL support" diff --git a/debian/patches/old/keymap.diff b/debian/patches/old/keymap.diff new file mode 100644 index 0000000..682cb9b --- /dev/null +++ b/debian/patches/old/keymap.diff @@ -0,0 +1,50 @@ +Index: new/ui/vnc.c +=================================================================== +--- new.orig/ui/vnc.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/ui/vnc.c 2014-11-20 06:50:51.000000000 +0100 +@@ -1633,6 +1633,10 @@ + + static void do_key_event(VncState *vs, int down, int keycode, int sym) + { ++ int mods = keycode & 0xf00; ++ ++ keycode &= SCANCODE_KEYMASK; ++ + /* QEMU console switch */ + switch(keycode) { + case 0x2a: /* Left Shift */ +@@ -1712,7 +1716,24 @@ + } + + if (qemu_console_is_graphic(NULL)) { ++ ++ /* our java vnc client never sends ALTGR, so we create ++ an artificial up/down event */ ++ ++ int emul_altgr = (mods & SCANCODE_ALTGR) && ++ !vs->modifiers_state[0xb8]; ++ ++ if (emul_altgr) { ++ reset_keys(vs); ++ qemu_input_event_send_key_number(vs->vd->dcl.con, 0xb8, true); ++ } ++ + qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); ++ ++ if (emul_altgr) { ++ qemu_input_event_send_key_number(vs->vd->dcl.con, 0xb8, false); ++ } ++ + } else { + bool numlock = vs->modifiers_state[0x45]; + bool control = (vs->modifiers_state[0x1d] || +@@ -1851,7 +1872,8 @@ + lsym = lsym - 'A' + 'a'; + } + +- keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK; ++ keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF); ++ + trace_vnc_key_event_map(down, sym, keycode, code2name(keycode)); + do_key_event(vs, down, keycode, sym); + } diff --git a/debian/patches/old/live-migration-fixes.diff b/debian/patches/old/live-migration-fixes.diff new file mode 100644 index 0000000..a7a2729 --- /dev/null +++ b/debian/patches/old/live-migration-fixes.diff @@ -0,0 +1,48 @@ +Index: new/arch_init.c +=================================================================== +--- new.orig/arch_init.c 2012-02-09 13:15:26.000000000 +0100 ++++ new/arch_init.c 2012-02-09 13:22:53.000000000 +0100 +@@ -264,6 +264,8 @@ + uint64_t bytes_transferred_last; + double bwidth = 0; + uint64_t expected_time = 0; ++ static int64_t starttime = 0; ++ double timediff; + int ret; + + if (stage < 0) { +@@ -299,10 +301,10 @@ + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->length); + } ++ ++ starttime = qemu_get_clock_ns(rt_clock); + } + +- bytes_transferred_last = bytes_transferred; +- bwidth = qemu_get_clock_ns(rt_clock); + + while ((ret = qemu_file_rate_limit(f)) == 0) { + int bytes_sent; +@@ -318,8 +320,8 @@ + return ret; + } + +- bwidth = qemu_get_clock_ns(rt_clock) - bwidth; +- bwidth = (bytes_transferred - bytes_transferred_last) / bwidth; ++ timediff = qemu_get_clock_ns(rt_clock) - starttime; ++ bwidth = bytes_transferred / timediff; + + /* if we haven't transferred anything this round, force expected_time to a + * a very high value, but without crashing */ +@@ -340,6 +342,10 @@ + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + ++ if ((stage == 2) && (bytes_transferred > 2*ram_bytes_total())) { ++ return 1; ++ } ++ + expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth; + + return (stage == 2) && (expected_time <= migrate_max_downtime()); diff --git a/debian/patches/old/mirror-fix-zero-init.patch b/debian/patches/old/mirror-fix-zero-init.patch new file mode 100644 index 0000000..cd76df5 --- /dev/null +++ b/debian/patches/old/mirror-fix-zero-init.patch @@ -0,0 +1,61 @@ +From patchwork Thu Oct 1 19:05:28 2015 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [PULL, + 1/1] block: mirror - fix full sync mode when target does not support + zero init +From: Jeff Cody +X-Patchwork-Id: 525278 +Message-Id: <1443726328-29484-2-git-send-email-jcody@redhat.com> +To: qemu-block@nongnu.org +Cc: peter.maydell@linaro.org, jcody@redhat.com, qemu-devel@nongnu.org +Date: Thu, 1 Oct 2015 15:05:28 -0400 + +During mirror, if the target device does not support zero init, a +mirror may result in a corrupted image for sync="full" mode. + +This is due to how the initial dirty bitmap is set up prior to copying +data - we did not mark sectors as dirty that are unallocated. This +means those unallocated sectors are skipped over on the target, and for +a device without zero init, invalid data may reside in those holes. + +If both of the following conditions are true, then we will explicitly +mark all sectors as dirty: + + 1.) sync = "full" + 2.) bdrv_has_zero_init(target) == false + +If the target does support zero init, but a target image is passed in +with data already present (i.e. an "existing" image), it is assumed the +data present in the existing image is valid data for those sectors. + +Reviewed-by: Paolo Bonzini +Message-id: 91ed4bc5bda7e2b09eb508b07c83f4071fe0b3c9.1443705220.git.jcody@redhat.com +Signed-off-by: Jeff Cody +--- + block/mirror.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/block/mirror.c b/block/mirror.c +index a258926..87928ab 100644 +--- a/block/mirror.c ++++ b/block/mirror.c +@@ -455,6 +455,8 @@ static void coroutine_fn mirror_run(void *opaque) + if (!s->is_none_mode) { + /* First part, loop on the sectors and initialize the dirty bitmap. */ + BlockDriverState *base = s->base; ++ bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(s->target); ++ + for (sector_num = 0; sector_num < end; ) { + /* Just to make sure we are not exceeding int limit. */ + int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS, +@@ -477,7 +479,7 @@ static void coroutine_fn mirror_run(void *opaque) + } + + assert(n > 0); +- if (ret == 1) { ++ if (ret == 1 || mark_all_dirty) { + bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); + } + sector_num += n; diff --git a/debian/patches/old/modify-query-machines.patch b/debian/patches/old/modify-query-machines.patch new file mode 100644 index 0000000..5edcdd2 --- /dev/null +++ b/debian/patches/old/modify-query-machines.patch @@ -0,0 +1,38 @@ +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-11-20 07:26:23.000000000 +0100 ++++ new/qapi-schema.json 2014-11-20 07:26:39.000000000 +0100 +@@ -2393,6 +2393,8 @@ + # + # @default: #optional whether the machine is default + # ++# @current: #optional whether this machine is currently used ++# + # @cpu-max: maximum number of CPUs supported by the machine type + # (since 1.5.0) + # +@@ -2400,7 +2402,7 @@ + ## + { 'struct': 'MachineInfo', + 'data': { 'name': 'str', '*alias': 'str', +- '*is-default': 'bool', 'cpu-max': 'int' } } ++ '*is-default': 'bool', '*is-current': 'bool', 'cpu-max': 'int' } } + + ## + # @query-machines: +Index: new/vl.c +=================================================================== +--- new.orig/vl.c 2014-11-20 06:50:55.000000000 +0100 ++++ new/vl.c 2014-11-20 07:26:39.000000000 +0100 +@@ -1534,6 +1534,11 @@ + info->name = g_strdup(mc->name); + info->cpu_max = !mc->max_cpus ? 1 : mc->max_cpus; + ++ if (strcmp(mc->name, MACHINE_GET_CLASS(current_machine)->name) == 0) { ++ info->has_is_current = true; ++ info->is_current = true; ++ } ++ + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = mach_list; diff --git a/debian/patches/old/modify-query-spice.patch b/debian/patches/old/modify-query-spice.patch new file mode 100644 index 0000000..4e5222d --- /dev/null +++ b/debian/patches/old/modify-query-spice.patch @@ -0,0 +1,44 @@ +Return last ticket with query-spice + +We use this to implement spice seemless migration. The current spice code +reconnect to the migrated VM using the original ticket. So we need +a way to read the original ticket. + +Limits: This only works for a single spice session. + + +Index: new/ui/spice-core.c +=================================================================== +--- new.orig/ui/spice-core.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/ui/spice-core.c 2014-11-20 07:26:43.000000000 +0100 +@@ -544,6 +544,11 @@ + micro = SPICE_SERVER_VERSION & 0xff; + info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro); + ++ if (auth_passwd) { ++ info->has_ticket = true; ++ info->ticket = g_strdup(auth_passwd); ++ } ++ + if (port) { + info->has_port = true; + info->port = port; +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-11-20 07:26:39.000000000 +0100 ++++ new/qapi-schema.json 2014-11-20 07:26:43.000000000 +0100 +@@ -868,11 +868,14 @@ + # + # @channels: a list of @SpiceChannel for each active spice channel + # ++# @ticket: #optional The last ticket set with set_password ++# + # Since: 0.14.0 + ## + { 'struct': 'SpiceInfo', + 'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int', + '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str', ++ '*ticket': 'str', + 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} } + + ## diff --git a/debian/patches/old/move-bdrv-snapshot-find.patch b/debian/patches/old/move-bdrv-snapshot-find.patch new file mode 100644 index 0000000..0cb7d48 --- /dev/null +++ b/debian/patches/old/move-bdrv-snapshot-find.patch @@ -0,0 +1,79 @@ +Index: new/block.c +=================================================================== +--- new.orig/block.c 2013-02-12 12:05:14.000000000 +0100 ++++ new/block.c 2013-02-12 12:06:52.000000000 +0100 +@@ -3162,6 +3162,28 @@ + return -ENOTSUP; + } + ++int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, ++ const char *name) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i, ret; ++ ++ ret = -ENOENT; ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns < 0) ++ return ret; ++ for(i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = 0; ++ break; ++ } ++ } ++ g_free(sn_tab); ++ return ret; ++} ++ + int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_name) + { +Index: new/include/block/block.h +=================================================================== +--- new.orig/include/block/block.h 2013-02-12 12:05:14.000000000 +0100 ++++ new/include/block/block.h 2013-02-12 12:06:52.000000000 +0100 +@@ -331,6 +331,8 @@ + int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); + int bdrv_snapshot_list(BlockDriverState *bs, + QEMUSnapshotInfo **psn_info); ++int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, ++ const char *name); + int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_name); + char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); +Index: new/savevm.c +=================================================================== +--- new.orig/savevm.c 2013-02-12 12:05:14.000000000 +0100 ++++ new/savevm.c 2013-02-12 12:06:52.000000000 +0100 +@@ -2055,28 +2055,6 @@ + return ret; + } + +-static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, +- const char *name) +-{ +- QEMUSnapshotInfo *sn_tab, *sn; +- int nb_sns, i, ret; +- +- ret = -ENOENT; +- nb_sns = bdrv_snapshot_list(bs, &sn_tab); +- if (nb_sns < 0) +- return ret; +- for(i = 0; i < nb_sns; i++) { +- sn = &sn_tab[i]; +- if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { +- *sn_info = *sn; +- ret = 0; +- break; +- } +- } +- g_free(sn_tab); +- return ret; +-} +- + /* + * Deletes snapshots of a given name in all opened images. + */ diff --git a/debian/patches/old/multicore.diff b/debian/patches/old/multicore.diff new file mode 100644 index 0000000..f4a4674 --- /dev/null +++ b/debian/patches/old/multicore.diff @@ -0,0 +1,201 @@ +Index: kvm-86/cpu-defs.h +=================================================================== +--- kvm-86.orig/cpu-defs.h 2009-09-24 14:19:14.000000000 +0200 ++++ kvm-86/cpu-defs.h 2009-09-24 14:47:00.000000000 +0200 +@@ -194,6 +194,8 @@ + int cpu_index; /* CPU index (informative) */ \ + uint32_t host_tid; /* host thread ID */ \ + int numa_node; /* NUMA node this cpu is belonging to */ \ ++ int nr_cores; /* number of cores within this CPU package */ \ ++ int nr_threads;/* number of threads within this CPU */ \ + int running; /* Nonzero if cpu is currently running(usermode). */ \ + int thread_id; \ + /* user data */ \ +Index: kvm-86/target-i386/helper.c +=================================================================== +--- kvm-86.orig/target-i386/helper.c 2009-09-24 14:19:14.000000000 +0200 ++++ kvm-86/target-i386/helper.c 2009-09-24 14:50:18.000000000 +0200 +@@ -121,7 +121,7 @@ + #ifdef TARGET_X86_64 + { + .name = "qemu64", +- .level = 2, ++ .level = 4, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, +@@ -192,7 +192,7 @@ + #endif + { + .name = "qemu32", +- .level = 2, ++ .level = 4, + .family = 6, + .model = 3, + .stepping = 3, +@@ -1638,6 +1638,12 @@ + *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ + *ecx = env->cpuid_ext_features; + *edx = env->cpuid_features; ++ ++ if (env->nr_cores * env->nr_threads > 1) { ++ *ebx |= (env->nr_cores * env->nr_threads) << 16; ++ *edx |= 1 << 28; /* HTT bit */ ++ } ++ + break; + case 2: + /* cache info: needed for Pentium Pro compatibility */ +@@ -1648,21 +1654,29 @@ + break; + case 4: + /* cache info: needed for Core compatibility */ ++ if (env->nr_cores > 1) { ++ *eax = (env->nr_cores - 1) << 26; ++ } else { ++ *eax = 0; ++ } + switch (count) { + case 0: /* L1 dcache info */ +- *eax = 0x0000121; ++ *eax |= 0x0000121; + *ebx = 0x1c0003f; + *ecx = 0x000003f; + *edx = 0x0000001; + break; + case 1: /* L1 icache info */ +- *eax = 0x0000122; ++ *eax |= 0x0000122; + *ebx = 0x1c0003f; + *ecx = 0x000003f; + *edx = 0x0000001; + break; + case 2: /* L2 cache info */ +- *eax = 0x0000143; ++ *eax |= 0x0000143; ++ if (env->nr_threads > 1) { ++ *eax |= (env->nr_threads - 1) << 14; ++ } + *ebx = 0x3c0003f; + *ecx = 0x0000fff; + *edx = 0x0000001; +@@ -1715,6 +1729,16 @@ + *ecx = env->cpuid_ext3_features; + *edx = env->cpuid_ext2_features; + ++ if (env->nr_cores * env->nr_threads > 1) { ++ uint32_t teax, tebx, tecx, tedx; ++ cpu_x86_cpuid(env, 0, 0, &teax, &tebx, &tecx, &tedx); ++ if ( tebx == CPUID_VENDOR_AMD_1 && ++ tedx == CPUID_VENDOR_AMD_2 && ++ tecx == CPUID_VENDOR_AMD_3) { ++ *ecx |= 1 << 1; /* CmpLegacy bit */ ++ } ++ } ++ + if (kvm_enabled()) { + uint32_t h_eax, h_edx; + +@@ -1790,6 +1814,9 @@ + *ebx = 0; + *ecx = 0; + *edx = 0; ++ if (env->nr_cores * env->nr_threads > 1) { ++ *ecx |= (env->nr_cores * env->nr_threads) - 1; ++ } + break; + case 0x8000000A: + *eax = 0x00000001; /* SVM Revision */ +Index: kvm-86/vl.c +=================================================================== +--- kvm-86.orig/vl.c 2009-09-24 14:30:14.000000000 +0200 ++++ kvm-86/vl.c 2009-09-24 14:47:00.000000000 +0200 +@@ -230,6 +230,8 @@ + const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE]; + int assigned_devices_index; + int smp_cpus = 1; ++int smp_cores = 1; ++int smp_threads = 1; + int fairsched_id = 0; + const char *vnc_display; + int acpi_enabled = 1; +@@ -2499,6 +2501,52 @@ + return; + } + ++static void smp_parse(const char *optarg) ++{ ++ int smp, sockets = 0, threads = 0, cores = 0; ++ char *endptr; ++ char option[128]; ++ ++ smp = strtoul(optarg, &endptr, 10); ++ if (endptr != optarg) { ++ if (*endptr == ',') { ++ endptr++; ++ } ++ } ++ if (get_param_value(option, 128, "sockets", endptr) != 0) ++ sockets = strtoull(option, NULL, 10); ++ if (get_param_value(option, 128, "cores", endptr) != 0) ++ cores = strtoull(option, NULL, 10); ++ if (get_param_value(option, 128, "threads", endptr) != 0) ++ threads = strtoull(option, NULL, 10); ++ ++ /* compute missing values, prefer sockets over cores over threads */ ++ if (smp == 0 || sockets == 0) { ++ sockets = sockets > 0 ? sockets : 1; ++ cores = cores > 0 ? cores : 1; ++ threads = threads > 0 ? threads : 1; ++ if (smp == 0) { ++ smp = cores * threads * sockets; ++ } else { ++ sockets = smp / (cores * threads); ++ } ++ } else { ++ if (cores == 0) { ++ threads = threads > 0 ? threads : 1; ++ cores = smp / (sockets * threads); ++ } else { ++ if (sockets == 0) { ++ sockets = smp / (cores * threads); ++ } else { ++ threads = smp / (cores * sockets); ++ } ++ } ++ } ++ smp_cpus = smp; ++ smp_cores = cores > 0 ? cores : 1; ++ smp_threads = threads > 0 ? threads : 1; ++} ++ + /***********************************************************/ + /* USB devices */ + +@@ -3727,6 +3775,8 @@ + + if (kvm_enabled()) + kvm_init_vcpu(env); ++ env->nr_cores = smp_cores; ++ env->nr_threads = smp_threads; + return; + } + +@@ -4060,6 +4110,8 @@ + kvm_start_vcpu(env); + else + tcg_init_vcpu(env); ++ env->nr_cores = smp_cores; ++ env->nr_threads = smp_threads; + } + + void qemu_notify_event(void) +@@ -5560,7 +5612,7 @@ + usb_devices_index++; + break; + case QEMU_OPTION_smp: +- smp_cpus = atoi(optarg); ++ smp_parse(optarg); + if (smp_cpus < 1) { + fprintf(stderr, "Invalid number of CPUs\n"); + exit(1); diff --git a/debian/patches/old/ps2-queue-size.diff b/debian/patches/old/ps2-queue-size.diff new file mode 100644 index 0000000..58564bc --- /dev/null +++ b/debian/patches/old/ps2-queue-size.diff @@ -0,0 +1,20 @@ + +bug description: boot a guest, open VNC console, and when it's still in BIOS / bootloader sequence, type as many keys as you can, move your mouse as much as you can. If needed, just reboot the guest. + +Linux guest will boot with "i8042.c: No controller found" and no keyboard. + +Further discussion seem to lead to a conclusion that there is some obscure bug in Qemu BIOS which makes this problem visible. + +Index: kvm-86/hw/ps2.c +=================================================================== +--- kvm-86.orig/hw/ps2.c 2009-06-15 13:31:24.000000000 +0200 ++++ kvm-86/hw/ps2.c 2009-06-15 13:31:50.000000000 +0200 +@@ -70,7 +70,7 @@ + #define MOUSE_STATUS_ENABLED 0x20 + #define MOUSE_STATUS_SCALE21 0x10 + +-#define PS2_QUEUE_SIZE 256 ++#define PS2_QUEUE_SIZE 15 + + typedef struct { + uint8_t data[PS2_QUEUE_SIZE]; diff --git a/debian/patches/old/pve-auth.patch b/debian/patches/old/pve-auth.patch new file mode 100644 index 0000000..9facc1a --- /dev/null +++ b/debian/patches/old/pve-auth.patch @@ -0,0 +1,426 @@ +Index: new/qemu-options.hx +=================================================================== +--- new.orig/qemu-options.hx 2014-11-20 06:45:06.000000000 +0100 ++++ new/qemu-options.hx 2014-11-20 06:50:44.000000000 +0100 +@@ -411,6 +411,9 @@ + @table @option + ETEXI + ++DEF("id", HAS_ARG, QEMU_OPTION_id, ++ "-id n set the VMID\n", QEMU_ARCH_ALL) ++ + DEF("fda", HAS_ARG, QEMU_OPTION_fda, + "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) + DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) +Index: new/vl.c +=================================================================== +--- new.orig/vl.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/vl.c 2014-11-20 06:50:44.000000000 +0100 +@@ -2730,6 +2730,7 @@ + int main(int argc, char **argv, char **envp) + { + int i; ++ long int vm_id_long = 0; + int snapshot, linux_boot; + const char *initrd_filename; + const char *kernel_filename, *kernel_cmdline; +@@ -3477,6 +3478,14 @@ + exit(1); + } + break; ++ case QEMU_OPTION_id: ++ vm_id_long = strtol(optarg, (char **) &optarg, 10); ++ if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) { ++ fprintf(stderr, "Invalid ID\n"); ++ exit(1); ++ } ++ pve_auth_setup(vm_id_long); ++ break; + case QEMU_OPTION_vnc: + { + #ifdef CONFIG_VNC +Index: new/include/ui/console.h +=================================================================== +--- new.orig/include/ui/console.h 2014-11-20 06:45:06.000000000 +0100 ++++ new/include/ui/console.h 2014-11-20 06:50:55.000000000 +0100 +@@ -328,6 +328,7 @@ + void cocoa_display_init(DisplayState *ds, int full_screen); + + /* vnc.c */ ++void pve_auth_setup(int vmid); + void vnc_display_init(const char *id); + void vnc_display_open(const char *id, Error **errp); + void vnc_display_add_client(const char *id, int csock, bool skipauth); +Index: new/ui/vnc.c +=================================================================== +--- new.orig/ui/vnc.c 2014-11-20 06:50:51.000000000 +0100 ++++ new/ui/vnc.c 2014-11-20 06:50:55.000000000 +0100 +@@ -48,6 +48,125 @@ + #include "vnc_keysym.h" + #include "crypto/cipher.h" + ++static int pve_vmid = 0; ++ ++void pve_auth_setup(int vmid) { ++ pve_vmid = vmid; ++} ++ ++static char * ++urlencode(char *buf, const char *value) ++{ ++ static const char *hexchar = "0123456789abcdef"; ++ char *p = buf; ++ int i; ++ int l = strlen(value); ++ for (i = 0; i < l; i++) { ++ char c = value[i]; ++ if (('a' <= c && c <= 'z') || ++ ('A' <= c && c <= 'Z') || ++ ('0' <= c && c <= '9')) { ++ *p++ = c; ++ } else if (c == 32) { ++ *p++ = '+'; ++ } else { ++ *p++ = '%'; ++ *p++ = hexchar[c >> 4]; ++ *p++ = hexchar[c & 15]; ++ } ++ } ++ *p = 0; ++ ++ return p; ++} ++ ++int ++pve_auth_verify(const char *clientip, const char *username, const char *passwd) ++{ ++ struct sockaddr_in server; ++ ++ int sfd = socket(AF_INET, SOCK_STREAM, 0); ++ if (sfd == -1) { ++ perror("pve_auth_verify: socket failed"); ++ return -1; ++ } ++ ++ struct hostent *he; ++ if ((he = gethostbyname("localhost")) == NULL) { ++ fprintf(stderr, "pve_auth_verify: error resolving hostname\n"); ++ goto err; ++ } ++ ++ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); ++ server.sin_family = AF_INET; ++ server.sin_port = htons(85); ++ ++ if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) { ++ perror("pve_auth_verify: error connecting to server"); ++ goto err; ++ } ++ ++ char buf[8192]; ++ char form[8192]; ++ ++ char *p = form; ++ p = urlencode(p, "username"); ++ *p++ = '='; ++ p = urlencode(p, username); ++ ++ *p++ = '&'; ++ p = urlencode(p, "password"); ++ *p++ = '='; ++ p = urlencode(p, passwd); ++ ++ *p++ = '&'; ++ p = urlencode(p, "path"); ++ *p++ = '='; ++ char authpath[256]; ++ sprintf(authpath, "/vms/%d", pve_vmid); ++ p = urlencode(p, authpath); ++ ++ *p++ = '&'; ++ p = urlencode(p, "privs"); ++ *p++ = '='; ++ p = urlencode(p, "VM.Console"); ++ ++ sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n" ++ "Host: localhost:85\n" ++ "Connection: close\n" ++ "PVEClientIP: %s\n" ++ "Content-Type: application/x-www-form-urlencoded\n" ++ "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form); ++ ssize_t len = strlen(buf); ++ ssize_t sb = send(sfd, buf, len, 0); ++ if (sb < 0) { ++ perror("pve_auth_verify: send failed"); ++ goto err; ++ } ++ if (sb != len) { ++ fprintf(stderr, "pve_auth_verify: partial send error\n"); ++ goto err; ++ } ++ ++ len = recv(sfd, buf, sizeof(buf) - 1, 0); ++ if (len < 0) { ++ perror("pve_auth_verify: recv failed"); ++ goto err; ++ } ++ ++ buf[len] = 0; ++ ++ //printf("DATA:%s\n", buf); ++ ++ shutdown(sfd, SHUT_RDWR); ++ ++ return strncmp(buf, "HTTP/1.1 200 OK", 15); ++ ++err: ++ shutdown(sfd, SHUT_RDWR); ++ return -1; ++} ++ + static QTAILQ_HEAD(, VncDisplay) vnc_displays = + QTAILQ_HEAD_INITIALIZER(vnc_displays); + +@@ -3393,10 +3512,10 @@ + } + if (x509) { + VNC_DEBUG("Initializing VNC server with x509 password auth\n"); +- vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; ++ vs->subauth = VNC_AUTH_VENCRYPT_X509PLAIN; + } else { + VNC_DEBUG("Initializing VNC server with TLS password auth\n"); +- vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; ++ vs->subauth = VNC_AUTH_VENCRYPT_TLSPLAIN; + } + } else { + VNC_DEBUG("Initializing VNC server with password auth\n"); +@@ -3557,7 +3676,14 @@ + vs->tls.x509verify = true; + } + } +- if (path) { ++ if (path && strcmp(path, "on") == 0) { ++ x509 = true; ++ tls = true; ++ if (pve_tls_set_x509_creds_dir(vs) < 0) { ++ error_setg(errp, "No certificate path provided"); ++ goto fail; ++ } ++ } else if (path) { + x509 = true; + if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { + error_setg(errp, "Failed to find x509 certificates/keys in %s", +Index: new/ui/vnc-auth-vencrypt.c +=================================================================== +--- new.orig/ui/vnc-auth-vencrypt.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/ui/vnc-auth-vencrypt.c 2014-11-20 06:50:55.000000000 +0100 +@@ -26,6 +26,107 @@ + + #include "vnc.h" + #include "qemu/main-loop.h" ++#include "qemu/sockets.h" ++ ++static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len) ++{ ++ const char *err = NULL; ++ char username[256]; ++ char passwd[512]; ++ ++ char clientip[256]; ++ clientip[0] = 0; ++ struct sockaddr_in client; ++ socklen_t addrlen = sizeof(client); ++ if (getpeername(vs->csock, &client, &addrlen) == 0) { ++ inet_ntop(client.sin_family, &client.sin_addr, ++ clientip, sizeof(clientip)); ++ } ++ ++ if ((len != (vs->username_len + vs->password_len)) || ++ (vs->username_len >= (sizeof(username)-1)) || ++ (vs->password_len >= (sizeof(passwd)-1)) ) { ++ err = "Got unexpected data length"; ++ goto err; ++ } ++ ++ strncpy(username, (char *)data, vs->username_len); ++ username[vs->username_len] = 0; ++ strncpy(passwd, (char *)data + vs->username_len, vs->password_len); ++ passwd[vs->password_len] = 0; ++ ++ VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd); ++ ++ if (pve_auth_verify(clientip, username, passwd) == 0) { ++ vnc_write_u32(vs, 0); /* Accept auth completion */ ++ start_client_init(vs); ++ return 0; ++ } ++ ++ err = "Authentication failed"; ++err: ++ if (err) { ++ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err); ++ vnc_write_u32(vs, 1); /* Reject auth */ ++ if (vs->minor >= 8) { ++ int elen = strlen(err); ++ vnc_write_u32(vs, elen); ++ vnc_write(vs, err, elen); ++ } ++ } ++ vnc_flush(vs); ++ vnc_client_error(vs); ++ ++ return 0; ++ ++} ++ ++static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len) ++{ ++ uint32_t ulen = read_u32(data, 0); ++ uint32_t pwlen = read_u32(data, 4); ++ const char *err = NULL; ++ ++ VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen); ++ ++ if (!ulen) { ++ err = "No User name."; ++ goto err; ++ } ++ if (ulen >= 255) { ++ err = "User name too long."; ++ goto err; ++ } ++ if (!pwlen) { ++ err = "Password too short"; ++ goto err; ++ } ++ if (pwlen >= 511) { ++ err = "Password too long."; ++ goto err; ++ } ++ ++ vs->username_len = ulen; ++ vs->password_len = pwlen; ++ ++ vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen); ++ ++ return 0; ++err: ++ if (err) { ++ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err); ++ vnc_write_u32(vs, 1); /* Reject auth */ ++ if (vs->minor >= 8) { ++ int elen = strlen(err); ++ vnc_write_u32(vs, elen); ++ vnc_write(vs, err, elen); ++ } ++ } ++ vnc_flush(vs); ++ vnc_client_error(vs); ++ ++ return 0; ++} + + static void start_auth_vencrypt_subauth(VncState *vs) + { +@@ -37,6 +138,12 @@ + start_client_init(vs); + break; + ++ case VNC_AUTH_VENCRYPT_TLSPLAIN: ++ case VNC_AUTH_VENCRYPT_X509PLAIN: ++ VNC_DEBUG("Start TLS auth PLAIN\n"); ++ vnc_read_when(vs, protocol_client_auth_plain_start, 8); ++ break; ++ + case VNC_AUTH_VENCRYPT_TLSVNC: + case VNC_AUTH_VENCRYPT_X509VNC: + VNC_DEBUG("Start TLS auth VNC\n"); +Index: new/ui/vnc.h +=================================================================== +--- new.orig/ui/vnc.h 2014-11-20 06:45:06.000000000 +0100 ++++ new/ui/vnc.h 2014-11-20 06:50:55.000000000 +0100 +@@ -288,6 +288,8 @@ + int auth; + int subauth; /* Used by VeNCrypt */ + char challenge[VNC_AUTH_CHALLENGE_SIZE]; ++ int username_len; ++ int password_len; + #ifdef CONFIG_VNC_TLS + VncStateTLS tls; + #endif +@@ -603,4 +605,6 @@ + int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); + void vnc_zrle_clear(VncState *vs); + ++int pve_auth_verify(const char *clientip, const char *username, const char *passwd); ++ + #endif /* __QEMU_VNC_H */ +Index: new/ui/vnc-tls.c +=================================================================== +--- new.orig/ui/vnc-tls.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/ui/vnc-tls.c 2014-11-20 06:50:55.000000000 +0100 +@@ -302,6 +302,14 @@ + + static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) + { ++ /* optimize for speed */ ++ static const int ciphers[] = { ++ GNUTLS_CIPHER_ARCFOUR_128, ++ GNUTLS_CIPHER_AES_128_CBC, ++ GNUTLS_CIPHER_3DES_CBC, ++ 0 ++ }; ++ + static const int cert_types[] = { GNUTLS_CRT_X509, 0 }; + static const int protocols[] = { + GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 +@@ -313,6 +321,11 @@ + }; + int rc; + ++ rc = gnutls_cipher_set_priority(s, ciphers); ++ if (rc != GNUTLS_E_SUCCESS) { ++ return -1; ++ } ++ + rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon); + if (rc != GNUTLS_E_SUCCESS) { + return -1; +@@ -448,6 +460,24 @@ + return 0; + } + ++int pve_tls_set_x509_creds_dir(VncDisplay *vd) ++{ ++ if (vnc_set_x509_credential(vd, "/etc/pve", "pve-root-ca.pem", &vd->tls.x509cacert, 0) < 0) ++ goto cleanup; ++ if (vnc_set_x509_credential(vd, "/etc/pve/local", "pve-ssl.pem", &vd->tls.x509cert, 0) < 0) ++ goto cleanup; ++ if (vnc_set_x509_credential(vd, "/etc/pve/local", "pve-ssl.key", &vd->tls.x509key, 0) < 0) ++ goto cleanup; ++ ++ return 0; ++ ++ cleanup: ++ g_free(vd->tls.x509cacert); ++ g_free(vd->tls.x509cert); ++ g_free(vd->tls.x509key); ++ vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; ++ return -1; ++} + + int vnc_tls_set_x509_creds_dir(VncDisplay *vd, + const char *certdir) +Index: new/ui/vnc-tls.h +=================================================================== +--- new.orig/ui/vnc-tls.h 2014-11-20 06:45:06.000000000 +0100 ++++ new/ui/vnc-tls.h 2014-11-20 06:50:55.000000000 +0100 +@@ -61,6 +61,8 @@ + + int vnc_tls_validate_certificate(VncState *vs); + ++int pve_tls_set_x509_creds_dir(VncDisplay *vd); ++ + int vnc_tls_set_x509_creds_dir(VncDisplay *vd, + const char *path); + diff --git a/debian/patches/old/series b/debian/patches/old/series new file mode 100644 index 0000000..c02c96c --- /dev/null +++ b/debian/patches/old/series @@ -0,0 +1,51 @@ +fr-ca-keymap-corrections.diff +adjust-path.diff +keymap.diff +pve-auth.patch +vencrypt-auth-plain.patch +fix-qemu-img-snapshot-removal.patch +enable-kvm-by-default.patch +virtio-balloon-fix-query.patch +set-cpu-model-to-kvm64.patch +modify-query-machines.patch +#spice-socket.patch +modify-query-spice.patch +spice-use-pve-certs.patch +backup-add-vma-binary.patch +backup-add-vma-verify-command.patch +backup-vma-add-dump-config.patch +backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch +backup-modify-job-api.patch +backup-add-pve-monitor-commands.patch +backup-add-dir-format.patch +backup-do-not-return-errors-in-dump-callback.patch +backup-vma-correctly-propagate-error.patch +backup-vma-remove-async-queue.patch +backup-run-flush-inside-coroutine.patch +backup-do-not-use-bdrv-drain-all.patch +internal-snapshot-async.patch +#disable-efi-enable-pxe-roms.patch +backup-vma-allow-empty-backups.patch +backup-vma-extract-add-block-driver-type.patch +glusterfs-daemonize.patch +gluster-backupserver.patch +add-qmp-get-link-status.patch +jemalloc.patch +0001-smm_available-false.patch +mirror-fix-zero-init.patch +fix-emulator-version-string.patch +add_firewall_to_vma.patch +0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch +0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch +0001-ui-vnc-avoid-floating-point-exception.patch +CVE-2015-7549-msix-pba-write-ro.patch +CVE-2015-8558-ehci_make_idt_processing_more_robust.patch +vmxnet3-host-memory-leakage.patch +CVE-2015-8613-scsi-initialize-info-object.patch +CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch +CVE-2015-8701-net-rocker-off-by-one.patch +CVE-2015-8743-ne2000-ioport-bounds-check.patch +CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch +CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch +CVE-2015-8619-hmp-sendkey-oob-fix.patch +CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch diff --git a/debian/patches/old/set-cpu-model-to-kvm64.patch b/debian/patches/old/set-cpu-model-to-kvm64.patch new file mode 100644 index 0000000..d306668 --- /dev/null +++ b/debian/patches/old/set-cpu-model-to-kvm64.patch @@ -0,0 +1,16 @@ +Index: new/hw/i386/pc.c +=================================================================== +--- new.orig/hw/i386/pc.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/i386/pc.c 2014-11-20 07:26:33.000000000 +0100 +@@ -1020,9 +1020,9 @@ + /* init CPUs */ + if (cpu_model == NULL) { + #ifdef TARGET_X86_64 +- cpu_model = "qemu64"; ++ cpu_model = "kvm64"; + #else +- cpu_model = "qemu32"; ++ cpu_model = "kvm32"; + #endif + } + current_cpu_model = cpu_model; diff --git a/debian/patches/old/spice-socket.patch b/debian/patches/old/spice-socket.patch new file mode 100644 index 0000000..00175c5 --- /dev/null +++ b/debian/patches/old/spice-socket.patch @@ -0,0 +1,107 @@ +From ea2bd3a49ab04110cde4e71d9afafcf5d93db909 Mon Sep 17 00:00:00 2001 +From: Alexandre Derumier +Date: Mon, 8 Apr 2013 12:42:48 +0200 +Subject: [PATCH 1/2] Add spice support for unix socket option + + +Signed-off-by: Alexandre Derumier +--- + qemu-options.hx | 5 ++++- + ui/spice-core.c | 22 +++++++++++++++++++--- + 2 files changed, 23 insertions(+), 4 deletions(-) + +diff --git a/qemu-options.hx b/qemu-options.hx +index 06b6e58..37d271a 100644 +--- a/qemu-options.hx ++++ b/qemu-options.hx +@@ -887,7 +887,7 @@ Enable SDL. + ETEXI + + DEF("spice", HAS_ARG, QEMU_OPTION_spice, +- "-spice [port=port][,tls-port=secured-port][,x509-dir=]\n" ++ "-spice [port=port][,tls-port=secured-port][,unix=][,x509-dir=]\n" + " [,x509-key-file=][,x509-key-password=]\n" + " [,x509-cert-file=][,x509-cacert-file=]\n" + " [,x509-dh-key-file=][,addr=addr][,ipv4|ipv6]\n" +@@ -911,6 +911,9 @@ Enable the spice remote desktop protocol. Valid options are + + @table @option + ++@item unix= ++Path on which to bind a UNIX socket. ++ + @item port= + Set the TCP port spice is listening on for plaintext channels. + +diff --git a/ui/spice-core.c b/ui/spice-core.c +index bcc4199..acc1626 100644 +--- a/ui/spice-core.c ++++ b/ui/spice-core.c +@@ -39,6 +39,8 @@ + #include "hw/hw.h" + #include "ui/spice-display.h" + ++static const int on=1, off=0; ++ + /* core bits */ + + static SpiceServer *spice_server; +@@ -428,6 +430,9 @@ static QemuOptsList qemu_spice_opts = { + .name = "tls-port", + .type = QEMU_OPT_NUMBER, + },{ ++ .name = "unix", ++ .type = QEMU_OPT_STRING, ++ },{ + .name = "addr", + .type = QEMU_OPT_STRING, + },{ +@@ -640,16 +645,18 @@ void qemu_spice_init(void) + spice_image_compression_t compression; + spice_wan_compression_t wan_compr; + bool seamless_migration; ++ const char *unix_socket; + + qemu_thread_get_self(&me); + + if (!opts) { + return; + } ++ unix_socket = qemu_opt_get(opts, "unix"); + port = qemu_opt_get_number(opts, "port", 0); + tls_port = qemu_opt_get_number(opts, "tls-port", 0); +- if (!port && !tls_port) { +- error_report("neither port nor tls-port specified for spice"); ++ if (!port && !tls_port && !unix_socket) { ++ error_report("neither sock, port nor tls-port specified for spice"); + exit(1); + } + if (port < 0 || port > 65535) { +@@ -705,7 +712,6 @@ void qemu_spice_init(void) + } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { + addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; + } +- + spice_server = spice_server_new(); + spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); + if (port) { +@@ -720,6 +726,16 @@ void qemu_spice_init(void) + x509_dh_file, + tls_ciphers); + } ++ if (unix_socket) { ++ char *dpy; ++ int lsock; ++ dpy = g_malloc(256); ++ pstrcpy(dpy, 256, unix_socket); ++ Error *local_err = NULL; ++ lsock = unix_listen(unix_socket, dpy, 256, &local_err); ++ setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); ++ spice_server_set_listen_socket_fd(spice_server, lsock); ++ } + if (password) { + spice_server_set_ticket(spice_server, password, 0, 0, 0); + } +-- +1.7.10.4 + diff --git a/debian/patches/old/spice-use-pve-certs.patch b/debian/patches/old/spice-use-pve-certs.patch new file mode 100644 index 0000000..1f7f7a4 --- /dev/null +++ b/debian/patches/old/spice-use-pve-certs.patch @@ -0,0 +1,46 @@ +Index: new/ui/spice-core.c +=================================================================== +--- new.orig/ui/spice-core.c 2014-11-20 07:26:43.000000000 +0100 ++++ new/ui/spice-core.c 2014-11-20 07:33:22.000000000 +0100 +@@ -682,32 +682,35 @@ + + if (tls_port) { + x509_dir = qemu_opt_get(opts, "x509-dir"); +- if (!x509_dir) { +- x509_dir = "."; +- } + + str = qemu_opt_get(opts, "x509-key-file"); + if (str) { + x509_key_file = g_strdup(str); +- } else { ++ } else if (x509_dir) { + x509_key_file = g_strdup_printf("%s/%s", x509_dir, + X509_SERVER_KEY_FILE); ++ } else { ++ x509_key_file = g_strdup("/etc/pve/local/pve-ssl.key"); + } + + str = qemu_opt_get(opts, "x509-cert-file"); + if (str) { + x509_cert_file = g_strdup(str); +- } else { ++ } else if (x509_dir) { + x509_cert_file = g_strdup_printf("%s/%s", x509_dir, + X509_SERVER_CERT_FILE); ++ } else { ++ x509_cert_file = g_strdup("/etc/pve/local/pve-ssl.pem"); + } + + str = qemu_opt_get(opts, "x509-cacert-file"); + if (str) { + x509_cacert_file = g_strdup(str); +- } else { ++ } else if (x509_dir) { + x509_cacert_file = g_strdup_printf("%s/%s", x509_dir, + X509_CA_CERT_FILE); ++ } else { ++ x509_cacert_file = g_strdup("/etc/pve/pve-root-ca.pem"); + } + + x509_key_password = qemu_opt_get(opts, "x509-key-password"); diff --git a/debian/patches/old/use-local-linux-kvm-h.diff b/debian/patches/old/use-local-linux-kvm-h.diff new file mode 100644 index 0000000..6990382 --- /dev/null +++ b/debian/patches/old/use-local-linux-kvm-h.diff @@ -0,0 +1,16 @@ + +Alway use our own version. + +Index: new/kvm/libkvm/libkvm.h +=================================================================== +--- new.orig/kvm/libkvm/libkvm.h 2011-08-11 10:04:51.000000000 +0200 ++++ new/kvm/libkvm/libkvm.h 2011-08-11 10:10:13.000000000 +0200 +@@ -15,7 +15,7 @@ + #define __user /* temporary, until installed via make headers_install */ + #endif + +-#include ++#include "linux/kvm.h" + + #include + diff --git a/debian/patches/old/vencrypt-auth-plain.patch b/debian/patches/old/vencrypt-auth-plain.patch new file mode 100644 index 0000000..c9d4502 --- /dev/null +++ b/debian/patches/old/vencrypt-auth-plain.patch @@ -0,0 +1,76 @@ +Index: new/ui/vnc-auth-vencrypt.c +=================================================================== +--- new.orig/ui/vnc-auth-vencrypt.c 2014-07-16 12:01:28.000000000 +0200 ++++ new/ui/vnc-auth-vencrypt.c 2014-07-16 12:01:28.000000000 +0200 +@@ -144,6 +144,11 @@ + vnc_read_when(vs, protocol_client_auth_plain_start, 8); + break; + ++ case VNC_AUTH_VENCRYPT_PLAIN: ++ VNC_DEBUG("Start auth PLAIN\n"); ++ vnc_read_when(vs, protocol_client_auth_plain_start, 8); ++ break; ++ + case VNC_AUTH_VENCRYPT_TLSVNC: + case VNC_AUTH_VENCRYPT_X509VNC: + VNC_DEBUG("Start TLS auth VNC\n"); +@@ -228,25 +233,32 @@ + { + int auth = read_u32(data, 0); + +- if (auth != vs->subauth) { ++ if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) { + VNC_DEBUG("Rejecting auth %d\n", auth); + vnc_write_u8(vs, 0); /* Reject auth */ + vnc_flush(vs); + vnc_client_error(vs); + } else { +- VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); +- vnc_write_u8(vs, 1); /* Accept auth */ +- vnc_flush(vs); +- +- if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { +- VNC_DEBUG("Failed to setup TLS\n"); +- return 0; ++ if (auth == VNC_AUTH_VENCRYPT_PLAIN) { ++ vs->subauth = auth; ++ start_auth_vencrypt_subauth(vs); + } +- +- VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); +- if (vnc_start_vencrypt_handshake(vs) < 0) { +- VNC_DEBUG("Failed to start TLS handshake\n"); +- return 0; ++ else ++ { ++ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); ++ vnc_write_u8(vs, 1); /* Accept auth */ ++ vnc_flush(vs); ++ ++ if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { ++ VNC_DEBUG("Failed to setup TLS\n"); ++ return 0; ++ } ++ ++ VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); ++ if (vnc_start_vencrypt_handshake(vs) < 0) { ++ VNC_DEBUG("Failed to start TLS handshake\n"); ++ return 0; ++ } + } + } + return 0; +@@ -261,10 +273,11 @@ + vnc_flush(vs); + vnc_client_error(vs); + } else { +- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); ++ VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN); + vnc_write_u8(vs, 0); /* Accept version */ +- vnc_write_u8(vs, 1); /* Number of sub-auths */ ++ vnc_write_u8(vs, 2); /* Number of sub-auths */ + vnc_write_u32(vs, vs->subauth); /* The supported auth */ ++ vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */ + vnc_flush(vs); + vnc_read_when(vs, protocol_client_vencrypt_auth, 4); + } diff --git a/debian/patches/old/virtio-balloon-document-stats.patch b/debian/patches/old/virtio-balloon-document-stats.patch new file mode 100644 index 0000000..aeebf38 --- /dev/null +++ b/debian/patches/old/virtio-balloon-document-stats.patch @@ -0,0 +1,159 @@ +From lcapitulino@redhat.com Fri Dec 14 16:49:59 2012 +Received: from rt.proxmox.com (192.168.2.18) by lisa.maurer-it.com + (192.168.2.121) with Microsoft SMTP Server id 14.2.328.9; Fri, 14 Dec 2012 + 16:49:59 +0100 +Received: from proxmox.maurer-it.com (proxmox.maurer-it.com + [192.168.2.110]) by rt.proxmox.com (Postfix) with ESMTP id 25351206E234 for + ; Fri, 14 Dec 2012 16:49:59 +0100 (CET) +Received: from proxmox.maurer-it.com (localhost.localdomain [127.0.0.1]) by + proxmox.maurer-it.com (Proxmox) with ESMTP id 1AAA626A090F for + ; Fri, 14 Dec 2012 16:49:59 +0100 (CET) +Received-SPF: pass (redhat.com: Sender is authorized to use + 'lcapitulino@redhat.com' in 'mfrom' identity (mechanism + 'include:spf-1.redhat.com' matched)) receiver=proxmox.maurer-it.com; + identity=mailfrom; envelope-from="lcapitulino@redhat.com"; + helo=mx1.redhat.com; client-ip=209.132.183.28 +Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by + proxmox.maurer-it.com (Proxmox) with ESMTP id EAC8B26A0914 for + ; Fri, 14 Dec 2012 16:49:57 +0100 (CET) +Received: from int-mx09.intmail.prod.int.phx2.redhat.com + (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com + (8.14.4/8.14.4) with ESMTP id qBEFnnhC005826 (version=TLSv1/SSLv3 + cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 14 Dec 2012 10:49:49 + -0500 +Received: from localhost (ovpn-113-80.phx2.redhat.com [10.3.113.80]) by + int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id + qBEFnlph004684; Fri, 14 Dec 2012 10:49:48 -0500 +From: Luiz Capitulino +To: +CC: , , , + , +Subject: [PATCH 3/3] docs: document virtio-balloon stats +Date: Fri, 14 Dec 2012 13:49:42 -0200 +Message-ID: <1355500182-12743-4-git-send-email-lcapitulino@redhat.com> +In-Reply-To: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> +References: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> +X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 +X-Proxmox-CTCH-Refid: + str=0001.0A0C0203.50CB4AA6.0198:SCFSTAT2484459,ss=1,re=-4.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0 +x-proxmoxspam-level: Spam detection results: 0 AWL + -0.199 From: address is in the auto white-list RCVD_IN_DNSWL_HI + -5 Sender listed at http://www.dnswl.org/, high trust SPF_HELO_PASS + -0.001 SPF: HELO matches SPF record SPF_PASS -0.001 SPF: + sender matches SPF record T_RP_MATCHES_RCVD -0.01 Envelope sender + domain matches handover relay domain +Content-Type: text/plain +Return-Path: lcapitulino@redhat.com +X-MS-Exchange-Organization-AuthSource: lisa.maurer-it.com +X-MS-Exchange-Organization-AuthAs: Anonymous +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Luiz Capitulino +--- + docs/virtio-balloon-stats.txt | 91 +++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 91 insertions(+) + create mode 100644 docs/virtio-balloon-stats.txt + +diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt +new file mode 100644 +index 0000000..990e746 +--- /dev/null ++++ b/docs/virtio-balloon-stats.txt +@@ -0,0 +1,91 @@ ++virtio balloon memory statistics ++================================ ++ ++The virtio balloon driver supports guest memory statistics reporting. These ++statistics are available to QEMU users as QOM (QEMU Object Model) device ++properties via a polling mechanism. ++ ++Basically, clients first have to enable polling, then they can query the ++available statistics. ++ ++There are two control properties and six memory statistics properties. ++ ++The control properties are: ++ ++ o stats-polling-interval: polling time interval in seconds, it can be: ++ ++ > 0 enables polling in the specified interval. If polling is already ++ enabled, the polling time interval will be changed to the new value ++ ++ 0 disables polling. Previous polled statistics are still valid and ++ can be queried. ++ ++ o stats-last-update: last stats update timestamp, in seconds ++ ++The following statistics are available, all values are in bytes: ++ ++ o stat-swap-in ++ o stat-swap-out ++ o stat-major-faults ++ o stat-minor-faults ++ o stat-free-memory ++ o stat-total-memory ++ ++Also, please note the following: ++ ++ - If a statistic is queried before the timer is enabled or if the guest ++ doesn't support a particular statistic, an error will be returned ++ ++ - Previously polled statistics remain available even if the timer is ++ later disabled ++ ++ - Polling can be enabled even if the guest doesn't support memory ++ statistics or its balloon driver hasn't been loaded. Applications ++ can check this condition by checking that stats-last-update doesn't ++ change ++ ++ - The polling timer is only re-armed when the guest responds to the ++ statistics request. This means that if a (buggy) guest doesn't ++ ever respond to the request the timer will never be re-armed, ++ which has the same effect as disabling polling ++ ++Here are a few examples. The virtio-balloon device is assumed to be in the ++'/machine/peripheral-anon/device[1]' QOM path. ++ ++Enable polling with 2 seconds interval: ++ ++{ "execute": "qom-set", ++ "arguments": { "path": "/machine/peripheral-anon/device[1]", ++ "property": "stats-polling-interval", "value": 2 } } ++ ++{ "return": {} } ++ ++Change polling to 10 seconds: ++ ++{ "execute": "qom-set", ++ "arguments": { "path": "/machine/peripheral-anon/device[1]", ++ "property": "stats-polling-interval", "value": 10 } } ++ ++{ "return": {} } ++ ++Get last update timestamp and free memory stat: ++ ++{ "execute": "qom-get", ++ "arguments": { "path": "/machine/peripheral-anon/device[1]", ++ "property": "stats-last-update" } } ++ ++{ "return": 1354629634 } ++ ++{ "execute": "qom-get", ++ "arguments": { "path": "/machine/peripheral-anon/device[1]", ++ "property": "stat-free-memory" } } ++ ++{ "return": 845115392 } ++ ++Disable polling: ++ ++{ "execute": "qom-set", ++ "arguments": { "path": "/machine/peripheral-anon/device[1]", ++ "property": "stats-polling-interval", "value": 0 } } ++ ++{ "return": {} } +-- +1.8.0 + + + diff --git a/debian/patches/old/virtio-balloon-drop-old-stats-code.patch b/debian/patches/old/virtio-balloon-drop-old-stats-code.patch new file mode 100644 index 0000000..cf606fe --- /dev/null +++ b/debian/patches/old/virtio-balloon-drop-old-stats-code.patch @@ -0,0 +1,50 @@ +From: Luiz Capitulino +Subject: [Qemu-devel] [PATCH 1/3] virtio-balloon: drop old stats code + +Next commit will re-enable balloon stats with a different interface, but +this old code conflicts with it. Drop it. + +Signed-off-by: Luiz Capitulino +--- + hw/virtio-balloon.c | 22 ---------------------- + 1 file changed, 22 deletions(-) + +diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c +index dd1a650..4398025 100644 +--- a/hw/virtio-balloon.c ++++ b/hw/virtio-balloon.c +@@ -164,28 +164,6 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) + static void virtio_balloon_stat(void *opaque, BalloonInfo *info) + { + VirtIOBalloon *dev = opaque; +- +-#if 0 +- /* Disable guest-provided stats for now. For more details please check: +- * https://bugzilla.redhat.com/show_bug.cgi?id=623903 +- * +- * If you do enable it (which is probably not going to happen as we +- * need a new command for it), remember that you also need to fill the +- * appropriate members of the BalloonInfo structure so that the stats +- * are returned to the client. +- */ +- if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { +- virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); +- virtio_notify(&dev->vdev, dev->svq); +- return; +- } +-#endif +- +- /* Stats are not supported. Clear out any stale values that might +- * have been set by a more featureful guest kernel. +- */ +- reset_stats(dev); +- + info->actual = ram_size - ((uint64_t) dev->actual << + VIRTIO_BALLOON_PFN_SHIFT); + } +-- +1.8.0 + + + + diff --git a/debian/patches/old/virtio-balloon-fix-query.patch b/debian/patches/old/virtio-balloon-fix-query.patch new file mode 100644 index 0000000..97574d7 --- /dev/null +++ b/debian/patches/old/virtio-balloon-fix-query.patch @@ -0,0 +1,152 @@ +Index: new/hmp.c +=================================================================== +--- new.orig/hmp.c 2014-11-20 06:45:05.000000000 +0100 ++++ new/hmp.c 2014-11-20 07:26:23.000000000 +0100 +@@ -635,7 +635,35 @@ + return; + } + +- monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); ++ monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20); ++ monitor_printf(mon, " max_mem=%" PRId64, info->max_mem >> 20); ++ if (info->has_total_mem) { ++ monitor_printf(mon, " total_mem=%" PRId64, info->total_mem >> 20); ++ } ++ if (info->has_free_mem) { ++ monitor_printf(mon, " free_mem=%" PRId64, info->free_mem >> 20); ++ } ++ ++ if (info->has_mem_swapped_in) { ++ monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in); ++ } ++ if (info->has_mem_swapped_out) { ++ monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out); ++ } ++ if (info->has_major_page_faults) { ++ monitor_printf(mon, " major_page_faults=%" PRId64, ++ info->major_page_faults); ++ } ++ if (info->has_minor_page_faults) { ++ monitor_printf(mon, " minor_page_faults=%" PRId64, ++ info->minor_page_faults); ++ } ++ if (info->has_last_update) { ++ monitor_printf(mon, " last_update=%" PRId64, ++ info->last_update); ++ } ++ ++ monitor_printf(mon, "\n"); + + qapi_free_BalloonInfo(info); + } +Index: new/hw/virtio/virtio-balloon.c +=================================================================== +--- new.orig/hw/virtio/virtio-balloon.c 2014-11-20 06:45:06.000000000 +0100 ++++ new/hw/virtio/virtio-balloon.c 2014-11-20 07:26:23.000000000 +0100 +@@ -319,8 +319,37 @@ + static void virtio_balloon_stat(void *opaque, BalloonInfo *info) + { + VirtIOBalloon *dev = opaque; +- info->actual = get_current_ram_size() - ((uint64_t) dev->actual << +- VIRTIO_BALLOON_PFN_SHIFT); ++ ram_addr_t ram_size = get_current_ram_size(); ++ info->actual = ram_size - ((uint64_t) dev->actual << ++ VIRTIO_BALLOON_PFN_SHIFT); ++ ++ info->max_mem = ram_size; ++ ++ if (!(balloon_stats_enabled(dev) && balloon_stats_supported(dev) && ++ dev->stats_last_update)) { ++ return; ++ } ++ ++ info->last_update = dev->stats_last_update; ++ info->has_last_update = true; ++ ++ info->mem_swapped_in = dev->stats[VIRTIO_BALLOON_S_SWAP_IN]; ++ info->has_mem_swapped_in = info->mem_swapped_in >= 0 ? true : false; ++ ++ info->mem_swapped_out = dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]; ++ info->has_mem_swapped_out = info->mem_swapped_out >= 0 ? true : false; ++ ++ info->major_page_faults = dev->stats[VIRTIO_BALLOON_S_MAJFLT]; ++ info->has_major_page_faults = info->major_page_faults >= 0 ? true : false; ++ ++ info->minor_page_faults = dev->stats[VIRTIO_BALLOON_S_MINFLT]; ++ info->has_minor_page_faults = info->minor_page_faults >= 0 ? true : false; ++ ++ info->free_mem = dev->stats[VIRTIO_BALLOON_S_MEMFREE]; ++ info->has_free_mem = info->free_mem >= 0 ? true : false; ++ ++ info->total_mem = dev->stats[VIRTIO_BALLOON_S_MEMTOT]; ++ info->has_total_mem = info->total_mem >= 0 ? true : false; + } + + static void virtio_balloon_to_target(void *opaque, ram_addr_t target) +Index: new/qapi-schema.json +=================================================================== +--- new.orig/qapi-schema.json 2014-11-20 06:45:06.000000000 +0100 ++++ new/qapi-schema.json 2014-11-20 07:26:23.000000000 +0100 +@@ -983,10 +983,29 @@ + # + # @actual: the number of bytes the balloon currently contains + # +-# Since: 0.14.0 ++# @last_update: #optional time when stats got updated from guest ++# ++# @mem_swapped_in: #optional number of pages swapped in within the guest ++# ++# @mem_swapped_out: #optional number of pages swapped out within the guest ++# ++# @major_page_faults: #optional number of major page faults within the guest + # ++# @minor_page_faults: #optional number of minor page faults within the guest ++# ++# @free_mem: #optional amount of memory (in bytes) free in the guest ++# ++# @total_mem: #optional amount of memory (in bytes) visible to the guest ++# ++# @max_mem: amount of memory (in bytes) assigned to the guest ++# ++# Since: 0.14.0 + ## +-{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } } ++{ 'struct': 'BalloonInfo', ++ 'data': {'actual': 'int', '*last_update': 'int', '*mem_swapped_in': 'int', ++ '*mem_swapped_out': 'int', '*major_page_faults': 'int', ++ '*minor_page_faults': 'int', '*free_mem': 'int', ++ '*total_mem': 'int', 'max_mem': 'int' } } + + ## + # @query-balloon: +Index: new/qmp-commands.hx +=================================================================== +--- new.orig/qmp-commands.hx 2014-11-20 06:45:06.000000000 +0100 ++++ new/qmp-commands.hx 2014-11-20 07:26:23.000000000 +0100 +@@ -3329,6 +3329,13 @@ + json-object will be returned containing the following data: + + - "actual": current balloon value in bytes (json-int) ++- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional) ++- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional) ++- "major_page_faults": Number of major faults (json-int, optional) ++- "minor_page_faults": Number of minor faults (json-int, optional) ++- "free_mem": Total amount of free and unused memory in ++ bytes (json-int, optional) ++- "total_mem": Total amount of available memory in bytes (json-int, optional) + + Example: + +@@ -3336,6 +3344,12 @@ + <- { + "return":{ + "actual":1073741824, ++ "mem_swapped_in":0, ++ "mem_swapped_out":0, ++ "major_page_faults":142, ++ "minor_page_faults":239245, ++ "free_mem":1014185984, ++ "total_mem":1044668416 + } + } + diff --git a/debian/patches/old/virtio-balloon-re-enable-balloon-stats.patch b/debian/patches/old/virtio-balloon-re-enable-balloon-stats.patch new file mode 100644 index 0000000..ea3e594 --- /dev/null +++ b/debian/patches/old/virtio-balloon-re-enable-balloon-stats.patch @@ -0,0 +1,333 @@ +From lcapitulino@redhat.com Fri Dec 14 16:49:58 2012 +Received: from rt.proxmox.com (192.168.2.18) by lisa.maurer-it.com + (192.168.2.121) with Microsoft SMTP Server id 14.2.328.9; Fri, 14 Dec 2012 + 16:49:58 +0100 +Received: from proxmox.maurer-it.com (proxmox.maurer-it.com + [192.168.2.110]) by rt.proxmox.com (Postfix) with ESMTP id 9D849206E234 for + ; Fri, 14 Dec 2012 16:49:58 +0100 (CET) +Received: from proxmox.maurer-it.com (localhost.localdomain [127.0.0.1]) by + proxmox.maurer-it.com (Proxmox) with ESMTP id 6DC3526A0912 for + ; Fri, 14 Dec 2012 16:49:58 +0100 (CET) +Received-SPF: pass (redhat.com: Sender is authorized to use + 'lcapitulino@redhat.com' in 'mfrom' identity (mechanism + 'include:spf-1.redhat.com' matched)) receiver=proxmox.maurer-it.com; + identity=mailfrom; envelope-from="lcapitulino@redhat.com"; + helo=mx1.redhat.com; client-ip=209.132.183.28 +Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by + proxmox.maurer-it.com (Proxmox) with ESMTP id 7B90426A090F for + ; Fri, 14 Dec 2012 16:49:55 +0100 (CET) +Received: from int-mx12.intmail.prod.int.phx2.redhat.com + (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com + (8.14.4/8.14.4) with ESMTP id qBEFnl3Y014706 (version=TLSv1/SSLv3 + cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 14 Dec 2012 10:49:47 + -0500 +Received: from localhost (ovpn-113-80.phx2.redhat.com [10.3.113.80]) by + int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id + qBEFnkfm012128; Fri, 14 Dec 2012 10:49:46 -0500 +From: Luiz Capitulino +To: +CC: , , , + , +Subject: [PATCH 2/3] balloon: re-enable balloon stats +Date: Fri, 14 Dec 2012 13:49:41 -0200 +Message-ID: <1355500182-12743-3-git-send-email-lcapitulino@redhat.com> +In-Reply-To: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> +References: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> +X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 +X-Proxmox-CTCH-Refid: + str=0001.0A0C0203.50CB4AA5.008C:SCFSTAT2484459,ss=1,re=-4.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0 +x-proxmoxspam-level: Spam detection results: 0 AWL + -0.200 From: address is in the auto white-list RCVD_IN_DNSWL_HI + -5 Sender listed at http://www.dnswl.org/, high trust SPF_HELO_PASS + -0.001 SPF: HELO matches SPF record SPF_PASS -0.001 SPF: + sender matches SPF record T_RP_MATCHES_RCVD -0.01 Envelope sender + domain matches handover relay domain +Content-Type: text/plain +Return-Path: lcapitulino@redhat.com +X-MS-Exchange-Organization-AuthSource: lisa.maurer-it.com +X-MS-Exchange-Organization-AuthAs: Anonymous +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit + +The statistics are now available through device properties via a +polling mechanism. First a client has to enable polling, then it +can query each stat individually. + +The following control properties are introduced: + + o stats-polling-interval: a value greater than zero enables polling + in the specified interval (in seconds). When value equals zero, + polling is disabled. If polling is already enabled and a value + greater than zero is written, the polling interval time is changed + + o stats-last-update: last stats update timestamp, in seconds. + +The following stats properties are introduced, all values are in bytes: + + o stat-swap-in + o stat-swap-out + o stat-major-faults + o stat-minor-faults + o stat-free-memory + o stat-total-memory + +Please, refer to the documentation introduced by the next commit for +more information and examples. + +Signed-off-by: Luiz Capitulino +--- + hw/virtio-balloon.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 176 insertions(+), 2 deletions(-) + +diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c +index 4398025..47e35b1 100644 +--- a/hw/virtio-balloon.c ++++ b/hw/virtio-balloon.c +@@ -22,6 +22,8 @@ + #include "virtio-balloon.h" + #include "kvm.h" + #include "exec-memory.h" ++#include "qemu-timer.h" ++#include "qapi/qapi-visit-core.h" + + #if defined(__linux__) + #include +@@ -36,6 +38,9 @@ typedef struct VirtIOBalloon + uint64_t stats[VIRTIO_BALLOON_S_NR]; + VirtQueueElement stats_vq_elem; + size_t stats_vq_offset; ++ QEMUTimer *stats_timer; ++ int64_t stats_last_update; ++ int64_t stats_poll_interval; + DeviceState *qdev; + } VirtIOBalloon; + +@@ -53,6 +58,16 @@ static void balloon_page(void *addr, int deflate) + #endif + } + ++static const char *balloon_stat_names[] = { ++ [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in", ++ [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out", ++ [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults", ++ [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults", ++ [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory", ++ [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", ++ [VIRTIO_BALLOON_S_NR] = NULL ++}; ++ + /* + * reset_stats - Mark all items in the stats array as unset + * +@@ -67,6 +82,138 @@ static inline void reset_stats(VirtIOBalloon *dev) + for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); + } + ++static bool balloon_stats_supported(const VirtIOBalloon *s) ++{ ++ return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ); ++} ++ ++static bool balloon_stats_enabled(const VirtIOBalloon *s) ++{ ++ return s->stats_poll_interval > 0; ++} ++ ++static void balloon_stats_destroy_timer(VirtIOBalloon *s) ++{ ++ if (balloon_stats_enabled(s)) { ++ qemu_del_timer(s->stats_timer); ++ qemu_free_timer(s->stats_timer); ++ s->stats_timer = NULL; ++ s->stats_poll_interval = 0; ++ } ++} ++ ++static void balloon_stats_change_timer(VirtIOBalloon *s, int secs) ++{ ++ qemu_mod_timer(s->stats_timer, qemu_get_clock_ms(vm_clock) + secs * 1000); ++} ++ ++static void balloon_stats_poll_cb(void *opaque) ++{ ++ VirtIOBalloon *s = opaque; ++ ++ if (!balloon_stats_supported(s)) { ++ /* re-schedule */ ++ balloon_stats_change_timer(s, s->stats_poll_interval); ++ return; ++ } ++ ++ virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset); ++ virtio_notify(&s->vdev, s->svq); ++} ++ ++static void balloon_stats_get_last_update(Object *obj, struct Visitor *v, ++ void *opaque, const char *name, ++ Error **errp) ++{ ++ VirtIOBalloon *s = opaque; ++ visit_type_int(v, &s->stats_last_update, name, errp); ++} ++ ++static void balloon_stats_get_stat(Object *obj, struct Visitor *v, ++ void *opaque, const char *name, Error **errp) ++{ ++ VirtIOBalloon *s = opaque; ++ int i; ++ ++ for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { ++ if (!strcmp(balloon_stat_names[i], name)) { ++ break; ++ } ++ } ++ ++ if (i == VIRTIO_BALLOON_S_NR) { ++ error_setg(errp, "invalid stat name '%s'", name); ++ return; ++ } ++ ++ if (s->stats[i] == -1) { ++ /* ++ * Possible reasons for this error: ++ * ++ * - The timer hasn't been enabled ++ * - The guest hasn't loaded its balloon driver ++ * - The guest's balloon driver doesn't support memory stats ++ * - The guest's balloon driver doesn't support this stat ++ * - The guest's balloon driver didn't send this stat for ++ * whatever reason ++ */ ++ error_setg(errp, ++ "guest didn't update '%s' (does the guest support it?)", name); ++ return; ++ } ++ ++ visit_type_int(v, (int64_t *) &s->stats[i], name, errp); ++} ++ ++static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, ++ void *opaque, const char *name, ++ Error **errp) ++{ ++ VirtIOBalloon *s = opaque; ++ visit_type_int(v, &s->stats_poll_interval, name, errp); ++} ++ ++static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v, ++ void *opaque, const char *name, ++ Error **errp) ++{ ++ VirtIOBalloon *s = opaque; ++ int64_t value; ++ ++ visit_type_int(v, &value, name, errp); ++ if (error_is_set(errp)) { ++ return; ++ } ++ ++ if (value < 0) { ++ error_setg(errp, "timer value must be positive"); ++ return; ++ } ++ ++ if (value == s->stats_poll_interval) { ++ return; ++ } ++ ++ if (value == 0) { ++ /* timer=0 disables the timer */ ++ balloon_stats_destroy_timer(s); ++ return; ++ } ++ ++ if (balloon_stats_enabled(s)) { ++ /* timer interval change */ ++ s->stats_poll_interval = value; ++ balloon_stats_change_timer(s, value); ++ return; ++ } ++ ++ /* create a new timer */ ++ g_assert(s->stats_timer == NULL); ++ s->stats_timer = qemu_new_timer_ms(vm_clock, balloon_stats_poll_cb, s); ++ s->stats_poll_interval = value; ++ balloon_stats_change_timer(s, 0); ++} ++ + static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) + { + VirtIOBalloon *s = to_virtio_balloon(vdev); +@@ -107,9 +254,10 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) + VirtQueueElement *elem = &s->stats_vq_elem; + VirtIOBalloonStat stat; + size_t offset = 0; ++ qemu_timeval tv; + + if (!virtqueue_pop(vq, elem)) { +- return; ++ goto out; + } + + /* Initialize the stats to get rid of any stale values. This is only +@@ -128,6 +276,18 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) + s->stats[tag] = val; + } + s->stats_vq_offset = offset; ++ ++ if (qemu_gettimeofday(&tv) < 0) { ++ fprintf(stderr, "warning: %s: failed to get time of day\n", __func__); ++ goto out; ++ } ++ ++ s->stats_last_update = tv.tv_sec; ++ ++out: ++ if (balloon_stats_enabled(s)) { ++ balloon_stats_change_timer(s, s->stats_poll_interval); ++ } + } + + static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) +@@ -212,7 +372,7 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) + VirtIODevice *virtio_balloon_init(DeviceState *dev) + { + VirtIOBalloon *s; +- int ret; ++ int i, ret; + + s = (VirtIOBalloon *)virtio_common_init("virtio-balloon", + VIRTIO_ID_BALLOON, +@@ -239,6 +399,19 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) + register_savevm(dev, "virtio-balloon", -1, 1, + virtio_balloon_save, virtio_balloon_load, s); + ++ for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { ++ object_property_add(OBJECT(dev), balloon_stat_names[i], "int", ++ balloon_stats_get_stat, NULL, NULL, s, NULL); ++ } ++ ++ object_property_add(OBJECT(dev), "stats-last-update", "int", ++ balloon_stats_get_last_update, NULL, NULL, s, NULL); ++ ++ object_property_add(OBJECT(dev), "stats-polling-interval", "int", ++ balloon_stats_get_poll_interval, ++ balloon_stats_set_poll_interval, ++ NULL, s, NULL); ++ + return &s->vdev; + } + +@@ -246,6 +419,7 @@ void virtio_balloon_exit(VirtIODevice *vdev) + { + VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + ++ balloon_stats_destroy_timer(s); + qemu_remove_balloon_handler(s); + unregister_savevm(s->qdev, "virtio-balloon", s); + virtio_cleanup(vdev); +-- +1.8.0 + + + diff --git a/debian/patches/old/vmxnet3-host-memory-leakage.patch b/debian/patches/old/vmxnet3-host-memory-leakage.patch new file mode 100644 index 0000000..dcc76b9 --- /dev/null +++ b/debian/patches/old/vmxnet3-host-memory-leakage.patch @@ -0,0 +1,91 @@ +From 3ef66b01874fcc2fe3bfc73d2b61ee3a5b29fdb6 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Tue, 15 Dec 2015 12:17:28 +0530 +Subject: [PATCH] net: vmxnet3: avoid memory leakage in activate_device + +Vmxnet3 device emulator does not check if the device is active +before activating it, also it did not free the transmit & receive +buffers while deactivating the device, thus resulting in memory +leakage on the host. This patch fixes both these issues to avoid +host memory leakage. + +Reported-by: Qinghao Tang +Signed-off-by: Prasad J Pandit +Reviewed-by: Dmitry Fleytman +--- + hw/net/vmxnet3.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c +index 37373e5..2b4aad7 100644 +--- a/hw/net/vmxnet3.c ++++ b/hw/net/vmxnet3.c +@@ -1194,8 +1194,13 @@ static void vmxnet3_reset_mac(VMXNET3State *s) + + static void vmxnet3_deactivate_device(VMXNET3State *s) + { +- VMW_CBPRN("Deactivating vmxnet3..."); +- s->device_active = false; ++ if (s->device_active) { ++ VMW_CBPRN("Deactivating vmxnet3..."); ++ vmxnet_tx_pkt_reset(s->tx_pkt); ++ vmxnet_tx_pkt_uninit(s->tx_pkt); ++ vmxnet_rx_pkt_uninit(s->rx_pkt); ++ s->device_active = false; ++ } + } + + static void vmxnet3_reset(VMXNET3State *s) +@@ -1204,7 +1209,6 @@ static void vmxnet3_reset(VMXNET3State *s) + + vmxnet3_deactivate_device(s); + vmxnet3_reset_interrupt_states(s); +- vmxnet_tx_pkt_reset(s->tx_pkt); + s->drv_shmem = 0; + s->tx_sop = true; + s->skip_current_tx_pkt = false; +@@ -1431,6 +1435,12 @@ static void vmxnet3_activate_device(VMXNET3State *s) + return; + } + ++ /* Verify if device is active */ ++ if (s->device_active) { ++ VMW_CFPRN("Vmxnet3 device is active"); ++ return; ++ } ++ + vmxnet3_adjust_by_guest_type(s); + vmxnet3_update_features(s); + vmxnet3_update_pm_state(s); +@@ -1627,7 +1637,7 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) + break; + + case VMXNET3_CMD_QUIESCE_DEV: +- VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device"); ++ VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device"); + vmxnet3_deactivate_device(s); + break; + +@@ -1741,7 +1751,7 @@ vmxnet3_io_bar1_write(void *opaque, + * shared address only after we get the high part + */ + if (val == 0) { +- s->device_active = false; ++ vmxnet3_deactivate_device(s); + } + s->temp_shared_guest_driver_memory = val; + s->drv_shmem = 0; +@@ -2021,9 +2031,7 @@ static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) + static void vmxnet3_net_uninit(VMXNET3State *s) + { + g_free(s->mcast_list); +- vmxnet_tx_pkt_reset(s->tx_pkt); +- vmxnet_tx_pkt_uninit(s->tx_pkt); +- vmxnet_rx_pkt_uninit(s->rx_pkt); ++ vmxnet3_deactivate_device(s); + qemu_del_nic(s->nic); + } + +-- +2.4.3 + diff --git a/debian/patches/old/vncticket.diff b/debian/patches/old/vncticket.diff new file mode 100644 index 0000000..fa1474d --- /dev/null +++ b/debian/patches/old/vncticket.diff @@ -0,0 +1,149 @@ +Index: qemu-kvm/console.h +=================================================================== +--- qemu-kvm.orig/console.h 2010-10-21 13:40:20.000000000 +0200 ++++ qemu-kvm/console.h 2010-10-21 14:06:21.000000000 +0200 +@@ -368,7 +368,7 @@ + void vnc_display_init(DisplayState *ds); + void vnc_display_close(DisplayState *ds); + int vnc_display_open(DisplayState *ds, const char *display); +-int vnc_display_password(DisplayState *ds, const char *password); ++int vnc_display_password(DisplayState *ds, const char *password, int limit); + void do_info_vnc_print(Monitor *mon, const QObject *data); + void do_info_vnc(Monitor *mon, QObject **ret_data); + char *vnc_display_local_addr(DisplayState *ds); +Index: qemu-kvm/ui/vnc.c +=================================================================== +--- qemu-kvm.orig/ui/vnc.c 2010-10-21 13:40:21.000000000 +0200 ++++ qemu-kvm/ui/vnc.c 2010-10-21 14:06:21.000000000 +0200 +@@ -1790,7 +1790,7 @@ + static void set_pixel_conversion(VncState *vs) + { + if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == +- (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && ++ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && + !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { + vs->write_pixels = vnc_write_pixels_copy; + vnc_hextile_set_pixel_conversion(vs, 0); +@@ -1876,7 +1876,7 @@ + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ +- vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ++ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), + ds_get_height(vs->ds), VNC_ENCODING_WMVi); + pixel_format_message(vs); + vnc_unlock_output(vs); +@@ -2079,7 +2079,10 @@ + int i, j, pwlen; + unsigned char key[8]; + +- if (!vs->vd->password || !vs->vd->password[0]) { ++ if (vs->vd->retries >= 0) ++ vs->vd->retries++; ++ ++ if (!vs->vd->password || !vs->vd->password[0] || vs->vd->retries > 3) { + VNC_DEBUG("No password configured on server"); + vnc_write_u32(vs, 1); /* Reject auth */ + if (vs->minor >= 8) { +@@ -2478,7 +2481,7 @@ + #endif + } + +-int vnc_display_password(DisplayState *ds, const char *password) ++int vnc_display_password(DisplayState *ds, const char *password, int limit) + { + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + +@@ -2493,6 +2496,7 @@ + if (password && password[0]) { + if (!(vs->password = qemu_strdup(password))) + return -1; ++ vs->retries = limit ? 0 : -1; + if (vs->auth == VNC_AUTH_NONE) { + vs->auth = VNC_AUTH_VNC; + } +Index: qemu-kvm/ui/vnc.h +=================================================================== +--- qemu-kvm.orig/ui/vnc.h 2010-10-21 13:40:20.000000000 +0200 ++++ qemu-kvm/ui/vnc.h 2010-10-21 14:06:21.000000000 +0200 +@@ -120,6 +120,7 @@ + + char *display; + char *password; ++ int retries; + int auth; + bool lossy; + #ifdef CONFIG_VNC_TLS +Index: qemu-kvm/monitor.c +=================================================================== +--- qemu-kvm.orig/monitor.c 2010-10-21 13:40:21.000000000 +0200 ++++ qemu-kvm/monitor.c 2010-10-21 14:14:38.000000000 +0200 +@@ -978,7 +978,7 @@ + + static int change_vnc_password(const char *password) + { +- if (vnc_display_password(NULL, password) < 0) { ++ if (vnc_display_password(NULL, password, 0) < 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; + } +@@ -986,6 +986,17 @@ + return 0; + } + ++static int change_vnc_ticket(const char *password) ++{ ++ if (vnc_display_password(NULL, password, 1) < 0) { ++ qerror_report(QERR_SET_PASSWD_FAILED); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ + static void change_vnc_password_cb(Monitor *mon, const char *password, + void *opaque) + { +@@ -996,12 +1007,16 @@ + static int do_change_vnc(Monitor *mon, const char *target, const char *arg) + { + if (strcmp(target, "passwd") == 0 || +- strcmp(target, "password") == 0) { ++ strcmp(target, "password") == 0 || ++ strcmp(target, "ticket") == 0) { + if (arg) { + char password[9]; + strncpy(password, arg, sizeof(password)); + password[sizeof(password) - 1] = '\0'; +- return change_vnc_password(password); ++ if (strcmp(target, "ticket") == 0) ++ return change_vnc_ticket(password); ++ else ++ return change_vnc_password(password); + } else { + return monitor_read_password(mon, change_vnc_password_cb, NULL); + } +@@ -3324,11 +3339,11 @@ + static int is_valid_option(const char *c, const char *typestr) + { + char option[3]; +- ++ + option[0] = '-'; + option[1] = *c; + option[2] = '\0'; +- ++ + typestr = strstr(typestr, option); + return (typestr != NULL); + } +@@ -3640,7 +3655,7 @@ + p++; + if(c != *p) { + if(!is_valid_option(p, typestr)) { +- ++ + monitor_printf(mon, "%s: unsupported option -%c\n", + cmdname, *p); + goto fail; diff --git a/debian/patches/ps2-queue-size.diff b/debian/patches/ps2-queue-size.diff deleted file mode 100644 index 58564bc..0000000 --- a/debian/patches/ps2-queue-size.diff +++ /dev/null @@ -1,20 +0,0 @@ - -bug description: boot a guest, open VNC console, and when it's still in BIOS / bootloader sequence, type as many keys as you can, move your mouse as much as you can. If needed, just reboot the guest. - -Linux guest will boot with "i8042.c: No controller found" and no keyboard. - -Further discussion seem to lead to a conclusion that there is some obscure bug in Qemu BIOS which makes this problem visible. - -Index: kvm-86/hw/ps2.c -=================================================================== ---- kvm-86.orig/hw/ps2.c 2009-06-15 13:31:24.000000000 +0200 -+++ kvm-86/hw/ps2.c 2009-06-15 13:31:50.000000000 +0200 -@@ -70,7 +70,7 @@ - #define MOUSE_STATUS_ENABLED 0x20 - #define MOUSE_STATUS_SCALE21 0x10 - --#define PS2_QUEUE_SIZE 256 -+#define PS2_QUEUE_SIZE 15 - - typedef struct { - uint8_t data[PS2_QUEUE_SIZE]; diff --git a/debian/patches/pve-auth.patch b/debian/patches/pve-auth.patch deleted file mode 100644 index 9facc1a..0000000 --- a/debian/patches/pve-auth.patch +++ /dev/null @@ -1,426 +0,0 @@ -Index: new/qemu-options.hx -=================================================================== ---- new.orig/qemu-options.hx 2014-11-20 06:45:06.000000000 +0100 -+++ new/qemu-options.hx 2014-11-20 06:50:44.000000000 +0100 -@@ -411,6 +411,9 @@ - @table @option - ETEXI - -+DEF("id", HAS_ARG, QEMU_OPTION_id, -+ "-id n set the VMID\n", QEMU_ARCH_ALL) -+ - DEF("fda", HAS_ARG, QEMU_OPTION_fda, - "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) - DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) -Index: new/vl.c -=================================================================== ---- new.orig/vl.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/vl.c 2014-11-20 06:50:44.000000000 +0100 -@@ -2730,6 +2730,7 @@ - int main(int argc, char **argv, char **envp) - { - int i; -+ long int vm_id_long = 0; - int snapshot, linux_boot; - const char *initrd_filename; - const char *kernel_filename, *kernel_cmdline; -@@ -3477,6 +3478,14 @@ - exit(1); - } - break; -+ case QEMU_OPTION_id: -+ vm_id_long = strtol(optarg, (char **) &optarg, 10); -+ if (*optarg != 0 || vm_id_long < 100 || vm_id_long > INT_MAX) { -+ fprintf(stderr, "Invalid ID\n"); -+ exit(1); -+ } -+ pve_auth_setup(vm_id_long); -+ break; - case QEMU_OPTION_vnc: - { - #ifdef CONFIG_VNC -Index: new/include/ui/console.h -=================================================================== ---- new.orig/include/ui/console.h 2014-11-20 06:45:06.000000000 +0100 -+++ new/include/ui/console.h 2014-11-20 06:50:55.000000000 +0100 -@@ -328,6 +328,7 @@ - void cocoa_display_init(DisplayState *ds, int full_screen); - - /* vnc.c */ -+void pve_auth_setup(int vmid); - void vnc_display_init(const char *id); - void vnc_display_open(const char *id, Error **errp); - void vnc_display_add_client(const char *id, int csock, bool skipauth); -Index: new/ui/vnc.c -=================================================================== ---- new.orig/ui/vnc.c 2014-11-20 06:50:51.000000000 +0100 -+++ new/ui/vnc.c 2014-11-20 06:50:55.000000000 +0100 -@@ -48,6 +48,125 @@ - #include "vnc_keysym.h" - #include "crypto/cipher.h" - -+static int pve_vmid = 0; -+ -+void pve_auth_setup(int vmid) { -+ pve_vmid = vmid; -+} -+ -+static char * -+urlencode(char *buf, const char *value) -+{ -+ static const char *hexchar = "0123456789abcdef"; -+ char *p = buf; -+ int i; -+ int l = strlen(value); -+ for (i = 0; i < l; i++) { -+ char c = value[i]; -+ if (('a' <= c && c <= 'z') || -+ ('A' <= c && c <= 'Z') || -+ ('0' <= c && c <= '9')) { -+ *p++ = c; -+ } else if (c == 32) { -+ *p++ = '+'; -+ } else { -+ *p++ = '%'; -+ *p++ = hexchar[c >> 4]; -+ *p++ = hexchar[c & 15]; -+ } -+ } -+ *p = 0; -+ -+ return p; -+} -+ -+int -+pve_auth_verify(const char *clientip, const char *username, const char *passwd) -+{ -+ struct sockaddr_in server; -+ -+ int sfd = socket(AF_INET, SOCK_STREAM, 0); -+ if (sfd == -1) { -+ perror("pve_auth_verify: socket failed"); -+ return -1; -+ } -+ -+ struct hostent *he; -+ if ((he = gethostbyname("localhost")) == NULL) { -+ fprintf(stderr, "pve_auth_verify: error resolving hostname\n"); -+ goto err; -+ } -+ -+ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); -+ server.sin_family = AF_INET; -+ server.sin_port = htons(85); -+ -+ if (connect(sfd, (struct sockaddr *)&server, sizeof(server))) { -+ perror("pve_auth_verify: error connecting to server"); -+ goto err; -+ } -+ -+ char buf[8192]; -+ char form[8192]; -+ -+ char *p = form; -+ p = urlencode(p, "username"); -+ *p++ = '='; -+ p = urlencode(p, username); -+ -+ *p++ = '&'; -+ p = urlencode(p, "password"); -+ *p++ = '='; -+ p = urlencode(p, passwd); -+ -+ *p++ = '&'; -+ p = urlencode(p, "path"); -+ *p++ = '='; -+ char authpath[256]; -+ sprintf(authpath, "/vms/%d", pve_vmid); -+ p = urlencode(p, authpath); -+ -+ *p++ = '&'; -+ p = urlencode(p, "privs"); -+ *p++ = '='; -+ p = urlencode(p, "VM.Console"); -+ -+ sprintf(buf, "POST /api2/json/access/ticket HTTP/1.1\n" -+ "Host: localhost:85\n" -+ "Connection: close\n" -+ "PVEClientIP: %s\n" -+ "Content-Type: application/x-www-form-urlencoded\n" -+ "Content-Length: %zd\n\n%s\n", clientip, strlen(form), form); -+ ssize_t len = strlen(buf); -+ ssize_t sb = send(sfd, buf, len, 0); -+ if (sb < 0) { -+ perror("pve_auth_verify: send failed"); -+ goto err; -+ } -+ if (sb != len) { -+ fprintf(stderr, "pve_auth_verify: partial send error\n"); -+ goto err; -+ } -+ -+ len = recv(sfd, buf, sizeof(buf) - 1, 0); -+ if (len < 0) { -+ perror("pve_auth_verify: recv failed"); -+ goto err; -+ } -+ -+ buf[len] = 0; -+ -+ //printf("DATA:%s\n", buf); -+ -+ shutdown(sfd, SHUT_RDWR); -+ -+ return strncmp(buf, "HTTP/1.1 200 OK", 15); -+ -+err: -+ shutdown(sfd, SHUT_RDWR); -+ return -1; -+} -+ - static QTAILQ_HEAD(, VncDisplay) vnc_displays = - QTAILQ_HEAD_INITIALIZER(vnc_displays); - -@@ -3393,10 +3512,10 @@ - } - if (x509) { - VNC_DEBUG("Initializing VNC server with x509 password auth\n"); -- vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; -+ vs->subauth = VNC_AUTH_VENCRYPT_X509PLAIN; - } else { - VNC_DEBUG("Initializing VNC server with TLS password auth\n"); -- vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; -+ vs->subauth = VNC_AUTH_VENCRYPT_TLSPLAIN; - } - } else { - VNC_DEBUG("Initializing VNC server with password auth\n"); -@@ -3557,7 +3676,14 @@ - vs->tls.x509verify = true; - } - } -- if (path) { -+ if (path && strcmp(path, "on") == 0) { -+ x509 = true; -+ tls = true; -+ if (pve_tls_set_x509_creds_dir(vs) < 0) { -+ error_setg(errp, "No certificate path provided"); -+ goto fail; -+ } -+ } else if (path) { - x509 = true; - if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { - error_setg(errp, "Failed to find x509 certificates/keys in %s", -Index: new/ui/vnc-auth-vencrypt.c -=================================================================== ---- new.orig/ui/vnc-auth-vencrypt.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/ui/vnc-auth-vencrypt.c 2014-11-20 06:50:55.000000000 +0100 -@@ -26,6 +26,107 @@ - - #include "vnc.h" - #include "qemu/main-loop.h" -+#include "qemu/sockets.h" -+ -+static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len) -+{ -+ const char *err = NULL; -+ char username[256]; -+ char passwd[512]; -+ -+ char clientip[256]; -+ clientip[0] = 0; -+ struct sockaddr_in client; -+ socklen_t addrlen = sizeof(client); -+ if (getpeername(vs->csock, &client, &addrlen) == 0) { -+ inet_ntop(client.sin_family, &client.sin_addr, -+ clientip, sizeof(clientip)); -+ } -+ -+ if ((len != (vs->username_len + vs->password_len)) || -+ (vs->username_len >= (sizeof(username)-1)) || -+ (vs->password_len >= (sizeof(passwd)-1)) ) { -+ err = "Got unexpected data length"; -+ goto err; -+ } -+ -+ strncpy(username, (char *)data, vs->username_len); -+ username[vs->username_len] = 0; -+ strncpy(passwd, (char *)data + vs->username_len, vs->password_len); -+ passwd[vs->password_len] = 0; -+ -+ VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd); -+ -+ if (pve_auth_verify(clientip, username, passwd) == 0) { -+ vnc_write_u32(vs, 0); /* Accept auth completion */ -+ start_client_init(vs); -+ return 0; -+ } -+ -+ err = "Authentication failed"; -+err: -+ if (err) { -+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err); -+ vnc_write_u32(vs, 1); /* Reject auth */ -+ if (vs->minor >= 8) { -+ int elen = strlen(err); -+ vnc_write_u32(vs, elen); -+ vnc_write(vs, err, elen); -+ } -+ } -+ vnc_flush(vs); -+ vnc_client_error(vs); -+ -+ return 0; -+ -+} -+ -+static int protocol_client_auth_plain_start(VncState *vs, uint8_t *data, size_t len) -+{ -+ uint32_t ulen = read_u32(data, 0); -+ uint32_t pwlen = read_u32(data, 4); -+ const char *err = NULL; -+ -+ VNC_DEBUG("AUTH PLAIN START %u %u\n", ulen, pwlen); -+ -+ if (!ulen) { -+ err = "No User name."; -+ goto err; -+ } -+ if (ulen >= 255) { -+ err = "User name too long."; -+ goto err; -+ } -+ if (!pwlen) { -+ err = "Password too short"; -+ goto err; -+ } -+ if (pwlen >= 511) { -+ err = "Password too long."; -+ goto err; -+ } -+ -+ vs->username_len = ulen; -+ vs->password_len = pwlen; -+ -+ vnc_read_when(vs, protocol_client_auth_plain, ulen + pwlen); -+ -+ return 0; -+err: -+ if (err) { -+ VNC_DEBUG("AUTH PLAIN ERROR: %s\n", err); -+ vnc_write_u32(vs, 1); /* Reject auth */ -+ if (vs->minor >= 8) { -+ int elen = strlen(err); -+ vnc_write_u32(vs, elen); -+ vnc_write(vs, err, elen); -+ } -+ } -+ vnc_flush(vs); -+ vnc_client_error(vs); -+ -+ return 0; -+} - - static void start_auth_vencrypt_subauth(VncState *vs) - { -@@ -37,6 +138,12 @@ - start_client_init(vs); - break; - -+ case VNC_AUTH_VENCRYPT_TLSPLAIN: -+ case VNC_AUTH_VENCRYPT_X509PLAIN: -+ VNC_DEBUG("Start TLS auth PLAIN\n"); -+ vnc_read_when(vs, protocol_client_auth_plain_start, 8); -+ break; -+ - case VNC_AUTH_VENCRYPT_TLSVNC: - case VNC_AUTH_VENCRYPT_X509VNC: - VNC_DEBUG("Start TLS auth VNC\n"); -Index: new/ui/vnc.h -=================================================================== ---- new.orig/ui/vnc.h 2014-11-20 06:45:06.000000000 +0100 -+++ new/ui/vnc.h 2014-11-20 06:50:55.000000000 +0100 -@@ -288,6 +288,8 @@ - int auth; - int subauth; /* Used by VeNCrypt */ - char challenge[VNC_AUTH_CHALLENGE_SIZE]; -+ int username_len; -+ int password_len; - #ifdef CONFIG_VNC_TLS - VncStateTLS tls; - #endif -@@ -603,4 +605,6 @@ - int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); - void vnc_zrle_clear(VncState *vs); - -+int pve_auth_verify(const char *clientip, const char *username, const char *passwd); -+ - #endif /* __QEMU_VNC_H */ -Index: new/ui/vnc-tls.c -=================================================================== ---- new.orig/ui/vnc-tls.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/ui/vnc-tls.c 2014-11-20 06:50:55.000000000 +0100 -@@ -302,6 +302,14 @@ - - static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) - { -+ /* optimize for speed */ -+ static const int ciphers[] = { -+ GNUTLS_CIPHER_ARCFOUR_128, -+ GNUTLS_CIPHER_AES_128_CBC, -+ GNUTLS_CIPHER_3DES_CBC, -+ 0 -+ }; -+ - static const int cert_types[] = { GNUTLS_CRT_X509, 0 }; - static const int protocols[] = { - GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 -@@ -313,6 +321,11 @@ - }; - int rc; - -+ rc = gnutls_cipher_set_priority(s, ciphers); -+ if (rc != GNUTLS_E_SUCCESS) { -+ return -1; -+ } -+ - rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon); - if (rc != GNUTLS_E_SUCCESS) { - return -1; -@@ -448,6 +460,24 @@ - return 0; - } - -+int pve_tls_set_x509_creds_dir(VncDisplay *vd) -+{ -+ if (vnc_set_x509_credential(vd, "/etc/pve", "pve-root-ca.pem", &vd->tls.x509cacert, 0) < 0) -+ goto cleanup; -+ if (vnc_set_x509_credential(vd, "/etc/pve/local", "pve-ssl.pem", &vd->tls.x509cert, 0) < 0) -+ goto cleanup; -+ if (vnc_set_x509_credential(vd, "/etc/pve/local", "pve-ssl.key", &vd->tls.x509key, 0) < 0) -+ goto cleanup; -+ -+ return 0; -+ -+ cleanup: -+ g_free(vd->tls.x509cacert); -+ g_free(vd->tls.x509cert); -+ g_free(vd->tls.x509key); -+ vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; -+ return -1; -+} - - int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *certdir) -Index: new/ui/vnc-tls.h -=================================================================== ---- new.orig/ui/vnc-tls.h 2014-11-20 06:45:06.000000000 +0100 -+++ new/ui/vnc-tls.h 2014-11-20 06:50:55.000000000 +0100 -@@ -61,6 +61,8 @@ - - int vnc_tls_validate_certificate(VncState *vs); - -+int pve_tls_set_x509_creds_dir(VncDisplay *vd); -+ - int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *path); - diff --git a/debian/patches/series b/debian/patches/series deleted file mode 100644 index c02c96c..0000000 --- a/debian/patches/series +++ /dev/null @@ -1,51 +0,0 @@ -fr-ca-keymap-corrections.diff -adjust-path.diff -keymap.diff -pve-auth.patch -vencrypt-auth-plain.patch -fix-qemu-img-snapshot-removal.patch -enable-kvm-by-default.patch -virtio-balloon-fix-query.patch -set-cpu-model-to-kvm64.patch -modify-query-machines.patch -#spice-socket.patch -modify-query-spice.patch -spice-use-pve-certs.patch -backup-add-vma-binary.patch -backup-add-vma-verify-command.patch -backup-vma-add-dump-config.patch -backup-vma-restore-tolerate-a-size-difference-up-to-4M.patch -backup-modify-job-api.patch -backup-add-pve-monitor-commands.patch -backup-add-dir-format.patch -backup-do-not-return-errors-in-dump-callback.patch -backup-vma-correctly-propagate-error.patch -backup-vma-remove-async-queue.patch -backup-run-flush-inside-coroutine.patch -backup-do-not-use-bdrv-drain-all.patch -internal-snapshot-async.patch -#disable-efi-enable-pxe-roms.patch -backup-vma-allow-empty-backups.patch -backup-vma-extract-add-block-driver-type.patch -glusterfs-daemonize.patch -gluster-backupserver.patch -add-qmp-get-link-status.patch -jemalloc.patch -0001-smm_available-false.patch -mirror-fix-zero-init.patch -fix-emulator-version-string.patch -add_firewall_to_vma.patch -0001-net-pcnet-add-check-to-validate-receive-data-size-CV.patch -0002-pcnet-fix-rx-buffer-overflow-CVE-2015-7512.patch -0001-ui-vnc-avoid-floating-point-exception.patch -CVE-2015-7549-msix-pba-write-ro.patch -CVE-2015-8558-ehci_make_idt_processing_more_robust.patch -vmxnet3-host-memory-leakage.patch -CVE-2015-8613-scsi-initialize-info-object.patch -CVE-2015-8666-acpi-fix-buffer-overrun-on-migration.patch -CVE-2015-8701-net-rocker-off-by-one.patch -CVE-2015-8743-ne2000-ioport-bounds-check.patch -CVE-2015-8744-vmxnet3-refine-l2-header-validation.patch -CVE-2015-8745-vmxnet3-support-reading-imr-registers.patch -CVE-2015-8619-hmp-sendkey-oob-fix.patch -CVE-2016-1568-ide-ahci-reset-ncq-object-to-unused-on-error.patch diff --git a/debian/patches/set-cpu-model-to-kvm64.patch b/debian/patches/set-cpu-model-to-kvm64.patch deleted file mode 100644 index d306668..0000000 --- a/debian/patches/set-cpu-model-to-kvm64.patch +++ /dev/null @@ -1,16 +0,0 @@ -Index: new/hw/i386/pc.c -=================================================================== ---- new.orig/hw/i386/pc.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/i386/pc.c 2014-11-20 07:26:33.000000000 +0100 -@@ -1020,9 +1020,9 @@ - /* init CPUs */ - if (cpu_model == NULL) { - #ifdef TARGET_X86_64 -- cpu_model = "qemu64"; -+ cpu_model = "kvm64"; - #else -- cpu_model = "qemu32"; -+ cpu_model = "kvm32"; - #endif - } - current_cpu_model = cpu_model; diff --git a/debian/patches/spice-socket.patch b/debian/patches/spice-socket.patch deleted file mode 100644 index 00175c5..0000000 --- a/debian/patches/spice-socket.patch +++ /dev/null @@ -1,107 +0,0 @@ -From ea2bd3a49ab04110cde4e71d9afafcf5d93db909 Mon Sep 17 00:00:00 2001 -From: Alexandre Derumier -Date: Mon, 8 Apr 2013 12:42:48 +0200 -Subject: [PATCH 1/2] Add spice support for unix socket option - - -Signed-off-by: Alexandre Derumier ---- - qemu-options.hx | 5 ++++- - ui/spice-core.c | 22 +++++++++++++++++++--- - 2 files changed, 23 insertions(+), 4 deletions(-) - -diff --git a/qemu-options.hx b/qemu-options.hx -index 06b6e58..37d271a 100644 ---- a/qemu-options.hx -+++ b/qemu-options.hx -@@ -887,7 +887,7 @@ Enable SDL. - ETEXI - - DEF("spice", HAS_ARG, QEMU_OPTION_spice, -- "-spice [port=port][,tls-port=secured-port][,x509-dir=]\n" -+ "-spice [port=port][,tls-port=secured-port][,unix=][,x509-dir=]\n" - " [,x509-key-file=][,x509-key-password=]\n" - " [,x509-cert-file=][,x509-cacert-file=]\n" - " [,x509-dh-key-file=][,addr=addr][,ipv4|ipv6]\n" -@@ -911,6 +911,9 @@ Enable the spice remote desktop protocol. Valid options are - - @table @option - -+@item unix= -+Path on which to bind a UNIX socket. -+ - @item port= - Set the TCP port spice is listening on for plaintext channels. - -diff --git a/ui/spice-core.c b/ui/spice-core.c -index bcc4199..acc1626 100644 ---- a/ui/spice-core.c -+++ b/ui/spice-core.c -@@ -39,6 +39,8 @@ - #include "hw/hw.h" - #include "ui/spice-display.h" - -+static const int on=1, off=0; -+ - /* core bits */ - - static SpiceServer *spice_server; -@@ -428,6 +430,9 @@ static QemuOptsList qemu_spice_opts = { - .name = "tls-port", - .type = QEMU_OPT_NUMBER, - },{ -+ .name = "unix", -+ .type = QEMU_OPT_STRING, -+ },{ - .name = "addr", - .type = QEMU_OPT_STRING, - },{ -@@ -640,16 +645,18 @@ void qemu_spice_init(void) - spice_image_compression_t compression; - spice_wan_compression_t wan_compr; - bool seamless_migration; -+ const char *unix_socket; - - qemu_thread_get_self(&me); - - if (!opts) { - return; - } -+ unix_socket = qemu_opt_get(opts, "unix"); - port = qemu_opt_get_number(opts, "port", 0); - tls_port = qemu_opt_get_number(opts, "tls-port", 0); -- if (!port && !tls_port) { -- error_report("neither port nor tls-port specified for spice"); -+ if (!port && !tls_port && !unix_socket) { -+ error_report("neither sock, port nor tls-port specified for spice"); - exit(1); - } - if (port < 0 || port > 65535) { -@@ -705,7 +712,6 @@ void qemu_spice_init(void) - } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { - addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; - } -- - spice_server = spice_server_new(); - spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); - if (port) { -@@ -720,6 +726,16 @@ void qemu_spice_init(void) - x509_dh_file, - tls_ciphers); - } -+ if (unix_socket) { -+ char *dpy; -+ int lsock; -+ dpy = g_malloc(256); -+ pstrcpy(dpy, 256, unix_socket); -+ Error *local_err = NULL; -+ lsock = unix_listen(unix_socket, dpy, 256, &local_err); -+ setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); -+ spice_server_set_listen_socket_fd(spice_server, lsock); -+ } - if (password) { - spice_server_set_ticket(spice_server, password, 0, 0, 0); - } --- -1.7.10.4 - diff --git a/debian/patches/spice-use-pve-certs.patch b/debian/patches/spice-use-pve-certs.patch deleted file mode 100644 index 1f7f7a4..0000000 --- a/debian/patches/spice-use-pve-certs.patch +++ /dev/null @@ -1,46 +0,0 @@ -Index: new/ui/spice-core.c -=================================================================== ---- new.orig/ui/spice-core.c 2014-11-20 07:26:43.000000000 +0100 -+++ new/ui/spice-core.c 2014-11-20 07:33:22.000000000 +0100 -@@ -682,32 +682,35 @@ - - if (tls_port) { - x509_dir = qemu_opt_get(opts, "x509-dir"); -- if (!x509_dir) { -- x509_dir = "."; -- } - - str = qemu_opt_get(opts, "x509-key-file"); - if (str) { - x509_key_file = g_strdup(str); -- } else { -+ } else if (x509_dir) { - x509_key_file = g_strdup_printf("%s/%s", x509_dir, - X509_SERVER_KEY_FILE); -+ } else { -+ x509_key_file = g_strdup("/etc/pve/local/pve-ssl.key"); - } - - str = qemu_opt_get(opts, "x509-cert-file"); - if (str) { - x509_cert_file = g_strdup(str); -- } else { -+ } else if (x509_dir) { - x509_cert_file = g_strdup_printf("%s/%s", x509_dir, - X509_SERVER_CERT_FILE); -+ } else { -+ x509_cert_file = g_strdup("/etc/pve/local/pve-ssl.pem"); - } - - str = qemu_opt_get(opts, "x509-cacert-file"); - if (str) { - x509_cacert_file = g_strdup(str); -- } else { -+ } else if (x509_dir) { - x509_cacert_file = g_strdup_printf("%s/%s", x509_dir, - X509_CA_CERT_FILE); -+ } else { -+ x509_cacert_file = g_strdup("/etc/pve/pve-root-ca.pem"); - } - - x509_key_password = qemu_opt_get(opts, "x509-key-password"); diff --git a/debian/patches/use-local-linux-kvm-h.diff b/debian/patches/use-local-linux-kvm-h.diff deleted file mode 100644 index 6990382..0000000 --- a/debian/patches/use-local-linux-kvm-h.diff +++ /dev/null @@ -1,16 +0,0 @@ - -Alway use our own version. - -Index: new/kvm/libkvm/libkvm.h -=================================================================== ---- new.orig/kvm/libkvm/libkvm.h 2011-08-11 10:04:51.000000000 +0200 -+++ new/kvm/libkvm/libkvm.h 2011-08-11 10:10:13.000000000 +0200 -@@ -15,7 +15,7 @@ - #define __user /* temporary, until installed via make headers_install */ - #endif - --#include -+#include "linux/kvm.h" - - #include - diff --git a/debian/patches/vencrypt-auth-plain.patch b/debian/patches/vencrypt-auth-plain.patch deleted file mode 100644 index c9d4502..0000000 --- a/debian/patches/vencrypt-auth-plain.patch +++ /dev/null @@ -1,76 +0,0 @@ -Index: new/ui/vnc-auth-vencrypt.c -=================================================================== ---- new.orig/ui/vnc-auth-vencrypt.c 2014-07-16 12:01:28.000000000 +0200 -+++ new/ui/vnc-auth-vencrypt.c 2014-07-16 12:01:28.000000000 +0200 -@@ -144,6 +144,11 @@ - vnc_read_when(vs, protocol_client_auth_plain_start, 8); - break; - -+ case VNC_AUTH_VENCRYPT_PLAIN: -+ VNC_DEBUG("Start auth PLAIN\n"); -+ vnc_read_when(vs, protocol_client_auth_plain_start, 8); -+ break; -+ - case VNC_AUTH_VENCRYPT_TLSVNC: - case VNC_AUTH_VENCRYPT_X509VNC: - VNC_DEBUG("Start TLS auth VNC\n"); -@@ -228,25 +233,32 @@ - { - int auth = read_u32(data, 0); - -- if (auth != vs->subauth) { -+ if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) { - VNC_DEBUG("Rejecting auth %d\n", auth); - vnc_write_u8(vs, 0); /* Reject auth */ - vnc_flush(vs); - vnc_client_error(vs); - } else { -- VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); -- vnc_write_u8(vs, 1); /* Accept auth */ -- vnc_flush(vs); -- -- if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { -- VNC_DEBUG("Failed to setup TLS\n"); -- return 0; -+ if (auth == VNC_AUTH_VENCRYPT_PLAIN) { -+ vs->subauth = auth; -+ start_auth_vencrypt_subauth(vs); - } -- -- VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); -- if (vnc_start_vencrypt_handshake(vs) < 0) { -- VNC_DEBUG("Failed to start TLS handshake\n"); -- return 0; -+ else -+ { -+ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); -+ vnc_write_u8(vs, 1); /* Accept auth */ -+ vnc_flush(vs); -+ -+ if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { -+ VNC_DEBUG("Failed to setup TLS\n"); -+ return 0; -+ } -+ -+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); -+ if (vnc_start_vencrypt_handshake(vs) < 0) { -+ VNC_DEBUG("Failed to start TLS handshake\n"); -+ return 0; -+ } - } - } - return 0; -@@ -261,10 +273,11 @@ - vnc_flush(vs); - vnc_client_error(vs); - } else { -- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); -+ VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN); - vnc_write_u8(vs, 0); /* Accept version */ -- vnc_write_u8(vs, 1); /* Number of sub-auths */ -+ vnc_write_u8(vs, 2); /* Number of sub-auths */ - vnc_write_u32(vs, vs->subauth); /* The supported auth */ -+ vnc_write_u32(vs, VNC_AUTH_VENCRYPT_PLAIN); /* Alternative supported auth */ - vnc_flush(vs); - vnc_read_when(vs, protocol_client_vencrypt_auth, 4); - } diff --git a/debian/patches/virtio-balloon-document-stats.patch b/debian/patches/virtio-balloon-document-stats.patch deleted file mode 100644 index aeebf38..0000000 --- a/debian/patches/virtio-balloon-document-stats.patch +++ /dev/null @@ -1,159 +0,0 @@ -From lcapitulino@redhat.com Fri Dec 14 16:49:59 2012 -Received: from rt.proxmox.com (192.168.2.18) by lisa.maurer-it.com - (192.168.2.121) with Microsoft SMTP Server id 14.2.328.9; Fri, 14 Dec 2012 - 16:49:59 +0100 -Received: from proxmox.maurer-it.com (proxmox.maurer-it.com - [192.168.2.110]) by rt.proxmox.com (Postfix) with ESMTP id 25351206E234 for - ; Fri, 14 Dec 2012 16:49:59 +0100 (CET) -Received: from proxmox.maurer-it.com (localhost.localdomain [127.0.0.1]) by - proxmox.maurer-it.com (Proxmox) with ESMTP id 1AAA626A090F for - ; Fri, 14 Dec 2012 16:49:59 +0100 (CET) -Received-SPF: pass (redhat.com: Sender is authorized to use - 'lcapitulino@redhat.com' in 'mfrom' identity (mechanism - 'include:spf-1.redhat.com' matched)) receiver=proxmox.maurer-it.com; - identity=mailfrom; envelope-from="lcapitulino@redhat.com"; - helo=mx1.redhat.com; client-ip=209.132.183.28 -Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by - proxmox.maurer-it.com (Proxmox) with ESMTP id EAC8B26A0914 for - ; Fri, 14 Dec 2012 16:49:57 +0100 (CET) -Received: from int-mx09.intmail.prod.int.phx2.redhat.com - (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com - (8.14.4/8.14.4) with ESMTP id qBEFnnhC005826 (version=TLSv1/SSLv3 - cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 14 Dec 2012 10:49:49 - -0500 -Received: from localhost (ovpn-113-80.phx2.redhat.com [10.3.113.80]) by - int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id - qBEFnlph004684; Fri, 14 Dec 2012 10:49:48 -0500 -From: Luiz Capitulino -To: -CC: , , , - , -Subject: [PATCH 3/3] docs: document virtio-balloon stats -Date: Fri, 14 Dec 2012 13:49:42 -0200 -Message-ID: <1355500182-12743-4-git-send-email-lcapitulino@redhat.com> -In-Reply-To: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> -References: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> -X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 -X-Proxmox-CTCH-Refid: - str=0001.0A0C0203.50CB4AA6.0198:SCFSTAT2484459,ss=1,re=-4.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0 -x-proxmoxspam-level: Spam detection results: 0 AWL - -0.199 From: address is in the auto white-list RCVD_IN_DNSWL_HI - -5 Sender listed at http://www.dnswl.org/, high trust SPF_HELO_PASS - -0.001 SPF: HELO matches SPF record SPF_PASS -0.001 SPF: - sender matches SPF record T_RP_MATCHES_RCVD -0.01 Envelope sender - domain matches handover relay domain -Content-Type: text/plain -Return-Path: lcapitulino@redhat.com -X-MS-Exchange-Organization-AuthSource: lisa.maurer-it.com -X-MS-Exchange-Organization-AuthAs: Anonymous -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Luiz Capitulino ---- - docs/virtio-balloon-stats.txt | 91 +++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 91 insertions(+) - create mode 100644 docs/virtio-balloon-stats.txt - -diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt -new file mode 100644 -index 0000000..990e746 ---- /dev/null -+++ b/docs/virtio-balloon-stats.txt -@@ -0,0 +1,91 @@ -+virtio balloon memory statistics -+================================ -+ -+The virtio balloon driver supports guest memory statistics reporting. These -+statistics are available to QEMU users as QOM (QEMU Object Model) device -+properties via a polling mechanism. -+ -+Basically, clients first have to enable polling, then they can query the -+available statistics. -+ -+There are two control properties and six memory statistics properties. -+ -+The control properties are: -+ -+ o stats-polling-interval: polling time interval in seconds, it can be: -+ -+ > 0 enables polling in the specified interval. If polling is already -+ enabled, the polling time interval will be changed to the new value -+ -+ 0 disables polling. Previous polled statistics are still valid and -+ can be queried. -+ -+ o stats-last-update: last stats update timestamp, in seconds -+ -+The following statistics are available, all values are in bytes: -+ -+ o stat-swap-in -+ o stat-swap-out -+ o stat-major-faults -+ o stat-minor-faults -+ o stat-free-memory -+ o stat-total-memory -+ -+Also, please note the following: -+ -+ - If a statistic is queried before the timer is enabled or if the guest -+ doesn't support a particular statistic, an error will be returned -+ -+ - Previously polled statistics remain available even if the timer is -+ later disabled -+ -+ - Polling can be enabled even if the guest doesn't support memory -+ statistics or its balloon driver hasn't been loaded. Applications -+ can check this condition by checking that stats-last-update doesn't -+ change -+ -+ - The polling timer is only re-armed when the guest responds to the -+ statistics request. This means that if a (buggy) guest doesn't -+ ever respond to the request the timer will never be re-armed, -+ which has the same effect as disabling polling -+ -+Here are a few examples. The virtio-balloon device is assumed to be in the -+'/machine/peripheral-anon/device[1]' QOM path. -+ -+Enable polling with 2 seconds interval: -+ -+{ "execute": "qom-set", -+ "arguments": { "path": "/machine/peripheral-anon/device[1]", -+ "property": "stats-polling-interval", "value": 2 } } -+ -+{ "return": {} } -+ -+Change polling to 10 seconds: -+ -+{ "execute": "qom-set", -+ "arguments": { "path": "/machine/peripheral-anon/device[1]", -+ "property": "stats-polling-interval", "value": 10 } } -+ -+{ "return": {} } -+ -+Get last update timestamp and free memory stat: -+ -+{ "execute": "qom-get", -+ "arguments": { "path": "/machine/peripheral-anon/device[1]", -+ "property": "stats-last-update" } } -+ -+{ "return": 1354629634 } -+ -+{ "execute": "qom-get", -+ "arguments": { "path": "/machine/peripheral-anon/device[1]", -+ "property": "stat-free-memory" } } -+ -+{ "return": 845115392 } -+ -+Disable polling: -+ -+{ "execute": "qom-set", -+ "arguments": { "path": "/machine/peripheral-anon/device[1]", -+ "property": "stats-polling-interval", "value": 0 } } -+ -+{ "return": {} } --- -1.8.0 - - - diff --git a/debian/patches/virtio-balloon-drop-old-stats-code.patch b/debian/patches/virtio-balloon-drop-old-stats-code.patch deleted file mode 100644 index cf606fe..0000000 --- a/debian/patches/virtio-balloon-drop-old-stats-code.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Luiz Capitulino -Subject: [Qemu-devel] [PATCH 1/3] virtio-balloon: drop old stats code - -Next commit will re-enable balloon stats with a different interface, but -this old code conflicts with it. Drop it. - -Signed-off-by: Luiz Capitulino ---- - hw/virtio-balloon.c | 22 ---------------------- - 1 file changed, 22 deletions(-) - -diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c -index dd1a650..4398025 100644 ---- a/hw/virtio-balloon.c -+++ b/hw/virtio-balloon.c -@@ -164,28 +164,6 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) - static void virtio_balloon_stat(void *opaque, BalloonInfo *info) - { - VirtIOBalloon *dev = opaque; -- --#if 0 -- /* Disable guest-provided stats for now. For more details please check: -- * https://bugzilla.redhat.com/show_bug.cgi?id=623903 -- * -- * If you do enable it (which is probably not going to happen as we -- * need a new command for it), remember that you also need to fill the -- * appropriate members of the BalloonInfo structure so that the stats -- * are returned to the client. -- */ -- if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { -- virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); -- virtio_notify(&dev->vdev, dev->svq); -- return; -- } --#endif -- -- /* Stats are not supported. Clear out any stale values that might -- * have been set by a more featureful guest kernel. -- */ -- reset_stats(dev); -- - info->actual = ram_size - ((uint64_t) dev->actual << - VIRTIO_BALLOON_PFN_SHIFT); - } --- -1.8.0 - - - - diff --git a/debian/patches/virtio-balloon-fix-query.patch b/debian/patches/virtio-balloon-fix-query.patch deleted file mode 100644 index 97574d7..0000000 --- a/debian/patches/virtio-balloon-fix-query.patch +++ /dev/null @@ -1,152 +0,0 @@ -Index: new/hmp.c -=================================================================== ---- new.orig/hmp.c 2014-11-20 06:45:05.000000000 +0100 -+++ new/hmp.c 2014-11-20 07:26:23.000000000 +0100 -@@ -635,7 +635,35 @@ - return; - } - -- monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20); -+ monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20); -+ monitor_printf(mon, " max_mem=%" PRId64, info->max_mem >> 20); -+ if (info->has_total_mem) { -+ monitor_printf(mon, " total_mem=%" PRId64, info->total_mem >> 20); -+ } -+ if (info->has_free_mem) { -+ monitor_printf(mon, " free_mem=%" PRId64, info->free_mem >> 20); -+ } -+ -+ if (info->has_mem_swapped_in) { -+ monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in); -+ } -+ if (info->has_mem_swapped_out) { -+ monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out); -+ } -+ if (info->has_major_page_faults) { -+ monitor_printf(mon, " major_page_faults=%" PRId64, -+ info->major_page_faults); -+ } -+ if (info->has_minor_page_faults) { -+ monitor_printf(mon, " minor_page_faults=%" PRId64, -+ info->minor_page_faults); -+ } -+ if (info->has_last_update) { -+ monitor_printf(mon, " last_update=%" PRId64, -+ info->last_update); -+ } -+ -+ monitor_printf(mon, "\n"); - - qapi_free_BalloonInfo(info); - } -Index: new/hw/virtio/virtio-balloon.c -=================================================================== ---- new.orig/hw/virtio/virtio-balloon.c 2014-11-20 06:45:06.000000000 +0100 -+++ new/hw/virtio/virtio-balloon.c 2014-11-20 07:26:23.000000000 +0100 -@@ -319,8 +319,37 @@ - static void virtio_balloon_stat(void *opaque, BalloonInfo *info) - { - VirtIOBalloon *dev = opaque; -- info->actual = get_current_ram_size() - ((uint64_t) dev->actual << -- VIRTIO_BALLOON_PFN_SHIFT); -+ ram_addr_t ram_size = get_current_ram_size(); -+ info->actual = ram_size - ((uint64_t) dev->actual << -+ VIRTIO_BALLOON_PFN_SHIFT); -+ -+ info->max_mem = ram_size; -+ -+ if (!(balloon_stats_enabled(dev) && balloon_stats_supported(dev) && -+ dev->stats_last_update)) { -+ return; -+ } -+ -+ info->last_update = dev->stats_last_update; -+ info->has_last_update = true; -+ -+ info->mem_swapped_in = dev->stats[VIRTIO_BALLOON_S_SWAP_IN]; -+ info->has_mem_swapped_in = info->mem_swapped_in >= 0 ? true : false; -+ -+ info->mem_swapped_out = dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]; -+ info->has_mem_swapped_out = info->mem_swapped_out >= 0 ? true : false; -+ -+ info->major_page_faults = dev->stats[VIRTIO_BALLOON_S_MAJFLT]; -+ info->has_major_page_faults = info->major_page_faults >= 0 ? true : false; -+ -+ info->minor_page_faults = dev->stats[VIRTIO_BALLOON_S_MINFLT]; -+ info->has_minor_page_faults = info->minor_page_faults >= 0 ? true : false; -+ -+ info->free_mem = dev->stats[VIRTIO_BALLOON_S_MEMFREE]; -+ info->has_free_mem = info->free_mem >= 0 ? true : false; -+ -+ info->total_mem = dev->stats[VIRTIO_BALLOON_S_MEMTOT]; -+ info->has_total_mem = info->total_mem >= 0 ? true : false; - } - - static void virtio_balloon_to_target(void *opaque, ram_addr_t target) -Index: new/qapi-schema.json -=================================================================== ---- new.orig/qapi-schema.json 2014-11-20 06:45:06.000000000 +0100 -+++ new/qapi-schema.json 2014-11-20 07:26:23.000000000 +0100 -@@ -983,10 +983,29 @@ - # - # @actual: the number of bytes the balloon currently contains - # --# Since: 0.14.0 -+# @last_update: #optional time when stats got updated from guest -+# -+# @mem_swapped_in: #optional number of pages swapped in within the guest -+# -+# @mem_swapped_out: #optional number of pages swapped out within the guest -+# -+# @major_page_faults: #optional number of major page faults within the guest - # -+# @minor_page_faults: #optional number of minor page faults within the guest -+# -+# @free_mem: #optional amount of memory (in bytes) free in the guest -+# -+# @total_mem: #optional amount of memory (in bytes) visible to the guest -+# -+# @max_mem: amount of memory (in bytes) assigned to the guest -+# -+# Since: 0.14.0 - ## --{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } } -+{ 'struct': 'BalloonInfo', -+ 'data': {'actual': 'int', '*last_update': 'int', '*mem_swapped_in': 'int', -+ '*mem_swapped_out': 'int', '*major_page_faults': 'int', -+ '*minor_page_faults': 'int', '*free_mem': 'int', -+ '*total_mem': 'int', 'max_mem': 'int' } } - - ## - # @query-balloon: -Index: new/qmp-commands.hx -=================================================================== ---- new.orig/qmp-commands.hx 2014-11-20 06:45:06.000000000 +0100 -+++ new/qmp-commands.hx 2014-11-20 07:26:23.000000000 +0100 -@@ -3329,6 +3329,13 @@ - json-object will be returned containing the following data: - - - "actual": current balloon value in bytes (json-int) -+- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional) -+- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional) -+- "major_page_faults": Number of major faults (json-int, optional) -+- "minor_page_faults": Number of minor faults (json-int, optional) -+- "free_mem": Total amount of free and unused memory in -+ bytes (json-int, optional) -+- "total_mem": Total amount of available memory in bytes (json-int, optional) - - Example: - -@@ -3336,6 +3344,12 @@ - <- { - "return":{ - "actual":1073741824, -+ "mem_swapped_in":0, -+ "mem_swapped_out":0, -+ "major_page_faults":142, -+ "minor_page_faults":239245, -+ "free_mem":1014185984, -+ "total_mem":1044668416 - } - } - diff --git a/debian/patches/virtio-balloon-re-enable-balloon-stats.patch b/debian/patches/virtio-balloon-re-enable-balloon-stats.patch deleted file mode 100644 index ea3e594..0000000 --- a/debian/patches/virtio-balloon-re-enable-balloon-stats.patch +++ /dev/null @@ -1,333 +0,0 @@ -From lcapitulino@redhat.com Fri Dec 14 16:49:58 2012 -Received: from rt.proxmox.com (192.168.2.18) by lisa.maurer-it.com - (192.168.2.121) with Microsoft SMTP Server id 14.2.328.9; Fri, 14 Dec 2012 - 16:49:58 +0100 -Received: from proxmox.maurer-it.com (proxmox.maurer-it.com - [192.168.2.110]) by rt.proxmox.com (Postfix) with ESMTP id 9D849206E234 for - ; Fri, 14 Dec 2012 16:49:58 +0100 (CET) -Received: from proxmox.maurer-it.com (localhost.localdomain [127.0.0.1]) by - proxmox.maurer-it.com (Proxmox) with ESMTP id 6DC3526A0912 for - ; Fri, 14 Dec 2012 16:49:58 +0100 (CET) -Received-SPF: pass (redhat.com: Sender is authorized to use - 'lcapitulino@redhat.com' in 'mfrom' identity (mechanism - 'include:spf-1.redhat.com' matched)) receiver=proxmox.maurer-it.com; - identity=mailfrom; envelope-from="lcapitulino@redhat.com"; - helo=mx1.redhat.com; client-ip=209.132.183.28 -Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by - proxmox.maurer-it.com (Proxmox) with ESMTP id 7B90426A090F for - ; Fri, 14 Dec 2012 16:49:55 +0100 (CET) -Received: from int-mx12.intmail.prod.int.phx2.redhat.com - (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com - (8.14.4/8.14.4) with ESMTP id qBEFnl3Y014706 (version=TLSv1/SSLv3 - cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 14 Dec 2012 10:49:47 - -0500 -Received: from localhost (ovpn-113-80.phx2.redhat.com [10.3.113.80]) by - int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id - qBEFnkfm012128; Fri, 14 Dec 2012 10:49:46 -0500 -From: Luiz Capitulino -To: -CC: , , , - , -Subject: [PATCH 2/3] balloon: re-enable balloon stats -Date: Fri, 14 Dec 2012 13:49:41 -0200 -Message-ID: <1355500182-12743-3-git-send-email-lcapitulino@redhat.com> -In-Reply-To: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> -References: <1355500182-12743-1-git-send-email-lcapitulino@redhat.com> -X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 -X-Proxmox-CTCH-Refid: - str=0001.0A0C0203.50CB4AA5.008C:SCFSTAT2484459,ss=1,re=-4.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0 -x-proxmoxspam-level: Spam detection results: 0 AWL - -0.200 From: address is in the auto white-list RCVD_IN_DNSWL_HI - -5 Sender listed at http://www.dnswl.org/, high trust SPF_HELO_PASS - -0.001 SPF: HELO matches SPF record SPF_PASS -0.001 SPF: - sender matches SPF record T_RP_MATCHES_RCVD -0.01 Envelope sender - domain matches handover relay domain -Content-Type: text/plain -Return-Path: lcapitulino@redhat.com -X-MS-Exchange-Organization-AuthSource: lisa.maurer-it.com -X-MS-Exchange-Organization-AuthAs: Anonymous -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit - -The statistics are now available through device properties via a -polling mechanism. First a client has to enable polling, then it -can query each stat individually. - -The following control properties are introduced: - - o stats-polling-interval: a value greater than zero enables polling - in the specified interval (in seconds). When value equals zero, - polling is disabled. If polling is already enabled and a value - greater than zero is written, the polling interval time is changed - - o stats-last-update: last stats update timestamp, in seconds. - -The following stats properties are introduced, all values are in bytes: - - o stat-swap-in - o stat-swap-out - o stat-major-faults - o stat-minor-faults - o stat-free-memory - o stat-total-memory - -Please, refer to the documentation introduced by the next commit for -more information and examples. - -Signed-off-by: Luiz Capitulino ---- - hw/virtio-balloon.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 176 insertions(+), 2 deletions(-) - -diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c -index 4398025..47e35b1 100644 ---- a/hw/virtio-balloon.c -+++ b/hw/virtio-balloon.c -@@ -22,6 +22,8 @@ - #include "virtio-balloon.h" - #include "kvm.h" - #include "exec-memory.h" -+#include "qemu-timer.h" -+#include "qapi/qapi-visit-core.h" - - #if defined(__linux__) - #include -@@ -36,6 +38,9 @@ typedef struct VirtIOBalloon - uint64_t stats[VIRTIO_BALLOON_S_NR]; - VirtQueueElement stats_vq_elem; - size_t stats_vq_offset; -+ QEMUTimer *stats_timer; -+ int64_t stats_last_update; -+ int64_t stats_poll_interval; - DeviceState *qdev; - } VirtIOBalloon; - -@@ -53,6 +58,16 @@ static void balloon_page(void *addr, int deflate) - #endif - } - -+static const char *balloon_stat_names[] = { -+ [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in", -+ [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out", -+ [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults", -+ [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults", -+ [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory", -+ [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", -+ [VIRTIO_BALLOON_S_NR] = NULL -+}; -+ - /* - * reset_stats - Mark all items in the stats array as unset - * -@@ -67,6 +82,138 @@ static inline void reset_stats(VirtIOBalloon *dev) - for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); - } - -+static bool balloon_stats_supported(const VirtIOBalloon *s) -+{ -+ return s->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ); -+} -+ -+static bool balloon_stats_enabled(const VirtIOBalloon *s) -+{ -+ return s->stats_poll_interval > 0; -+} -+ -+static void balloon_stats_destroy_timer(VirtIOBalloon *s) -+{ -+ if (balloon_stats_enabled(s)) { -+ qemu_del_timer(s->stats_timer); -+ qemu_free_timer(s->stats_timer); -+ s->stats_timer = NULL; -+ s->stats_poll_interval = 0; -+ } -+} -+ -+static void balloon_stats_change_timer(VirtIOBalloon *s, int secs) -+{ -+ qemu_mod_timer(s->stats_timer, qemu_get_clock_ms(vm_clock) + secs * 1000); -+} -+ -+static void balloon_stats_poll_cb(void *opaque) -+{ -+ VirtIOBalloon *s = opaque; -+ -+ if (!balloon_stats_supported(s)) { -+ /* re-schedule */ -+ balloon_stats_change_timer(s, s->stats_poll_interval); -+ return; -+ } -+ -+ virtqueue_push(s->svq, &s->stats_vq_elem, s->stats_vq_offset); -+ virtio_notify(&s->vdev, s->svq); -+} -+ -+static void balloon_stats_get_last_update(Object *obj, struct Visitor *v, -+ void *opaque, const char *name, -+ Error **errp) -+{ -+ VirtIOBalloon *s = opaque; -+ visit_type_int(v, &s->stats_last_update, name, errp); -+} -+ -+static void balloon_stats_get_stat(Object *obj, struct Visitor *v, -+ void *opaque, const char *name, Error **errp) -+{ -+ VirtIOBalloon *s = opaque; -+ int i; -+ -+ for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { -+ if (!strcmp(balloon_stat_names[i], name)) { -+ break; -+ } -+ } -+ -+ if (i == VIRTIO_BALLOON_S_NR) { -+ error_setg(errp, "invalid stat name '%s'", name); -+ return; -+ } -+ -+ if (s->stats[i] == -1) { -+ /* -+ * Possible reasons for this error: -+ * -+ * - The timer hasn't been enabled -+ * - The guest hasn't loaded its balloon driver -+ * - The guest's balloon driver doesn't support memory stats -+ * - The guest's balloon driver doesn't support this stat -+ * - The guest's balloon driver didn't send this stat for -+ * whatever reason -+ */ -+ error_setg(errp, -+ "guest didn't update '%s' (does the guest support it?)", name); -+ return; -+ } -+ -+ visit_type_int(v, (int64_t *) &s->stats[i], name, errp); -+} -+ -+static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v, -+ void *opaque, const char *name, -+ Error **errp) -+{ -+ VirtIOBalloon *s = opaque; -+ visit_type_int(v, &s->stats_poll_interval, name, errp); -+} -+ -+static void balloon_stats_set_poll_interval(Object *obj, struct Visitor *v, -+ void *opaque, const char *name, -+ Error **errp) -+{ -+ VirtIOBalloon *s = opaque; -+ int64_t value; -+ -+ visit_type_int(v, &value, name, errp); -+ if (error_is_set(errp)) { -+ return; -+ } -+ -+ if (value < 0) { -+ error_setg(errp, "timer value must be positive"); -+ return; -+ } -+ -+ if (value == s->stats_poll_interval) { -+ return; -+ } -+ -+ if (value == 0) { -+ /* timer=0 disables the timer */ -+ balloon_stats_destroy_timer(s); -+ return; -+ } -+ -+ if (balloon_stats_enabled(s)) { -+ /* timer interval change */ -+ s->stats_poll_interval = value; -+ balloon_stats_change_timer(s, value); -+ return; -+ } -+ -+ /* create a new timer */ -+ g_assert(s->stats_timer == NULL); -+ s->stats_timer = qemu_new_timer_ms(vm_clock, balloon_stats_poll_cb, s); -+ s->stats_poll_interval = value; -+ balloon_stats_change_timer(s, 0); -+} -+ - static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) - { - VirtIOBalloon *s = to_virtio_balloon(vdev); -@@ -107,9 +254,10 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) - VirtQueueElement *elem = &s->stats_vq_elem; - VirtIOBalloonStat stat; - size_t offset = 0; -+ qemu_timeval tv; - - if (!virtqueue_pop(vq, elem)) { -- return; -+ goto out; - } - - /* Initialize the stats to get rid of any stale values. This is only -@@ -128,6 +276,18 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) - s->stats[tag] = val; - } - s->stats_vq_offset = offset; -+ -+ if (qemu_gettimeofday(&tv) < 0) { -+ fprintf(stderr, "warning: %s: failed to get time of day\n", __func__); -+ goto out; -+ } -+ -+ s->stats_last_update = tv.tv_sec; -+ -+out: -+ if (balloon_stats_enabled(s)) { -+ balloon_stats_change_timer(s, s->stats_poll_interval); -+ } - } - - static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) -@@ -212,7 +372,7 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) - VirtIODevice *virtio_balloon_init(DeviceState *dev) - { - VirtIOBalloon *s; -- int ret; -+ int i, ret; - - s = (VirtIOBalloon *)virtio_common_init("virtio-balloon", - VIRTIO_ID_BALLOON, -@@ -239,6 +399,19 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) - register_savevm(dev, "virtio-balloon", -1, 1, - virtio_balloon_save, virtio_balloon_load, s); - -+ for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { -+ object_property_add(OBJECT(dev), balloon_stat_names[i], "int", -+ balloon_stats_get_stat, NULL, NULL, s, NULL); -+ } -+ -+ object_property_add(OBJECT(dev), "stats-last-update", "int", -+ balloon_stats_get_last_update, NULL, NULL, s, NULL); -+ -+ object_property_add(OBJECT(dev), "stats-polling-interval", "int", -+ balloon_stats_get_poll_interval, -+ balloon_stats_set_poll_interval, -+ NULL, s, NULL); -+ - return &s->vdev; - } - -@@ -246,6 +419,7 @@ void virtio_balloon_exit(VirtIODevice *vdev) - { - VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); - -+ balloon_stats_destroy_timer(s); - qemu_remove_balloon_handler(s); - unregister_savevm(s->qdev, "virtio-balloon", s); - virtio_cleanup(vdev); --- -1.8.0 - - - diff --git a/debian/patches/vmxnet3-host-memory-leakage.patch b/debian/patches/vmxnet3-host-memory-leakage.patch deleted file mode 100644 index dcc76b9..0000000 --- a/debian/patches/vmxnet3-host-memory-leakage.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 3ef66b01874fcc2fe3bfc73d2b61ee3a5b29fdb6 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Tue, 15 Dec 2015 12:17:28 +0530 -Subject: [PATCH] net: vmxnet3: avoid memory leakage in activate_device - -Vmxnet3 device emulator does not check if the device is active -before activating it, also it did not free the transmit & receive -buffers while deactivating the device, thus resulting in memory -leakage on the host. This patch fixes both these issues to avoid -host memory leakage. - -Reported-by: Qinghao Tang -Signed-off-by: Prasad J Pandit -Reviewed-by: Dmitry Fleytman ---- - hw/net/vmxnet3.c | 24 ++++++++++++++++-------- - 1 file changed, 16 insertions(+), 8 deletions(-) - -diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c -index 37373e5..2b4aad7 100644 ---- a/hw/net/vmxnet3.c -+++ b/hw/net/vmxnet3.c -@@ -1194,8 +1194,13 @@ static void vmxnet3_reset_mac(VMXNET3State *s) - - static void vmxnet3_deactivate_device(VMXNET3State *s) - { -- VMW_CBPRN("Deactivating vmxnet3..."); -- s->device_active = false; -+ if (s->device_active) { -+ VMW_CBPRN("Deactivating vmxnet3..."); -+ vmxnet_tx_pkt_reset(s->tx_pkt); -+ vmxnet_tx_pkt_uninit(s->tx_pkt); -+ vmxnet_rx_pkt_uninit(s->rx_pkt); -+ s->device_active = false; -+ } - } - - static void vmxnet3_reset(VMXNET3State *s) -@@ -1204,7 +1209,6 @@ static void vmxnet3_reset(VMXNET3State *s) - - vmxnet3_deactivate_device(s); - vmxnet3_reset_interrupt_states(s); -- vmxnet_tx_pkt_reset(s->tx_pkt); - s->drv_shmem = 0; - s->tx_sop = true; - s->skip_current_tx_pkt = false; -@@ -1431,6 +1435,12 @@ static void vmxnet3_activate_device(VMXNET3State *s) - return; - } - -+ /* Verify if device is active */ -+ if (s->device_active) { -+ VMW_CFPRN("Vmxnet3 device is active"); -+ return; -+ } -+ - vmxnet3_adjust_by_guest_type(s); - vmxnet3_update_features(s); - vmxnet3_update_pm_state(s); -@@ -1627,7 +1637,7 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd) - break; - - case VMXNET3_CMD_QUIESCE_DEV: -- VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device"); -+ VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - deactivate the device"); - vmxnet3_deactivate_device(s); - break; - -@@ -1741,7 +1751,7 @@ vmxnet3_io_bar1_write(void *opaque, - * shared address only after we get the high part - */ - if (val == 0) { -- s->device_active = false; -+ vmxnet3_deactivate_device(s); - } - s->temp_shared_guest_driver_memory = val; - s->drv_shmem = 0; -@@ -2021,9 +2031,7 @@ static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s) - static void vmxnet3_net_uninit(VMXNET3State *s) - { - g_free(s->mcast_list); -- vmxnet_tx_pkt_reset(s->tx_pkt); -- vmxnet_tx_pkt_uninit(s->tx_pkt); -- vmxnet_rx_pkt_uninit(s->rx_pkt); -+ vmxnet3_deactivate_device(s); - qemu_del_nic(s->nic); - } - --- -2.4.3 - diff --git a/debian/patches/vncticket.diff b/debian/patches/vncticket.diff deleted file mode 100644 index fa1474d..0000000 --- a/debian/patches/vncticket.diff +++ /dev/null @@ -1,149 +0,0 @@ -Index: qemu-kvm/console.h -=================================================================== ---- qemu-kvm.orig/console.h 2010-10-21 13:40:20.000000000 +0200 -+++ qemu-kvm/console.h 2010-10-21 14:06:21.000000000 +0200 -@@ -368,7 +368,7 @@ - void vnc_display_init(DisplayState *ds); - void vnc_display_close(DisplayState *ds); - int vnc_display_open(DisplayState *ds, const char *display); --int vnc_display_password(DisplayState *ds, const char *password); -+int vnc_display_password(DisplayState *ds, const char *password, int limit); - void do_info_vnc_print(Monitor *mon, const QObject *data); - void do_info_vnc(Monitor *mon, QObject **ret_data); - char *vnc_display_local_addr(DisplayState *ds); -Index: qemu-kvm/ui/vnc.c -=================================================================== ---- qemu-kvm.orig/ui/vnc.c 2010-10-21 13:40:21.000000000 +0200 -+++ qemu-kvm/ui/vnc.c 2010-10-21 14:06:21.000000000 +0200 -@@ -1790,7 +1790,7 @@ - static void set_pixel_conversion(VncState *vs) - { - if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == -- (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && -+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && - !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { - vs->write_pixels = vnc_write_pixels_copy; - vnc_hextile_set_pixel_conversion(vs, 0); -@@ -1876,7 +1876,7 @@ - vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); /* number of rects */ -- vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), -+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), - ds_get_height(vs->ds), VNC_ENCODING_WMVi); - pixel_format_message(vs); - vnc_unlock_output(vs); -@@ -2079,7 +2079,10 @@ - int i, j, pwlen; - unsigned char key[8]; - -- if (!vs->vd->password || !vs->vd->password[0]) { -+ if (vs->vd->retries >= 0) -+ vs->vd->retries++; -+ -+ if (!vs->vd->password || !vs->vd->password[0] || vs->vd->retries > 3) { - VNC_DEBUG("No password configured on server"); - vnc_write_u32(vs, 1); /* Reject auth */ - if (vs->minor >= 8) { -@@ -2478,7 +2481,7 @@ - #endif - } - --int vnc_display_password(DisplayState *ds, const char *password) -+int vnc_display_password(DisplayState *ds, const char *password, int limit) - { - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - -@@ -2493,6 +2496,7 @@ - if (password && password[0]) { - if (!(vs->password = qemu_strdup(password))) - return -1; -+ vs->retries = limit ? 0 : -1; - if (vs->auth == VNC_AUTH_NONE) { - vs->auth = VNC_AUTH_VNC; - } -Index: qemu-kvm/ui/vnc.h -=================================================================== ---- qemu-kvm.orig/ui/vnc.h 2010-10-21 13:40:20.000000000 +0200 -+++ qemu-kvm/ui/vnc.h 2010-10-21 14:06:21.000000000 +0200 -@@ -120,6 +120,7 @@ - - char *display; - char *password; -+ int retries; - int auth; - bool lossy; - #ifdef CONFIG_VNC_TLS -Index: qemu-kvm/monitor.c -=================================================================== ---- qemu-kvm.orig/monitor.c 2010-10-21 13:40:21.000000000 +0200 -+++ qemu-kvm/monitor.c 2010-10-21 14:14:38.000000000 +0200 -@@ -978,7 +978,7 @@ - - static int change_vnc_password(const char *password) - { -- if (vnc_display_password(NULL, password) < 0) { -+ if (vnc_display_password(NULL, password, 0) < 0) { - qerror_report(QERR_SET_PASSWD_FAILED); - return -1; - } -@@ -986,6 +986,17 @@ - return 0; - } - -+static int change_vnc_ticket(const char *password) -+{ -+ if (vnc_display_password(NULL, password, 1) < 0) { -+ qerror_report(QERR_SET_PASSWD_FAILED); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+ - static void change_vnc_password_cb(Monitor *mon, const char *password, - void *opaque) - { -@@ -996,12 +1007,16 @@ - static int do_change_vnc(Monitor *mon, const char *target, const char *arg) - { - if (strcmp(target, "passwd") == 0 || -- strcmp(target, "password") == 0) { -+ strcmp(target, "password") == 0 || -+ strcmp(target, "ticket") == 0) { - if (arg) { - char password[9]; - strncpy(password, arg, sizeof(password)); - password[sizeof(password) - 1] = '\0'; -- return change_vnc_password(password); -+ if (strcmp(target, "ticket") == 0) -+ return change_vnc_ticket(password); -+ else -+ return change_vnc_password(password); - } else { - return monitor_read_password(mon, change_vnc_password_cb, NULL); - } -@@ -3324,11 +3339,11 @@ - static int is_valid_option(const char *c, const char *typestr) - { - char option[3]; -- -+ - option[0] = '-'; - option[1] = *c; - option[2] = '\0'; -- -+ - typestr = strstr(typestr, option); - return (typestr != NULL); - } -@@ -3640,7 +3655,7 @@ - p++; - if(c != *p) { - if(!is_valid_option(p, typestr)) { -- -+ - monitor_printf(mon, "%s: unsupported option -%c\n", - cmdname, *p); - goto fail;