From 6402d961001c354ae5a397610da662e98f18df54 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Tue, 10 Mar 2020 15:12:50 +0100 Subject: [PATCH] update submodule and patches for 4.2.0 The long overdue nice rebase+cleanup was done by Dietmar Originally-by: Dietmar Maurer Signed-off-by: Thomas Lamprecht --- debian/control | 1 + ...sume-monitor-when-clearing-its-queue.patch | 45 - ...ule-virtio_notify_config-to-run-on-m.patch | 76 - ...-fix-memory-leak-when-vnc-disconnect.patch | 1016 ---------- .../0004-util-add-slirp_fmt-helpers.patch | 126 -- ...5-tcp_emu-fix-unsafe-snprintf-usages.patch | 135 -- ...k-file-change-locking-default-to-off.patch | 10 +- ...djust-network-script-path-to-etc-kvm.patch | 8 +- ...he-CPU-model-to-kvm64-32-instead-of-.patch | 8 +- ...ui-spice-default-to-pve-certificates.patch | 6 +- .../0005-PVE-Config-smm_available-false.patch | 8 +- ...erfs-no-default-logfile-if-daemonize.patch | 7 +- ...lock-rbd-disable-rbd_cache_writethro.patch | 8 +- .../0008-PVE-Up-qmp-add-get_link_status.patch | 12 +- ...PVE-Up-glusterfs-allow-partial-reads.patch | 10 +- ...return-success-on-info-without-snaps.patch | 9 +- ...dd-add-osize-and-read-from-to-stdin-.patch | 20 +- ...E-Up-qemu-img-dd-add-isize-parameter.patch | 16 +- ...PVE-Up-qemu-img-dd-add-n-skip_create.patch | 14 +- ...virtio-balloon-improve-query-balloon.patch | 14 +- .../0015-PVE-qapi-modify-query-machines.patch | 32 +- .../0016-PVE-qapi-modify-spice-query.patch | 8 +- .../0017-PVE-internal-snapshot-async.patch | 67 +- ...add-the-zeroinit-block-driver-filter.patch | 12 +- .../pve/0019-PVE-backup-modify-job-api.patch | 63 +- ...Add-dummy-id-command-line-parameter.patch} | 14 +- ...-backup-introduce-vma-archive-format.patch | 1563 ---------------- ...-target-i386-disable-LINT0-after-re.patch} | 11 +- ...e-posix-make-locking-optiono-on-cre.patch} | 25 +- ...ttling-options-to-drive-mapping-fifo.patch | 187 -- ...-kick-AIO-wait-on-block-state-write.patch} | 8 +- ...E-vma-add-cache-option-to-device-map.patch | 93 - ...e-snapshot-cleanup-into-bottom-half.patch} | 10 +- ...VE-vma-remove-forced-NO_FLUSH-option.patch | 28 - ...-PVE-monitor-disable-oob-capability.patch} | 8 +- ...ontext-before-calling-block_job_add.patch} | 11 +- ...ed-balloon-qemu-4-0-config-size-fal.patch} | 10 +- ...-Allow-version-code-in-machine-type.patch} | 37 +- ...E-Backup-add-vma-backup-format-code.patch} | 831 ++------- ...-Backup-add-backup-dump-block-driver.patch | 322 ++++ ...ckup-proxmox-backup-patches-for-qemu.patch | 1633 +++++++++++++++++ ...-vma-writer.c-use-correct-AioContext.patch | 50 - ...estore-new-command-to-restore-from-p.patch | 248 +++ ...ackup-related-code-inside-coroutines.patch | 511 ------ ...-CoMutex-to-protect-access-to-backup.patch | 274 --- ...-avoid-call-to-aio_poll-acquire-flus.patch | 35 - ...ob_create-pass-cluster-size-for-dump.patch | 123 -- ...mp_cb-with-NULL-data-pointer-for-sma.patch | 31 - ..._to_vma-into-pvebackup_co_add_config.patch | 109 -- ...up_co_dump_cb-do-not-call-job-cancel.patch | 27 - .../pve/0039-fix-backup-job-completion.patch | 44 - ...te_cb-avoid-poll-loop-if-already-ins.patch | 43 - ...consider-source-cluster-size-as-well.patch | 28 - .../patches/pve/0042-PVE-fixup-vma-tool.patch | 33 - ...ev-pvebackup-integration-fix-blockjo.patch | 81 - ...-backup-cmd-for-not-initialized-back.patch | 31 - ...eparate-CoRwlock-for-data-accessed-b.patch | 365 ---- ...k_on_coroutine_wrapper-call-aio_wait.patch | 25 - ...-backup_state.cancel-to-backup_state.patch | 95 - debian/patches/series | 49 +- debian/rules | 1 - qemu | 2 +- 62 files changed, 2578 insertions(+), 6149 deletions(-) delete mode 100644 debian/patches/extra/0001-monitor-qmp-resume-monitor-when-clearing-its-queue.patch delete mode 100644 debian/patches/extra/0002-virtio-blk-schedule-virtio_notify_config-to-run-on-m.patch delete mode 100644 debian/patches/extra/0003-vnc-fix-memory-leak-when-vnc-disconnect.patch delete mode 100644 debian/patches/extra/0004-util-add-slirp_fmt-helpers.patch delete mode 100644 debian/patches/extra/0005-tcp_emu-fix-unsafe-snprintf-usages.patch rename debian/patches/pve/{0025-PVE-Add-dummy-id-command-line-parameter.patch => 0020-PVE-Add-dummy-id-command-line-parameter.patch} (82%) delete mode 100644 debian/patches/pve/0020-PVE-backup-introduce-vma-archive-format.patch rename debian/patches/pve/{0026-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch => 0021-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch} (74%) rename debian/patches/pve/{0027-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch => 0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch} (87%) delete mode 100644 debian/patches/pve/0022-PVE-vma-add-throttling-options-to-drive-mapping-fifo.patch rename debian/patches/pve/{0028-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch => 0023-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch} (73%) delete mode 100644 debian/patches/pve/0023-PVE-vma-add-cache-option-to-device-map.patch rename debian/patches/pve/{0029-PVE-move-snapshot-cleanup-into-bottom-half.patch => 0024-PVE-move-snapshot-cleanup-into-bottom-half.patch} (79%) delete mode 100644 debian/patches/pve/0024-PVE-vma-remove-forced-NO_FLUSH-option.patch rename debian/patches/pve/{0030-PVE-monitor-disable-oob-capability.patch => 0025-PVE-monitor-disable-oob-capability.patch} (85%) rename debian/patches/pve/{0044-Acquire-aio_context-before-calling-block_job_add_bdr.patch => 0026-PVE-Acquire-aio_context-before-calling-block_job_add.patch} (80%) rename debian/patches/pve/{0045-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch => 0027-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch} (88%) rename debian/patches/pve/{0046-PVE-Allow-version-code-in-machine-type.patch => 0028-PVE-Allow-version-code-in-machine-type.patch} (73%) rename debian/patches/pve/{0021-PVE-Deprecated-adding-old-vma-files.patch => 0029-PVE-Backup-add-vma-backup-format-code.patch} (72%) create mode 100644 debian/patches/pve/0030-PVE-Backup-add-backup-dump-block-driver.patch create mode 100644 debian/patches/pve/0031-PVE-Backup-proxmox-backup-patches-for-qemu.patch delete mode 100644 debian/patches/pve/0031-PVE-bug-fix-1071-vma-writer.c-use-correct-AioContext.patch create mode 100644 debian/patches/pve/0032-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch delete mode 100644 debian/patches/pve/0032-qmp_backup-run-backup-related-code-inside-coroutines.patch delete mode 100644 debian/patches/pve/0033-qmp_backup-use-a-CoMutex-to-protect-access-to-backup.patch delete mode 100644 debian/patches/pve/0034-vma_writer_close-avoid-call-to-aio_poll-acquire-flus.patch delete mode 100644 debian/patches/pve/0035-backup_job_create-pass-cluster-size-for-dump.patch delete mode 100644 debian/patches/pve/0036-avoid-calling-dump_cb-with-NULL-data-pointer-for-sma.patch delete mode 100644 debian/patches/pve/0037-rename-config_to_vma-into-pvebackup_co_add_config.patch delete mode 100644 debian/patches/pve/0038-pvebackup_co_dump_cb-do-not-call-job-cancel.patch delete mode 100644 debian/patches/pve/0039-fix-backup-job-completion.patch delete mode 100644 debian/patches/pve/0040-pvebackup_complete_cb-avoid-poll-loop-if-already-ins.patch delete mode 100644 debian/patches/pve/0041-PVE-backup-consider-source-cluster-size-as-well.patch delete mode 100644 debian/patches/pve/0042-PVE-fixup-vma-tool.patch delete mode 100644 debian/patches/pve/0043-PVE-fixup-blockdev-pvebackup-integration-fix-blockjo.patch delete mode 100644 debian/patches/pve/0047-PVE-fix-hmp-info-backup-cmd-for-not-initialized-back.patch delete mode 100644 debian/patches/pve/0048-PVE-backup-use-separate-CoRwlock-for-data-accessed-b.patch delete mode 100644 debian/patches/pve/0049-PVE-backup-block_on_coroutine_wrapper-call-aio_wait.patch delete mode 100644 debian/patches/pve/0050-PVE-backup-move-backup_state.cancel-to-backup_state.patch diff --git a/debian/control b/debian/control index 35c4580..c52b282 100644 --- a/debian/control +++ b/debian/control @@ -18,6 +18,7 @@ Build-Depends: autotools-dev, libnuma-dev, libpci-dev, libpixman-1-dev, + libproxmox-backup-qemu0-dev, librbd-dev (>= 0.48), libsdl1.2-dev, libseccomp-dev, diff --git a/debian/patches/extra/0001-monitor-qmp-resume-monitor-when-clearing-its-queue.patch b/debian/patches/extra/0001-monitor-qmp-resume-monitor-when-clearing-its-queue.patch deleted file mode 100644 index d07c251..0000000 --- a/debian/patches/extra/0001-monitor-qmp-resume-monitor-when-clearing-its-queue.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 2 Oct 2019 10:30:03 +0200 -Subject: [PATCH] monitor/qmp: resume monitor when clearing its queue - -When a monitor's queue is filled up in handle_qmp_command() -it gets suspended. It's the dispatcher bh's job currently to -resume the monitor, which it does after processing an event -from the queue. However, it is possible for a -CHR_EVENT_CLOSED event to be processed before before the bh -is scheduled, which will clear the queue without resuming -the monitor, thereby preventing the dispatcher from reaching -the resume() call. -Fix this by resuming the monitor when clearing a queue which -was filled up. - -Signed-off-by: Wolfgang Bumiller ---- - monitor/qmp.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/monitor/qmp.c b/monitor/qmp.c -index e1b196217d..fb3e66c62a 100644 ---- a/monitor/qmp.c -+++ b/monitor/qmp.c -@@ -70,9 +70,19 @@ static void qmp_request_free(QMPRequest *req) - /* Caller must hold mon->qmp.qmp_queue_lock */ - static void monitor_qmp_cleanup_req_queue_locked(MonitorQMP *mon) - { -+ bool need_resume = (!qmp_oob_enabled(mon) && mon->qmp_requests->length > 0) -+ || mon->qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX; - while (!g_queue_is_empty(mon->qmp_requests)) { - qmp_request_free(g_queue_pop_head(mon->qmp_requests)); - } -+ if (need_resume) { -+ /* -+ * Pairs with the monitor_suspend() in handle_qmp_command() in case the -+ * queue gets cleared from a CH_EVENT_CLOSED event before the dispatch -+ * bh got scheduled. -+ */ -+ monitor_resume(&mon->common); -+ } - } - - static void monitor_qmp_cleanup_queues(MonitorQMP *mon) diff --git a/debian/patches/extra/0002-virtio-blk-schedule-virtio_notify_config-to-run-on-m.patch b/debian/patches/extra/0002-virtio-blk-schedule-virtio_notify_config-to-run-on-m.patch deleted file mode 100644 index f20c7d7..0000000 --- a/debian/patches/extra/0002-virtio-blk-schedule-virtio_notify_config-to-run-on-m.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Sergio Lopez -Date: Mon, 16 Sep 2019 13:24:12 +0200 -Subject: [PATCH] virtio-blk: schedule virtio_notify_config to run on main - context - -virtio_notify_config() needs to acquire the global mutex, which isn't -allowed from an iothread, and may lead to a deadlock like this: - - - main thead - * Has acquired: qemu_global_mutex. - * Is trying the acquire: iothread AioContext lock via - AIO_WAIT_WHILE (after aio_poll). - - - iothread - * Has acquired: AioContext lock. - * Is trying to acquire: qemu_global_mutex (via - virtio_notify_config->prepare_mmio_access). - -If virtio_blk_resize() is called from an iothread, schedule -virtio_notify_config() to be run in the main context BH. - -[Removed unnecessary newline as suggested by Kevin Wolf -. ---Stefan] - -Signed-off-by: Sergio Lopez -Reviewed-by: Kevin Wolf -Message-id: 20190916112411.21636-1-slp@redhat.com -Message-Id: <20190916112411.21636-1-slp@redhat.com> -Signed-off-by: Stefan Hajnoczi ---- - hw/block/virtio-blk.c | 16 +++++++++++++++- - 1 file changed, 15 insertions(+), 1 deletion(-) - -diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c -index cbb3729158..0d9adcdaff 100644 ---- a/hw/block/virtio-blk.c -+++ b/hw/block/virtio-blk.c -@@ -16,6 +16,7 @@ - #include "qemu/iov.h" - #include "qemu/module.h" - #include "qemu/error-report.h" -+#include "qemu/main-loop.h" - #include "trace.h" - #include "hw/block/block.h" - #include "sysemu/blockdev.h" -@@ -1082,11 +1083,24 @@ static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, - return 0; - } - -+static void virtio_resize_cb(void *opaque) -+{ -+ VirtIODevice *vdev = opaque; -+ -+ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); -+ virtio_notify_config(vdev); -+} -+ - static void virtio_blk_resize(void *opaque) - { - VirtIODevice *vdev = VIRTIO_DEVICE(opaque); - -- virtio_notify_config(vdev); -+ /* -+ * virtio_notify_config() needs to acquire the global mutex, -+ * so it can't be called from an iothread. Instead, schedule -+ * it to be run in the main context BH. -+ */ -+ aio_bh_schedule_oneshot(qemu_get_aio_context(), virtio_resize_cb, vdev); - } - - static const BlockDevOps virtio_block_ops = { --- -2.20.1 - diff --git a/debian/patches/extra/0003-vnc-fix-memory-leak-when-vnc-disconnect.patch b/debian/patches/extra/0003-vnc-fix-memory-leak-when-vnc-disconnect.patch deleted file mode 100644 index 3c95274..0000000 --- a/debian/patches/extra/0003-vnc-fix-memory-leak-when-vnc-disconnect.patch +++ /dev/null @@ -1,1016 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Li Qiang -Date: Sat, 31 Aug 2019 08:39:22 -0700 -Subject: [PATCH] vnc: fix memory leak when vnc disconnect - -Currently when qemu receives a vnc connect, it creates a 'VncState' to -represent this connection. In 'vnc_worker_thread_loop' it creates a -local 'VncState'. The connection 'VcnState' and local 'VncState' exchange -data in 'vnc_async_encoding_start' and 'vnc_async_encoding_end'. -In 'zrle_compress_data' it calls 'deflateInit2' to allocate the libz library -opaque data. The 'VncState' used in 'zrle_compress_data' is the local -'VncState'. In 'vnc_zrle_clear' it calls 'deflateEnd' to free the libz -library opaque data. The 'VncState' used in 'vnc_zrle_clear' is the connection -'VncState'. In currently implementation there will be a memory leak when the -vnc disconnect. Following is the asan output backtrack: - -Direct leak of 29760 byte(s) in 5 object(s) allocated from: - 0 0xffffa67ef3c3 in __interceptor_calloc (/lib64/libasan.so.4+0xd33c3) - 1 0xffffa65071cb in g_malloc0 (/lib64/libglib-2.0.so.0+0x571cb) - 2 0xffffa5e968f7 in deflateInit2_ (/lib64/libz.so.1+0x78f7) - 3 0xaaaacec58613 in zrle_compress_data ui/vnc-enc-zrle.c:87 - 4 0xaaaacec58613 in zrle_send_framebuffer_update ui/vnc-enc-zrle.c:344 - 5 0xaaaacec34e77 in vnc_send_framebuffer_update ui/vnc.c:919 - 6 0xaaaacec5e023 in vnc_worker_thread_loop ui/vnc-jobs.c:271 - 7 0xaaaacec5e5e7 in vnc_worker_thread ui/vnc-jobs.c:340 - 8 0xaaaacee4d3c3 in qemu_thread_start util/qemu-thread-posix.c:502 - 9 0xffffa544e8bb in start_thread (/lib64/libpthread.so.0+0x78bb) - 10 0xffffa53965cb in thread_start (/lib64/libc.so.6+0xd55cb) - -This is because the opaque allocated in 'deflateInit2' is not freed in -'deflateEnd'. The reason is that the 'deflateEnd' calls 'deflateStateCheck' -and in the latter will check whether 's->strm != strm'(libz's data structure). -This check will be true so in 'deflateEnd' it just return 'Z_STREAM_ERROR' and -not free the data allocated in 'deflateInit2'. - -The reason this happens is that the 'VncState' contains the whole 'VncZrle', -so when calling 'deflateInit2', the 's->strm' will be the local address. -So 's->strm != strm' will be true. - -To fix this issue, we need to make 'zrle' of 'VncState' to be a pointer. -Then the connection 'VncState' and local 'VncState' exchange mechanism will -work as expection. The 'tight' of 'VncState' has the same issue, let's also turn -it to a pointer. - -Reported-by: Ying Fang -Signed-off-by: Li Qiang -Message-id: 20190831153922.121308-1-liq3ea@163.com -Signed-off-by: Gerd Hoffmann -(cherry picked from commit 6bf21f3d83e95bcc4ba35a7a07cc6655e8b010b0) -Signed-off-by: Oguz Bektas ---- - ui/vnc-enc-tight.c | 219 +++++++++++++++++++++--------------------- - ui/vnc-enc-zlib.c | 11 ++- - ui/vnc-enc-zrle.c | 68 ++++++------- - ui/vnc-enc-zrle.inc.c | 2 +- - ui/vnc.c | 28 +++--- - ui/vnc.h | 4 +- - 6 files changed, 170 insertions(+), 162 deletions(-) - -diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c -index 9084c2201b..1e0851826a 100644 ---- a/ui/vnc-enc-tight.c -+++ b/ui/vnc-enc-tight.c -@@ -116,7 +116,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, - - static bool tight_can_send_png_rect(VncState *vs, int w, int h) - { -- if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) { -+ if (vs->tight->type != VNC_ENCODING_TIGHT_PNG) { - return false; - } - -@@ -144,7 +144,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) - int pixels = 0; - int pix, left[3]; - unsigned int errors; -- unsigned char *buf = vs->tight.tight.buffer; -+ unsigned char *buf = vs->tight->tight.buffer; - - /* - * If client is big-endian, color samples begin from the second -@@ -215,7 +215,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) - int pixels = 0; \ - int sample, sum, left[3]; \ - unsigned int errors; \ -- unsigned char *buf = vs->tight.tight.buffer; \ -+ unsigned char *buf = vs->tight->tight.buffer; \ - \ - endian = 0; /* FIXME */ \ - \ -@@ -296,8 +296,8 @@ static int - tight_detect_smooth_image(VncState *vs, int w, int h) - { - unsigned int errors; -- int compression = vs->tight.compression; -- int quality = vs->tight.quality; -+ int compression = vs->tight->compression; -+ int quality = vs->tight->quality; - - if (!vs->vd->lossy) { - return 0; -@@ -309,7 +309,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h) - return 0; - } - -- if (vs->tight.quality != (uint8_t)-1) { -+ if (vs->tight->quality != (uint8_t)-1) { - if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) { - return 0; - } -@@ -320,9 +320,9 @@ tight_detect_smooth_image(VncState *vs, int w, int h) - } - - if (vs->client_pf.bytes_per_pixel == 4) { -- if (vs->tight.pixel24) { -+ if (vs->tight->pixel24) { - errors = tight_detect_smooth_image24(vs, w, h); -- if (vs->tight.quality != (uint8_t)-1) { -+ if (vs->tight->quality != (uint8_t)-1) { - return (errors < tight_conf[quality].jpeg_threshold24); - } - return (errors < tight_conf[compression].gradient_threshold24); -@@ -352,7 +352,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h) - uint##bpp##_t c0, c1, ci; \ - int i, n0, n1; \ - \ -- data = (uint##bpp##_t *)vs->tight.tight.buffer; \ -+ data = (uint##bpp##_t *)vs->tight->tight.buffer; \ - \ - c0 = data[0]; \ - i = 1; \ -@@ -423,9 +423,9 @@ static int tight_fill_palette(VncState *vs, int x, int y, - { - int max; - -- max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor; -+ max = count / tight_conf[vs->tight->compression].idx_max_colors_divisor; - if (max < 2 && -- count >= tight_conf[vs->tight.compression].mono_min_rect_size) { -+ count >= tight_conf[vs->tight->compression].mono_min_rect_size) { - max = 2; - } - if (max >= 256) { -@@ -558,7 +558,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) - int x, y, c; - - buf32 = (uint32_t *)buf; -- memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); -+ memset(vs->tight->gradient.buffer, 0, w * 3 * sizeof(int)); - - if (1 /* FIXME */) { - shift[0] = vs->client_pf.rshift; -@@ -575,7 +575,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) - upper[c] = 0; - here[c] = 0; - } -- prev = (int *)vs->tight.gradient.buffer; -+ prev = (int *)vs->tight->gradient.buffer; - for (x = 0; x < w; x++) { - pix32 = *buf32++; - for (c = 0; c < 3; c++) { -@@ -615,7 +615,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) - int prediction; \ - int x, y, c; \ - \ -- memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); \ -+ memset(vs->tight->gradient.buffer, 0, w * 3 * sizeof(int)); \ - \ - endian = 0; /* FIXME */ \ - \ -@@ -631,7 +631,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) - upper[c] = 0; \ - here[c] = 0; \ - } \ -- prev = (int *)vs->tight.gradient.buffer; \ -+ prev = (int *)vs->tight->gradient.buffer; \ - for (x = 0; x < w; x++) { \ - pix = *buf; \ - if (endian) { \ -@@ -785,7 +785,7 @@ static void extend_solid_area(VncState *vs, int x, int y, int w, int h, - static int tight_init_stream(VncState *vs, int stream_id, - int level, int strategy) - { -- z_streamp zstream = &vs->tight.stream[stream_id]; -+ z_streamp zstream = &vs->tight->stream[stream_id]; - - if (zstream->opaque == NULL) { - int err; -@@ -803,15 +803,15 @@ static int tight_init_stream(VncState *vs, int stream_id, - return -1; - } - -- vs->tight.levels[stream_id] = level; -+ vs->tight->levels[stream_id] = level; - zstream->opaque = vs; - } - -- if (vs->tight.levels[stream_id] != level) { -+ if (vs->tight->levels[stream_id] != level) { - if (deflateParams(zstream, level, strategy) != Z_OK) { - return -1; - } -- vs->tight.levels[stream_id] = level; -+ vs->tight->levels[stream_id] = level; - } - return 0; - } -@@ -839,11 +839,11 @@ static void tight_send_compact_size(VncState *vs, size_t len) - static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, - int level, int strategy) - { -- z_streamp zstream = &vs->tight.stream[stream_id]; -+ z_streamp zstream = &vs->tight->stream[stream_id]; - int previous_out; - - if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) { -- vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset); -+ vnc_write(vs, vs->tight->tight.buffer, vs->tight->tight.offset); - return bytes; - } - -@@ -852,13 +852,13 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, - } - - /* reserve memory in output buffer */ -- buffer_reserve(&vs->tight.zlib, bytes + 64); -+ buffer_reserve(&vs->tight->zlib, bytes + 64); - - /* set pointers */ -- zstream->next_in = vs->tight.tight.buffer; -- zstream->avail_in = vs->tight.tight.offset; -- zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset; -- zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset; -+ zstream->next_in = vs->tight->tight.buffer; -+ zstream->avail_in = vs->tight->tight.offset; -+ zstream->next_out = vs->tight->zlib.buffer + vs->tight->zlib.offset; -+ zstream->avail_out = vs->tight->zlib.capacity - vs->tight->zlib.offset; - previous_out = zstream->avail_out; - zstream->data_type = Z_BINARY; - -@@ -868,14 +868,14 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, - return -1; - } - -- vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out; -+ vs->tight->zlib.offset = vs->tight->zlib.capacity - zstream->avail_out; - /* ...how much data has actually been produced by deflate() */ - bytes = previous_out - zstream->avail_out; - - tight_send_compact_size(vs, bytes); -- vnc_write(vs, vs->tight.zlib.buffer, bytes); -+ vnc_write(vs, vs->tight->zlib.buffer, bytes); - -- buffer_reset(&vs->tight.zlib); -+ buffer_reset(&vs->tight->zlib); - - return bytes; - } -@@ -927,16 +927,17 @@ static int send_full_color_rect(VncState *vs, int x, int y, int w, int h) - - vnc_write_u8(vs, stream << 4); /* no flushing, no filter */ - -- if (vs->tight.pixel24) { -- tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset); -+ if (vs->tight->pixel24) { -+ tight_pack24(vs, vs->tight->tight.buffer, w * h, -+ &vs->tight->tight.offset); - bytes = 3; - } else { - bytes = vs->client_pf.bytes_per_pixel; - } - - bytes = tight_compress_data(vs, stream, w * h * bytes, -- tight_conf[vs->tight.compression].raw_zlib_level, -- Z_DEFAULT_STRATEGY); -+ tight_conf[vs->tight->compression].raw_zlib_level, -+ Z_DEFAULT_STRATEGY); - - return (bytes >= 0); - } -@@ -947,14 +948,14 @@ static int send_solid_rect(VncState *vs) - - vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */ - -- if (vs->tight.pixel24) { -- tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset); -+ if (vs->tight->pixel24) { -+ tight_pack24(vs, vs->tight->tight.buffer, 1, &vs->tight->tight.offset); - bytes = 3; - } else { - bytes = vs->client_pf.bytes_per_pixel; - } - -- vnc_write(vs, vs->tight.tight.buffer, bytes); -+ vnc_write(vs, vs->tight->tight.buffer, bytes); - return 1; - } - -@@ -963,7 +964,7 @@ static int send_mono_rect(VncState *vs, int x, int y, - { - ssize_t bytes; - int stream = 1; -- int level = tight_conf[vs->tight.compression].mono_zlib_level; -+ int level = tight_conf[vs->tight->compression].mono_zlib_level; - - #ifdef CONFIG_VNC_PNG - if (tight_can_send_png_rect(vs, w, h)) { -@@ -991,26 +992,26 @@ static int send_mono_rect(VncState *vs, int x, int y, - uint32_t buf[2] = {bg, fg}; - size_t ret = sizeof (buf); - -- if (vs->tight.pixel24) { -+ if (vs->tight->pixel24) { - tight_pack24(vs, (unsigned char*)buf, 2, &ret); - } - vnc_write(vs, buf, ret); - -- tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg); -+ tight_encode_mono_rect32(vs->tight->tight.buffer, w, h, bg, fg); - break; - } - case 2: - vnc_write(vs, &bg, 2); - vnc_write(vs, &fg, 2); -- tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg); -+ tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg); - break; - default: - vnc_write_u8(vs, bg); - vnc_write_u8(vs, fg); -- tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg); -+ tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg); - break; - } -- vs->tight.tight.offset = bytes; -+ vs->tight->tight.offset = bytes; - - bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); - return (bytes >= 0); -@@ -1040,7 +1041,7 @@ static void write_palette(int idx, uint32_t color, void *opaque) - static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) - { - int stream = 3; -- int level = tight_conf[vs->tight.compression].gradient_zlib_level; -+ int level = tight_conf[vs->tight->compression].gradient_zlib_level; - ssize_t bytes; - - if (vs->client_pf.bytes_per_pixel == 1) { -@@ -1050,23 +1051,23 @@ static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) - vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); - vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT); - -- buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int)); -+ buffer_reserve(&vs->tight->gradient, w * 3 * sizeof(int)); - -- if (vs->tight.pixel24) { -- tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h); -+ if (vs->tight->pixel24) { -+ tight_filter_gradient24(vs, vs->tight->tight.buffer, w, h); - bytes = 3; - } else if (vs->client_pf.bytes_per_pixel == 4) { -- tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h); -+ tight_filter_gradient32(vs, (uint32_t *)vs->tight->tight.buffer, w, h); - bytes = 4; - } else { -- tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h); -+ tight_filter_gradient16(vs, (uint16_t *)vs->tight->tight.buffer, w, h); - bytes = 2; - } - -- buffer_reset(&vs->tight.gradient); -+ buffer_reset(&vs->tight->gradient); - - bytes = w * h * bytes; -- vs->tight.tight.offset = bytes; -+ vs->tight->tight.offset = bytes; - - bytes = tight_compress_data(vs, stream, bytes, - level, Z_FILTERED); -@@ -1077,7 +1078,7 @@ static int send_palette_rect(VncState *vs, int x, int y, - int w, int h, VncPalette *palette) - { - int stream = 2; -- int level = tight_conf[vs->tight.compression].idx_zlib_level; -+ int level = tight_conf[vs->tight->compression].idx_zlib_level; - int colors; - ssize_t bytes; - -@@ -1104,12 +1105,12 @@ static int send_palette_rect(VncState *vs, int x, int y, - palette_iter(palette, write_palette, &priv); - vnc_write(vs, header, sizeof(header)); - -- if (vs->tight.pixel24) { -+ if (vs->tight->pixel24) { - tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); - vs->output.offset = old_offset + offset; - } - -- tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); -+ tight_encode_indexed_rect32(vs->tight->tight.buffer, w * h, palette); - break; - } - case 2: -@@ -1119,7 +1120,7 @@ static int send_palette_rect(VncState *vs, int x, int y, - - palette_iter(palette, write_palette, &priv); - vnc_write(vs, header, sizeof(header)); -- tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); -+ tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette); - break; - } - default: -@@ -1127,7 +1128,7 @@ static int send_palette_rect(VncState *vs, int x, int y, - break; - } - bytes = w * h; -- vs->tight.tight.offset = bytes; -+ vs->tight->tight.offset = bytes; - - bytes = tight_compress_data(vs, stream, bytes, - level, Z_DEFAULT_STRATEGY); -@@ -1146,7 +1147,7 @@ static int send_palette_rect(VncState *vs, int x, int y, - static void jpeg_init_destination(j_compress_ptr cinfo) - { - VncState *vs = cinfo->client_data; -- Buffer *buffer = &vs->tight.jpeg; -+ Buffer *buffer = &vs->tight->jpeg; - - cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; - cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset); -@@ -1156,7 +1157,7 @@ static void jpeg_init_destination(j_compress_ptr cinfo) - static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) - { - VncState *vs = cinfo->client_data; -- Buffer *buffer = &vs->tight.jpeg; -+ Buffer *buffer = &vs->tight->jpeg; - - buffer->offset = buffer->capacity; - buffer_reserve(buffer, 2048); -@@ -1168,7 +1169,7 @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) - static void jpeg_term_destination(j_compress_ptr cinfo) - { - VncState *vs = cinfo->client_data; -- Buffer *buffer = &vs->tight.jpeg; -+ Buffer *buffer = &vs->tight->jpeg; - - buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; - } -@@ -1187,7 +1188,7 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) - return send_full_color_rect(vs, x, y, w, h); - } - -- buffer_reserve(&vs->tight.jpeg, 2048); -+ buffer_reserve(&vs->tight->jpeg, 2048); - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); -@@ -1222,9 +1223,9 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) - - vnc_write_u8(vs, VNC_TIGHT_JPEG << 4); - -- tight_send_compact_size(vs, vs->tight.jpeg.offset); -- vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset); -- buffer_reset(&vs->tight.jpeg); -+ tight_send_compact_size(vs, vs->tight->jpeg.offset); -+ vnc_write(vs, vs->tight->jpeg.buffer, vs->tight->jpeg.offset); -+ buffer_reset(&vs->tight->jpeg); - - return 1; - } -@@ -1240,7 +1241,7 @@ static void write_png_palette(int idx, uint32_t pix, void *opaque) - VncState *vs = priv->vs; - png_colorp color = &priv->png_palette[idx]; - -- if (vs->tight.pixel24) -+ if (vs->tight->pixel24) - { - color->red = (pix >> vs->client_pf.rshift) & vs->client_pf.rmax; - color->green = (pix >> vs->client_pf.gshift) & vs->client_pf.gmax; -@@ -1267,10 +1268,10 @@ static void png_write_data(png_structp png_ptr, png_bytep data, - { - VncState *vs = png_get_io_ptr(png_ptr); - -- buffer_reserve(&vs->tight.png, vs->tight.png.offset + length); -- memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length); -+ buffer_reserve(&vs->tight->png, vs->tight->png.offset + length); -+ memcpy(vs->tight->png.buffer + vs->tight->png.offset, data, length); - -- vs->tight.png.offset += length; -+ vs->tight->png.offset += length; - } - - static void png_flush_data(png_structp png_ptr) -@@ -1295,8 +1296,8 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, - png_infop info_ptr; - png_colorp png_palette = NULL; - pixman_image_t *linebuf; -- int level = tight_png_conf[vs->tight.compression].png_zlib_level; -- int filters = tight_png_conf[vs->tight.compression].png_filters; -+ int level = tight_png_conf[vs->tight->compression].png_zlib_level; -+ int filters = tight_png_conf[vs->tight->compression].png_filters; - uint8_t *buf; - int dy; - -@@ -1340,21 +1341,23 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, - png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); - - if (vs->client_pf.bytes_per_pixel == 4) { -- tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); -+ tight_encode_indexed_rect32(vs->tight->tight.buffer, w * h, -+ palette); - } else { -- tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); -+ tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, -+ palette); - } - } - - png_write_info(png_ptr, info_ptr); - -- buffer_reserve(&vs->tight.png, 2048); -+ buffer_reserve(&vs->tight->png, 2048); - linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, w); - buf = (uint8_t *)pixman_image_get_data(linebuf); - for (dy = 0; dy < h; dy++) - { - if (color_type == PNG_COLOR_TYPE_PALETTE) { -- memcpy(buf, vs->tight.tight.buffer + (dy * w), w); -+ memcpy(buf, vs->tight->tight.buffer + (dy * w), w); - } else { - qemu_pixman_linebuf_fill(linebuf, vs->vd->server, w, x, y + dy); - } -@@ -1372,27 +1375,27 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, - - vnc_write_u8(vs, VNC_TIGHT_PNG << 4); - -- tight_send_compact_size(vs, vs->tight.png.offset); -- vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset); -- buffer_reset(&vs->tight.png); -+ tight_send_compact_size(vs, vs->tight->png.offset); -+ vnc_write(vs, vs->tight->png.buffer, vs->tight->png.offset); -+ buffer_reset(&vs->tight->png); - return 1; - } - #endif /* CONFIG_VNC_PNG */ - - static void vnc_tight_start(VncState *vs) - { -- buffer_reset(&vs->tight.tight); -+ buffer_reset(&vs->tight->tight); - - // make the output buffer be the zlib buffer, so we can compress it later -- vs->tight.tmp = vs->output; -- vs->output = vs->tight.tight; -+ vs->tight->tmp = vs->output; -+ vs->output = vs->tight->tight; - } - - static void vnc_tight_stop(VncState *vs) - { - // switch back to normal output/zlib buffers -- vs->tight.tight = vs->output; -- vs->output = vs->tight.tmp; -+ vs->tight->tight = vs->output; -+ vs->output = vs->tight->tmp; - } - - static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, -@@ -1426,9 +1429,9 @@ static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, - int ret; - - if (colors == 0) { -- if (force || (tight_jpeg_conf[vs->tight.quality].jpeg_full && -+ if (force || (tight_jpeg_conf[vs->tight->quality].jpeg_full && - tight_detect_smooth_image(vs, w, h))) { -- int quality = tight_conf[vs->tight.quality].jpeg_quality; -+ int quality = tight_conf[vs->tight->quality].jpeg_quality; - - ret = send_jpeg_rect(vs, x, y, w, h, quality); - } else { -@@ -1440,9 +1443,9 @@ static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, - ret = send_mono_rect(vs, x, y, w, h, bg, fg); - } else if (colors <= 256) { - if (force || (colors > 96 && -- tight_jpeg_conf[vs->tight.quality].jpeg_idx && -+ tight_jpeg_conf[vs->tight->quality].jpeg_idx && - tight_detect_smooth_image(vs, w, h))) { -- int quality = tight_conf[vs->tight.quality].jpeg_quality; -+ int quality = tight_conf[vs->tight->quality].jpeg_quality; - - ret = send_jpeg_rect(vs, x, y, w, h, quality); - } else { -@@ -1480,20 +1483,20 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) - qemu_thread_atexit_add(&vnc_tight_cleanup_notifier); - } - -- vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); -+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight->type); - - vnc_tight_start(vs); - vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vnc_tight_stop(vs); - - #ifdef CONFIG_VNC_JPEG -- if (!vs->vd->non_adaptive && vs->tight.quality != (uint8_t)-1) { -+ if (!vs->vd->non_adaptive && vs->tight->quality != (uint8_t)-1) { - double freq = vnc_update_freq(vs, x, y, w, h); - -- if (freq < tight_jpeg_conf[vs->tight.quality].jpeg_freq_min) { -+ if (freq < tight_jpeg_conf[vs->tight->quality].jpeg_freq_min) { - allow_jpeg = false; - } -- if (freq >= tight_jpeg_conf[vs->tight.quality].jpeg_freq_threshold) { -+ if (freq >= tight_jpeg_conf[vs->tight->quality].jpeg_freq_threshold) { - force_jpeg = true; - vnc_sent_lossy_rect(vs, x, y, w, h); - } -@@ -1503,7 +1506,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) - colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, color_count_palette); - - #ifdef CONFIG_VNC_JPEG -- if (allow_jpeg && vs->tight.quality != (uint8_t)-1) { -+ if (allow_jpeg && vs->tight->quality != (uint8_t)-1) { - ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, - color_count_palette, force_jpeg); - } else { -@@ -1520,7 +1523,7 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) - - static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h) - { -- vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); -+ vnc_framebuffer_update(vs, x, y, w, h, vs->tight->type); - - vnc_tight_start(vs); - vnc_raw_send_framebuffer_update(vs, x, y, w, h); -@@ -1538,8 +1541,8 @@ static int send_rect_simple(VncState *vs, int x, int y, int w, int h, - int rw, rh; - int n = 0; - -- max_size = tight_conf[vs->tight.compression].max_rect_size; -- max_width = tight_conf[vs->tight.compression].max_rect_width; -+ max_size = tight_conf[vs->tight->compression].max_rect_size; -+ max_width = tight_conf[vs->tight->compression].max_rect_width; - - if (split && (w > max_width || w * h > max_size)) { - max_sub_width = (w > max_width) ? max_width : w; -@@ -1648,16 +1651,16 @@ static int tight_send_framebuffer_update(VncState *vs, int x, int y, - - if (vs->client_pf.bytes_per_pixel == 4 && vs->client_pf.rmax == 0xFF && - vs->client_pf.bmax == 0xFF && vs->client_pf.gmax == 0xFF) { -- vs->tight.pixel24 = true; -+ vs->tight->pixel24 = true; - } else { -- vs->tight.pixel24 = false; -+ vs->tight->pixel24 = false; - } - - #ifdef CONFIG_VNC_JPEG -- if (vs->tight.quality != (uint8_t)-1) { -+ if (vs->tight->quality != (uint8_t)-1) { - double freq = vnc_update_freq(vs, x, y, w, h); - -- if (freq > tight_jpeg_conf[vs->tight.quality].jpeg_freq_threshold) { -+ if (freq > tight_jpeg_conf[vs->tight->quality].jpeg_freq_threshold) { - return send_rect_simple(vs, x, y, w, h, false); - } - } -@@ -1669,8 +1672,8 @@ static int tight_send_framebuffer_update(VncState *vs, int x, int y, - - /* Calculate maximum number of rows in one non-solid rectangle. */ - -- max_rows = tight_conf[vs->tight.compression].max_rect_size; -- max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w); -+ max_rows = tight_conf[vs->tight->compression].max_rect_size; -+ max_rows /= MIN(tight_conf[vs->tight->compression].max_rect_width, w); - - return find_large_solid_color_rect(vs, x, y, w, h, max_rows); - } -@@ -1678,33 +1681,33 @@ static int tight_send_framebuffer_update(VncState *vs, int x, int y, - int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) - { -- vs->tight.type = VNC_ENCODING_TIGHT; -+ vs->tight->type = VNC_ENCODING_TIGHT; - return tight_send_framebuffer_update(vs, x, y, w, h); - } - - int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) - { -- vs->tight.type = VNC_ENCODING_TIGHT_PNG; -+ vs->tight->type = VNC_ENCODING_TIGHT_PNG; - return tight_send_framebuffer_update(vs, x, y, w, h); - } - - void vnc_tight_clear(VncState *vs) - { - int i; -- for (i=0; itight.stream); i++) { -- if (vs->tight.stream[i].opaque) { -- deflateEnd(&vs->tight.stream[i]); -+ for (i = 0; i < ARRAY_SIZE(vs->tight->stream); i++) { -+ if (vs->tight->stream[i].opaque) { -+ deflateEnd(&vs->tight->stream[i]); - } - } - -- buffer_free(&vs->tight.tight); -- buffer_free(&vs->tight.zlib); -- buffer_free(&vs->tight.gradient); -+ buffer_free(&vs->tight->tight); -+ buffer_free(&vs->tight->zlib); -+ buffer_free(&vs->tight->gradient); - #ifdef CONFIG_VNC_JPEG -- buffer_free(&vs->tight.jpeg); -+ buffer_free(&vs->tight->jpeg); - #endif - #ifdef CONFIG_VNC_PNG -- buffer_free(&vs->tight.png); -+ buffer_free(&vs->tight->png); - #endif - } -diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c -index 33e9df2f6a..900ae5b30f 100644 ---- a/ui/vnc-enc-zlib.c -+++ b/ui/vnc-enc-zlib.c -@@ -76,7 +76,8 @@ static int vnc_zlib_stop(VncState *vs) - zstream->zalloc = vnc_zlib_zalloc; - zstream->zfree = vnc_zlib_zfree; - -- err = deflateInit2(zstream, vs->tight.compression, Z_DEFLATED, MAX_WBITS, -+ err = deflateInit2(zstream, vs->tight->compression, Z_DEFLATED, -+ MAX_WBITS, - MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); - - if (err != Z_OK) { -@@ -84,16 +85,16 @@ static int vnc_zlib_stop(VncState *vs) - return -1; - } - -- vs->zlib.level = vs->tight.compression; -+ vs->zlib.level = vs->tight->compression; - zstream->opaque = vs; - } - -- if (vs->tight.compression != vs->zlib.level) { -- if (deflateParams(zstream, vs->tight.compression, -+ if (vs->tight->compression != vs->zlib.level) { -+ if (deflateParams(zstream, vs->tight->compression, - Z_DEFAULT_STRATEGY) != Z_OK) { - return -1; - } -- vs->zlib.level = vs->tight.compression; -+ vs->zlib.level = vs->tight->compression; - } - - // reserve memory in output buffer -diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c -index 7493a84723..17fd28a2e2 100644 ---- a/ui/vnc-enc-zrle.c -+++ b/ui/vnc-enc-zrle.c -@@ -37,18 +37,18 @@ static const int bits_per_packed_pixel[] = { - - static void vnc_zrle_start(VncState *vs) - { -- buffer_reset(&vs->zrle.zrle); -+ buffer_reset(&vs->zrle->zrle); - - /* make the output buffer be the zlib buffer, so we can compress it later */ -- vs->zrle.tmp = vs->output; -- vs->output = vs->zrle.zrle; -+ vs->zrle->tmp = vs->output; -+ vs->output = vs->zrle->zrle; - } - - static void vnc_zrle_stop(VncState *vs) - { - /* switch back to normal output/zlib buffers */ -- vs->zrle.zrle = vs->output; -- vs->output = vs->zrle.tmp; -+ vs->zrle->zrle = vs->output; -+ vs->output = vs->zrle->tmp; - } - - static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h, -@@ -56,24 +56,24 @@ static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h, - { - Buffer tmp; - -- buffer_reset(&vs->zrle.fb); -- buffer_reserve(&vs->zrle.fb, w * h * bpp + bpp); -+ buffer_reset(&vs->zrle->fb); -+ buffer_reserve(&vs->zrle->fb, w * h * bpp + bpp); - - tmp = vs->output; -- vs->output = vs->zrle.fb; -+ vs->output = vs->zrle->fb; - - vnc_raw_send_framebuffer_update(vs, x, y, w, h); - -- vs->zrle.fb = vs->output; -+ vs->zrle->fb = vs->output; - vs->output = tmp; -- return vs->zrle.fb.buffer; -+ return vs->zrle->fb.buffer; - } - - static int zrle_compress_data(VncState *vs, int level) - { -- z_streamp zstream = &vs->zrle.stream; -+ z_streamp zstream = &vs->zrle->stream; - -- buffer_reset(&vs->zrle.zlib); -+ buffer_reset(&vs->zrle->zlib); - - if (zstream->opaque != vs) { - int err; -@@ -93,13 +93,13 @@ static int zrle_compress_data(VncState *vs, int level) - } - - /* reserve memory in output buffer */ -- buffer_reserve(&vs->zrle.zlib, vs->zrle.zrle.offset + 64); -+ buffer_reserve(&vs->zrle->zlib, vs->zrle->zrle.offset + 64); - - /* set pointers */ -- zstream->next_in = vs->zrle.zrle.buffer; -- zstream->avail_in = vs->zrle.zrle.offset; -- zstream->next_out = vs->zrle.zlib.buffer + vs->zrle.zlib.offset; -- zstream->avail_out = vs->zrle.zlib.capacity - vs->zrle.zlib.offset; -+ zstream->next_in = vs->zrle->zrle.buffer; -+ zstream->avail_in = vs->zrle->zrle.offset; -+ zstream->next_out = vs->zrle->zlib.buffer + vs->zrle->zlib.offset; -+ zstream->avail_out = vs->zrle->zlib.capacity - vs->zrle->zlib.offset; - zstream->data_type = Z_BINARY; - - /* start encoding */ -@@ -108,8 +108,8 @@ static int zrle_compress_data(VncState *vs, int level) - return -1; - } - -- vs->zrle.zlib.offset = vs->zrle.zlib.capacity - zstream->avail_out; -- return vs->zrle.zlib.offset; -+ vs->zrle->zlib.offset = vs->zrle->zlib.capacity - zstream->avail_out; -+ return vs->zrle->zlib.offset; - } - - /* Try to work out whether to use RLE and/or a palette. We do this by -@@ -259,14 +259,14 @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y, - size_t bytes; - int zywrle_level; - -- if (vs->zrle.type == VNC_ENCODING_ZYWRLE) { -- if (!vs->vd->lossy || vs->tight.quality == (uint8_t)-1 -- || vs->tight.quality == 9) { -+ if (vs->zrle->type == VNC_ENCODING_ZYWRLE) { -+ if (!vs->vd->lossy || vs->tight->quality == (uint8_t)-1 -+ || vs->tight->quality == 9) { - zywrle_level = 0; -- vs->zrle.type = VNC_ENCODING_ZRLE; -- } else if (vs->tight.quality < 3) { -+ vs->zrle->type = VNC_ENCODING_ZRLE; -+ } else if (vs->tight->quality < 3) { - zywrle_level = 3; -- } else if (vs->tight.quality < 6) { -+ } else if (vs->tight->quality < 6) { - zywrle_level = 2; - } else { - zywrle_level = 1; -@@ -337,30 +337,30 @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y, - - vnc_zrle_stop(vs); - bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION); -- vnc_framebuffer_update(vs, x, y, w, h, vs->zrle.type); -+ vnc_framebuffer_update(vs, x, y, w, h, vs->zrle->type); - vnc_write_u32(vs, bytes); -- vnc_write(vs, vs->zrle.zlib.buffer, vs->zrle.zlib.offset); -+ vnc_write(vs, vs->zrle->zlib.buffer, vs->zrle->zlib.offset); - return 1; - } - - int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) - { -- vs->zrle.type = VNC_ENCODING_ZRLE; -+ vs->zrle->type = VNC_ENCODING_ZRLE; - return zrle_send_framebuffer_update(vs, x, y, w, h); - } - - int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) - { -- vs->zrle.type = VNC_ENCODING_ZYWRLE; -+ vs->zrle->type = VNC_ENCODING_ZYWRLE; - return zrle_send_framebuffer_update(vs, x, y, w, h); - } - - void vnc_zrle_clear(VncState *vs) - { -- if (vs->zrle.stream.opaque) { -- deflateEnd(&vs->zrle.stream); -+ if (vs->zrle->stream.opaque) { -+ deflateEnd(&vs->zrle->stream); - } -- buffer_free(&vs->zrle.zrle); -- buffer_free(&vs->zrle.fb); -- buffer_free(&vs->zrle.zlib); -+ buffer_free(&vs->zrle->zrle); -+ buffer_free(&vs->zrle->fb); -+ buffer_free(&vs->zrle->zlib); - } -diff --git a/ui/vnc-enc-zrle.inc.c b/ui/vnc-enc-zrle.inc.c -index abf6b86e4e..c107d8affc 100644 ---- a/ui/vnc-enc-zrle.inc.c -+++ b/ui/vnc-enc-zrle.inc.c -@@ -96,7 +96,7 @@ static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, - static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, - int zywrle_level) - { -- VncPalette *palette = &vs->zrle.palette; -+ VncPalette *palette = &vs->zrle->palette; - - int runs = 0; - int single_pixels = 0; -diff --git a/ui/vnc.c b/ui/vnc.c -index 38f92bfca3..e0756ca53a 100644 ---- a/ui/vnc.c -+++ b/ui/vnc.c -@@ -1304,6 +1304,8 @@ void vnc_disconnect_finish(VncState *vs) - object_unref(OBJECT(vs->sioc)); - vs->sioc = NULL; - vs->magic = 0; -+ g_free(vs->zrle); -+ g_free(vs->tight); - g_free(vs); - } - -@@ -2055,8 +2057,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) - - vs->features = 0; - vs->vnc_encoding = 0; -- vs->tight.compression = 9; -- vs->tight.quality = -1; /* Lossless by default */ -+ vs->tight->compression = 9; -+ vs->tight->quality = -1; /* Lossless by default */ - vs->absolute = -1; - - /* -@@ -2124,11 +2126,11 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) - vs->features |= VNC_FEATURE_LED_STATE_MASK; - break; - case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: -- vs->tight.compression = (enc & 0x0F); -+ vs->tight->compression = (enc & 0x0F); - break; - case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: - if (vs->vd->lossy) { -- vs->tight.quality = (enc & 0x0F); -+ vs->tight->quality = (enc & 0x0F); - } - break; - default: -@@ -3031,6 +3033,8 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, - int i; - - trace_vnc_client_connect(vs, sioc); -+ vs->zrle = g_new0(VncZrle, 1); -+ vs->tight = g_new0(VncTight, 1); - vs->magic = VNC_MAGIC; - vs->sioc = sioc; - object_ref(OBJECT(vs->sioc)); -@@ -3042,19 +3046,19 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, - buffer_init(&vs->output, "vnc-output/%p", sioc); - buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); - -- buffer_init(&vs->tight.tight, "vnc-tight/%p", sioc); -- buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%p", sioc); -- buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%p", sioc); -+ buffer_init(&vs->tight->tight, "vnc-tight/%p", sioc); -+ buffer_init(&vs->tight->zlib, "vnc-tight-zlib/%p", sioc); -+ buffer_init(&vs->tight->gradient, "vnc-tight-gradient/%p", sioc); - #ifdef CONFIG_VNC_JPEG -- buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%p", sioc); -+ buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc); - #endif - #ifdef CONFIG_VNC_PNG -- buffer_init(&vs->tight.png, "vnc-tight-png/%p", sioc); -+ buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); - #endif - buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); -- buffer_init(&vs->zrle.zrle, "vnc-zrle/%p", sioc); -- buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%p", sioc); -- buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%p", sioc); -+ buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc); -+ buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc); -+ buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc); - - if (skipauth) { - vs->auth = VNC_AUTH_NONE; -diff --git a/ui/vnc.h b/ui/vnc.h -index 2f84db3142..75888e1558 100644 ---- a/ui/vnc.h -+++ b/ui/vnc.h -@@ -337,10 +337,10 @@ struct VncState - /* Encoding specific, if you add something here, don't forget to - * update vnc_async_encoding_start() - */ -- VncTight tight; -+ VncTight *tight; - VncZlib zlib; - VncHextile hextile; -- VncZrle zrle; -+ VncZrle *zrle; - VncZywrle zywrle; - - Notifier mouse_mode_notifier; --- -2.20.1 - diff --git a/debian/patches/extra/0004-util-add-slirp_fmt-helpers.patch b/debian/patches/extra/0004-util-add-slirp_fmt-helpers.patch deleted file mode 100644 index af944f8..0000000 --- a/debian/patches/extra/0004-util-add-slirp_fmt-helpers.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= -Date: Mon, 27 Jan 2020 10:24:09 +0100 -Subject: [PATCH 1/2] util: add slirp_fmt() helpers -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Various calls to snprintf() in libslirp assume that snprintf() returns -"only" the number of bytes written (excluding terminating NUL). - -https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04 - -"Upon successful completion, the snprintf() function shall return the -number of bytes that would be written to s had n been sufficiently -large excluding the terminating null byte." - -Introduce slirp_fmt() that handles several pathological cases the -way libslirp usually expect: - -- treat error as fatal (instead of silently returning -1) - -- fmt0() will always \0 end - -- return the number of bytes actually written (instead of what would -have been written, which would usually result in OOB later), including -the ending \0 for fmt0() - -- warn if truncation happened (instead of ignoring) - -Other less common cases can still be handled with strcpy/snprintf() etc. - -Signed-off-by: Marc-André Lureau -Reviewed-by: Samuel Thibault -Message-Id: <20200127092414.169796-2-marcandre.lureau@redhat.com> -Signed-off-by: Oguz Bektas ---- - slirp/src/util.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ - slirp/src/util.h | 3 +++ - 2 files changed, 65 insertions(+) - -diff --git a/slirp/src/util.c b/slirp/src/util.c -index e596087..e3b6257 100644 ---- a/slirp/src/util.c -+++ b/slirp/src/util.c -@@ -364,3 +364,65 @@ void slirp_pstrcpy(char *buf, int buf_size, const char *str) - } - *q = '\0'; - } -+ -+static int slirp_vsnprintf(char *str, size_t size, -+ const char *format, va_list args) -+{ -+ int rv = vsnprintf(str, size, format, args); -+ -+ if (rv < 0) { -+ g_error("vsnprintf() failed: %s", g_strerror(errno)); -+ } -+ -+ return rv; -+} -+ -+/* -+ * A snprintf()-like function that: -+ * - returns the number of bytes written (excluding optional \0-ending) -+ * - dies on error -+ * - warn on truncation -+ */ -+int slirp_fmt(char *str, size_t size, const char *format, ...) -+{ -+ va_list args; -+ int rv; -+ -+ va_start(args, format); -+ rv = slirp_vsnprintf(str, size, format, args); -+ va_end(args); -+ -+ if (rv > size) { -+ g_critical("vsnprintf() truncation"); -+ } -+ -+ return MIN(rv, size); -+} -+ -+/* -+ * A snprintf()-like function that: -+ * - always \0-end (unless size == 0) -+ * - returns the number of bytes actually written, including \0 ending -+ * - dies on error -+ * - warn on truncation -+ */ -+int slirp_fmt0(char *str, size_t size, const char *format, ...) -+{ -+ va_list args; -+ int rv; -+ -+ va_start(args, format); -+ rv = slirp_vsnprintf(str, size, format, args); -+ va_end(args); -+ -+ if (rv >= size) { -+ g_critical("vsnprintf() truncation"); -+ if (size > 0) -+ str[size - 1] = '\0'; -+ rv = size; -+ } else { -+ rv += 1; /* include \0 */ -+ } -+ -+ return rv; -+} -diff --git a/slirp/src/util.h b/slirp/src/util.h -index 3c6223c..0558dfc 100644 ---- a/slirp/src/util.h -+++ b/slirp/src/util.h -@@ -177,4 +177,7 @@ static inline int slirp_socket_set_fast_reuse(int fd) - - void slirp_pstrcpy(char *buf, int buf_size, const char *str); - -+int slirp_fmt(char *str, size_t size, const char *format, ...); -+int slirp_fmt0(char *str, size_t size, const char *format, ...); -+ - #endif --- -2.20.1 - diff --git a/debian/patches/extra/0005-tcp_emu-fix-unsafe-snprintf-usages.patch b/debian/patches/extra/0005-tcp_emu-fix-unsafe-snprintf-usages.patch deleted file mode 100644 index 099fecd..0000000 --- a/debian/patches/extra/0005-tcp_emu-fix-unsafe-snprintf-usages.patch +++ /dev/null @@ -1,135 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= -Date: Mon, 27 Jan 2020 10:24:14 +0100 -Subject: [PATCH 2/2] tcp_emu: fix unsafe snprintf() usages -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Various calls to snprintf() assume that snprintf() returns "only" the -number of bytes written (excluding terminating NUL). - -https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04 - -"Upon successful completion, the snprintf() function shall return the -number of bytes that would be written to s had n been sufficiently -large excluding the terminating null byte." - -Before patch ce131029, if there isn't enough room in "m_data" for the -"DCC ..." message, we overflow "m_data". - -After the patch, if there isn't enough room for the same, we don't -overflow "m_data", but we set "m_len" out-of-bounds. The next time an -access is bounded by "m_len", we'll have a buffer overflow then. - -Use slirp_fmt*() to fix potential OOB memory access. - -Reported-by: Laszlo Ersek -Signed-off-by: Marc-André Lureau -Reviewed-by: Samuel Thibault -Message-Id: <20200127092414.169796-7-marcandre.lureau@redhat.com> -Signed-off-by: Oguz Bektas ---- - slirp/src/tcp_subr.c | 44 +++++++++++++++++++++----------------------- - 1 file changed, 21 insertions(+), 23 deletions(-) - -diff --git a/slirp/src/tcp_subr.c b/slirp/src/tcp_subr.c -index d6dd133..93b3488 100644 ---- a/slirp/src/tcp_subr.c -+++ b/slirp/src/tcp_subr.c -@@ -655,8 +655,7 @@ int tcp_emu(struct socket *so, struct mbuf *m) - NTOHS(n1); - NTOHS(n2); - m_inc(m, snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1); -- m->m_len = snprintf(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); -- assert(m->m_len < M_ROOM(m)); -+ m->m_len = slirp_fmt(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); - } else { - *eol = '\r'; - } -@@ -696,9 +695,9 @@ int tcp_emu(struct socket *so, struct mbuf *m) - n4 = (laddr & 0xff); - - m->m_len = bptr - m->m_data; /* Adjust length */ -- m->m_len += snprintf(bptr, m->m_size - m->m_len, -- "ORT %d,%d,%d,%d,%d,%d\r\n%s", n1, n2, n3, n4, -- n5, n6, x == 7 ? buff : ""); -+ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), -+ "ORT %d,%d,%d,%d,%d,%d\r\n%s", -+ n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); - return 1; - } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) { - /* -@@ -731,11 +730,9 @@ int tcp_emu(struct socket *so, struct mbuf *m) - n4 = (laddr & 0xff); - - m->m_len = bptr - m->m_data; /* Adjust length */ -- m->m_len += -- snprintf(bptr, m->m_size - m->m_len, -- "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", -- n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); -- -+ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), -+ "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", -+ n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); - return 1; - } - -@@ -758,8 +755,8 @@ int tcp_emu(struct socket *so, struct mbuf *m) - if (m->m_data[m->m_len - 1] == '\0' && lport != 0 && - (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr, - htons(lport), SS_FACCEPTONCE)) != NULL) -- m->m_len = -- snprintf(m->m_data, m->m_size, "%d", ntohs(so->so_fport)) + 1; -+ m->m_len = slirp_fmt0(m->m_data, M_ROOM(m), -+ "%d", ntohs(so->so_fport)); - return 1; - - case EMU_IRC: -@@ -778,9 +775,10 @@ int tcp_emu(struct socket *so, struct mbuf *m) - return 1; - } - m->m_len = bptr - m->m_data; /* Adjust length */ -- m->m_len += snprintf(bptr, m->m_size, "DCC CHAT chat %lu %u%c\n", -- (unsigned long)ntohl(so->so_faddr.s_addr), -- ntohs(so->so_fport), 1); -+ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), -+ "DCC CHAT chat %lu %u%c\n", -+ (unsigned long)ntohl(so->so_faddr.s_addr), -+ ntohs(so->so_fport), 1); - } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, - &n1) == 4) { - if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), -@@ -788,10 +786,10 @@ int tcp_emu(struct socket *so, struct mbuf *m) - return 1; - } - m->m_len = bptr - m->m_data; /* Adjust length */ -- m->m_len += -- snprintf(bptr, m->m_size, "DCC SEND %s %lu %u %u%c\n", buff, -- (unsigned long)ntohl(so->so_faddr.s_addr), -- ntohs(so->so_fport), n1, 1); -+ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), -+ "DCC SEND %s %lu %u %u%c\n", buff, -+ (unsigned long)ntohl(so->so_faddr.s_addr), -+ ntohs(so->so_fport), n1, 1); - } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, - &n1) == 4) { - if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), -@@ -799,10 +797,10 @@ int tcp_emu(struct socket *so, struct mbuf *m) - return 1; - } - m->m_len = bptr - m->m_data; /* Adjust length */ -- m->m_len += -- snprintf(bptr, m->m_size, "DCC MOVE %s %lu %u %u%c\n", buff, -- (unsigned long)ntohl(so->so_faddr.s_addr), -- ntohs(so->so_fport), n1, 1); -+ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), -+ "DCC MOVE %s %lu %u %u%c\n", buff, -+ (unsigned long)ntohl(so->so_faddr.s_addr), -+ ntohs(so->so_fport), n1, 1); - } - return 1; - --- -2.20.1 - diff --git a/debian/patches/pve/0001-PVE-Config-block-file-change-locking-default-to-off.patch b/debian/patches/pve/0001-PVE-Config-block-file-change-locking-default-to-off.patch index a14d8c6..f581728 100644 --- a/debian/patches/pve/0001-PVE-Config-block-file-change-locking-default-to-off.patch +++ b/debian/patches/pve/0001-PVE-Config-block-file-change-locking-default-to-off.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 29 Nov 2017 11:11:46 +0100 -Subject: [PATCH] PVE: [Config] block/file: change locking default to off +Date: Tue, 10 Mar 2020 12:54:58 +0100 +Subject: [PATCH 01/32] PVE: [Config] block/file: change locking default to off 'auto' only checks whether the system generally supports OFD locks but not whether the storage the file resides on @@ -14,10 +14,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c -index 2184aa980c..71aa45ce5d 100644 +index 1b805bd938..44b49265ae 100644 --- a/block/file-posix.c +++ b/block/file-posix.c -@@ -444,7 +444,7 @@ static QemuOptsList raw_runtime_opts = { +@@ -449,7 +449,7 @@ static QemuOptsList raw_runtime_opts = { { .name = "locking", .type = QEMU_OPT_STRING, @@ -26,7 +26,7 @@ index 2184aa980c..71aa45ce5d 100644 }, { .name = "pr-manager", -@@ -533,7 +533,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, +@@ -538,7 +538,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, s->use_lock = false; break; case ON_OFF_AUTO_AUTO: diff --git a/debian/patches/pve/0002-PVE-Config-Adjust-network-script-path-to-etc-kvm.patch b/debian/patches/pve/0002-PVE-Config-Adjust-network-script-path-to-etc-kvm.patch index a3630b7..e9383bc 100644 --- a/debian/patches/pve/0002-PVE-Config-Adjust-network-script-path-to-etc-kvm.patch +++ b/debian/patches/pve/0002-PVE-Config-Adjust-network-script-path-to-etc-kvm.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:16:49 +0100 -Subject: [PATCH] PVE: [Config] Adjust network script path to /etc/kvm/ +Date: Tue, 10 Mar 2020 12:54:59 +0100 +Subject: [PATCH 02/32] PVE: [Config] Adjust network script path to /etc/kvm/ Signed-off-by: Thomas Lamprecht --- @@ -9,10 +9,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/net.h b/include/net/net.h -index acf0451fc4..4a64633577 100644 +index e175ba9677..5b9f099d21 100644 --- a/include/net/net.h +++ b/include/net/net.h -@@ -209,8 +209,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); +@@ -208,8 +208,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); int net_hub_id_for_client(NetClientState *nc, int *id); NetClientState *net_hub_port_find(int hub_id); diff --git a/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch b/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch index 45decdf..92b1e79 100644 --- a/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch +++ b/debian/patches/pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:30:21 +0100 -Subject: [PATCH] PVE: [Config] set the CPU model to kvm64/32 instead of +Date: Tue, 10 Mar 2020 12:55:00 +0100 +Subject: [PATCH 03/32] PVE: [Config] set the CPU model to kvm64/32 instead of qemu64/32 Signed-off-by: Thomas Lamprecht @@ -10,10 +10,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h -index 8b3dc5533e..1fea162e02 100644 +index cde2a16b94..3e73104bf9 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h -@@ -1725,9 +1725,9 @@ uint64_t cpu_get_tsc(CPUX86State *env); +@@ -1940,9 +1940,9 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define CPU_RESOLVING_TYPE TYPE_X86_CPU #ifdef TARGET_X86_64 diff --git a/debian/patches/pve/0004-PVE-Config-ui-spice-default-to-pve-certificates.patch b/debian/patches/pve/0004-PVE-Config-ui-spice-default-to-pve-certificates.patch index 8f76010..2e446cf 100644 --- a/debian/patches/pve/0004-PVE-Config-ui-spice-default-to-pve-certificates.patch +++ b/debian/patches/pve/0004-PVE-Config-ui-spice-default-to-pve-certificates.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:33:34 +0100 -Subject: [PATCH] PVE: [Config] ui/spice: default to pve certificates +Date: Tue, 10 Mar 2020 12:55:01 +0100 +Subject: [PATCH 04/32] PVE: [Config] ui/spice: default to pve certificates Signed-off-by: Thomas Lamprecht --- @@ -9,7 +9,7 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ui/spice-core.c b/ui/spice-core.c -index 2ffc3335f0..c95bbd6c77 100644 +index ecc2ec2c55..ca04965ead 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -668,32 +668,35 @@ void qemu_spice_init(void) diff --git a/debian/patches/pve/0005-PVE-Config-smm_available-false.patch b/debian/patches/pve/0005-PVE-Config-smm_available-false.patch index daf2e19..6b03c0e 100644 --- a/debian/patches/pve/0005-PVE-Config-smm_available-false.patch +++ b/debian/patches/pve/0005-PVE-Config-smm_available-false.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexandre Derumier -Date: Tue, 29 Sep 2015 15:37:44 +0200 -Subject: [PATCH] PVE: [Config] smm_available = false +Date: Tue, 10 Mar 2020 12:55:02 +0100 +Subject: [PATCH 05/32] PVE: [Config] smm_available = false Signed-off-by: Alexandre Derumier Signed-off-by: Thomas Lamprecht @@ -10,10 +10,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c -index d011733ff7..e20e189a5f 100644 +index ac08e63604..4bd9ab52a0 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c -@@ -2723,7 +2723,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) +@@ -2040,7 +2040,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) if (tcg_enabled() || qtest_enabled()) { smm_available = true; } else if (kvm_enabled()) { diff --git a/debian/patches/pve/0006-PVE-Config-glusterfs-no-default-logfile-if-daemonize.patch b/debian/patches/pve/0006-PVE-Config-glusterfs-no-default-logfile-if-daemonize.patch index f3c098b..37d5d86 100644 --- a/debian/patches/pve/0006-PVE-Config-glusterfs-no-default-logfile-if-daemonize.patch +++ b/debian/patches/pve/0006-PVE-Config-glusterfs-no-default-logfile-if-daemonize.patch @@ -1,7 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Mon, 24 Oct 2016 09:32:36 +0200 -Subject: [PATCH] PVE: [Config] glusterfs: no default logfile if daemonized +Date: Tue, 10 Mar 2020 12:55:03 +0100 +Subject: [PATCH 06/32] PVE: [Config] glusterfs: no default logfile if + daemonized Signed-off-by: Thomas Lamprecht --- @@ -9,7 +10,7 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/block/gluster.c b/block/gluster.c -index f64dc5b01e..061cab48c0 100644 +index 4fa4a77a47..bfb57ba098 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -42,7 +42,7 @@ diff --git a/debian/patches/pve/0007-PVE-Config-rbd-block-rbd-disable-rbd_cache_writethro.patch b/debian/patches/pve/0007-PVE-Config-rbd-block-rbd-disable-rbd_cache_writethro.patch index ca452d8..8183e39 100644 --- a/debian/patches/pve/0007-PVE-Config-rbd-block-rbd-disable-rbd_cache_writethro.patch +++ b/debian/patches/pve/0007-PVE-Config-rbd-block-rbd-disable-rbd_cache_writethro.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Fri, 31 Aug 2018 09:32:55 +0200 -Subject: [PATCH] PVE: [Config] rbd: block: rbd: disable +Date: Tue, 10 Mar 2020 12:55:04 +0100 +Subject: [PATCH 07/32] PVE: [Config] rbd: block: rbd: disable rbd_cache_writethrough_until_flush Either the cache mode asks for a cache or not. There's no @@ -18,10 +18,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 2 insertions(+) diff --git a/block/rbd.c b/block/rbd.c -index 59757b3120..d00c9d2d12 100644 +index 027cbcc695..3ac7ff7bd5 100644 --- a/block/rbd.c +++ b/block/rbd.c -@@ -636,6 +636,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, +@@ -637,6 +637,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, rados_conf_set(*cluster, "rbd_cache", "false"); } diff --git a/debian/patches/pve/0008-PVE-Up-qmp-add-get_link_status.patch b/debian/patches/pve/0008-PVE-Up-qmp-add-get_link_status.patch index ab31e94..fb0d8db 100644 --- a/debian/patches/pve/0008-PVE-Up-qmp-add-get_link_status.patch +++ b/debian/patches/pve/0008-PVE-Up-qmp-add-get_link_status.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 16:34:41 +0100 -Subject: [PATCH] PVE: [Up] qmp: add get_link_status +Date: Tue, 10 Mar 2020 12:55:05 +0100 +Subject: [PATCH 08/32] PVE: [Up] qmp: add get_link_status Signed-off-by: Thomas Lamprecht --- @@ -11,10 +11,10 @@ Signed-off-by: Thomas Lamprecht 3 files changed, 43 insertions(+) diff --git a/net/net.c b/net/net.c -index 7d4098254f..c6d5e66bfc 100644 +index 84aa6d8d00..f548202ec6 100644 --- a/net/net.c +++ b/net/net.c -@@ -1347,6 +1347,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict) +@@ -1349,6 +1349,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict) } } @@ -49,7 +49,7 @@ index 7d4098254f..c6d5e66bfc 100644 { NetClientState *nc; diff --git a/qapi/net.json b/qapi/net.json -index 728990f4fb..d53c66320b 100644 +index 335295be50..7f3ea194c8 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -34,6 +34,21 @@ @@ -75,7 +75,7 @@ index 728990f4fb..d53c66320b 100644 # @netdev_add: # diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json -index 38af54d6b3..d6a4177935 100644 +index 9751b11f8f..a449f158e1 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -61,6 +61,7 @@ diff --git a/debian/patches/pve/0009-PVE-Up-glusterfs-allow-partial-reads.patch b/debian/patches/pve/0009-PVE-Up-glusterfs-allow-partial-reads.patch index 4da3e7f..a83b824 100644 --- a/debian/patches/pve/0009-PVE-Up-glusterfs-allow-partial-reads.patch +++ b/debian/patches/pve/0009-PVE-Up-glusterfs-allow-partial-reads.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 30 Nov 2016 10:27:47 +0100 -Subject: [PATCH] PVE: [Up] glusterfs: allow partial reads +Date: Tue, 10 Mar 2020 12:55:06 +0100 +Subject: [PATCH 09/32] PVE: [Up] glusterfs: allow partial reads This should deal with qemu bug #1644754 until upstream decides which way to go. The general direction seems to be @@ -16,7 +16,7 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/block/gluster.c b/block/gluster.c -index 061cab48c0..0ed1ec5856 100644 +index bfb57ba098..81fff09c6c 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -57,6 +57,7 @@ typedef struct GlusterAIOCB { @@ -59,7 +59,7 @@ index 061cab48c0..0ed1ec5856 100644 ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0, gluster_finish_aiocb, &acb); } -@@ -1279,6 +1285,7 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) +@@ -1280,6 +1286,7 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); @@ -67,7 +67,7 @@ index 061cab48c0..0ed1ec5856 100644 ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb); if (ret < 0) { -@@ -1325,6 +1332,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, +@@ -1326,6 +1333,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); diff --git a/debian/patches/pve/0010-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch b/debian/patches/pve/0010-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch index 17a3e61..0fe0880 100644 --- a/debian/patches/pve/0010-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch +++ b/debian/patches/pve/0010-PVE-Up-qemu-img-return-success-on-info-without-snaps.patch @@ -1,7 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:18:46 +0100 -Subject: [PATCH] PVE: [Up] qemu-img: return success on info without snapshots +Date: Tue, 10 Mar 2020 12:55:07 +0100 +Subject: [PATCH 10/32] PVE: [Up] qemu-img: return success on info without + snapshots Signed-off-by: Thomas Lamprecht --- @@ -9,10 +10,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c -index 79983772de..c64f260876 100644 +index 95a24b9762..12211bed76 100644 --- a/qemu-img.c +++ b/qemu-img.c -@@ -2773,7 +2773,8 @@ static int img_info(int argc, char **argv) +@@ -2791,7 +2791,8 @@ static int img_info(int argc, char **argv) list = collect_image_info_list(image_opts, filename, fmt, chain, force_share); if (!list) { diff --git a/debian/patches/pve/0011-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch b/debian/patches/pve/0011-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch index 0c5a9c0..6340585 100644 --- a/debian/patches/pve/0011-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch +++ b/debian/patches/pve/0011-PVE-Up-qemu-img-dd-add-osize-and-read-from-to-stdin-.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Fri, 23 Jun 2017 12:01:43 +0200 -Subject: [PATCH] PVE: [Up] qemu-img dd: add osize and read from/to +Date: Tue, 10 Mar 2020 12:55:08 +0100 +Subject: [PATCH 11/32] PVE: [Up] qemu-img dd: add osize and read from/to stdin/stdout Neither convert nor dd were previously able to write to or @@ -53,10 +53,10 @@ index 1c93e6d185..8094abb3ee 100644 DEF("info", img_info, diff --git a/qemu-img.c b/qemu-img.c -index c64f260876..8129677d7a 100644 +index 12211bed76..d2516968c6 100644 --- a/qemu-img.c +++ b/qemu-img.c -@@ -4413,10 +4413,12 @@ out: +@@ -4405,10 +4405,12 @@ out: #define C_IF 04 #define C_OF 010 #define C_SKIP 020 @@ -69,7 +69,7 @@ index c64f260876..8129677d7a 100644 }; struct DdIo { -@@ -4495,6 +4497,20 @@ static int img_dd_skip(const char *arg, +@@ -4487,6 +4489,20 @@ static int img_dd_skip(const char *arg, return 0; } @@ -90,7 +90,7 @@ index c64f260876..8129677d7a 100644 static int img_dd(int argc, char **argv) { int ret = 0; -@@ -4535,6 +4551,7 @@ static int img_dd(int argc, char **argv) +@@ -4527,6 +4543,7 @@ static int img_dd(int argc, char **argv) { "if", img_dd_if, C_IF }, { "of", img_dd_of, C_OF }, { "skip", img_dd_skip, C_SKIP }, @@ -98,7 +98,7 @@ index c64f260876..8129677d7a 100644 { NULL, NULL, 0 } }; const struct option long_options[] = { -@@ -4613,8 +4630,13 @@ static int img_dd(int argc, char **argv) +@@ -4605,8 +4622,13 @@ static int img_dd(int argc, char **argv) arg = NULL; } @@ -114,7 +114,7 @@ index c64f260876..8129677d7a 100644 ret = -1; goto out; } -@@ -4626,85 +4648,101 @@ static int img_dd(int argc, char **argv) +@@ -4618,85 +4640,101 @@ static int img_dd(int argc, char **argv) goto out; } @@ -280,7 +280,7 @@ index c64f260876..8129677d7a 100644 } if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || -@@ -4722,11 +4760,17 @@ static int img_dd(int argc, char **argv) +@@ -4714,11 +4752,17 @@ static int img_dd(int argc, char **argv) for (out_pos = 0; in_pos < size; block_count++) { int in_ret, out_ret; @@ -302,7 +302,7 @@ index c64f260876..8129677d7a 100644 } if (in_ret < 0) { error_report("error while reading from input image file: %s", -@@ -4736,9 +4780,13 @@ static int img_dd(int argc, char **argv) +@@ -4728,9 +4772,13 @@ static int img_dd(int argc, char **argv) } in_pos += in_ret; diff --git a/debian/patches/pve/0012-PVE-Up-qemu-img-dd-add-isize-parameter.patch b/debian/patches/pve/0012-PVE-Up-qemu-img-dd-add-isize-parameter.patch index dfd592f..926905a 100644 --- a/debian/patches/pve/0012-PVE-Up-qemu-img-dd-add-isize-parameter.patch +++ b/debian/patches/pve/0012-PVE-Up-qemu-img-dd-add-isize-parameter.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Fri, 23 Feb 2018 08:43:18 +0100 -Subject: [PATCH] PVE: [Up] qemu-img dd: add isize parameter +Date: Tue, 10 Mar 2020 12:55:09 +0100 +Subject: [PATCH 12/32] PVE: [Up] qemu-img dd: add isize parameter for writing small images from stdin to bigger ones @@ -15,10 +15,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/qemu-img.c b/qemu-img.c -index 8129677d7a..1b7f211368 100644 +index d2516968c6..8da1ea3951 100644 --- a/qemu-img.c +++ b/qemu-img.c -@@ -4414,11 +4414,13 @@ out: +@@ -4406,11 +4406,13 @@ out: #define C_OF 010 #define C_SKIP 020 #define C_OSIZE 040 @@ -32,7 +32,7 @@ index 8129677d7a..1b7f211368 100644 }; struct DdIo { -@@ -4511,6 +4513,20 @@ static int img_dd_osize(const char *arg, +@@ -4503,6 +4505,20 @@ static int img_dd_osize(const char *arg, return 0; } @@ -53,7 +53,7 @@ index 8129677d7a..1b7f211368 100644 static int img_dd(int argc, char **argv) { int ret = 0; -@@ -4525,12 +4541,14 @@ static int img_dd(int argc, char **argv) +@@ -4517,12 +4533,14 @@ static int img_dd(int argc, char **argv) int c, i; const char *out_fmt = "raw"; const char *fmt = NULL; @@ -69,7 +69,7 @@ index 8129677d7a..1b7f211368 100644 }; struct DdIo in = { .bsz = 512, /* Block size is by default 512 bytes */ -@@ -4552,6 +4570,7 @@ static int img_dd(int argc, char **argv) +@@ -4544,6 +4562,7 @@ static int img_dd(int argc, char **argv) { "of", img_dd_of, C_OF }, { "skip", img_dd_skip, C_SKIP }, { "osize", img_dd_osize, C_OSIZE }, @@ -77,7 +77,7 @@ index 8129677d7a..1b7f211368 100644 { NULL, NULL, 0 } }; const struct option long_options[] = { -@@ -4758,14 +4777,18 @@ static int img_dd(int argc, char **argv) +@@ -4750,14 +4769,18 @@ static int img_dd(int argc, char **argv) in.buf = g_new(uint8_t, in.bsz); diff --git a/debian/patches/pve/0013-PVE-Up-qemu-img-dd-add-n-skip_create.patch b/debian/patches/pve/0013-PVE-Up-qemu-img-dd-add-n-skip_create.patch index 0fc237d..dc382fa 100644 --- a/debian/patches/pve/0013-PVE-Up-qemu-img-dd-add-n-skip_create.patch +++ b/debian/patches/pve/0013-PVE-Up-qemu-img-dd-add-n-skip_create.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Alexandre Derumier -Date: Wed, 21 Mar 2018 08:51:34 +0100 -Subject: [PATCH] PVE: [Up] qemu-img dd : add -n skip_create +Date: Tue, 10 Mar 2020 12:55:10 +0100 +Subject: [PATCH 13/32] PVE: [Up] qemu-img dd : add -n skip_create Signed-off-by: Thomas Lamprecht --- @@ -9,10 +9,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/qemu-img.c b/qemu-img.c -index 1b7f211368..e14d2370c4 100644 +index 8da1ea3951..ea3edb4f04 100644 --- a/qemu-img.c +++ b/qemu-img.c -@@ -4543,7 +4543,7 @@ static int img_dd(int argc, char **argv) +@@ -4535,7 +4535,7 @@ static int img_dd(int argc, char **argv) const char *fmt = NULL; int64_t size = 0, readsize = 0; int64_t block_count = 0, out_pos, in_pos; @@ -21,7 +21,7 @@ index 1b7f211368..e14d2370c4 100644 struct DdInfo dd = { .flags = 0, .count = 0, -@@ -4581,7 +4581,7 @@ static int img_dd(int argc, char **argv) +@@ -4573,7 +4573,7 @@ static int img_dd(int argc, char **argv) { 0, 0, 0, 0 } }; @@ -30,7 +30,7 @@ index 1b7f211368..e14d2370c4 100644 if (c == EOF) { break; } -@@ -4601,6 +4601,9 @@ static int img_dd(int argc, char **argv) +@@ -4593,6 +4593,9 @@ static int img_dd(int argc, char **argv) case 'h': help(); break; @@ -40,7 +40,7 @@ index 1b7f211368..e14d2370c4 100644 case 'U': force_share = true; break; -@@ -4741,13 +4744,15 @@ static int img_dd(int argc, char **argv) +@@ -4733,13 +4736,15 @@ static int img_dd(int argc, char **argv) size - in.bsz * in.offset, &error_abort); } diff --git a/debian/patches/pve/0014-PVE-virtio-balloon-improve-query-balloon.patch b/debian/patches/pve/0014-PVE-virtio-balloon-improve-query-balloon.patch index baf8764..6004544 100644 --- a/debian/patches/pve/0014-PVE-virtio-balloon-improve-query-balloon.patch +++ b/debian/patches/pve/0014-PVE-virtio-balloon-improve-query-balloon.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:27:49 +0100 -Subject: [PATCH] PVE: virtio-balloon: improve query-balloon +Date: Tue, 10 Mar 2020 12:55:11 +0100 +Subject: [PATCH 14/32] PVE: virtio-balloon: improve query-balloon Actually provide memory information via the query-balloon command. @@ -14,10 +14,10 @@ Signed-off-by: Thomas Lamprecht 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c -index 25de154307..7c09716035 100644 +index 40b04f5180..76e907e628 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c -@@ -712,8 +712,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f, +@@ -713,8 +713,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f, static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; @@ -58,10 +58,10 @@ index 25de154307..7c09716035 100644 static void virtio_balloon_to_target(void *opaque, ram_addr_t target) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c -index 5ca3ebe942..1b32c59329 100644 +index b2551c16d1..2e725ed818 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c -@@ -870,7 +870,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) +@@ -854,7 +854,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) return; } @@ -99,7 +99,7 @@ index 5ca3ebe942..1b32c59329 100644 qapi_free_BalloonInfo(info); } diff --git a/qapi/misc.json b/qapi/misc.json -index a7fba7230c..2445c950cc 100644 +index 33b94e3589..ed65ed27e3 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -408,10 +408,30 @@ diff --git a/debian/patches/pve/0015-PVE-qapi-modify-query-machines.patch b/debian/patches/pve/0015-PVE-qapi-modify-query-machines.patch index 6f75ce6..998cb7c 100644 --- a/debian/patches/pve/0015-PVE-qapi-modify-query-machines.patch +++ b/debian/patches/pve/0015-PVE-qapi-modify-query-machines.patch @@ -1,37 +1,39 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:31:18 +0100 -Subject: [PATCH] PVE: qapi: modify query machines +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 12:55:12 +0100 +Subject: [PATCH 15/32] PVE: qapi: modify query machines provide '*is-current' in MachineInfo struct Signed-off-by: Thomas Lamprecht +Signed-off-by: Dietmar Maurer --- - hw/core/machine-qmp-cmds.c | 5 +++++ + hw/core/machine-qmp-cmds.c | 6 ++++++ qapi/machine.json | 4 +++- - 2 files changed, 8 insertions(+), 1 deletion(-) + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c -index 5bd95b8ab0..fd68f9baf8 100644 +index eed5aeb2f7..1953633e82 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c -@@ -229,6 +229,11 @@ MachineInfoList *qmp_query_machines(Error **errp) +@@ -230,6 +230,12 @@ MachineInfoList *qmp_query_machines(Error **errp) + info->hotpluggable_cpus = mc->has_hotpluggable_cpus; info->numa_mem_supported = mc->numa_mem_supported; info->deprecated = !!mc->deprecation_reason; - ++ + 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; + if (mc->default_cpu_type) { + info->default_cpu_type = g_strdup(mc->default_cpu_type); + info->has_default_cpu_type = true; diff --git a/qapi/machine.json b/qapi/machine.json -index 6db8a7e2ec..7b82c5f7f5 100644 +index ca26779f1a..cbdb6f6d66 100644 --- a/qapi/machine.json +++ b/qapi/machine.json -@@ -313,6 +313,8 @@ +@@ -336,6 +336,8 @@ # # @is-default: whether the machine is default # @@ -40,12 +42,12 @@ index 6db8a7e2ec..7b82c5f7f5 100644 # @cpu-max: maximum number of CPUs supported by the machine type # (since 1.5.0) # -@@ -329,7 +331,7 @@ +@@ -355,7 +357,7 @@ ## { 'struct': 'MachineInfo', 'data': { 'name': 'str', '*alias': 'str', - '*is-default': 'bool', 'cpu-max': 'int', + '*is-default': 'bool', '*is-current': 'bool', 'cpu-max': 'int', 'hotpluggable-cpus': 'bool', 'numa-mem-supported': 'bool', - 'deprecated': 'bool' } } + 'deprecated': 'bool', '*default-cpu-type': 'str' } } diff --git a/debian/patches/pve/0016-PVE-qapi-modify-spice-query.patch b/debian/patches/pve/0016-PVE-qapi-modify-spice-query.patch index 71a3c90..5b7bcf3 100644 --- a/debian/patches/pve/0016-PVE-qapi-modify-spice-query.patch +++ b/debian/patches/pve/0016-PVE-qapi-modify-spice-query.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:32:11 +0100 -Subject: [PATCH] PVE: qapi: modify spice query +Date: Tue, 10 Mar 2020 12:55:13 +0100 +Subject: [PATCH 16/32] PVE: qapi: modify spice query Provide the last ticket in the SpiceInfo struct optionally. @@ -12,7 +12,7 @@ Signed-off-by: Thomas Lamprecht 2 files changed, 8 insertions(+) diff --git a/qapi/ui.json b/qapi/ui.json -index 59e412139a..bcd781a1b9 100644 +index e04525d8b4..6127990e23 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -211,11 +211,14 @@ @@ -31,7 +31,7 @@ index 59e412139a..bcd781a1b9 100644 'if': 'defined(CONFIG_SPICE)' } diff --git a/ui/spice-core.c b/ui/spice-core.c -index c95bbd6c77..ccba92a6ed 100644 +index ca04965ead..243466c13d 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -539,6 +539,11 @@ SpiceInfo *qmp_query_spice(Error **errp) diff --git a/debian/patches/pve/0017-PVE-internal-snapshot-async.patch b/debian/patches/pve/0017-PVE-internal-snapshot-async.patch index 93fef3b..15918dc 100644 --- a/debian/patches/pve/0017-PVE-internal-snapshot-async.patch +++ b/debian/patches/pve/0017-PVE-internal-snapshot-async.patch @@ -1,9 +1,12 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 16:04:32 +0100 -Subject: [PATCH] PVE: internal snapshot async +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 13:49:27 +0100 +Subject: [PATCH 17/32] PVE: internal snapshot async + +Truncate at 1024 boundary (Fabian Ebner will send a patch for stable) Signed-off-by: Thomas Lamprecht +Signed-off-by: Dietmar Maurer --- Makefile.objs | 1 + hmp-commands-info.hx | 13 + @@ -14,13 +17,13 @@ Signed-off-by: Thomas Lamprecht qapi/migration.json | 34 +++ qapi/misc.json | 32 +++ qemu-options.hx | 13 + - savevm-async.c | 460 +++++++++++++++++++++++++++++++++++ + savevm-async.c | 464 +++++++++++++++++++++++++++++++++++ vl.c | 10 + - 11 files changed, 658 insertions(+) + 11 files changed, 662 insertions(+) create mode 100644 savevm-async.c diff --git a/Makefile.objs b/Makefile.objs -index 6a143dcd57..21dd93b58c 100644 +index 11ba1a36bd..f97b40f232 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -48,6 +48,7 @@ common-obj-y += bootdevice.o iothread.o @@ -32,7 +35,7 @@ index 6a143dcd57..21dd93b58c 100644 common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx -index c59444c461..444bd8e43d 100644 +index 257ee7d7a3..139e673bea 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -608,6 +608,19 @@ STEXI @@ -56,10 +59,10 @@ index c59444c461..444bd8e43d 100644 { diff --git a/hmp-commands.hx b/hmp-commands.hx -index bfa5681dd2..e075d413c0 100644 +index cfcc044ce4..104288322d 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx -@@ -1944,3 +1944,35 @@ ETEXI +@@ -1945,3 +1945,35 @@ ETEXI STEXI @end table ETEXI @@ -130,10 +133,10 @@ index a0e9511440..c6ee8295f0 100644 void hmp_screendump(Monitor *mon, const QDict *qdict); void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c -index 1b32c59329..39a8020367 100644 +index 2e725ed818..90aa34be25 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c -@@ -2640,6 +2640,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) +@@ -2607,6 +2607,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } @@ -198,10 +201,10 @@ index 1b32c59329..39a8020367 100644 { IOThreadInfoList *info_list = qmp_query_iothreads(NULL); diff --git a/qapi/migration.json b/qapi/migration.json -index 9cfbaf8c6c..e206355d56 100644 +index b7348d0c8b..2792409977 100644 --- a/qapi/migration.json +++ b/qapi/migration.json -@@ -219,6 +219,40 @@ +@@ -222,6 +222,40 @@ '*compression': 'CompressionStats', '*socket-address': ['SocketAddress'] } } @@ -243,12 +246,12 @@ index 9cfbaf8c6c..e206355d56 100644 # @query-migrate: # diff --git a/qapi/misc.json b/qapi/misc.json -index 2445c950cc..31029e3132 100644 +index ed65ed27e3..4c4618a574 100644 --- a/qapi/misc.json +++ b/qapi/misc.json -@@ -1384,6 +1384,38 @@ +@@ -1368,6 +1368,38 @@ ## - { 'command': 'query-target', 'returns': 'TargetInfo' } + { 'command': 'query-fdsets', 'returns': ['FdsetInfo'] } +## +# @savevm-start: @@ -286,10 +289,10 @@ index 2445c950cc..31029e3132 100644 # @AcpiTableOptions: # diff --git a/qemu-options.hx b/qemu-options.hx -index 9621e934c0..34994daafd 100644 +index 65c9473b73..4cb2681bfc 100644 --- a/qemu-options.hx +++ b/qemu-options.hx -@@ -3731,6 +3731,19 @@ STEXI +@@ -3818,6 +3818,19 @@ STEXI Start right away with a saved state (@code{loadvm} in monitor) ETEXI @@ -311,10 +314,10 @@ index 9621e934c0..34994daafd 100644 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) diff --git a/savevm-async.c b/savevm-async.c new file mode 100644 -index 0000000000..2149010bb8 +index 0000000000..54ceeae26c --- /dev/null +++ b/savevm-async.c -@@ -0,0 +1,460 @@ +@@ -0,0 +1,464 @@ +#include "qemu/osdep.h" +#include "migration/migration.h" +#include "migration/savevm.h" @@ -323,6 +326,7 @@ index 0000000000..2149010bb8 +#include "migration/ram.h" +#include "migration/qemu-file.h" +#include "sysemu/sysemu.h" ++#include "sysemu/runstate.h" +#include "block/block.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" @@ -332,6 +336,8 @@ index 0000000000..2149010bb8 +#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-block.h" +#include "qemu/cutils.h" ++#include "qemu/main-loop.h" ++#include "qemu/rcu.h" + +/* #define DEBUG_SAVEVM_STATE */ + @@ -421,10 +427,11 @@ index 0000000000..2149010bb8 + + if (snap_state.target) { + /* try to truncate, but ignore errors (will fail on block devices). -+ * note: bdrv_read() need whole blocks, so we round up ++ * note1: bdrv_read() need whole blocks, so we need to round up ++ * note2: PVE requires 1024 (BDRV_SECTOR_SIZE*2) alignment + */ -+ size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK; -+ blk_truncate(snap_state.target, size, PREALLOC_MODE_OFF, NULL); ++ size_t size = QEMU_ALIGN_UP(snap_state.bs_pos, BDRV_SECTOR_SIZE*2); ++ blk_truncate(snap_state.target, size, false, PREALLOC_MODE_OFF, NULL); + blk_op_unblock_all(snap_state.target, snap_state.blocker); + error_free(snap_state.blocker); + snap_state.blocker = NULL; @@ -455,7 +462,7 @@ index 0000000000..2149010bb8 + snap_state.state = SAVE_STATE_ERROR; +} + -+static int block_state_close(void *opaque) ++static int block_state_close(void *opaque, Error **errp) +{ + snap_state.file = NULL; + return blk_flush(snap_state.target); @@ -474,7 +481,7 @@ index 0000000000..2149010bb8 +} + +static ssize_t block_state_writev_buffer(void *opaque, struct iovec *iov, -+ int iovcnt, int64_t pos) ++ int iovcnt, int64_t pos, Error **errp) +{ + QEMUIOVector qiov; + BlkRwCo rwco; @@ -709,7 +716,7 @@ index 0000000000..2149010bb8 +} + +static ssize_t loadstate_get_buffer(void *opaque, uint8_t *buf, int64_t pos, -+ size_t size) ++ size_t size, Error **errp) +{ + BlockBackend *be = opaque; + int64_t maxlen = blk_getlength(be); @@ -776,10 +783,10 @@ index 0000000000..2149010bb8 + return ret; +} diff --git a/vl.c b/vl.c -index b426b32134..1c5536e5bb 100644 +index 6a65a64bfd..1616f55a38 100644 --- a/vl.c +++ b/vl.c -@@ -2869,6 +2869,7 @@ int main(int argc, char **argv, char **envp) +@@ -2840,6 +2840,7 @@ int main(int argc, char **argv, char **envp) int optind; const char *optarg; const char *loadvm = NULL; @@ -787,7 +794,7 @@ index b426b32134..1c5536e5bb 100644 MachineClass *machine_class; const char *cpu_option; const char *vga_model = NULL; -@@ -3445,6 +3446,9 @@ int main(int argc, char **argv, char **envp) +@@ -3430,6 +3431,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_loadvm: loadvm = optarg; break; @@ -797,7 +804,7 @@ index b426b32134..1c5536e5bb 100644 case QEMU_OPTION_full_screen: dpy.has_full_screen = true; dpy.full_screen = true; -@@ -4444,6 +4448,12 @@ int main(int argc, char **argv, char **envp) +@@ -4442,6 +4446,12 @@ int main(int argc, char **argv, char **envp) autostart = 0; exit(1); } diff --git a/debian/patches/pve/0018-PVE-block-add-the-zeroinit-block-driver-filter.patch b/debian/patches/pve/0018-PVE-block-add-the-zeroinit-block-driver-filter.patch index 5adbafe..249f863 100644 --- a/debian/patches/pve/0018-PVE-block-add-the-zeroinit-block-driver-filter.patch +++ b/debian/patches/pve/0018-PVE-block-add-the-zeroinit-block-driver-filter.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Thu, 17 Mar 2016 11:33:37 +0100 -Subject: [PATCH] PVE: block: add the zeroinit block driver filter +Date: Tue, 10 Mar 2020 12:55:15 +0100 +Subject: [PATCH 18/32] PVE: block: add the zeroinit block driver filter Signed-off-by: Thomas Lamprecht --- @@ -11,7 +11,7 @@ Signed-off-by: Thomas Lamprecht create mode 100644 block/zeroinit.c diff --git a/block/Makefile.objs b/block/Makefile.objs -index 35f3bca4d9..6022242c3f 100644 +index e394fe0b6c..a10ceabf5b 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -11,6 +11,7 @@ block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o @@ -24,7 +24,7 @@ index 35f3bca4d9..6022242c3f 100644 block-obj-y += blklogwrites.o diff --git a/block/zeroinit.c b/block/zeroinit.c new file mode 100644 -index 0000000000..e78511d36c +index 0000000000..b74a78ece6 --- /dev/null +++ b/block/zeroinit.c @@ -0,0 +1,204 @@ @@ -186,9 +186,9 @@ index 0000000000..e78511d36c +} + +static int zeroinit_co_truncate(BlockDriverState *bs, int64_t offset, -+ PreallocMode prealloc, Error **errp) ++ _Bool exact, PreallocMode prealloc, Error **errp) +{ -+ return bdrv_co_truncate(bs->file, offset, prealloc, errp); ++ return bdrv_co_truncate(bs->file, offset, exact, prealloc, errp); +} + +static int zeroinit_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) diff --git a/debian/patches/pve/0019-PVE-backup-modify-job-api.patch b/debian/patches/pve/0019-PVE-backup-modify-job-api.patch index 7bd2457..4b4b8b9 100644 --- a/debian/patches/pve/0019-PVE-backup-modify-job-api.patch +++ b/debian/patches/pve/0019-PVE-backup-modify-job-api.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 15:04:57 +0100 -Subject: [PATCH] PVE: backup: modify job api +Date: Tue, 10 Mar 2020 12:55:16 +0100 +Subject: [PATCH 19/32] PVE: backup: modify job api Introduce a pause_count parameter to start a backup in paused mode. This way backups of multiple drives can be @@ -10,18 +10,18 @@ having been started at the same point in time. Signed-off-by: Thomas Lamprecht --- - block/backup.c | 2 ++ + block/backup.c | 3 +++ block/replication.c | 2 +- - blockdev.c | 4 ++-- + blockdev.c | 3 ++- include/block/block_int.h | 1 + job.c | 2 +- - 5 files changed, 7 insertions(+), 4 deletions(-) + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/block/backup.c b/block/backup.c -index 8761f1f9a7..30008fcc34 100644 +index cf62b1a38c..c155081de2 100644 --- a/block/backup.c +++ b/block/backup.c -@@ -550,6 +550,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -347,6 +347,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, @@ -29,20 +29,21 @@ index 8761f1f9a7..30008fcc34 100644 JobTxn *txn, Error **errp) { int64_t len; -@@ -675,6 +676,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -468,6 +469,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); - job->len = len; -+ job->common.job.pause_count += pause_count; ++ job->common.job.pause_count += pause_count; ++ return &job->common; + error: diff --git a/block/replication.c b/block/replication.c -index 23b2993d74..e70a6cf2bd 100644 +index 99532ce521..ec8de7b427 100644 --- a/block/replication.c +++ b/block/replication.c @@ -546,7 +546,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, - 0, MIRROR_SYNC_MODE_NONE, NULL, false, + 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, NULL, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, - backup_job_completed, bs, NULL, &local_err); @@ -51,32 +52,24 @@ index 23b2993d74..e70a6cf2bd 100644 error_propagate(errp, local_err); backup_job_cleanup(bs); diff --git a/blockdev.c b/blockdev.c -index 4d141e9a1f..a7c97b1585 100644 +index 8e029e9c01..c7fa663ebf 100644 --- a/blockdev.c +++ b/blockdev.c -@@ -3574,7 +3574,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, - job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, bmap, backup->compress, - backup->on_source_error, backup->on_target_error, -- job_flags, NULL, NULL, txn, &local_err); -+ job_flags, NULL, NULL, 0, txn, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - goto unref; -@@ -3679,7 +3679,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, - job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, bmap, backup->compress, - backup->on_source_error, backup->on_target_error, -- job_flags, NULL, NULL, txn, &local_err); -+ job_flags, NULL, NULL, 0, txn, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - } +@@ -3583,7 +3583,8 @@ static BlockJob *do_backup_common(BackupCommon *backup, + backup->filter_node_name, + backup->on_source_error, + backup->on_target_error, +- job_flags, NULL, NULL, txn, errp); ++ job_flags, NULL, NULL, 0, txn, errp); ++ + return job; + } + diff --git a/include/block/block_int.h b/include/block/block_int.h -index 05ee6b4866..bb2dddca83 100644 +index dd033d0b37..b0d5eb9485 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h -@@ -1173,6 +1173,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -1215,6 +1215,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, @@ -85,10 +78,10 @@ index 05ee6b4866..bb2dddca83 100644 void hmp_drive_add_node(Monitor *mon, const char *optstr); diff --git a/job.c b/job.c -index 28dd48f8a5..7a21e83780 100644 +index 04409b40aa..7554f735e3 100644 --- a/job.c +++ b/job.c -@@ -898,7 +898,7 @@ void job_start(Job *job) +@@ -888,7 +888,7 @@ void job_start(Job *job) job->co = qemu_coroutine_create(job_co_entry, job); job->pause_count--; job->busy = true; diff --git a/debian/patches/pve/0025-PVE-Add-dummy-id-command-line-parameter.patch b/debian/patches/pve/0020-PVE-Add-dummy-id-command-line-parameter.patch similarity index 82% rename from debian/patches/pve/0025-PVE-Add-dummy-id-command-line-parameter.patch rename to debian/patches/pve/0020-PVE-Add-dummy-id-command-line-parameter.patch index 94ec6fb..703b97d 100644 --- a/debian/patches/pve/0025-PVE-Add-dummy-id-command-line-parameter.patch +++ b/debian/patches/pve/0020-PVE-Add-dummy-id-command-line-parameter.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Thu, 30 Aug 2018 14:52:56 +0200 -Subject: [PATCH] PVE: Add dummy -id command line parameter +Date: Tue, 10 Mar 2020 12:55:17 +0100 +Subject: [PATCH 20/32] PVE: Add dummy -id command line parameter This used to be part of the qemu-side PVE authentication for VNC. Now this does nothing. @@ -14,10 +14,10 @@ Signed-off-by: Thomas Lamprecht 2 files changed, 11 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx -index 34994daafd..6db02fe903 100644 +index 4cb2681bfc..b84e260fa5 100644 --- a/qemu-options.hx +++ b/qemu-options.hx -@@ -802,6 +802,9 @@ STEXI +@@ -826,6 +826,9 @@ STEXI @table @option ETEXI @@ -28,10 +28,10 @@ index 34994daafd..6db02fe903 100644 "-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) diff --git a/vl.c b/vl.c -index 1c5536e5bb..7ffcd271f1 100644 +index 1616f55a38..4df15640c5 100644 --- a/vl.c +++ b/vl.c -@@ -2857,6 +2857,7 @@ static void user_register_global_props(void) +@@ -2828,6 +2828,7 @@ static void user_register_global_props(void) int main(int argc, char **argv, char **envp) { int i; @@ -39,7 +39,7 @@ index 1c5536e5bb..7ffcd271f1 100644 int snapshot, linux_boot; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; -@@ -3570,6 +3571,13 @@ int main(int argc, char **argv, char **envp) +@@ -3560,6 +3561,13 @@ int main(int argc, char **argv, char **envp) exit(1); } break; diff --git a/debian/patches/pve/0020-PVE-backup-introduce-vma-archive-format.patch b/debian/patches/pve/0020-PVE-backup-introduce-vma-archive-format.patch deleted file mode 100644 index 6bb45cc..0000000 --- a/debian/patches/pve/0020-PVE-backup-introduce-vma-archive-format.patch +++ /dev/null @@ -1,1563 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 2 Aug 2017 13:51:02 +0200 -Subject: [PATCH] PVE: backup: introduce vma archive format - -TODO: Move to a libvma block backend. -Signed-off-by: Thomas Lamprecht ---- - MAINTAINERS | 6 + - block/Makefile.objs | 3 + - block/vma.c | 503 +++++++++++++++++++++++++++++++++++++++ - blockdev.c | 536 ++++++++++++++++++++++++++++++++++++++++++ - configure | 30 +++ - hmp-commands-info.hx | 13 + - hmp-commands.hx | 31 +++ - include/monitor/hmp.h | 3 + - monitor/hmp-cmds.c | 63 +++++ - qapi/block-core.json | 109 ++++++++- - qapi/common.json | 13 + - qapi/misc.json | 13 - - 12 files changed, 1309 insertions(+), 14 deletions(-) - create mode 100644 block/vma.c - -diff --git a/MAINTAINERS b/MAINTAINERS -index d6de200453..de09d099f6 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -2563,6 +2563,12 @@ L: qemu-block@nongnu.org - S: Supported - F: block/vvfat.c - -+VMA -+M: Wolfgang Bumiller . -+L: pve-devel@proxmox.com -+S: Supported -+F: block/vma.c -+ - Image format fuzzer - M: Stefan Hajnoczi - L: qemu-block@nongnu.org -diff --git a/block/Makefile.objs b/block/Makefile.objs -index 6022242c3f..86b10d8ea7 100644 ---- a/block/Makefile.objs -+++ b/block/Makefile.objs -@@ -33,6 +33,7 @@ block-obj-$(CONFIG_RBD) += rbd.o - block-obj-$(CONFIG_GLUSTERFS) += gluster.o - block-obj-$(CONFIG_VXHS) += vxhs.o - block-obj-$(CONFIG_LIBSSH) += ssh.o -+block-obj-$(CONFIG_VMA) += vma.o - block-obj-y += accounting.o dirty-bitmap.o - block-obj-y += write-threshold.o - block-obj-y += backup.o -@@ -64,3 +65,5 @@ qcow.o-libs := -lz - linux-aio.o-libs := -laio - parallels.o-cflags := $(LIBXML2_CFLAGS) - parallels.o-libs := $(LIBXML2_LIBS) -+vma.o-cflags := $(VMA_CFLAGS) -+vma.o-libs := $(VMA_LIBS) -diff --git a/block/vma.c b/block/vma.c -new file mode 100644 -index 0000000000..b911b198dc ---- /dev/null -+++ b/block/vma.c -@@ -0,0 +1,503 @@ -+/* -+ * VMA archive backend for QEMU, container object -+ * -+ * Copyright (C) 2017 Proxmox Server Solutions GmbH -+ * -+ * 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 "qemu/osdep.h" -+#include "qemu/uuid.h" -+#include "qemu/option.h" -+#include "qemu-common.h" -+#include "qapi/error.h" -+#include "qapi/qmp/qerror.h" -+#include "qapi/qmp/qstring.h" -+#include "qapi/qmp/qdict.h" -+#include "qom/object.h" -+#include "qom/object_interfaces.h" -+#include "block/block_int.h" -+ -+/* exported interface */ -+void vma_object_add_config_file(Object *obj, const char *name, -+ const char *contents, size_t len, -+ Error **errp); -+ -+#define TYPE_VMA_OBJECT "vma" -+#define VMA_OBJECT(obj) \ -+ OBJECT_CHECK(VMAObjectState, (obj), TYPE_VMA_OBJECT) -+#define VMA_OBJECT_GET_CLASS(obj) \ -+ OBJECT_GET_CLASS(VMAObjectClass, (obj), TYPE_VMA_OBJECT) -+ -+typedef struct VMAObjectClass { -+ ObjectClass parent_class; -+} VMAObjectClass; -+ -+typedef struct VMAObjectState { -+ Object parent; -+ -+ char *filename; -+ -+ QemuUUID uuid; -+ bool blocked; -+ VMAWriter *vma; -+ QemuMutex mutex; -+} VMAObjectState; -+ -+static VMAObjectState *vma_by_id(const char *name) -+{ -+ Object *container; -+ Object *obj; -+ -+ container = object_get_objects_root(); -+ obj = object_resolve_path_component(container, name); -+ -+ return VMA_OBJECT(obj); -+} -+ -+static void vma_object_class_complete(UserCreatable *uc, Error **errp) -+{ -+ int rc; -+ VMAObjectState *vo = VMA_OBJECT(uc); -+ VMAObjectClass *voc = VMA_OBJECT_GET_CLASS(uc); -+ (void)!vo; -+ (void)!voc; -+ -+ if (!vo->filename) { -+ error_setg(errp, "Parameter 'filename' is required"); -+ return; -+ } -+ -+ vo->vma = VMAWriter_fopen(vo->filename); -+ if (!vo->vma) { -+ error_setg_errno(errp, errno, "failed to create VMA archive"); -+ return; -+ } -+ -+ rc = VMAWriter_set_uuid(vo->vma, vo->uuid.data, sizeof(vo->uuid.data)); -+ if (rc < 0) { -+ error_setg_errno(errp, -rc, "failed to set UUID of VMA archive"); -+ return; -+ } -+ -+ qemu_mutex_init(&vo->mutex); -+} -+ -+static bool vma_object_can_be_deleted(UserCreatable *uc) -+{ -+ //VMAObjectState *vo = VMA_OBJECT(uc); -+ //if (!vo->vma) { -+ // return true; -+ //} -+ //return false; -+ return true; -+} -+ -+static void vma_object_class_init(ObjectClass *oc, void *data) -+{ -+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); -+ -+ ucc->can_be_deleted = vma_object_can_be_deleted; -+ ucc->complete = vma_object_class_complete; -+} -+ -+static char *vma_object_get_filename(Object *obj, Error **errp) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ return g_strdup(vo->filename); -+} -+ -+static void vma_object_set_filename(Object *obj, const char *str, Error **errp) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ if (vo->vma) { -+ error_setg(errp, "filename cannot be changed after creation"); -+ return; -+ } -+ -+ g_free(vo->filename); -+ vo->filename = g_strdup(str); -+} -+ -+static char *vma_object_get_uuid(Object *obj, Error **errp) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ return qemu_uuid_unparse_strdup(&vo->uuid); -+} -+ -+static void vma_object_set_uuid(Object *obj, const char *str, Error **errp) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ if (vo->vma) { -+ error_setg(errp, "uuid cannot be changed after creation"); -+ return; -+ } -+ -+ qemu_uuid_parse(str, &vo->uuid); -+} -+ -+static bool vma_object_get_blocked(Object *obj, Error **errp) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ return vo->blocked; -+} -+ -+static void vma_object_set_blocked(Object *obj, bool blocked, Error **errp) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ (void)errp; -+ -+ vo->blocked = blocked; -+} -+ -+void vma_object_add_config_file(Object *obj, const char *name, -+ const char *contents, size_t len, -+ Error **errp) -+{ -+ int rc; -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ -+ if (!vo || !vo->vma) { -+ error_setg(errp, "not a valid vma object to add config files to"); -+ return; -+ } -+ -+ rc = VMAWriter_addConfigFile(vo->vma, name, contents, len); -+ if (rc < 0) { -+ error_setg_errno(errp, -rc, "failed to add config file to VMA"); -+ return; -+ } -+} -+ -+static void vma_object_init(Object *obj) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ (void)!vo; -+ -+ object_property_add_str(obj, "filename", -+ vma_object_get_filename, vma_object_set_filename, -+ NULL); -+ object_property_add_str(obj, "uuid", -+ vma_object_get_uuid, vma_object_set_uuid, -+ NULL); -+ object_property_add_bool(obj, "blocked", -+ vma_object_get_blocked, vma_object_set_blocked, -+ NULL); -+} -+ -+static void vma_object_finalize(Object *obj) -+{ -+ VMAObjectState *vo = VMA_OBJECT(obj); -+ VMAObjectClass *voc = VMA_OBJECT_GET_CLASS(obj); -+ (void)!voc; -+ -+ qemu_mutex_destroy(&vo->mutex); -+ -+ VMAWriter_destroy(vo->vma, true); -+ g_free(vo->filename); -+} -+ -+static const TypeInfo vma_object_info = { -+ .name = TYPE_VMA_OBJECT, -+ .parent = TYPE_OBJECT, -+ .class_size = sizeof(VMAObjectClass), -+ .class_init = vma_object_class_init, -+ .instance_size = sizeof(VMAObjectState), -+ .instance_init = vma_object_init, -+ .instance_finalize = vma_object_finalize, -+ .interfaces = (InterfaceInfo[]) { -+ { TYPE_USER_CREATABLE }, -+ { } -+ } -+}; -+ -+static void register_types(void) -+{ -+ type_register_static(&vma_object_info); -+} -+ -+type_init(register_types); -+ -+typedef struct { -+ VMAObjectState *vma_obj; -+ char *name; -+ size_t device_id; -+ uint64_t byte_size; -+} BDRVVMAState; -+ -+static void qemu_vma_parse_filename(const char *filename, QDict *options, -+ Error **errp) -+{ -+ char *sep; -+ -+ if (strncmp(filename, "vma:", sizeof("vma:")-1) == 0) { -+ filename += sizeof("vma:")-1; -+ } -+ -+ sep = strchr(filename, '/'); -+ if (!sep || sep == filename) { -+ error_setg(errp, "VMA file should be //"); -+ return; -+ } -+ -+ qdict_put(options, "vma", qstring_from_substr(filename, 0, sep-filename)); -+ -+ while (*sep && *sep == '/') -+ ++sep; -+ if (!*sep) { -+ error_setg(errp, "missing device name\n"); -+ return; -+ } -+ -+ filename = sep; -+ sep = strchr(filename, '/'); -+ if (!sep || sep == filename) { -+ error_setg(errp, "VMA file should be //"); -+ return; -+ } -+ -+ qdict_put(options, "name", qstring_from_substr(filename, 0, sep-filename)); -+ -+ while (*sep && *sep == '/') -+ ++sep; -+ if (!*sep) { -+ error_setg(errp, "missing device size\n"); -+ return; -+ } -+ -+ filename = sep; -+ qdict_put_str(options, "size", filename); -+} -+ -+static QemuOptsList runtime_opts = { -+ .name = "vma-drive", -+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), -+ .desc = { -+ { -+ .name = "vma", -+ .type = QEMU_OPT_STRING, -+ .help = "VMA Object name", -+ }, -+ { -+ .name = "name", -+ .type = QEMU_OPT_STRING, -+ .help = "VMA device name", -+ }, -+ { -+ .name = BLOCK_OPT_SIZE, -+ .type = QEMU_OPT_SIZE, -+ .help = "Virtual disk size" -+ }, -+ { /* end of list */ } -+ }, -+}; -+static int qemu_vma_open(BlockDriverState *bs, QDict *options, int flags, -+ Error **errp) -+{ -+ Error *local_err = NULL; -+ BDRVVMAState *s = bs->opaque; -+ QemuOpts *opts; -+ const char *vma_id, *device_name; -+ ssize_t dev_id; -+ int64_t bytes = 0; -+ int ret; -+ -+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); -+ qemu_opts_absorb_qdict(opts, options, &local_err); -+ if (local_err) { -+ error_propagate(errp, local_err); -+ ret = -EINVAL; -+ goto failed_opts; -+ } -+ -+ bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), -+ BDRV_SECTOR_SIZE); -+ -+ vma_id = qemu_opt_get(opts, "vma"); -+ if (!vma_id) { -+ ret = -EINVAL; -+ error_setg(errp, "missing 'vma' property"); -+ goto failed_opts; -+ } -+ -+ device_name = qemu_opt_get(opts, "name"); -+ if (!device_name) { -+ ret = -EINVAL; -+ error_setg(errp, "missing 'name' property"); -+ goto failed_opts; -+ } -+ -+ VMAObjectState *vma = vma_by_id(vma_id); -+ if (!vma) { -+ ret = -EINVAL; -+ error_setg(errp, "no such VMA object: %s", vma_id); -+ goto failed_opts; -+ } -+ -+ dev_id = VMAWriter_findDevice(vma->vma, device_name); -+ if (dev_id >= 0) { -+ error_setg(errp, "drive already exists in VMA object"); -+ ret = -EIO; -+ goto failed_opts; -+ } -+ -+ dev_id = VMAWriter_addDevice(vma->vma, device_name, (uint64_t)bytes); -+ if (dev_id < 0) { -+ error_setg_errno(errp, -dev_id, "failed to add VMA device"); -+ ret = -EIO; -+ goto failed_opts; -+ } -+ -+ object_ref(OBJECT(vma)); -+ s->vma_obj = vma; -+ s->name = g_strdup(device_name); -+ s->device_id = (size_t)dev_id; -+ s->byte_size = bytes; -+ -+ ret = 0; -+ -+failed_opts: -+ qemu_opts_del(opts); -+ return ret; -+} -+ -+static void qemu_vma_close(BlockDriverState *bs) -+{ -+ BDRVVMAState *s = bs->opaque; -+ -+ (void)VMAWriter_finishDevice(s->vma_obj->vma, s->device_id); -+ object_unref(OBJECT(s->vma_obj)); -+ -+ g_free(s->name); -+} -+ -+static int64_t qemu_vma_getlength(BlockDriverState *bs) -+{ -+ BDRVVMAState *s = bs->opaque; -+ -+ return s->byte_size; -+} -+ -+static coroutine_fn int qemu_vma_co_writev(BlockDriverState *bs, -+ int64_t sector_num, -+ int nb_sectors, -+ QEMUIOVector *qiov, -+ int flags) -+{ -+ size_t i; -+ ssize_t rc; -+ BDRVVMAState *s = bs->opaque; -+ VMAObjectState *vo = s->vma_obj; -+ off_t offset = sector_num * BDRV_SECTOR_SIZE; -+ /* flags can be only values we set in supported_write_flags */ -+ assert(flags == 0); -+ -+ qemu_mutex_lock(&vo->mutex); -+ if (vo->blocked) { -+ return -EPERM; -+ } -+ for (i = 0; i != qiov->niov; ++i) { -+ const struct iovec *v = &qiov->iov[i]; -+ size_t blocks = v->iov_len / VMA_BLOCK_SIZE; -+ if (blocks * VMA_BLOCK_SIZE != v->iov_len) { -+ return -EIO; -+ } -+ rc = VMAWriter_writeBlocks(vo->vma, s->device_id, -+ v->iov_base, blocks, offset); -+ if (errno) { -+ return -errno; -+ } -+ if (rc != blocks) { -+ return -EIO; -+ } -+ offset += v->iov_len; -+ } -+ qemu_mutex_unlock(&vo->mutex); -+ return 0; -+} -+ -+static int qemu_vma_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) -+{ -+ bdi->cluster_size = VMA_CLUSTER_SIZE; -+ bdi->unallocated_blocks_are_zero = true; -+ return 0; -+} -+ -+static int qemu_vma_check_perm(BlockDriverState *bs, -+ uint64_t perm, -+ uint64_t shared, -+ Error **errp) -+{ -+ /* Nothing to do. */ -+ return 0; -+} -+ -+static void qemu_vma_set_perm(BlockDriverState *bs, -+ uint64_t perm, -+ uint64_t shared) -+{ -+ /* Nothing to do. */ -+} -+ -+static void qemu_vma_abort_perm_update(BlockDriverState *bs) -+{ -+ /* Nothing to do. */ -+} -+ -+static void qemu_vma_refresh_limits(BlockDriverState *bs, Error **errp) -+{ -+ bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ -+} -+static void qemu_vma_child_perm(BlockDriverState *bs, BdrvChild *c, -+ const BdrvChildRole *role, -+ BlockReopenQueue *reopen_queue, -+ uint64_t perm, uint64_t shared, -+ uint64_t *nperm, uint64_t *nshared) -+{ -+ *nperm = BLK_PERM_ALL; -+ *nshared = BLK_PERM_ALL; -+} -+ -+static BlockDriver bdrv_vma_drive = { -+ .format_name = "vma-drive", -+ .protocol_name = "vma", -+ .instance_size = sizeof(BDRVVMAState), -+ -+#if 0 -+ .bdrv_create = qemu_vma_create, -+ .create_opts = &qemu_vma_create_opts, -+#endif -+ -+ .bdrv_parse_filename = qemu_vma_parse_filename, -+ .bdrv_file_open = qemu_vma_open, -+ -+ .bdrv_close = qemu_vma_close, -+ .bdrv_has_zero_init = bdrv_has_zero_init_1, -+ .bdrv_getlength = qemu_vma_getlength, -+ .bdrv_get_info = qemu_vma_get_info, -+ -+ //.bdrv_co_preadv = qemu_vma_co_preadv, -+ .bdrv_co_writev = qemu_vma_co_writev, -+ -+ .bdrv_refresh_limits = qemu_vma_refresh_limits, -+ .bdrv_check_perm = qemu_vma_check_perm, -+ .bdrv_set_perm = qemu_vma_set_perm, -+ .bdrv_abort_perm_update = qemu_vma_abort_perm_update, -+ .bdrv_child_perm = qemu_vma_child_perm, -+}; -+ -+static void bdrv_vma_init(void) -+{ -+ bdrv_register(&bdrv_vma_drive); -+} -+ -+block_init(bdrv_vma_init); -diff --git a/blockdev.c b/blockdev.c -index a7c97b1585..7047475a3c 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -31,11 +31,13 @@ - */ - - #include "qemu/osdep.h" -+#include "qemu/uuid.h" - #include "sysemu/block-backend.h" - #include "sysemu/blockdev.h" - #include "hw/block/block.h" - #include "block/blockjob.h" - #include "block/qdict.h" -+#include "block/blockjob_int.h" - #include "block/throttle-groups.h" - #include "monitor/monitor.h" - #include "qemu/error-report.h" -@@ -45,6 +47,7 @@ - #include "qapi/qapi-commands-block.h" - #include "qapi/qapi-commands-transaction.h" - #include "qapi/qapi-visit-block-core.h" -+#include "qapi/qapi-types-misc.h" - #include "qapi/qmp/qdict.h" - #include "qapi/qmp/qnum.h" - #include "qapi/qmp/qstring.h" -@@ -3168,6 +3171,539 @@ out: - aio_context_release(aio_context); - } - -+/* PVE backup related function */ -+ -+static struct PVEBackupState { -+ Error *error; -+ bool cancel; -+ QemuUUID uuid; -+ char uuid_str[37]; -+ int64_t speed; -+ time_t start_time; -+ time_t end_time; -+ char *backup_file; -+ Object *vmaobj; -+ GList *di_list; -+ size_t next_job; -+ size_t total; -+ size_t transferred; -+ size_t zero_bytes; -+ QemuMutex backup_mutex; -+ bool backup_mutex_initialized; -+} backup_state; -+ -+typedef struct PVEBackupDevInfo { -+ BlockDriverState *bs; -+ size_t size; -+ uint8_t dev_id; -+ bool completed; -+ char targetfile[PATH_MAX]; -+ BlockDriverState *target; -+} PVEBackupDevInfo; -+ -+static void pvebackup_run_next_job(void); -+ -+static void pvebackup_cleanup(void) -+{ -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ // Avoid race between block jobs and backup-cancel command: -+ if (!backup_state.vmaw) { -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ return; -+ } -+ -+ backup_state.end_time = time(NULL); -+ -+ if (backup_state.vmaobj) { -+ object_unparent(backup_state.vmaobj); -+ backup_state.vmaobj = NULL; -+ } -+ -+ g_list_free(backup_state.di_list); -+ backup_state.di_list = NULL; -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+} -+ -+static void pvebackup_complete_cb(void *opaque, int ret) -+{ -+ // This always runs in the main loop -+ -+ PVEBackupDevInfo *di = opaque; -+ -+ di->completed = true; -+ -+ if (ret < 0 && !backup_state.error) { -+ error_setg(&backup_state.error, "job failed with err %d - %s", -+ ret, strerror(-ret)); -+ } -+ -+ di->bs = NULL; -+ di->target = NULL; -+ -+ if (backup_state.vmaobj) { -+ object_unparent(backup_state.vmaobj); -+ backup_state.vmaobj = NULL; -+ } -+ -+ // remove self from job queue -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ backup_state.di_list = g_list_remove(backup_state.di_list, di); -+ g_free(di); -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ -+ if (!backup_state.cancel) { -+ pvebackup_run_next_job(); -+ } -+} -+ -+static void pvebackup_cancel(void *opaque) -+{ -+ backup_state.cancel = true; -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ // Avoid race between block jobs and backup-cancel command: -+ if (!backup_state.vmaw) { -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ return; -+ } -+ -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, "backup cancelled"); -+ } -+ -+ if (backup_state.vmaobj) { -+ Error *err; -+ /* make sure vma writer does not block anymore */ -+ if (!object_set_props(backup_state.vmaobj, &err, "blocked", "yes", NULL)) { -+ if (err) { -+ error_report_err(err); -+ } -+ } -+ } -+ -+ 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) { -+ AioContext *aio_context = blk_get_aio_context(job->blk); -+ aio_context_acquire(aio_context); -+ if (!di->completed) { -+ job_cancel(&job->job, false); -+ } -+ aio_context_release(aio_context); -+ } -+ } -+ } -+ -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ pvebackup_cleanup(); -+} -+ -+void qmp_backup_cancel(Error **errp) -+{ -+ if (!backup_state.backup_mutex_initialized) -+ return; -+ Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL); -+ qemu_coroutine_enter(co); -+ -+ while (backup_state.vmaobj) { -+ /* FIXME: Find something better for this */ -+ aio_poll(qemu_get_aio_context(), true); -+ } -+} -+ -+void vma_object_add_config_file(Object *obj, const char *name, -+ const char *contents, size_t len, -+ Error **errp); -+static int config_to_vma(const char *file, BackupFormat format, -+ Object *vmaobj, -+ const char *backup_dir, -+ 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) { -+ vma_object_add_config_file(vmaobj, basename, cdata, clen, errp); -+ } 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) -+{ -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ -+ GList *next = g_list_nth(backup_state.di_list, backup_state.next_job); -+ while (next) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data; -+ backup_state.next_job++; -+ if (!di->completed && di->bs && di->bs->job) { -+ BlockJob *job = di->bs->job; -+ AioContext *aio_context = blk_get_aio_context(job->blk); -+ aio_context_acquire(aio_context); -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ if (backup_state.error || backup_state.cancel) { -+ job_cancel_sync(job); -+ } else { -+ job_resume(job); -+ } -+ aio_context_release(aio_context); -+ return; -+ } -+ next = g_list_next(next); -+ } -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ -+ // no more jobs, run the cleanup -+ pvebackup_cleanup(); -+} -+ -+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) -+{ -+ BlockBackend *blk; -+ BlockDriverState *bs = NULL; -+ const char *backup_dir = NULL; -+ Error *local_err = NULL; -+ QemuUUID uuid; -+ gchar **devs = NULL; -+ GList *di_list = NULL; -+ GList *l; -+ UuidInfo *uuid_info; -+ BlockJob *job; -+ -+ if (!backup_state.backup_mutex_initialized) { -+ qemu_mutex_init(&backup_state.backup_mutex); -+ backup_state.backup_mutex_initialized = true; -+ } -+ -+ if (backup_state.di_list || backup_state.vmaobj) { -+ 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 (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 { -+ BdrvNextIterator it; -+ -+ bs = NULL; -+ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { -+ 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; -+ } -+ -+ qemu_uuid_generate(&uuid); -+ -+ if (format == BACKUP_FORMAT_VMA) { -+ char uuidstr[UUID_FMT_LEN+1]; -+ qemu_uuid_unparse(&uuid, uuidstr); -+ uuidstr[UUID_FMT_LEN] = 0; -+ backup_state.vmaobj = -+ object_new_with_props("vma", object_get_objects_root(), -+ "vma-backup-obj", &local_err, -+ "filename", backup_file, -+ "uuid", uuidstr, -+ NULL); -+ if (!backup_state.vmaobj) { -+ if (local_err) { -+ error_propagate(errp, local_err); -+ } -+ goto err; -+ } -+ -+ l = di_list; -+ while (l) { -+ QDict *options = qdict_new(); -+ -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ -+ const char *devname = bdrv_get_device_name(di->bs); -+ snprintf(di->targetfile, PATH_MAX, "vma-backup-obj/%s.raw", devname); -+ -+ qdict_put(options, "driver", qstring_from_str("vma-drive")); -+ qdict_put(options, "size", qint_from_int(di->size)); -+ di->target = bdrv_open(di->targetfile, NULL, options, BDRV_O_RDWR, &local_err); -+ if (!di->target) { -+ error_propagate(errp, local_err); -+ 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_img_create(di->targetfile, "raw", NULL, NULL, NULL, -+ di->size, flags, false, &local_err); -+ if (local_err) { -+ error_propagate(errp, local_err); -+ goto err; -+ } -+ -+ di->target = bdrv_open(di->targetfile, NULL, NULL, flags, &local_err); -+ if (!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 */ -+ if (has_config_file) { -+ if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -+ goto err; -+ } -+ } -+ -+ /* add firewall file to archive */ -+ if (has_firewall_file) { -+ if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -+ goto err; -+ } -+ } -+ /* 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); -+ -+ memcpy(&backup_state.uuid, &uuid, sizeof(uuid)); -+ qemu_uuid_unparse(&uuid, backup_state.uuid_str); -+ -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ backup_state.di_list = di_list; -+ backup_state.next_job = 0; -+ -+ 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); -+ -+ job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL, -+ false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, -+ JOB_DEFAULT, -+ pvebackup_complete_cb, di, 2, NULL, &local_err); -+ if (di->target) { -+ bdrv_unref(di->target); -+ di->target = NULL; -+ } -+ if (!job || local_err != NULL) { -+ error_setg(&backup_state.error, "backup_job_create failed"); -+ pvebackup_cancel(NULL); -+ } else { -+ job_start(&job->job); -+ } -+ } -+ -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ -+ 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) { -+ 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); -+ -+ if (devs) { -+ g_strfreev(devs); -+ } -+ -+ if (backup_state.vmaobj) { -+ object_unparent(backup_state.vmaobj); -+ backup_state.vmaobj = NULL; -+ } -+ -+ if (backup_dir) { -+ rmdir(backup_dir); -+ } -+ -+ 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(bool has_job_id, const char *job_id, const char *device, - bool has_base, const char *base, - bool has_base_node, const char *base_node, -diff --git a/configure b/configure -index 714e7fb6a1..5e16d8e562 100755 ---- a/configure -+++ b/configure -@@ -499,6 +499,8 @@ docker="no" - debug_mutex="no" - libpmem="" - default_devices="yes" -+libudev="no" -+vma="" - - # cross compilers defaults, can be overridden with --cross-cc-ARCH - cross_cc_aarch64="aarch64-linux-gnu-gcc" -@@ -1543,6 +1545,10 @@ for opt do - ;; - --disable-libpmem) libpmem=no - ;; -+ --enable-vma) vma=yes -+ ;; -+ --disable-vma) vma=no -+ ;; - *) - echo "ERROR: unknown option $opt" - echo "Try '$0 --help' for more information" -@@ -1842,6 +1848,7 @@ disabled with --disable-FEATURE, default is enabled if available: - capstone capstone disassembler support - debug-mutex mutex debugging support - libpmem libpmem support -+ vma VMA archive backend - - NOTE: The object files are built at the place where configure is launched - EOF -@@ -4402,6 +4409,22 @@ EOF - fi - fi - -+########################################## -+# vma probe -+if test "$vma" != "no" ; then -+ if $pkg_config --exact-version=0.1.0 vma; then -+ vma="yes" -+ vma_cflags=$($pkg_config --cflags vma) -+ vma_libs=$($pkg_config --libs vma) -+ else -+ if test "$vma" = "yes" ; then -+ feature_not_found "VMA Archive backend support" \ -+ "Install libvma devel" -+ fi -+ vma="no" -+ fi -+fi -+ - ########################################## - # signalfd probe - signalfd="no" -@@ -6481,6 +6504,7 @@ echo "docker $docker" - echo "libpmem support $libpmem" - echo "libudev $libudev" - echo "default devices $default_devices" -+echo "VMA support $vma" - - if test "$supported_cpu" = "no"; then - echo -@@ -6983,6 +7007,12 @@ if test "$usb_redir" = "yes" ; then - echo "USB_REDIR_LIBS=$usb_redir_libs" >> $config_host_mak - fi - -+if test "$vma" = "yes" ; then -+ echo "CONFIG_VMA=y" >> $config_host_mak -+ echo "VMA_CFLAGS=$vma_cflags" >> $config_host_mak -+ echo "VMA_LIBS=$vma_libs" >> $config_host_mak -+fi -+ - if test "$opengl" = "yes" ; then - echo "CONFIG_OPENGL=y" >> $config_host_mak - echo "OPENGL_LIBS=$opengl_libs" >> $config_host_mak -diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx -index 444bd8e43d..21106bcbe6 100644 ---- a/hmp-commands-info.hx -+++ b/hmp-commands-info.hx -@@ -536,6 +536,19 @@ STEXI - @item info cpustats - @findex info cpustats - Show CPU statistics. -+ETEXI -+ -+ { -+ .name = "backup", -+ .args_type = "", -+ .params = "", -+ .help = "show backup status", -+ .cmd = hmp_info_backup, -+ }, -+ -+STEXI -+@item info backup -+show backup status - ETEXI - - #if defined(CONFIG_SLIRP) -diff --git a/hmp-commands.hx b/hmp-commands.hx -index e075d413c0..5bdfdeaf57 100644 ---- a/hmp-commands.hx -+++ b/hmp-commands.hx -@@ -105,6 +105,37 @@ STEXI - @item block_stream - @findex block_stream - Copy data from a backing file into a block device. -+ETEXI -+ -+ { -+ .name = "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.", -+ .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", -+ .cmd = hmp_backup_cancel, -+ }, -+ -+STEXI -+@item backup_cancel -+@findex backup_cancel -+Cancel the current VM backup. -+ - ETEXI - - { -diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h -index c6ee8295f0..0f2f96c4af 100644 ---- a/include/monitor/hmp.h -+++ b/include/monitor/hmp.h -@@ -30,6 +30,7 @@ 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); - 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); -@@ -90,6 +91,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/hmp-cmds.c b/monitor/hmp-cmds.c -index 39a8020367..bc9ca346f7 100644 ---- a/monitor/hmp-cmds.c -+++ b/monitor/hmp-cmds.c -@@ -196,6 +196,44 @@ 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) { -+ 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); -+} -+ - static char *SocketAddress_to_str(SocketAddress *addr) - { - switch (addr->type) { -@@ -2078,6 +2116,31 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) - 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; -+ -+ 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, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, -+ false, NULL, 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) - { - Error *error = NULL; -diff --git a/qapi/block-core.json b/qapi/block-core.json -index 0d43d4f37c..97cb7ec41c 100644 ---- a/qapi/block-core.json -+++ b/qapi/block-core.json -@@ -796,6 +796,97 @@ - { 'command': 'query-block', 'returns': ['BlockInfo'] } - - -+## -+# @BackupStatus: -+# -+# Detailed backup status. -+# -+# @status: 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: error message (only returned if status is 'error') -+# -+# @total: total amount of bytes involved in the backup process -+# -+# @transferred: amount of bytes already backed up. -+# -+# @zero-bytes: amount of 'zero' bytes detected. -+# -+# @start-time: time (epoch) when backup job started. -+# -+# @end-time: time (epoch) when backup job finished. -+# -+# @backup-file: backup file name -+# -+# @uuid: 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', 'dir' ] } -+ -+## -+# @backup: -+# -+# Starts a VM backup. -+# -+# @backup-file: the backup file name -+# -+# @format: format of the backup file -+# -+# @config-file: a configuration file to include into -+# the backup archive. -+# -+# @speed: the maximum speed, in bytes per second -+# -+# @devlist: 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', -+ '*firewall-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' } -+ - ## - # @BlockDeviceTimedStats: - # -@@ -2835,7 +2926,7 @@ - 'qcow2', 'qed', 'quorum', 'raw', 'rbd', - { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' }, - 'sheepdog', -- 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } -+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vma-drive', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } - - ## - # @BlockdevOptionsFile: -@@ -3898,6 +3989,21 @@ - 'server': 'InetSocketAddressBase', - '*tls-creds': 'str' } } - -+## -+# @BlockdevOptionsVMADrive: -+# -+# Driver specific block device options for VMA Drives -+# -+# @filename: vma-drive path -+# -+# @size: drive size in bytes -+# -+# Since: 2.9 -+## -+{ 'struct': 'BlockdevOptionsVMADrive', -+ 'data': { 'filename': 'str', -+ 'size': 'int' } } -+ - ## - # @BlockdevOptionsThrottle: - # -@@ -3993,6 +4099,7 @@ - 'throttle': 'BlockdevOptionsThrottle', - 'vdi': 'BlockdevOptionsGenericFormat', - 'vhdx': 'BlockdevOptionsGenericFormat', -+ 'vma-drive': 'BlockdevOptionsVMADrive', - 'vmdk': 'BlockdevOptionsGenericCOWFormat', - 'vpc': 'BlockdevOptionsGenericFormat', - 'vvfat': 'BlockdevOptionsVVFAT', -diff --git a/qapi/common.json b/qapi/common.json -index 99d313ef3b..bae0650c51 100644 ---- a/qapi/common.json -+++ b/qapi/common.json -@@ -193,3 +193,16 @@ - 'ppc64', 'riscv32', 'riscv64', 's390x', 'sh4', - 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', - 'x86_64', 'xtensa', 'xtensaeb' ] } -+ -+## -+# @UuidInfo: -+# -+# Guest UUID information (Universally Unique Identifier). -+# -+# @UUID: the UUID of the guest -+# -+# Since: 0.14.0 -+# -+# Notes: If no UUID was specified for the guest, a null UUID is returned. -+## -+{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } -diff --git a/qapi/misc.json b/qapi/misc.json -index 31029e3132..335035c04d 100644 ---- a/qapi/misc.json -+++ b/qapi/misc.json -@@ -270,19 +270,6 @@ - ## - { 'command': 'query-kvm', 'returns': 'KvmInfo' } - --## --# @UuidInfo: --# --# Guest UUID information (Universally Unique Identifier). --# --# @UUID: the UUID of the guest --# --# Since: 0.14.0 --# --# Notes: If no UUID was specified for the guest, a null UUID is returned. --## --{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } -- - ## - # @query-uuid: - # diff --git a/debian/patches/pve/0026-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch b/debian/patches/pve/0021-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch similarity index 74% rename from debian/patches/pve/0026-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch rename to debian/patches/pve/0021-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch index 727e13b..b52b4d6 100644 --- a/debian/patches/pve/0026-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch +++ b/debian/patches/pve/0021-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch @@ -1,7 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Mon, 4 Jul 2016 15:02:26 +0200 -Subject: [PATCH] PVE: [Config] Revert "target-i386: disable LINT0 after reset" +Date: Tue, 10 Mar 2020 12:55:18 +0100 +Subject: [PATCH 21/32] PVE: [Config] Revert "target-i386: disable LINT0 after + reset" This reverts commit b8eb5512fd8a115f164edbbe897cdf8884920ccb. @@ -11,10 +12,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 9 insertions(+) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c -index e764a2bb03..f66ea72d78 100644 +index 375cb6abe9..e7d479c7e9 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c -@@ -258,6 +258,15 @@ static void apic_reset_common(DeviceState *dev) +@@ -259,6 +259,15 @@ static void apic_reset_common(DeviceState *dev) info->vapic_base_update(s); apic_init_reset(dev); @@ -29,4 +30,4 @@ index e764a2bb03..f66ea72d78 100644 + } } - /* This function is only used for old state version 1 and 2 */ + static const VMStateDescription vmstate_apic_common; diff --git a/debian/patches/pve/0027-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch b/debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch similarity index 87% rename from debian/patches/pve/0027-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch rename to debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch index 8f13e04..23b1076 100644 --- a/debian/patches/pve/0027-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch +++ b/debian/patches/pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch @@ -1,7 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Fri, 26 Apr 2019 09:15:28 +0200 -Subject: [PATCH] PVE: [Up+Config] file-posix: make locking optiono on create +Date: Tue, 10 Mar 2020 12:55:19 +0100 +Subject: [PATCH 22/32] PVE: [Up+Config] file-posix: make locking optiono on + create Otherwise creating images on nfs/cifs can be problematic. @@ -13,10 +14,10 @@ Signed-off-by: Thomas Lamprecht 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c -index 71aa45ce5d..ae1cd3980e 100644 +index 44b49265ae..0722b0f529 100644 --- a/block/file-posix.c +++ b/block/file-posix.c -@@ -2230,6 +2230,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) +@@ -2250,6 +2250,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) int fd; uint64_t perm, shared; int result = 0; @@ -24,7 +25,7 @@ index 71aa45ce5d..ae1cd3980e 100644 /* Validate options and set default values */ assert(options->driver == BLOCKDEV_DRIVER_FILE); -@@ -2263,19 +2264,22 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) +@@ -2283,19 +2284,22 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) perm = BLK_PERM_WRITE | BLK_PERM_RESIZE; shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; @@ -59,7 +60,7 @@ index 71aa45ce5d..ae1cd3980e 100644 } /* Clear the file by truncating it to 0 */ -@@ -2308,13 +2312,15 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) +@@ -2328,13 +2332,15 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) } out_unlock: @@ -82,7 +83,7 @@ index 71aa45ce5d..ae1cd3980e 100644 } out_close: -@@ -2335,6 +2341,7 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, +@@ -2355,6 +2361,7 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, PreallocMode prealloc; char *buf = NULL; Error *local_err = NULL; @@ -90,7 +91,7 @@ index 71aa45ce5d..ae1cd3980e 100644 /* Skip file: protocol prefix */ strstart(filename, "file:", &filename); -@@ -2352,6 +2359,18 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, +@@ -2372,6 +2379,18 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, return -EINVAL; } @@ -109,7 +110,7 @@ index 71aa45ce5d..ae1cd3980e 100644 options = (BlockdevCreateOptions) { .driver = BLOCKDEV_DRIVER_FILE, .u.file = { -@@ -2361,6 +2380,8 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, +@@ -2381,6 +2400,8 @@ static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts, .preallocation = prealloc, .has_nocow = true, .nocow = nocow, @@ -118,7 +119,7 @@ index 71aa45ce5d..ae1cd3980e 100644 }, }; return raw_co_create(&options, errp); -@@ -2838,7 +2859,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, +@@ -2901,7 +2922,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, } /* Copy locks to the new fd */ @@ -128,10 +129,10 @@ index 71aa45ce5d..ae1cd3980e 100644 false, errp); if (ret < 0) { diff --git a/qapi/block-core.json b/qapi/block-core.json -index 97cb7ec41c..f432d4f3ec 100644 +index 0cf68fea14..783a868eb2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json -@@ -4284,7 +4284,8 @@ +@@ -4259,7 +4259,8 @@ 'data': { 'filename': 'str', 'size': 'size', '*preallocation': 'PreallocMode', diff --git a/debian/patches/pve/0022-PVE-vma-add-throttling-options-to-drive-mapping-fifo.patch b/debian/patches/pve/0022-PVE-vma-add-throttling-options-to-drive-mapping-fifo.patch deleted file mode 100644 index 7ba8e21..0000000 --- a/debian/patches/pve/0022-PVE-vma-add-throttling-options-to-drive-mapping-fifo.patch +++ /dev/null @@ -1,187 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Thu, 15 Feb 2018 11:07:56 +0100 -Subject: [PATCH] PVE: vma: add throttling options to drive mapping fifo - protocol - -We now need to call initialize the qom module as well. - -Signed-off-by: Wolfgang Bumiller -Signed-off-by: Thomas Lamprecht ---- - vma.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- - 1 file changed, 76 insertions(+), 12 deletions(-) - -diff --git a/vma.c b/vma.c -index 1b59fd1555..f9f5c308fe 100644 ---- a/vma.c -+++ b/vma.c -@@ -18,7 +18,8 @@ - #include "qemu-common.h" - #include "qemu/error-report.h" - #include "qemu/main-loop.h" --#include "qapi/qmp/qstring.h" -+#include "qemu/cutils.h" -+#include "qapi/qmp/qdict.h" - #include "sysemu/block-backend.h" - - static void help(void) -@@ -132,9 +133,39 @@ typedef struct RestoreMap { - char *devname; - char *path; - char *format; -+ uint64_t throttling_bps; -+ char *throttling_group; - bool write_zero; - } RestoreMap; - -+static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) { -+ size_t optlen = strlen(optname); -+ if (strncmp(*line, optname, optlen) != 0 || (*line)[optlen] != '=') { -+ return false; -+ } -+ if (*out) { -+ g_error("read map failed - duplicate value for option '%s'", optname); -+ } -+ char *value = (*line) + optlen + 1; /* including a '=' */ -+ char *colon = strchr(value, ':'); -+ if (!colon) { -+ g_error("read map failed - option '%s' not terminated ('%s')", -+ optname, inbuf); -+ } -+ *line = colon+1; -+ *out = g_strndup(value, colon - value); -+ return true; -+} -+ -+static uint64_t verify_u64(const char *text) { -+ uint64_t value; -+ const char *endptr = NULL; -+ if (qemu_strtou64(text, &endptr, 0, &value) != 0 || !endptr || *endptr) { -+ g_error("read map failed - not a number: %s", text); -+ } -+ return value; -+} -+ - static int extract_content(int argc, char **argv) - { - int c, ret = 0; -@@ -208,6 +239,9 @@ static int extract_content(int argc, char **argv) - while (1) { - char inbuf[8192]; - char *line = fgets(inbuf, sizeof(inbuf), map); -+ char *format = NULL; -+ char *bps = NULL; -+ char *group = NULL; - if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { - break; - } -@@ -219,15 +253,19 @@ static int extract_content(int argc, char **argv) - } - } - -- char *format = NULL; -- if (strncmp(line, "format=", sizeof("format=")-1) == 0) { -- format = line + sizeof("format=")-1; -- char *colon = strchr(format, ':'); -- if (!colon) { -- g_error("read map failed - found only a format ('%s')", inbuf); -+ while (1) { -+ if (!try_parse_option(&line, "format", &format, inbuf) && -+ !try_parse_option(&line, "throttling.bps", &bps, inbuf) && -+ !try_parse_option(&line, "throttling.group", &group, inbuf)) -+ { -+ break; - } -- format = g_strndup(format, colon - format); -- line = colon+1; -+ } -+ -+ uint64_t bps_value = 0; -+ if (bps) { -+ bps_value = verify_u64(bps); -+ g_free(bps); - } - - const char *path; -@@ -253,6 +291,8 @@ static int extract_content(int argc, char **argv) - map->devname = g_strdup(devname); - map->path = g_strdup(path); - map->format = format; -+ map->throttling_bps = bps_value; -+ map->throttling_group = group; - map->write_zero = write_zero; - - g_hash_table_insert(devmap, map->devname, map); -@@ -280,6 +320,8 @@ static int extract_content(int argc, char **argv) - } else if (di) { - char *devfn = NULL; - const char *format = NULL; -+ uint64_t throttling_bps = 0; -+ const char *throttling_group = NULL; - int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH; - bool write_zero = true; - -@@ -291,6 +333,8 @@ static int extract_content(int argc, char **argv) - } - devfn = map->path; - format = map->format; -+ throttling_bps = map->throttling_bps; -+ throttling_group = map->throttling_group; - write_zero = map->write_zero; - } else { - devfn = g_strdup_printf("%s/tmp-disk-%s.raw", -@@ -315,7 +359,7 @@ static int extract_content(int argc, char **argv) - if (format) { - /* explicit format from commandline */ - options = qdict_new(); -- qdict_put(options, "driver", qstring_from_str(format)); -+ qdict_put_str(options, "driver", format); - } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) || - strncmp(devfn, "/dev/", 5) == 0) - { -@@ -324,15 +368,34 @@ static int extract_content(int argc, char **argv) - */ - /* explicit raw format */ - options = qdict_new(); -- qdict_put(options, "driver", qstring_from_str("raw")); -+ qdict_put_str(options, "driver", "raw"); - } - -- - if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) { - g_error("can't open file %s - %s", devfn, - error_get_pretty(errp)); - } - -+ if (throttling_group) { -+ blk_io_limits_enable(blk, throttling_group); -+ } -+ -+ if (throttling_bps) { -+ if (!throttling_group) { -+ blk_io_limits_enable(blk, devfn); -+ } -+ -+ ThrottleConfig cfg; -+ throttle_config_init(&cfg); -+ cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps; -+ Error *err = NULL; -+ if (!throttle_is_valid(&cfg, &err)) { -+ error_report_err(err); -+ g_error("failed to apply throttling"); -+ } -+ blk_set_io_limits(blk, &cfg); -+ } -+ - if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) { - g_error("%s", error_get_pretty(errp)); - } -@@ -730,6 +793,7 @@ int main(int argc, char **argv) - } - - bdrv_init(); -+ module_call_init(MODULE_INIT_QOM); - - if (argc < 2) { - help(); diff --git a/debian/patches/pve/0028-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch b/debian/patches/pve/0023-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch similarity index 73% rename from debian/patches/pve/0028-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch rename to debian/patches/pve/0023-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch index 77fcb9b..d8b8646 100644 --- a/debian/patches/pve/0028-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch +++ b/debian/patches/pve/0023-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht -Date: Fri, 21 Jun 2019 10:38:46 +0200 -Subject: [PATCH] PVE: savevm-async: kick AIO wait on block state write +Date: Tue, 10 Mar 2020 12:55:20 +0100 +Subject: [PATCH 23/32] PVE: savevm-async: kick AIO wait on block state write Signed-off-by: Thomas Lamprecht --- @@ -9,10 +9,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 1 insertion(+) diff --git a/savevm-async.c b/savevm-async.c -index 2149010bb8..0bbbbf51ba 100644 +index 54ceeae26c..393d55af2a 100644 --- a/savevm-async.c +++ b/savevm-async.c -@@ -154,6 +154,7 @@ static void coroutine_fn block_state_write_entry(void *opaque) { +@@ -158,6 +158,7 @@ static void coroutine_fn block_state_write_entry(void *opaque) { BlkRwCo *rwco = opaque; rwco->ret = blk_co_pwritev(snap_state.target, rwco->offset, rwco->qiov->size, rwco->qiov, 0); diff --git a/debian/patches/pve/0023-PVE-vma-add-cache-option-to-device-map.patch b/debian/patches/pve/0023-PVE-vma-add-cache-option-to-device-map.patch deleted file mode 100644 index 505ca2a..0000000 --- a/debian/patches/pve/0023-PVE-vma-add-cache-option-to-device-map.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Thu, 22 Mar 2018 15:32:04 +0100 -Subject: [PATCH] PVE: vma: add cache option to device map - -Signed-off-by: Wolfgang Bumiller -Signed-off-by: Thomas Lamprecht ---- - vma.c | 16 +++++++++++++++- - 1 file changed, 15 insertions(+), 1 deletion(-) - -diff --git a/vma.c b/vma.c -index f9f5c308fe..476b7bee00 100644 ---- a/vma.c -+++ b/vma.c -@@ -135,6 +135,7 @@ typedef struct RestoreMap { - char *format; - uint64_t throttling_bps; - char *throttling_group; -+ char *cache; - bool write_zero; - } RestoreMap; - -@@ -242,6 +243,7 @@ static int extract_content(int argc, char **argv) - char *format = NULL; - char *bps = NULL; - char *group = NULL; -+ char *cache = NULL; - if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { - break; - } -@@ -256,7 +258,8 @@ static int extract_content(int argc, char **argv) - while (1) { - if (!try_parse_option(&line, "format", &format, inbuf) && - !try_parse_option(&line, "throttling.bps", &bps, inbuf) && -- !try_parse_option(&line, "throttling.group", &group, inbuf)) -+ !try_parse_option(&line, "throttling.group", &group, inbuf) && -+ !try_parse_option(&line, "cache", &cache, inbuf)) - { - break; - } -@@ -293,6 +296,7 @@ static int extract_content(int argc, char **argv) - map->format = format; - map->throttling_bps = bps_value; - map->throttling_group = group; -+ map->cache = cache; - map->write_zero = write_zero; - - g_hash_table_insert(devmap, map->devname, map); -@@ -322,6 +326,7 @@ static int extract_content(int argc, char **argv) - const char *format = NULL; - uint64_t throttling_bps = 0; - const char *throttling_group = NULL; -+ const char *cache = NULL; - int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH; - bool write_zero = true; - -@@ -335,6 +340,7 @@ static int extract_content(int argc, char **argv) - format = map->format; - throttling_bps = map->throttling_bps; - throttling_group = map->throttling_group; -+ cache = map->cache; - write_zero = map->write_zero; - } else { - devfn = g_strdup_printf("%s/tmp-disk-%s.raw", -@@ -356,6 +362,7 @@ static int extract_content(int argc, char **argv) - - size_t devlen = strlen(devfn); - QDict *options = NULL; -+ bool writethrough; - if (format) { - /* explicit format from commandline */ - options = qdict_new(); -@@ -370,12 +377,19 @@ static int extract_content(int argc, char **argv) - options = qdict_new(); - qdict_put_str(options, "driver", "raw"); - } -+ if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) { -+ g_error("invalid cache option: %s\n", cache); -+ } - - if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) { - g_error("can't open file %s - %s", devfn, - error_get_pretty(errp)); - } - -+ if (cache) { -+ blk_set_enable_write_cache(blk, !writethrough); -+ } -+ - if (throttling_group) { - blk_io_limits_enable(blk, throttling_group); - } diff --git a/debian/patches/pve/0029-PVE-move-snapshot-cleanup-into-bottom-half.patch b/debian/patches/pve/0024-PVE-move-snapshot-cleanup-into-bottom-half.patch similarity index 79% rename from debian/patches/pve/0029-PVE-move-snapshot-cleanup-into-bottom-half.patch rename to debian/patches/pve/0024-PVE-move-snapshot-cleanup-into-bottom-half.patch index fd07494..444426f 100644 --- a/debian/patches/pve/0029-PVE-move-snapshot-cleanup-into-bottom-half.patch +++ b/debian/patches/pve/0024-PVE-move-snapshot-cleanup-into-bottom-half.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Mon, 24 Jun 2019 10:19:50 +0200 -Subject: [PATCH] PVE: move snapshot cleanup into bottom half +Date: Tue, 10 Mar 2020 12:55:21 +0100 +Subject: [PATCH 24/32] PVE: move snapshot cleanup into bottom half as per: (0ceccd858a8d) migration: qemu_savevm_state_cleanup() in cleanup @@ -16,10 +16,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/savevm-async.c b/savevm-async.c -index 0bbbbf51ba..f9355c5036 100644 +index 393d55af2a..790e27ae37 100644 --- a/savevm-async.c +++ b/savevm-async.c -@@ -197,6 +197,8 @@ static void process_savevm_cleanup(void *opaque) +@@ -201,6 +201,8 @@ static void process_savevm_cleanup(void *opaque) int ret; qemu_bh_delete(snap_state.cleanup_bh); snap_state.cleanup_bh = NULL; @@ -28,7 +28,7 @@ index 0bbbbf51ba..f9355c5036 100644 qemu_mutex_unlock_iothread(); qemu_thread_join(&snap_state.thread); qemu_mutex_lock_iothread(); -@@ -273,7 +275,6 @@ static void *process_savevm_thread(void *opaque) +@@ -277,7 +279,6 @@ static void *process_savevm_thread(void *opaque) save_snapshot_error("qemu_savevm_state_iterate error %d", ret); break; } diff --git a/debian/patches/pve/0024-PVE-vma-remove-forced-NO_FLUSH-option.patch b/debian/patches/pve/0024-PVE-vma-remove-forced-NO_FLUSH-option.patch deleted file mode 100644 index c98c777..0000000 --- a/debian/patches/pve/0024-PVE-vma-remove-forced-NO_FLUSH-option.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Tue, 27 Mar 2018 10:49:03 +0200 -Subject: [PATCH] PVE: vma: remove forced NO_FLUSH option - -This one's rbd specific and in no way a sane choice for all -types storages. Instead, we want to honor the cache option -passed along. - -Signed-off-by: Wolfgang Bumiller -Signed-off-by: Thomas Lamprecht ---- - vma.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/vma.c b/vma.c -index 476b7bee00..3289fd722f 100644 ---- a/vma.c -+++ b/vma.c -@@ -327,7 +327,7 @@ static int extract_content(int argc, char **argv) - uint64_t throttling_bps = 0; - const char *throttling_group = NULL; - const char *cache = NULL; -- int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH; -+ int flags = BDRV_O_RDWR; - bool write_zero = true; - - if (readmap) { diff --git a/debian/patches/pve/0030-PVE-monitor-disable-oob-capability.patch b/debian/patches/pve/0025-PVE-monitor-disable-oob-capability.patch similarity index 85% rename from debian/patches/pve/0030-PVE-monitor-disable-oob-capability.patch rename to debian/patches/pve/0025-PVE-monitor-disable-oob-capability.patch index d144da0..360532f 100644 --- a/debian/patches/pve/0030-PVE-monitor-disable-oob-capability.patch +++ b/debian/patches/pve/0025-PVE-monitor-disable-oob-capability.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller -Date: Thu, 14 Nov 2019 14:38:02 +0100 -Subject: [PATCH] PVE: monitor: disable oob capability +Date: Tue, 10 Mar 2020 12:55:22 +0100 +Subject: [PATCH 25/32] PVE: monitor: disable oob capability A bisect revealed that commit 8258292e18c3 ("monitor: Remove "x-oob", offer capability "oob" unconditionally") @@ -18,10 +18,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monitor/qmp.c b/monitor/qmp.c -index fb3e66c62a..ad6703dc52 100644 +index b67a8e7d1f..8f44fed944 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c -@@ -379,8 +379,7 @@ void monitor_init_qmp(Chardev *chr, bool pretty) +@@ -395,8 +395,7 @@ void monitor_init_qmp(Chardev *chr, bool pretty) MonitorQMP *mon = g_new0(MonitorQMP, 1); /* Note: we run QMP monitor in I/O thread when @chr supports that */ diff --git a/debian/patches/pve/0044-Acquire-aio_context-before-calling-block_job_add_bdr.patch b/debian/patches/pve/0026-PVE-Acquire-aio_context-before-calling-block_job_add.patch similarity index 80% rename from debian/patches/pve/0044-Acquire-aio_context-before-calling-block_job_add_bdr.patch rename to debian/patches/pve/0026-PVE-Acquire-aio_context-before-calling-block_job_add.patch index b1861d3..7f9d5a2 100644 --- a/debian/patches/pve/0044-Acquire-aio_context-before-calling-block_job_add_bdr.patch +++ b/debian/patches/pve/0026-PVE-Acquire-aio_context-before-calling-block_job_add.patch @@ -1,7 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Stefan Reiter -Date: Mon, 21 Oct 2019 17:32:22 +0200 -Subject: [PATCH] Acquire aio_context before calling block_job_add_bdrv +Date: Tue, 10 Mar 2020 12:55:23 +0100 +Subject: [PATCH 26/32] PVE: Acquire aio_context before calling + block_job_add_bdrv Otherwise backups immediately fail with 'permission denied' since _add_bdrv tries to release a lock we don't own. @@ -12,10 +13,10 @@ Signed-off-by: Stefan Reiter 1 file changed, 10 insertions(+) diff --git a/blockjob.c b/blockjob.c -index 74abb97bfd..75b9180a7f 100644 +index c6e20e2fcd..4e6074f18c 100644 --- a/blockjob.c +++ b/blockjob.c -@@ -448,10 +448,20 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, +@@ -436,10 +436,20 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, notifier_list_add(&job->job.on_ready, &job->ready_notifier); notifier_list_add(&job->job.on_idle, &job->idle_notifier); @@ -35,4 +36,4 @@ index 74abb97bfd..75b9180a7f 100644 + bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); - blk_set_allow_aio_context_change(blk, true); + /* Disable request queuing in the BlockBackend to avoid deadlocks on drain: diff --git a/debian/patches/pve/0045-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch b/debian/patches/pve/0027-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch similarity index 88% rename from debian/patches/pve/0045-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch rename to debian/patches/pve/0027-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch index 96fc6a8..9aaea65 100644 --- a/debian/patches/pve/0045-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch +++ b/debian/patches/pve/0027-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch @@ -1,8 +1,8 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht -Date: Fri, 22 Nov 2019 08:56:20 +0100 -Subject: [PATCH] PVE: [Compat]: 4.0 used balloon qemu-4-0-config-size false - here +Date: Tue, 10 Mar 2020 12:55:24 +0100 +Subject: [PATCH 27/32] PVE: [Compat]: 4.0 used balloon qemu-4-0-config-size + false here The underlying issue why this change from upstream to us arised in the first place is that QEMU 4.0 was already released at the point we @@ -26,10 +26,10 @@ Signed-off-by: Thomas Lamprecht 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/core/machine.c b/hw/core/machine.c -index 32d1ca9abc..0ee68f1473 100644 +index 1689ad3bf8..bdcb351ede 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c -@@ -34,7 +34,8 @@ GlobalProperty hw_compat_4_0[] = { +@@ -39,7 +39,8 @@ GlobalProperty hw_compat_4_0[] = { { "virtio-vga", "edid", "false" }, { "virtio-gpu", "edid", "false" }, { "virtio-device", "use-started", "false" }, diff --git a/debian/patches/pve/0046-PVE-Allow-version-code-in-machine-type.patch b/debian/patches/pve/0028-PVE-Allow-version-code-in-machine-type.patch similarity index 73% rename from debian/patches/pve/0046-PVE-Allow-version-code-in-machine-type.patch rename to debian/patches/pve/0028-PVE-Allow-version-code-in-machine-type.patch index e50112e..a286333 100644 --- a/debian/patches/pve/0046-PVE-Allow-version-code-in-machine-type.patch +++ b/debian/patches/pve/0028-PVE-Allow-version-code-in-machine-type.patch @@ -1,7 +1,7 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Stefan Reiter -Date: Thu, 14 Nov 2019 17:56:12 +0100 -Subject: [PATCH] PVE: Allow version code in machine type +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 12:55:25 +0100 +Subject: [PATCH 28/32] PVE: Allow version code in machine type E.g. pc-i440fx-4.0+pve3 would print 'pve3' as version code while selecting pc-i440fx-4.0 as machine type. @@ -13,15 +13,15 @@ Signed-off-by: Stefan Reiter --- hw/core/machine-qmp-cmds.c | 6 ++++++ include/hw/boards.h | 2 ++ - qapi/machine.json | 2 +- + qapi/machine.json | 3 ++- vl.c | 15 ++++++++++++++- - 4 files changed, 23 insertions(+), 2 deletions(-) + 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c -index fd68f9baf8..61fcad138d 100644 +index 1953633e82..ca8c0dc53d 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c -@@ -232,6 +232,12 @@ MachineInfoList *qmp_query_machines(Error **errp) +@@ -234,6 +234,12 @@ MachineInfoList *qmp_query_machines(Error **errp) if (strcmp(mc->name, MACHINE_GET_CLASS(current_machine)->name) == 0) { info->has_is_current = true; info->is_current = true; @@ -33,12 +33,12 @@ index fd68f9baf8..61fcad138d 100644 + } } - entry = g_malloc0(sizeof(*entry)); + if (mc->default_cpu_type) { diff --git a/include/hw/boards.h b/include/hw/boards.h -index a71d1a53a5..952d2add82 100644 +index de45087f34..e24d2134c0 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h -@@ -178,6 +178,8 @@ struct MachineClass { +@@ -185,6 +185,8 @@ struct MachineClass { const char *desc; const char *deprecation_reason; @@ -46,25 +46,26 @@ index a71d1a53a5..952d2add82 100644 + void (*init)(MachineState *state); void (*reset)(MachineState *state); - void (*hot_add_cpu)(MachineState *state, const int64_t id, Error **errp); + void (*wakeup)(MachineState *state); diff --git a/qapi/machine.json b/qapi/machine.json -index 7b82c5f7f5..11fef6714e 100644 +index cbdb6f6d66..a2bd4dd304 100644 --- a/qapi/machine.json +++ b/qapi/machine.json -@@ -333,7 +333,7 @@ +@@ -359,7 +359,8 @@ 'data': { 'name': 'str', '*alias': 'str', '*is-default': 'bool', '*is-current': 'bool', 'cpu-max': 'int', 'hotpluggable-cpus': 'bool', 'numa-mem-supported': 'bool', -- 'deprecated': 'bool' } } -+ 'deprecated': 'bool', '*pve-version': 'str' } } +- 'deprecated': 'bool', '*default-cpu-type': 'str' } } ++ 'deprecated': 'bool', '*default-cpu-type': 'str', ++ '*pve-version': 'str' } } ## # @query-machines: diff --git a/vl.c b/vl.c -index 7ffcd271f1..1305cdaee7 100644 +index 4df15640c5..e7f3ce7607 100644 --- a/vl.c +++ b/vl.c -@@ -2454,6 +2454,8 @@ static MachineClass *machine_parse(const char *name, GSList *machines) +@@ -2475,6 +2475,8 @@ static MachineClass *machine_parse(const char *name, GSList *machines) { MachineClass *mc; GSList *el; @@ -73,7 +74,7 @@ index 7ffcd271f1..1305cdaee7 100644 if (is_help_option(name)) { printf("Supported machines are:\n"); -@@ -2470,12 +2472,23 @@ static MachineClass *machine_parse(const char *name, GSList *machines) +@@ -2491,12 +2493,23 @@ static MachineClass *machine_parse(const char *name, GSList *machines) exit(0); } diff --git a/debian/patches/pve/0021-PVE-Deprecated-adding-old-vma-files.patch b/debian/patches/pve/0029-PVE-Backup-add-vma-backup-format-code.patch similarity index 72% rename from debian/patches/pve/0021-PVE-Deprecated-adding-old-vma-files.patch rename to debian/patches/pve/0029-PVE-Backup-add-vma-backup-format-code.patch index f40bc6d..c7a4275 100644 --- a/debian/patches/pve/0021-PVE-Deprecated-adding-old-vma-files.patch +++ b/debian/patches/pve/0029-PVE-Backup-add-vma-backup-format-code.patch @@ -1,33 +1,26 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Mon, 7 Aug 2017 08:51:16 +0200 -Subject: [PATCH] PVE: [Deprecated] adding old vma files +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 12:55:26 +0100 +Subject: [PATCH 29/32] PVE-Backup: add vma backup format code -TODO: Move to using a libvma block backend -Signed-off-by: Thomas Lamprecht --- - Makefile | 3 +- - Makefile.objs | 1 + - block/backup.c | 88 ++-- - block/replication.c | 1 + - blockdev.c | 208 +++++---- - include/block/block_int.h | 4 + - job.c | 3 +- - vma-reader.c | 857 ++++++++++++++++++++++++++++++++++++++ - vma-writer.c | 771 ++++++++++++++++++++++++++++++++++ - vma.c | 756 +++++++++++++++++++++++++++++++++ - vma.h | 150 +++++++ - 11 files changed, 2737 insertions(+), 105 deletions(-) + Makefile | 3 +- + Makefile.objs | 1 + + vma-reader.c | 857 ++++++++++++++++++++++++++++++++++++++++++++++++++ + vma-writer.c | 771 +++++++++++++++++++++++++++++++++++++++++++++ + vma.c | 837 ++++++++++++++++++++++++++++++++++++++++++++++++ + vma.h | 150 +++++++++ + 6 files changed, 2618 insertions(+), 1 deletion(-) 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 85862fb81a..421e7b486c 100644 +index b437a346d7..18d2dba2e4 100644 --- a/Makefile +++ b/Makefile -@@ -436,7 +436,7 @@ dummy := $(call unnest-vars,, \ +@@ -453,7 +453,7 @@ dummy := $(call unnest-vars,, \ include $(SRC_PATH)/tests/Makefile.include @@ -36,7 +29,7 @@ index 85862fb81a..421e7b486c 100644 qemu-version.h: FORCE $(call quiet-command, \ -@@ -544,6 +544,7 @@ qemu-img.o: qemu-img-cmds.h +@@ -567,6 +567,7 @@ qemu-img.o: qemu-img-cmds.h qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) @@ -45,7 +38,7 @@ index 85862fb81a..421e7b486c 100644 qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) diff --git a/Makefile.objs b/Makefile.objs -index 21dd93b58c..87c6033bc1 100644 +index f97b40f232..db7fbbe73b 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -18,6 +18,7 @@ block-obj-y += block.o blockjob.o job.o @@ -56,665 +49,6 @@ index 21dd93b58c..87c6033bc1 100644 block-obj-m = block/ -diff --git a/block/backup.c b/block/backup.c -index 30008fcc34..fdcfb0529c 100644 ---- a/block/backup.c -+++ b/block/backup.c -@@ -41,6 +41,7 @@ typedef struct BackupBlockJob { - /* bitmap for sync=incremental */ - BdrvDirtyBitmap *sync_bitmap; - MirrorSyncMode sync_mode; -+ BackupDumpFunc *dump_cb; - BlockdevOnError on_source_error; - BlockdevOnError on_target_error; - CoRwlock flush_rwlock; -@@ -129,12 +130,20 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, - } - - if (buffer_is_zero(*bounce_buffer, nbytes)) { -- ret = blk_co_pwrite_zeroes(job->target, start, -- nbytes, write_flags | BDRV_REQ_MAY_UNMAP); -+ if (job->dump_cb) { -+ ret = job->dump_cb(job->common.job.opaque, job->target, start, nbytes, NULL); -+ } else { -+ ret = blk_co_pwrite_zeroes(job->target, start, -+ nbytes, write_flags | BDRV_REQ_MAY_UNMAP); -+ } - } else { -- ret = blk_co_pwrite(job->target, start, -- nbytes, *bounce_buffer, write_flags | -- (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); -+ if (job->dump_cb) { -+ ret = job->dump_cb(job->common.job.opaque, job->target, start, nbytes, *bounce_buffer); -+ } else { -+ ret = blk_co_pwrite(job->target, start, -+ nbytes, *bounce_buffer, write_flags | -+ (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); -+ } - } - if (ret < 0) { - trace_backup_do_cow_write_fail(job, start, ret); -@@ -218,8 +227,11 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, - trace_backup_do_cow_process(job, start); - - if (job->use_copy_range) { -- ret = backup_cow_with_offload(job, start, dirty_end, -- is_write_notifier); -+ if (job->dump_cb) { -+ ret = - 1; -+ } else { -+ ret = backup_cow_with_offload(job, start, dirty_end, is_write_notifier); -+ } - if (ret < 0) { - job->use_copy_range = false; - } -@@ -304,7 +316,9 @@ static void backup_abort(Job *job) - static void backup_clean(Job *job) - { - BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); -- assert(s->target); -+ if (!s->target) { -+ return; -+ } - blk_unref(s->target); - s->target = NULL; - -@@ -350,9 +364,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, - if (read) { - return block_job_error_action(&job->common, job->on_source_error, - true, error); -- } else { -+ } else if (job->target) { - return block_job_error_action(&job->common, job->on_target_error, - false, error); -+ } else { -+ return BLOCK_ERROR_ACTION_REPORT; - } - } - -@@ -549,6 +565,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, - int creation_flags, -+ BackupDumpFunc *dump_cb, - BlockCompletionFunc *cb, void *opaque, - int pause_count, - JobTxn *txn, Error **errp) -@@ -560,7 +577,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - HBitmap *copy_bitmap = NULL; - - assert(bs); -- assert(target); -+ assert(target || dump_cb); - - if (bs == target) { - error_setg(errp, "Source and target cannot be the same"); -@@ -573,13 +590,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - return NULL; - } - -- 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 NULL; - } - -- if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) { -+ if (target && compress && target->drv->bdrv_co_pwritev_compressed == NULL) { - error_setg(errp, "Compression is not supported for this drive %s", - bdrv_get_device_name(target)); - return NULL; -@@ -589,7 +606,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - return NULL; - } - -- 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 NULL; - } - -@@ -619,7 +636,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - goto error; - } - -- cluster_size = backup_calculate_cluster_size(target, errp); -+ cluster_size = backup_calculate_cluster_size(target ? target : bs, errp); - if (cluster_size < 0) { - goto error; - } -@@ -636,16 +653,19 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - goto error; - } - -- /* The target must match the source in size, so no resize here either */ -- job->target = blk_new(job->common.job.aio_context, -- BLK_PERM_WRITE, -- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | -- BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD); -- ret = blk_insert_bs(job->target, target, errp); -- if (ret < 0) { -- goto error; -+ if (target) { -+ /* The target must match the source in size, so no resize here either */ -+ job->target = blk_new(job->common.job.aio_context, -+ BLK_PERM_WRITE, -+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | -+ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD); -+ ret = blk_insert_bs(job->target, target, errp); -+ if (ret < 0) { -+ goto error; -+ } - } - -+ job->dump_cb = dump_cb; - job->on_source_error = on_source_error; - job->on_target_error = on_target_error; - job->sync_mode = sync_mode; -@@ -658,10 +678,14 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - job->cluster_size = cluster_size; - job->copy_bitmap = copy_bitmap; - copy_bitmap = NULL; -- job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), -- blk_get_max_transfer(job->target)); -- job->copy_range_size = QEMU_ALIGN_DOWN(job->copy_range_size, -- job->cluster_size); -+ -+ if (target) { -+ job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), -+ blk_get_max_transfer(job->target)); -+ job->copy_range_size = QEMU_ALIGN_DOWN(job->copy_range_size, -+ job->cluster_size); -+ } -+ - /* - * Set use_copy_range, consider the following: - * 1. Compression is not supported for copy_range. -@@ -669,12 +693,16 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - * that in here. If max_transfer is smaller than the job->cluster_size, - * we do not use copy_range (in that case it's zero after aligning down - * above). -+ * 3. If !target, we're using PVE dump_cb callback - */ -- job->use_copy_range = !compress && job->copy_range_size > 0; -+ job->use_copy_range = target && !compress && job->copy_range_size > 0; -+ -+ if (target) { -+ /* Required permissions are already taken with target's blk_new() */ -+ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, -+ &error_abort); -+ } - -- /* Required permissions are already taken with target's blk_new() */ -- block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, -- &error_abort); - job->len = len; - job->common.job.pause_count += pause_count; - -diff --git a/block/replication.c b/block/replication.c -index e70a6cf2bd..f060755713 100644 ---- a/block/replication.c -+++ b/block/replication.c -@@ -546,6 +546,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, - 0, MIRROR_SYNC_MODE_NONE, NULL, false, - BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, -+ NULL, - backup_job_completed, bs, 0, NULL, &local_err); - if (local_err) { - error_propagate(errp, local_err); -diff --git a/blockdev.c b/blockdev.c -index 7047475a3c..cee7952bbb 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -31,7 +31,6 @@ - */ - - #include "qemu/osdep.h" --#include "qemu/uuid.h" - #include "sysemu/block-backend.h" - #include "sysemu/blockdev.h" - #include "hw/block/block.h" -@@ -64,6 +63,7 @@ - #include "qemu/cutils.h" - #include "qemu/help_option.h" - #include "qemu/throttle-options.h" -+#include "vma.h" - - static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = - QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); -@@ -3176,15 +3176,14 @@ out: - static struct PVEBackupState { - Error *error; - bool cancel; -- QemuUUID uuid; -+ uuid_t uuid; - char uuid_str[37]; - int64_t speed; - time_t start_time; - time_t end_time; - char *backup_file; -- Object *vmaobj; -+ VmaWriter *vmaw; - GList *di_list; -- size_t next_job; - size_t total; - size_t transferred; - size_t zero_bytes; -@@ -3203,6 +3202,71 @@ typedef struct PVEBackupDevInfo { - - static void pvebackup_run_next_job(void); - -+static int pvebackup_dump_cb(void *opaque, BlockBackend *target, -+ uint64_t start, uint64_t bytes, -+ const void *pbuf) -+{ -+ const uint64_t size = bytes; -+ const unsigned char *buf = pbuf; -+ PVEBackupDevInfo *di = opaque; -+ -+ if (backup_state.cancel) { -+ return size; // return success -+ } -+ -+ uint64_t cluster_num = start / VMA_CLUSTER_SIZE; -+ if ((cluster_num * VMA_CLUSTER_SIZE) != start) { -+ if (!backup_state.error) { -+ error_setg(&backup_state.error, -+ "got unaligned write inside backup dump " -+ "callback (sector %ld)", start); -+ } -+ return -1; // not aligned to cluster size -+ } -+ -+ int ret = -1; -+ -+ if (backup_state.vmaw) { -+ size_t zero_bytes = 0; -+ uint64_t remaining = size; -+ while (remaining > 0) { -+ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, -+ buf, &zero_bytes); -+ ++cluster_num; -+ if (buf) { -+ buf += VMA_CLUSTER_SIZE; -+ } -+ if (ret < 0) { -+ if (!backup_state.error) { -+ vma_writer_error_propagate(backup_state.vmaw, &backup_state.error); -+ } -+ if (di->bs && di->bs->job) { -+ job_cancel(&di->bs->job->job, true); -+ } -+ break; -+ } else { -+ backup_state.zero_bytes += zero_bytes; -+ if (remaining >= VMA_CLUSTER_SIZE) { -+ backup_state.transferred += VMA_CLUSTER_SIZE; -+ remaining -= VMA_CLUSTER_SIZE; -+ } else { -+ backup_state.transferred += remaining; -+ remaining = 0; -+ } -+ } -+ } -+ } else { -+ if (!buf) { -+ backup_state.zero_bytes += size; -+ } -+ backup_state.transferred += size; -+ } -+ -+ // Note: always return success, because we want that writes succeed anyways. -+ -+ return size; -+} -+ - static void pvebackup_cleanup(void) - { - qemu_mutex_lock(&backup_state.backup_mutex); -@@ -3214,9 +3278,11 @@ static void pvebackup_cleanup(void) - - backup_state.end_time = time(NULL); - -- if (backup_state.vmaobj) { -- object_unparent(backup_state.vmaobj); -- backup_state.vmaobj = 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; - } - - g_list_free(backup_state.di_list); -@@ -3224,6 +3290,13 @@ static void pvebackup_cleanup(void) - qemu_mutex_unlock(&backup_state.backup_mutex); - } - -+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) - { - // This always runs in the main loop -@@ -3240,9 +3313,9 @@ static void pvebackup_complete_cb(void *opaque, int ret) - di->bs = NULL; - di->target = NULL; - -- if (backup_state.vmaobj) { -- object_unparent(backup_state.vmaobj); -- backup_state.vmaobj = NULL; -+ if (backup_state.vmaw) { -+ Coroutine *co = qemu_coroutine_create(backup_close_vma_stream, di); -+ qemu_coroutine_enter(co); - } - - // remove self from job queue -@@ -3270,14 +3343,9 @@ static void pvebackup_cancel(void *opaque) - error_setg(&backup_state.error, "backup cancelled"); - } - -- if (backup_state.vmaobj) { -- Error *err; -+ if (backup_state.vmaw) { - /* make sure vma writer does not block anymore */ -- if (!object_set_props(backup_state.vmaobj, &err, "blocked", "yes", NULL)) { -- if (err) { -- error_report_err(err); -- } -- } -+ vma_writer_set_error(backup_state.vmaw, "backup cancelled"); - } - - GList *l = backup_state.di_list; -@@ -3308,18 +3376,14 @@ void qmp_backup_cancel(Error **errp) - Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL); - qemu_coroutine_enter(co); - -- while (backup_state.vmaobj) { -- /* FIXME: Find something better for this */ -+ while (backup_state.vmaw) { -+ /* vma writer use main aio context */ - aio_poll(qemu_get_aio_context(), true); - } - } - --void vma_object_add_config_file(Object *obj, const char *name, -- const char *contents, size_t len, -- Error **errp); - static int config_to_vma(const char *file, BackupFormat format, -- Object *vmaobj, -- const char *backup_dir, -+ const char *backup_dir, VmaWriter *vmaw, - Error **errp) - { - char *cdata = NULL; -@@ -3333,7 +3397,12 @@ static int config_to_vma(const char *file, BackupFormat format, - char *basename = g_path_get_basename(file); - - if (format == BACKUP_FORMAT_VMA) { -- vma_object_add_config_file(vmaobj, basename, cdata, clen, errp); -+ 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); -@@ -3350,28 +3419,30 @@ static int config_to_vma(const char *file, BackupFormat format, - return 0; - } - -+bool job_should_pause(Job *job); - static void pvebackup_run_next_job(void) - { - qemu_mutex_lock(&backup_state.backup_mutex); - -- GList *next = g_list_nth(backup_state.di_list, backup_state.next_job); -- while (next) { -- PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data; -- backup_state.next_job++; -+ 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; - AioContext *aio_context = blk_get_aio_context(job->blk); - aio_context_acquire(aio_context); - qemu_mutex_unlock(&backup_state.backup_mutex); -- if (backup_state.error || backup_state.cancel) { -- job_cancel_sync(job); -- } else { -- job_resume(job); -+ if (job_should_pause(&job->job)) { -+ if (backup_state.error || backup_state.cancel) { -+ job_cancel_sync(&job->job); -+ } else { -+ job_resume(&job->job); -+ } - } - aio_context_release(aio_context); - return; - } -- next = g_list_next(next); - } - qemu_mutex_unlock(&backup_state.backup_mutex); - -@@ -3382,7 +3453,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_firewall_file, const char *firewall_file, - bool has_devlist, const char *devlist, - bool has_speed, int64_t speed, Error **errp) - { -@@ -3390,7 +3461,8 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - BlockDriverState *bs = NULL; - const char *backup_dir = NULL; - Error *local_err = NULL; -- QemuUUID uuid; -+ uuid_t uuid; -+ VmaWriter *vmaw = NULL; - gchar **devs = NULL; - GList *di_list = NULL; - GList *l; -@@ -3402,7 +3474,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - backup_state.backup_mutex_initialized = true; - } - -- if (backup_state.di_list || backup_state.vmaobj) { -+ if (backup_state.di_list) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "previous backup not finished"); - return NULL; -@@ -3477,40 +3549,28 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - total += size; - } - -- qemu_uuid_generate(&uuid); -+ uuid_generate(uuid); - - if (format == BACKUP_FORMAT_VMA) { -- char uuidstr[UUID_FMT_LEN+1]; -- qemu_uuid_unparse(&uuid, uuidstr); -- uuidstr[UUID_FMT_LEN] = 0; -- backup_state.vmaobj = -- object_new_with_props("vma", object_get_objects_root(), -- "vma-backup-obj", &local_err, -- "filename", backup_file, -- "uuid", uuidstr, -- NULL); -- if (!backup_state.vmaobj) { -+ 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) { -- QDict *options = qdict_new(); -- - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); - - const char *devname = bdrv_get_device_name(di->bs); -- snprintf(di->targetfile, PATH_MAX, "vma-backup-obj/%s.raw", devname); -- -- qdict_put(options, "driver", qstring_from_str("vma-drive")); -- qdict_put(options, "size", qint_from_int(di->size)); -- di->target = bdrv_open(di->targetfile, NULL, options, BDRV_O_RDWR, &local_err); -- if (!di->target) { -- error_propagate(errp, local_err); -+ 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; - } - } -@@ -3551,14 +3611,14 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - - /* add configuration file to archive */ - if (has_config_file) { -- if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -+ 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_state.vmaobj, backup_dir, errp) != 0) { -+ if (config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) { - goto err; - } - } -@@ -3581,12 +3641,13 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - } - backup_state.backup_file = g_strdup(backup_file); - -- memcpy(&backup_state.uuid, &uuid, sizeof(uuid)); -- qemu_uuid_unparse(&uuid, backup_state.uuid_str); -+ backup_state.vmaw = vmaw; -+ -+ uuid_copy(backup_state.uuid, uuid); -+ uuid_unparse_lower(uuid, backup_state.uuid_str); - - qemu_mutex_lock(&backup_state.backup_mutex); - backup_state.di_list = di_list; -- backup_state.next_job = 0; - - backup_state.total = total; - backup_state.transferred = 0; -@@ -3597,21 +3658,21 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - while (l) { - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); -- - job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL, - false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, - JOB_DEFAULT, -- pvebackup_complete_cb, di, 2, NULL, &local_err); -- if (di->target) { -- bdrv_unref(di->target); -- di->target = NULL; -- } -+ pvebackup_dump_cb, pvebackup_complete_cb, di, -+ 1, NULL, &local_err); - if (!job || local_err != NULL) { - error_setg(&backup_state.error, "backup_job_create failed"); - pvebackup_cancel(NULL); - } else { - job_start(&job->job); - } -+ if (di->target) { -+ bdrv_unref(di->target); -+ di->target = NULL; -+ } - } - - qemu_mutex_unlock(&backup_state.backup_mutex); -@@ -3647,9 +3708,10 @@ err: - g_strfreev(devs); - } - -- if (backup_state.vmaobj) { -- object_unparent(backup_state.vmaobj); -- backup_state.vmaobj = NULL; -+ if (vmaw) { -+ Error *err = NULL; -+ vma_writer_close(vmaw, &err); -+ unlink(backup_file); - } - - if (backup_dir) { -@@ -4110,7 +4172,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, - job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, bmap, backup->compress, - backup->on_source_error, backup->on_target_error, -- job_flags, NULL, NULL, 0, txn, &local_err); -+ job_flags, NULL, NULL, NULL, 0, txn, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - goto unref; -@@ -4215,7 +4277,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, - job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, bmap, backup->compress, - backup->on_source_error, backup->on_target_error, -- job_flags, NULL, NULL, 0, txn, &local_err); -+ job_flags, NULL, NULL, NULL, 0, txn, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - } -diff --git a/include/block/block_int.h b/include/block/block_int.h -index bb2dddca83..5a8b2e06c1 100644 ---- a/include/block/block_int.h -+++ b/include/block/block_int.h -@@ -61,6 +61,9 @@ - - #define BLOCK_PROBE_BUF_SIZE 512 - -+typedef int BackupDumpFunc(void *opaque, BlockBackend *be, -+ uint64_t offset, uint64_t bytes, const void *buf); -+ - enum BdrvTrackedRequestType { - BDRV_TRACKED_READ, - BDRV_TRACKED_WRITE, -@@ -1172,6 +1175,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - BlockdevOnError on_source_error, - BlockdevOnError on_target_error, - int creation_flags, -+ BackupDumpFunc *dump_cb, - BlockCompletionFunc *cb, void *opaque, - int pause_count, - JobTxn *txn, Error **errp); -diff --git a/job.c b/job.c -index 7a21e83780..9d27999678 100644 ---- a/job.c -+++ b/job.c -@@ -248,7 +248,8 @@ static bool job_started(Job *job) - return job->co; - } - --static bool job_should_pause(Job *job) -+bool job_should_pause(Job *job); -+bool job_should_pause(Job *job) - { - return job->pause_count > 0; - } diff --git a/vma-reader.c b/vma-reader.c new file mode 100644 index 0000000000..2b1d1cdab3 @@ -1580,7 +914,7 @@ index 0000000000..2b1d1cdab3 + diff --git a/vma-writer.c b/vma-writer.c new file mode 100644 -index 0000000000..fd9567634d +index 0000000000..fe86b18a60 --- /dev/null +++ b/vma-writer.c @@ -0,0 +1,771 @@ @@ -1785,12 +1119,14 @@ index 0000000000..fd9567634d + return n; +} + -+static void vma_co_continue_write(void *opaque) ++static void coroutine_fn yield_until_fd_writable(int fd) +{ -+ VmaWriter *vmaw = opaque; -+ -+ DPRINTF("vma_co_continue_write\n"); -+ qemu_coroutine_enter(vmaw->co_writer); ++ assert(qemu_in_coroutine()); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ aio_set_fd_handler(ctx, fd, false, NULL, (IOHandler *)qemu_coroutine_enter, ++ NULL, qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ aio_set_fd_handler(ctx, fd, false, NULL, NULL, NULL, NULL); +} + +static ssize_t coroutine_fn @@ -1810,14 +1146,12 @@ index 0000000000..fd9567634d + vmaw->co_writer = qemu_coroutine_self(); + + while (done < bytes) { -+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, NULL, vmaw); -+ qemu_coroutine_yield(); -+ aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, NULL, NULL, NULL); + if (vmaw->status < 0) { + DPRINTF("vma_queue_write detected canceled backup\n"); + done = -1; + break; + } ++ yield_until_fd_writable(vmaw->fd); + ret = write(vmaw->fd, buf + done, bytes - done); + if (ret > 0) { + done += ret; @@ -2291,9 +1625,7 @@ index 0000000000..fd9567634d + + int i; + -+ while (vmaw->co_writer) { -+ aio_poll(qemu_get_aio_context(), true); -+ } ++ qemu_co_mutex_lock(&vmaw->flush_lock); // wait for pending writes + + assert(vmaw->co_writer == NULL); + @@ -2334,6 +1666,8 @@ index 0000000000..fd9567634d + error_setg(errp, "%s", vmaw->errmsg); + } + ++ qemu_co_mutex_unlock(&vmaw->flush_lock); ++ + return vmaw->status; +} + @@ -2357,10 +1691,10 @@ index 0000000000..fd9567634d +} diff --git a/vma.c b/vma.c new file mode 100644 -index 0000000000..1b59fd1555 +index 0000000000..a82752448a --- /dev/null +++ b/vma.c -@@ -0,0 +1,756 @@ +@@ -0,0 +1,837 @@ +/* + * VMA: Virtual Machine Archive + * @@ -2379,9 +1713,11 @@ index 0000000000..1b59fd1555 + +#include "vma.h" +#include "qemu-common.h" ++#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" -+#include "qapi/qmp/qstring.h" ++#include "qemu/cutils.h" ++#include "qapi/qmp/qdict.h" +#include "sysemu/block-backend.h" + +static void help(void) @@ -2495,9 +1831,40 @@ index 0000000000..1b59fd1555 + char *devname; + char *path; + char *format; ++ uint64_t throttling_bps; ++ char *throttling_group; ++ char *cache; + bool write_zero; +} RestoreMap; + ++static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) { ++ size_t optlen = strlen(optname); ++ if (strncmp(*line, optname, optlen) != 0 || (*line)[optlen] != '=') { ++ return false; ++ } ++ if (*out) { ++ g_error("read map failed - duplicate value for option '%s'", optname); ++ } ++ char *value = (*line) + optlen + 1; /* including a '=' */ ++ char *colon = strchr(value, ':'); ++ if (!colon) { ++ g_error("read map failed - option '%s' not terminated ('%s')", ++ optname, inbuf); ++ } ++ *line = colon+1; ++ *out = g_strndup(value, colon - value); ++ return true; ++} ++ ++static uint64_t verify_u64(const char *text) { ++ uint64_t value; ++ const char *endptr = NULL; ++ if (qemu_strtou64(text, &endptr, 0, &value) != 0 || !endptr || *endptr) { ++ g_error("read map failed - not a number: %s", text); ++ } ++ return value; ++} ++ +static int extract_content(int argc, char **argv) +{ + int c, ret = 0; @@ -2571,6 +1938,10 @@ index 0000000000..1b59fd1555 + while (1) { + char inbuf[8192]; + char *line = fgets(inbuf, sizeof(inbuf), map); ++ char *format = NULL; ++ char *bps = NULL; ++ char *group = NULL; ++ char *cache = NULL; + if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { + break; + } @@ -2582,15 +1953,20 @@ index 0000000000..1b59fd1555 + } + } + -+ char *format = NULL; -+ if (strncmp(line, "format=", sizeof("format=")-1) == 0) { -+ format = line + sizeof("format=")-1; -+ char *colon = strchr(format, ':'); -+ if (!colon) { -+ g_error("read map failed - found only a format ('%s')", inbuf); ++ while (1) { ++ if (!try_parse_option(&line, "format", &format, inbuf) && ++ !try_parse_option(&line, "throttling.bps", &bps, inbuf) && ++ !try_parse_option(&line, "throttling.group", &group, inbuf) && ++ !try_parse_option(&line, "cache", &cache, inbuf)) ++ { ++ break; + } -+ format = g_strndup(format, colon - format); -+ line = colon+1; ++ } ++ ++ uint64_t bps_value = 0; ++ if (bps) { ++ bps_value = verify_u64(bps); ++ g_free(bps); + } + + const char *path; @@ -2616,6 +1992,9 @@ index 0000000000..1b59fd1555 + map->devname = g_strdup(devname); + map->path = g_strdup(path); + map->format = format; ++ map->throttling_bps = bps_value; ++ map->throttling_group = group; ++ map->cache = cache; + map->write_zero = write_zero; + + g_hash_table_insert(devmap, map->devname, map); @@ -2643,7 +2022,10 @@ index 0000000000..1b59fd1555 + } else if (di) { + char *devfn = NULL; + const char *format = NULL; -+ int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH; ++ uint64_t throttling_bps = 0; ++ const char *throttling_group = NULL; ++ const char *cache = NULL; ++ int flags = BDRV_O_RDWR; + bool write_zero = true; + + if (readmap) { @@ -2654,6 +2036,9 @@ index 0000000000..1b59fd1555 + } + devfn = map->path; + format = map->format; ++ throttling_bps = map->throttling_bps; ++ throttling_group = map->throttling_group; ++ cache = map->cache; + write_zero = map->write_zero; + } else { + devfn = g_strdup_printf("%s/tmp-disk-%s.raw", @@ -2675,10 +2060,11 @@ index 0000000000..1b59fd1555 + + size_t devlen = strlen(devfn); + QDict *options = NULL; ++ bool writethrough; + if (format) { + /* explicit format from commandline */ + options = qdict_new(); -+ qdict_put(options, "driver", qstring_from_str(format)); ++ qdict_put_str(options, "driver", format); + } else if ((devlen > 4 && strcmp(devfn+devlen-4, ".raw") == 0) || + strncmp(devfn, "/dev/", 5) == 0) + { @@ -2687,15 +2073,41 @@ index 0000000000..1b59fd1555 + */ + /* explicit raw format */ + options = qdict_new(); -+ qdict_put(options, "driver", qstring_from_str("raw")); ++ qdict_put_str(options, "driver", "raw"); + } -+ ++ if (cache && bdrv_parse_cache_mode(cache, &flags, &writethrough)) { ++ g_error("invalid cache option: %s\n", cache); ++ } + + if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) { + g_error("can't open file %s - %s", devfn, + error_get_pretty(errp)); + } + ++ if (cache) { ++ blk_set_enable_write_cache(blk, !writethrough); ++ } ++ ++ if (throttling_group) { ++ blk_io_limits_enable(blk, throttling_group); ++ } ++ ++ if (throttling_bps) { ++ if (!throttling_group) { ++ blk_io_limits_enable(blk, devfn); ++ } ++ ++ ThrottleConfig cfg; ++ throttle_config_init(&cfg); ++ cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps; ++ Error *err = NULL; ++ if (!throttle_is_valid(&cfg, &err)) { ++ error_report_err(err); ++ g_error("failed to apply throttling"); ++ } ++ blk_set_io_limits(blk, &cfg); ++ } ++ + if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) { + g_error("%s", error_get_pretty(errp)); + } @@ -3086,13 +2498,16 @@ index 0000000000..1b59fd1555 + const char *cmdname; + Error *main_loop_err = NULL; + -+ error_set_progname(argv[0]); ++ error_init(argv[0]); ++ module_call_init(MODULE_INIT_TRACE); ++ qemu_init_exec_dir(argv[0]); + + if (qemu_init_main_loop(&main_loop_err)) { + g_error("%s", error_get_pretty(main_loop_err)); + } + + bdrv_init(); ++ module_call_init(MODULE_INIT_QOM); + + if (argc < 2) { + help(); diff --git a/debian/patches/pve/0030-PVE-Backup-add-backup-dump-block-driver.patch b/debian/patches/pve/0030-PVE-Backup-add-backup-dump-block-driver.patch new file mode 100644 index 0000000..9b3f891 --- /dev/null +++ b/debian/patches/pve/0030-PVE-Backup-add-backup-dump-block-driver.patch @@ -0,0 +1,322 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 12:55:27 +0100 +Subject: [PATCH 30/32] PVE-Backup: add backup-dump block driver + +- add backup-dump block driver block/backup-dump.c +- move BackupBlockJob declaration from block/backup.c to include/block/block_int.h +- block/backup.c - backup-job-create: also consider source cluster size +- block/io.c - bdrv_do_drained_begin_quiesce: check for coroutine +- job.c: make job_should_pause non-static +--- + block/Makefile.objs | 1 + + block/backup-dump.c | 169 ++++++++++++++++++++++++++++++++++++++ + block/backup.c | 23 ++---- + block/io.c | 8 +- + include/block/block_int.h | 30 +++++++ + job.c | 3 +- + 6 files changed, 214 insertions(+), 20 deletions(-) + create mode 100644 block/backup-dump.c + +diff --git a/block/Makefile.objs b/block/Makefile.objs +index a10ceabf5b..5cd9e40d8d 100644 +--- a/block/Makefile.objs ++++ b/block/Makefile.objs +@@ -33,6 +33,7 @@ block-obj-$(CONFIG_RBD) += rbd.o + block-obj-$(CONFIG_GLUSTERFS) += gluster.o + block-obj-$(CONFIG_VXHS) += vxhs.o + block-obj-$(CONFIG_LIBSSH) += ssh.o ++block-obj-y += backup-dump.o + block-obj-y += accounting.o dirty-bitmap.o + block-obj-y += write-threshold.o + block-obj-y += backup.o +diff --git a/block/backup-dump.c b/block/backup-dump.c +new file mode 100644 +index 0000000000..3066ab0698 +--- /dev/null ++++ b/block/backup-dump.c +@@ -0,0 +1,169 @@ ++/* ++ * BlockDriver to send backup data stream to a callback function ++ * ++ * Copyright (C) 2020 Proxmox Server Solutions GmbH ++ * ++ * 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 "qemu/osdep.h" ++#include "qemu-common.h" ++#include "qom/object_interfaces.h" ++#include "block/block_int.h" ++ ++typedef struct { ++ int dump_cb_block_size; ++ uint64_t byte_size; ++ BackupDumpFunc *dump_cb; ++ void *dump_cb_data; ++} BDRVBackupDumpState; ++ ++static int qemu_backup_dump_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ BDRVBackupDumpState *s = bs->opaque; ++ ++ bdi->cluster_size = s->dump_cb_block_size; ++ bdi->unallocated_blocks_are_zero = true; ++ return 0; ++} ++ ++static int qemu_backup_dump_check_perm( ++ BlockDriverState *bs, ++ uint64_t perm, ++ uint64_t shared, ++ Error **errp) ++{ ++ /* Nothing to do. */ ++ return 0; ++} ++ ++static void qemu_backup_dump_set_perm( ++ BlockDriverState *bs, ++ uint64_t perm, ++ uint64_t shared) ++{ ++ /* Nothing to do. */ ++} ++ ++static void qemu_backup_dump_abort_perm_update(BlockDriverState *bs) ++{ ++ /* Nothing to do. */ ++} ++ ++static void qemu_backup_dump_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */ ++} ++ ++static void qemu_backup_dump_close(BlockDriverState *bs) ++{ ++ /* Nothing to do. */ ++} ++ ++static int64_t qemu_backup_dump_getlength(BlockDriverState *bs) ++{ ++ BDRVBackupDumpState *s = bs->opaque; ++ ++ return s->byte_size; ++} ++ ++static coroutine_fn int qemu_backup_dump_co_writev( ++ BlockDriverState *bs, ++ int64_t sector_num, ++ int nb_sectors, ++ QEMUIOVector *qiov, ++ int flags) ++{ ++ /* flags can be only values we set in supported_write_flags */ ++ assert(flags == 0); ++ ++ BDRVBackupDumpState *s = bs->opaque; ++ off_t offset = sector_num * BDRV_SECTOR_SIZE; ++ ++ uint64_t written = 0; ++ ++ for (int i = 0; i < qiov->niov; ++i) { ++ const struct iovec *v = &qiov->iov[i]; ++ ++ int rc = s->dump_cb(s->dump_cb_data, offset, v->iov_len, v->iov_base); ++ if (rc < 0) { ++ return rc; ++ } ++ ++ if (rc != v->iov_len) { ++ return -EIO; ++ } ++ ++ written += v->iov_len; ++ offset += v->iov_len; ++ } ++ ++ return written; ++} ++ ++static void qemu_backup_dump_child_perm( ++ BlockDriverState *bs, ++ BdrvChild *c, ++ const BdrvChildRole *role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ *nperm = BLK_PERM_ALL; ++ *nshared = BLK_PERM_ALL; ++} ++ ++static BlockDriver bdrv_backup_dump_drive = { ++ .format_name = "backup-dump-drive", ++ .protocol_name = "backup-dump", ++ .instance_size = sizeof(BDRVBackupDumpState), ++ ++ .bdrv_close = qemu_backup_dump_close, ++ .bdrv_has_zero_init = bdrv_has_zero_init_1, ++ .bdrv_getlength = qemu_backup_dump_getlength, ++ .bdrv_get_info = qemu_backup_dump_get_info, ++ ++ .bdrv_co_writev = qemu_backup_dump_co_writev, ++ ++ .bdrv_refresh_limits = qemu_backup_dump_refresh_limits, ++ .bdrv_check_perm = qemu_backup_dump_check_perm, ++ .bdrv_set_perm = qemu_backup_dump_set_perm, ++ .bdrv_abort_perm_update = qemu_backup_dump_abort_perm_update, ++ .bdrv_child_perm = qemu_backup_dump_child_perm, ++}; ++ ++static void bdrv_backup_dump_init(void) ++{ ++ bdrv_register(&bdrv_backup_dump_drive); ++} ++ ++block_init(bdrv_backup_dump_init); ++ ++ ++BlockDriverState *bdrv_backup_dump_create( ++ int dump_cb_block_size, ++ uint64_t byte_size, ++ BackupDumpFunc *dump_cb, ++ void *dump_cb_data, ++ Error **errp) ++{ ++ BDRVBackupDumpState *state; ++ BlockDriverState *bs = bdrv_new_open_driver( ++ &bdrv_backup_dump_drive, NULL, BDRV_O_RDWR, errp); ++ ++ if (!bs) { ++ return NULL; ++ } ++ ++ bs->total_sectors = byte_size / BDRV_SECTOR_SIZE; ++ bs->opaque = state = g_new0(BDRVBackupDumpState, 1); ++ ++ state->dump_cb_block_size = dump_cb_block_size; ++ state->byte_size = byte_size; ++ state->dump_cb = dump_cb; ++ state->dump_cb_data = dump_cb_data; ++ ++ return bs; ++} +diff --git a/block/backup.c b/block/backup.c +index c155081de2..9d23da027f 100644 +--- a/block/backup.c ++++ b/block/backup.c +@@ -32,24 +32,6 @@ + + #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) + +-typedef struct BackupBlockJob { +- BlockJob common; +- BlockDriverState *backup_top; +- BlockDriverState *source_bs; +- +- BdrvDirtyBitmap *sync_bitmap; +- +- MirrorSyncMode sync_mode; +- BitmapSyncMode bitmap_mode; +- BlockdevOnError on_source_error; +- BlockdevOnError on_target_error; +- uint64_t len; +- uint64_t bytes_read; +- int64_t cluster_size; +- +- BlockCopyState *bcs; +-} BackupBlockJob; +- + static const BlockJobDriver backup_job_driver; + + static void backup_progress_bytes_callback(int64_t bytes, void *opaque) +@@ -420,6 +402,11 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + goto error; + } + ++ BlockDriverInfo bdi; ++ if (bdrv_get_info(bs, &bdi) == 0) { ++ cluster_size = MAX(cluster_size, bdi.cluster_size); ++ } ++ + /* + * If source is in backing chain of target assume that target is going to be + * used for "image fleecing", i.e. it should represent a kind of snapshot of +diff --git a/block/io.c b/block/io.c +index f75777f5ea..75dea5ae33 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -381,7 +381,13 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + BdrvChild *parent, bool ignore_bds_parents) + { +- assert(!qemu_in_coroutine()); ++ // AFAICT this function is just an optimization, but sadly it doesn't play ++ // nice with the PVE backup code (when we're in a coroutine, even in ++ // pvebackup_co_start), so just call the full-blown drain begin instead ++ if (qemu_in_coroutine()) { ++ bdrv_do_drained_begin(bs, false, parent, ignore_bds_parents, false); ++ return; ++ } + + /* Stop things in parent-to-child order */ + if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { +diff --git a/include/block/block_int.h b/include/block/block_int.h +index b0d5eb9485..105bffe0f7 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -60,6 +60,36 @@ + + #define BLOCK_PROBE_BUF_SIZE 512 + ++typedef int BackupDumpFunc(void *opaque, uint64_t offset, uint64_t bytes, const void *buf); ++ ++BlockDriverState *bdrv_backuo_dump_create( ++ int dump_cb_block_size, ++ uint64_t byte_size, ++ BackupDumpFunc *dump_cb, ++ void *dump_cb_data, ++ Error **errp); ++ ++// Needs to be defined here, since it's used in blockdev.c to detect PVE backup ++// jobs with source_bs ++typedef struct BlockCopyState BlockCopyState; ++typedef struct BackupBlockJob { ++ BlockJob common; ++ BlockDriverState *backup_top; ++ BlockDriverState *source_bs; ++ ++ BdrvDirtyBitmap *sync_bitmap; ++ ++ MirrorSyncMode sync_mode; ++ BitmapSyncMode bitmap_mode; ++ BlockdevOnError on_source_error; ++ BlockdevOnError on_target_error; ++ uint64_t len; ++ uint64_t bytes_read; ++ int64_t cluster_size; ++ ++ BlockCopyState *bcs; ++} BackupBlockJob; ++ + enum BdrvTrackedRequestType { + BDRV_TRACKED_READ, + BDRV_TRACKED_WRITE, +diff --git a/job.c b/job.c +index 7554f735e3..b03ef989e2 100644 +--- a/job.c ++++ b/job.c +@@ -248,7 +248,8 @@ static bool job_started(Job *job) + return job->co; + } + +-static bool job_should_pause(Job *job) ++bool job_should_pause(Job *job); ++bool job_should_pause(Job *job) + { + return job->pause_count > 0; + } diff --git a/debian/patches/pve/0031-PVE-Backup-proxmox-backup-patches-for-qemu.patch b/debian/patches/pve/0031-PVE-Backup-proxmox-backup-patches-for-qemu.patch new file mode 100644 index 0000000..745d9b3 --- /dev/null +++ b/debian/patches/pve/0031-PVE-Backup-proxmox-backup-patches-for-qemu.patch @@ -0,0 +1,1633 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 12:55:28 +0100 +Subject: [PATCH 31/32] PVE-Backup: proxmox backup patches for qemu + +--- + Makefile.objs | 1 + + Makefile.target | 2 +- + blockdev.c | 1 + + hmp-commands-info.hx | 13 + + hmp-commands.hx | 31 ++ + include/block/block_int.h | 2 +- + include/monitor/hmp.h | 3 + + monitor/hmp-cmds.c | 77 +++ + proxmox-backup-client.c | 182 ++++++++ + proxmox-backup-client.h | 52 +++ + pve-backup.c | 959 ++++++++++++++++++++++++++++++++++++++ + qapi/block-core.json | 109 +++++ + qapi/common.json | 13 + + qapi/misc.json | 13 - + 14 files changed, 1443 insertions(+), 15 deletions(-) + create mode 100644 proxmox-backup-client.c + create mode 100644 proxmox-backup-client.h + create mode 100644 pve-backup.c + +diff --git a/Makefile.objs b/Makefile.objs +index db7fbbe73b..4406dcfeba 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -46,6 +46,7 @@ io-obj-y = io/ + ifeq ($(CONFIG_SOFTMMU),y) + common-obj-y = blockdev.o blockdev-nbd.o block/ + common-obj-y += bootdevice.o iothread.o ++common-obj-y += proxmox-backup-client.o pve-backup.o + common-obj-y += dump/ + common-obj-y += job-qmp.o + common-obj-y += monitor/ +diff --git a/Makefile.target b/Makefile.target +index 24d79d26eb..d7d91085a4 100644 +--- a/Makefile.target ++++ b/Makefile.target +@@ -160,7 +160,7 @@ obj-y += qapi/ + obj-y += memory.o + obj-y += memory_mapping.o + obj-y += migration/ram.o +-LIBS := $(libs_softmmu) $(LIBS) ++LIBS := $(libs_softmmu) $(LIBS) -lproxmox_backup_qemu + + # Hardware support + ifeq ($(TARGET_NAME), sparc64) +diff --git a/blockdev.c b/blockdev.c +index c7fa663ebf..e5310cb939 100644 +--- a/blockdev.c ++++ b/blockdev.c +@@ -36,6 +36,7 @@ + #include "hw/block/block.h" + #include "block/blockjob.h" + #include "block/qdict.h" ++#include "block/blockjob_int.h" + #include "block/throttle-groups.h" + #include "monitor/monitor.h" + #include "qemu/error-report.h" +diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx +index 139e673bea..8db5ce03a7 100644 +--- a/hmp-commands-info.hx ++++ b/hmp-commands-info.hx +@@ -536,6 +536,19 @@ STEXI + @item info cpustats + @findex info cpustats + Show CPU statistics. ++ETEXI ++ ++ { ++ .name = "backup", ++ .args_type = "", ++ .params = "", ++ .help = "show backup status", ++ .cmd = hmp_info_backup, ++ }, ++ ++STEXI ++@item info backup ++show backup status + ETEXI + + #if defined(CONFIG_SLIRP) +diff --git a/hmp-commands.hx b/hmp-commands.hx +index 104288322d..29d11dd321 100644 +--- a/hmp-commands.hx ++++ b/hmp-commands.hx +@@ -105,6 +105,37 @@ STEXI + @item block_stream + @findex block_stream + Copy data from a backing file into a block device. ++ETEXI ++ ++ { ++ .name = "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.", ++ .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", ++ .cmd = hmp_backup_cancel, ++ }, ++ ++STEXI ++@item backup_cancel ++@findex backup_cancel ++Cancel the current VM backup. ++ + ETEXI + + { +diff --git a/include/block/block_int.h b/include/block/block_int.h +index 105bffe0f7..43b00c15ac 100644 +--- a/include/block/block_int.h ++++ b/include/block/block_int.h +@@ -62,7 +62,7 @@ + + typedef int BackupDumpFunc(void *opaque, uint64_t offset, uint64_t bytes, const void *buf); + +-BlockDriverState *bdrv_backuo_dump_create( ++BlockDriverState *bdrv_backup_dump_create( + int dump_cb_block_size, + uint64_t byte_size, + BackupDumpFunc *dump_cb, +diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h +index c6ee8295f0..0f2f96c4af 100644 +--- a/include/monitor/hmp.h ++++ b/include/monitor/hmp.h +@@ -30,6 +30,7 @@ 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); + 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); +@@ -90,6 +91,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/hmp-cmds.c b/monitor/hmp-cmds.c +index 90aa34be25..1e33c61a2c 100644 +--- a/monitor/hmp-cmds.c ++++ b/monitor/hmp-cmds.c +@@ -194,6 +194,50 @@ 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) { ++ monitor_printf(mon, "Backup status: not initialized\n"); ++ return; ++ } ++ ++ 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); ++} ++ + static char *SocketAddress_to_str(SocketAddress *addr) + { + switch (addr->type) { +@@ -2062,6 +2106,39 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) + 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; ++ ++ 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, ++ false, NULL, // BPS password ++ false, NULL, // BPS keyfile ++ false, NULL, // BPS key_password ++ false, NULL, // BPS fingerprint ++ false, NULL, // BPS backup-id ++ false, 0, // BPS backup-time ++ true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA, ++ false, NULL, 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) + { + Error *error = NULL; +diff --git a/proxmox-backup-client.c b/proxmox-backup-client.c +new file mode 100644 +index 0000000000..b7bc7f2574 +--- /dev/null ++++ b/proxmox-backup-client.c +@@ -0,0 +1,182 @@ ++#include "proxmox-backup-client.h" ++#include "qemu/main-loop.h" ++#include "block/aio-wait.h" ++#include "qapi/error.h" ++ ++/* Proxmox Backup Server client bindings using coroutines */ ++ ++typedef struct BlockOnCoroutineWrapper { ++ AioContext *ctx; ++ CoroutineEntry *entry; ++ void *entry_arg; ++ bool finished; ++} BlockOnCoroutineWrapper; ++ ++// Waker implementaion to syncronice with proxmox backup rust code ++typedef struct ProxmoxBackupWaker { ++ Coroutine *co; ++ AioContext *ctx; ++} ProxmoxBackupWaker; ++ ++static void coroutine_fn block_on_coroutine_wrapper(void *opaque) ++{ ++ BlockOnCoroutineWrapper *wrapper = opaque; ++ wrapper->entry(wrapper->entry_arg); ++ wrapper->finished = true; ++ aio_wait_kick(); ++} ++ ++void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) ++{ ++ assert(!qemu_in_coroutine()); ++ ++ AioContext *ctx = qemu_get_current_aio_context(); ++ BlockOnCoroutineWrapper wrapper = { ++ .finished = false, ++ .entry = entry, ++ .entry_arg = entry_arg, ++ .ctx = ctx, ++ }; ++ Coroutine *wrapper_co = qemu_coroutine_create(block_on_coroutine_wrapper, &wrapper); ++ aio_co_enter(ctx, wrapper_co); ++ AIO_WAIT_WHILE(ctx, !wrapper.finished); ++} ++ ++// This is called from another thread, so we use aio_co_schedule() ++static void proxmox_backup_schedule_wake(void *data) { ++ ProxmoxBackupWaker *waker = (ProxmoxBackupWaker *)data; ++ aio_co_schedule(waker->ctx, waker->co); ++} ++ ++int coroutine_fn ++proxmox_backup_co_connect(ProxmoxBackupHandle *pbs, Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_self(); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ProxmoxBackupWaker waker = { .co = co, .ctx = ctx }; ++ char *pbs_err = NULL; ++ int pbs_res = -1; ++ ++ proxmox_backup_connect_async(pbs, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); ++ qemu_coroutine_yield(); ++ if (pbs_res < 0) { ++ if (errp) error_setg(errp, "backup connect failed: %s", pbs_err ? pbs_err : "unknown error"); ++ if (pbs_err) proxmox_backup_free_error(pbs_err); ++ } ++ return pbs_res; ++} ++ ++int coroutine_fn ++proxmox_backup_co_add_config( ++ ProxmoxBackupHandle *pbs, ++ const char *name, ++ const uint8_t *data, ++ uint64_t size, ++ Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_self(); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ProxmoxBackupWaker waker = { .co = co, .ctx = ctx }; ++ char *pbs_err = NULL; ++ int pbs_res = -1; ++ ++ proxmox_backup_add_config_async( ++ pbs, name, data, size ,proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); ++ qemu_coroutine_yield(); ++ if (pbs_res < 0) { ++ if (errp) error_setg(errp, "backup add_config %s failed: %s", name, pbs_err ? pbs_err : "unknown error"); ++ if (pbs_err) proxmox_backup_free_error(pbs_err); ++ } ++ return pbs_res; ++} ++ ++int coroutine_fn ++proxmox_backup_co_register_image( ++ ProxmoxBackupHandle *pbs, ++ const char *device_name, ++ uint64_t size, ++ Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_self(); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ProxmoxBackupWaker waker = { .co = co, .ctx = ctx }; ++ char *pbs_err = NULL; ++ int pbs_res = -1; ++ ++ proxmox_backup_register_image_async( ++ pbs, device_name, size ,proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); ++ qemu_coroutine_yield(); ++ if (pbs_res < 0) { ++ if (errp) error_setg(errp, "backup register image failed: %s", pbs_err ? pbs_err : "unknown error"); ++ if (pbs_err) proxmox_backup_free_error(pbs_err); ++ } ++ return pbs_res; ++} ++ ++int coroutine_fn ++proxmox_backup_co_finish( ++ ProxmoxBackupHandle *pbs, ++ Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_self(); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ProxmoxBackupWaker waker = { .co = co, .ctx = ctx }; ++ char *pbs_err = NULL; ++ int pbs_res = -1; ++ ++ proxmox_backup_finish_async( ++ pbs, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); ++ qemu_coroutine_yield(); ++ if (pbs_res < 0) { ++ if (errp) error_setg(errp, "backup finish failed: %s", pbs_err ? pbs_err : "unknown error"); ++ if (pbs_err) proxmox_backup_free_error(pbs_err); ++ } ++ return pbs_res; ++} ++ ++int coroutine_fn ++proxmox_backup_co_close_image( ++ ProxmoxBackupHandle *pbs, ++ uint8_t dev_id, ++ Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_self(); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ProxmoxBackupWaker waker = { .co = co, .ctx = ctx }; ++ char *pbs_err = NULL; ++ int pbs_res = -1; ++ ++ proxmox_backup_close_image_async( ++ pbs, dev_id, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); ++ qemu_coroutine_yield(); ++ if (pbs_res < 0) { ++ if (errp) error_setg(errp, "backup close image failed: %s", pbs_err ? pbs_err : "unknown error"); ++ if (pbs_err) proxmox_backup_free_error(pbs_err); ++ } ++ return pbs_res; ++} ++ ++int coroutine_fn ++proxmox_backup_co_write_data( ++ ProxmoxBackupHandle *pbs, ++ uint8_t dev_id, ++ const uint8_t *data, ++ uint64_t offset, ++ uint64_t size, ++ Error **errp) ++{ ++ Coroutine *co = qemu_coroutine_self(); ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ProxmoxBackupWaker waker = { .co = co, .ctx = ctx }; ++ char *pbs_err = NULL; ++ int pbs_res = -1; ++ ++ proxmox_backup_write_data_async( ++ pbs, dev_id, data, offset, size, proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err); ++ qemu_coroutine_yield(); ++ if (pbs_res < 0) { ++ if (errp) error_setg(errp, "backup write data failed: %s", pbs_err ? pbs_err : "unknown error"); ++ if (pbs_err) proxmox_backup_free_error(pbs_err); ++ } ++ return pbs_res; ++} +diff --git a/proxmox-backup-client.h b/proxmox-backup-client.h +new file mode 100644 +index 0000000000..b311bf8de8 +--- /dev/null ++++ b/proxmox-backup-client.h +@@ -0,0 +1,52 @@ ++#ifndef PROXMOX_BACKUP_CLIENT_H ++#define PROXMOX_BACKUP_CLIENT_H ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine.h" ++#include "proxmox-backup-qemu.h" ++ ++void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg); ++ ++int coroutine_fn ++proxmox_backup_co_connect( ++ ProxmoxBackupHandle *pbs, ++ Error **errp); ++ ++int coroutine_fn ++proxmox_backup_co_add_config( ++ ProxmoxBackupHandle *pbs, ++ const char *name, ++ const uint8_t *data, ++ uint64_t size, ++ Error **errp); ++ ++int coroutine_fn ++proxmox_backup_co_register_image( ++ ProxmoxBackupHandle *pbs, ++ const char *device_name, ++ uint64_t size, ++ Error **errp); ++ ++ ++int coroutine_fn ++proxmox_backup_co_finish( ++ ProxmoxBackupHandle *pbs, ++ Error **errp); ++ ++int coroutine_fn ++proxmox_backup_co_close_image( ++ ProxmoxBackupHandle *pbs, ++ uint8_t dev_id, ++ Error **errp); ++ ++int coroutine_fn ++proxmox_backup_co_write_data( ++ ProxmoxBackupHandle *pbs, ++ uint8_t dev_id, ++ const uint8_t *data, ++ uint64_t offset, ++ uint64_t size, ++ Error **errp); ++ ++ ++#endif /* PROXMOX_BACKUP_CLIENT_H */ +diff --git a/pve-backup.c b/pve-backup.c +new file mode 100644 +index 0000000000..9ae89fb679 +--- /dev/null ++++ b/pve-backup.c +@@ -0,0 +1,959 @@ ++#include "proxmox-backup-client.h" ++#include "vma.h" ++ ++#include "qemu/osdep.h" ++#include "qemu/module.h" ++#include "sysemu/block-backend.h" ++#include "sysemu/blockdev.h" ++#include "block/blockjob.h" ++#include "qapi/qapi-commands-block.h" ++#include "qapi/qmp/qerror.h" ++ ++/* PVE backup state and related function */ ++ ++ ++static struct PVEBackupState { ++ struct { ++ // Everithing accessed from qmp command, protected using rwlock ++ CoRwlock rwlock; ++ Error *error; ++ time_t start_time; ++ time_t end_time; ++ char *backup_file; ++ uuid_t uuid; ++ char uuid_str[37]; ++ size_t total; ++ size_t transferred; ++ size_t zero_bytes; ++ bool cancel; ++ } stat; ++ int64_t speed; ++ VmaWriter *vmaw; ++ ProxmoxBackupHandle *pbs; ++ GList *di_list; ++ CoMutex backup_mutex; ++} backup_state; ++ ++static void pvebackup_init(void) ++{ ++ qemu_co_rwlock_init(&backup_state.stat.rwlock); ++ qemu_co_mutex_init(&backup_state.backup_mutex); ++} ++ ++// initialize PVEBackupState at startup ++opts_init(pvebackup_init); ++ ++typedef struct PVEBackupDevInfo { ++ BlockDriverState *bs; ++ size_t size; ++ uint8_t dev_id; ++ bool completed; ++ char targetfile[PATH_MAX]; ++ BlockDriverState *target; ++} PVEBackupDevInfo; ++ ++static void pvebackup_co_run_next_job(void); ++ ++static int coroutine_fn ++pvebackup_co_dump_cb( ++ void *opaque, ++ uint64_t start, ++ uint64_t bytes, ++ const void *pbuf) ++{ ++ assert(qemu_in_coroutine()); ++ ++ const uint64_t size = bytes; ++ const unsigned char *buf = pbuf; ++ PVEBackupDevInfo *di = opaque; ++ ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ bool cancel = backup_state.stat.cancel; ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ if (cancel) { ++ return size; // return success ++ } ++ ++ qemu_co_mutex_lock(&backup_state.backup_mutex); ++ ++ int ret = -1; ++ ++ if (backup_state.vmaw) { ++ size_t zero_bytes = 0; ++ uint64_t remaining = size; ++ ++ uint64_t cluster_num = start / VMA_CLUSTER_SIZE; ++ if ((cluster_num * VMA_CLUSTER_SIZE) != start) { ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ if (!backup_state.stat.error) { ++ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); ++ error_setg(&backup_state.stat.error, ++ "got unaligned write inside backup dump " ++ "callback (sector %ld)", start); ++ } ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ return -1; // not aligned to cluster size ++ } ++ ++ while (remaining > 0) { ++ ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, ++ buf, &zero_bytes); ++ ++cluster_num; ++ if (buf) { ++ buf += VMA_CLUSTER_SIZE; ++ } ++ if (ret < 0) { ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ if (!backup_state.stat.error) { ++ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); ++ vma_writer_error_propagate(backup_state.vmaw, &backup_state.stat.error); ++ } ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ return ret; ++ } else { ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ backup_state.stat.zero_bytes += zero_bytes; ++ if (remaining >= VMA_CLUSTER_SIZE) { ++ backup_state.stat.transferred += VMA_CLUSTER_SIZE; ++ remaining -= VMA_CLUSTER_SIZE; ++ } else { ++ backup_state.stat.transferred += remaining; ++ remaining = 0; ++ } ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ } ++ } ++ } else if (backup_state.pbs) { ++ Error *local_err = NULL; ++ int pbs_res = -1; ++ ++ pbs_res = proxmox_backup_co_write_data(backup_state.pbs, di->dev_id, buf, start, size, &local_err); ++ ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ ++ if (pbs_res < 0) { ++ error_propagate(&backup_state.stat.error, local_err); ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ return pbs_res; ++ } else { ++ if (!buf) { ++ backup_state.stat.zero_bytes += size; ++ } ++ backup_state.stat.transferred += size; ++ } ++ ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ } else { ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ if (!buf) { ++ backup_state.stat.zero_bytes += size; ++ } ++ backup_state.stat.transferred += size; ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ } ++ ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ ++ return size; ++} ++ ++static void coroutine_fn pvebackup_co_cleanup(void) ++{ ++ assert(qemu_in_coroutine()); ++ ++ qemu_co_mutex_lock(&backup_state.backup_mutex); ++ ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ backup_state.stat.end_time = time(NULL); ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ if (backup_state.vmaw) { ++ Error *local_err = NULL; ++ vma_writer_close(backup_state.vmaw, &local_err); ++ ++ if (local_err != NULL) { ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ error_propagate(&backup_state.stat.error, local_err); ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ } ++ ++ backup_state.vmaw = NULL; ++ } ++ ++ if (backup_state.pbs) { ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ bool error_or_canceled = backup_state.stat.error || backup_state.stat.cancel; ++ if (!error_or_canceled) { ++ Error *local_err = NULL; ++ proxmox_backup_co_finish(backup_state.pbs, &local_err); ++ if (local_err != NULL) { ++ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); ++ error_propagate(&backup_state.stat.error, local_err); ++ } ++ } ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ proxmox_backup_disconnect(backup_state.pbs); ++ backup_state.pbs = NULL; ++ } ++ ++ g_list_free(backup_state.di_list); ++ backup_state.di_list = NULL; ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++} ++ ++typedef struct PVEBackupCompeteCallbackData { ++ PVEBackupDevInfo *di; ++ int result; ++} PVEBackupCompeteCallbackData; ++ ++static void coroutine_fn pvebackup_co_complete_cb(void *opaque) ++{ ++ assert(qemu_in_coroutine()); ++ ++ PVEBackupCompeteCallbackData *cb_data = opaque; ++ ++ qemu_co_mutex_lock(&backup_state.backup_mutex); ++ ++ PVEBackupDevInfo *di = cb_data->di; ++ int ret = cb_data->result; ++ ++ di->completed = true; ++ ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ bool error_or_canceled = backup_state.stat.error || backup_state.stat.cancel; ++ ++ if (ret < 0 && !backup_state.stat.error) { ++ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); ++ error_setg(&backup_state.stat.error, "job failed with err %d - %s", ++ ret, strerror(-ret)); ++ } ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ di->bs = NULL; ++ ++ if (di->target) { ++ bdrv_unref(di->target); ++ di->target = NULL; ++ } ++ ++ if (backup_state.vmaw) { ++ vma_writer_close_stream(backup_state.vmaw, di->dev_id); ++ } ++ ++ if (backup_state.pbs && !error_or_canceled) { ++ Error *local_err = NULL; ++ proxmox_backup_co_close_image(backup_state.pbs, di->dev_id, &local_err); ++ if (local_err != NULL) { ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ error_propagate(&backup_state.stat.error, local_err); ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ } ++ } ++ ++ // remove self from job queue ++ backup_state.di_list = g_list_remove(backup_state.di_list, di); ++ g_free(di); ++ ++ int pending_jobs = g_list_length(backup_state.di_list); ++ ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ ++ if (pending_jobs > 0) { ++ pvebackup_co_run_next_job(); ++ } else { ++ pvebackup_co_cleanup(); ++ } ++} ++ ++static void pvebackup_complete_cb(void *opaque, int ret) ++{ ++ // This can be called from the main loop, or from a coroutine ++ PVEBackupCompeteCallbackData cb_data = { ++ .di = opaque, ++ .result = ret, ++ }; ++ ++ if (qemu_in_coroutine()) { ++ pvebackup_co_complete_cb(&cb_data); ++ } else { ++ block_on_coroutine_fn(pvebackup_co_complete_cb, &cb_data); ++ } ++} ++ ++static void coroutine_fn pvebackup_co_cancel(void *opaque) ++{ ++ assert(qemu_in_coroutine()); ++ ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ backup_state.stat.cancel = true; ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ qemu_co_mutex_lock(&backup_state.backup_mutex); ++ ++ // Avoid race between block jobs and backup-cancel command: ++ if (!(backup_state.vmaw || backup_state.pbs)) { ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ return; ++ } ++ ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ if (!backup_state.stat.error) { ++ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); ++ error_setg(&backup_state.stat.error, "backup cancelled"); ++ } ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ if (backup_state.vmaw) { ++ /* make sure vma writer does not block anymore */ ++ vma_writer_set_error(backup_state.vmaw, "backup cancelled"); ++ } ++ ++ if (backup_state.pbs) { ++ proxmox_backup_abort(backup_state.pbs, "backup cancelled"); ++ } ++ ++ bool running_jobs = 0; ++ GList *l = backup_state.di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ if (!di->completed && di->bs) { ++ for (BlockJob *job = block_job_next(NULL); job; job = block_job_next(job)) { ++ if (job->job.driver->job_type != JOB_TYPE_BACKUP) { ++ continue; ++ } ++ ++ BackupBlockJob *bjob = container_of(job, BackupBlockJob, common); ++ if (bjob && bjob->source_bs == di->bs) { ++ AioContext *aio_context = job->job.aio_context; ++ aio_context_acquire(aio_context); ++ ++ if (!di->completed) { ++ running_jobs += 1; ++ job_cancel(&job->job, false); ++ } ++ aio_context_release(aio_context); ++ } ++ } ++ } ++ } ++ ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ ++ if (running_jobs == 0) pvebackup_co_cleanup(); // else job will call completion handler ++} ++ ++void qmp_backup_cancel(Error **errp) ++{ ++ block_on_coroutine_fn(pvebackup_co_cancel, NULL); ++} ++ ++static int coroutine_fn pvebackup_co_add_config( ++ const char *file, ++ const char *name, ++ BackupFormat format, ++ const char *backup_dir, ++ VmaWriter *vmaw, ++ ProxmoxBackupHandle *pbs, ++ Error **errp) ++{ ++ int res = 0; ++ ++ 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 (name == NULL) name = basename; ++ ++ if (format == BACKUP_FORMAT_VMA) { ++ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) { ++ error_setg(errp, "unable to add %s config data to vma archive", file); ++ goto err; ++ } ++ } else if (format == BACKUP_FORMAT_PBS) { ++ if (proxmox_backup_co_add_config(pbs, name, (unsigned char *)cdata, clen, errp) < 0) ++ goto err; ++ } else if (format == BACKUP_FORMAT_DIR) { ++ char config_path[PATH_MAX]; ++ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, name); ++ if (!g_file_set_contents(config_path, cdata, clen, &err)) { ++ error_setg(errp, "unable to write config file '%s'", config_path); ++ goto err; ++ } ++ } ++ ++ out: ++ g_free(basename); ++ g_free(cdata); ++ return res; ++ ++ err: ++ res = -1; ++ goto out; ++} ++ ++bool job_should_pause(Job *job); ++ ++static void coroutine_fn pvebackup_co_run_next_job(void) ++{ ++ assert(qemu_in_coroutine()); ++ ++ qemu_co_mutex_lock(&backup_state.backup_mutex); ++ ++ GList *l = backup_state.di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ if (!di->completed && di->bs) { ++ for (BlockJob *job = block_job_next(NULL); job; job = block_job_next(job)) { ++ if (job->job.driver->job_type != JOB_TYPE_BACKUP) { ++ continue; ++ } ++ ++ BackupBlockJob *bjob = container_of(job, BackupBlockJob, common); ++ if (bjob && bjob->source_bs == di->bs) { ++ AioContext *aio_context = job->job.aio_context; ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ aio_context_acquire(aio_context); ++ ++ if (job_should_pause(&job->job)) { ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ bool error_or_canceled = backup_state.stat.error || backup_state.stat.cancel; ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ if (error_or_canceled) { ++ job_cancel(&job->job, false); ++ } else { ++ job_resume(&job->job); ++ } ++ } ++ aio_context_release(aio_context); ++ return; ++ } ++ } ++ } ++ } ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++} ++ ++typedef struct QmpBackupTask { ++ const char *backup_file; ++ bool has_password; ++ const char *password; ++ bool has_keyfile; ++ const char *keyfile; ++ bool has_key_password; ++ const char *key_password; ++ bool has_backup_id; ++ const char *backup_id; ++ bool has_backup_time; ++ const char *fingerprint; ++ bool has_fingerprint; ++ int64_t backup_time; ++ 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; ++ UuidInfo *result; ++} QmpBackupTask; ++ ++static void coroutine_fn pvebackup_co_start(void *opaque) ++{ ++ assert(qemu_in_coroutine()); ++ ++ QmpBackupTask *task = opaque; ++ ++ task->result = NULL; // just to be sure ++ ++ BlockBackend *blk; ++ BlockDriverState *bs = NULL; ++ const char *backup_dir = NULL; ++ Error *local_err = NULL; ++ uuid_t uuid; ++ VmaWriter *vmaw = NULL; ++ ProxmoxBackupHandle *pbs = NULL; ++ gchar **devs = NULL; ++ GList *di_list = NULL; ++ GList *l; ++ UuidInfo *uuid_info; ++ BlockJob *job; ++ ++ const char *config_name = "qemu-server.conf"; ++ const char *firewall_name = "qemu-server.fw"; ++ ++ qemu_co_mutex_lock(&backup_state.backup_mutex); ++ ++ if (backup_state.di_list) { ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, ++ "previous backup not finished"); ++ return; ++ } ++ ++ /* Todo: try to auto-detect format based on file name */ ++ BackupFormat format = task->has_format ? task->format : BACKUP_FORMAT_VMA; ++ ++ if (task->has_devlist) { ++ devs = g_strsplit_set(task->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(task->errp, "Node '%s' is read only", *d); ++ goto err; ++ } ++ if (!bdrv_is_inserted(bs)) { ++ error_setg(task->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(task->errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", *d); ++ goto err; ++ } ++ d++; ++ } ++ ++ } else { ++ BdrvNextIterator it; ++ ++ bs = NULL; ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ 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(task->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, task->errp)) { ++ goto err; ++ } ++ ++ ssize_t size = bdrv_getlength(di->bs); ++ if (size < 0) { ++ error_setg_errno(task->errp, -di->size, "bdrv_getlength failed"); ++ goto err; ++ } ++ di->size = size; ++ total += size; ++ } ++ ++ uuid_generate(uuid); ++ ++ if (format == BACKUP_FORMAT_PBS) { ++ if (!task->has_password) { ++ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'password'"); ++ goto err; ++ } ++ if (!task->has_backup_id) { ++ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-id'"); ++ goto err; ++ } ++ if (!task->has_backup_time) { ++ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "missing parameter 'backup-time'"); ++ goto err; ++ } ++ ++ int dump_cb_block_size = PROXMOX_BACKUP_DEFAULT_CHUNK_SIZE; // Hardcoded (4M) ++ firewall_name = "fw.conf"; ++ ++ char *pbs_err = NULL; ++ pbs = proxmox_backup_new( ++ task->backup_file, ++ task->backup_id, ++ task->backup_time, ++ dump_cb_block_size, ++ task->has_password ? task->password : NULL, ++ task->has_keyfile ? task->keyfile : NULL, ++ task->has_key_password ? task->key_password : NULL, ++ task->has_fingerprint ? task->fingerprint : NULL, ++ &pbs_err); ++ ++ if (!pbs) { ++ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, ++ "proxmox_backup_new failed: %s", pbs_err); ++ proxmox_backup_free_error(pbs_err); ++ goto err; ++ } ++ ++ if (proxmox_backup_co_connect(pbs, task->errp) < 0) ++ goto err; ++ ++ /* register all devices */ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ ++ const char *devname = bdrv_get_device_name(di->bs); ++ ++ int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, task->errp); ++ if (dev_id < 0) ++ goto err; ++ ++ if (!(di->target = bdrv_backup_dump_create(dump_cb_block_size, di->size, pvebackup_co_dump_cb, di, task->errp))) { ++ goto err; ++ } ++ ++ di->dev_id = dev_id; ++ } ++ } else if (format == BACKUP_FORMAT_VMA) { ++ vmaw = vma_writer_create(task->backup_file, uuid, &local_err); ++ if (!vmaw) { ++ if (local_err) { ++ error_propagate(task->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); ++ ++ if (!(di->target = bdrv_backup_dump_create(VMA_CLUSTER_SIZE, di->size, pvebackup_co_dump_cb, di, task->errp))) { ++ goto err; ++ } ++ ++ 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(task->errp, ERROR_CLASS_GENERIC_ERROR, ++ "register_stream failed"); ++ goto err; ++ } ++ } ++ } else if (format == BACKUP_FORMAT_DIR) { ++ if (mkdir(task->backup_file, 0640) != 0) { ++ error_setg_errno(task->errp, errno, "can't create directory '%s'\n", ++ task->backup_file); ++ goto err; ++ } ++ backup_dir = task->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_img_create(di->targetfile, "raw", NULL, NULL, NULL, ++ di->size, flags, false, &local_err); ++ if (local_err) { ++ error_propagate(task->errp, local_err); ++ goto err; ++ } ++ ++ di->target = bdrv_open(di->targetfile, NULL, NULL, flags, &local_err); ++ if (!di->target) { ++ error_propagate(task->errp, local_err); ++ goto err; ++ } ++ } ++ } else { ++ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); ++ goto err; ++ } ++ ++ ++ /* add configuration file to archive */ ++ if (task->has_config_file) { ++ if (pvebackup_co_add_config(task->config_file, config_name, format, backup_dir, ++ vmaw, pbs, task->errp) != 0) { ++ goto err; ++ } ++ } ++ ++ /* add firewall file to archive */ ++ if (task->has_firewall_file) { ++ if (pvebackup_co_add_config(task->firewall_file, firewall_name, format, backup_dir, ++ vmaw, pbs, task->errp) != 0) { ++ goto err; ++ } ++ } ++ /* initialize global backup_state now */ ++ ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ ++ backup_state.stat.cancel = false; ++ ++ if (backup_state.stat.error) { ++ error_free(backup_state.stat.error); ++ backup_state.stat.error = NULL; ++ } ++ ++ backup_state.stat.start_time = time(NULL); ++ backup_state.stat.end_time = 0; ++ ++ if (backup_state.stat.backup_file) { ++ g_free(backup_state.stat.backup_file); ++ } ++ backup_state.stat.backup_file = g_strdup(task->backup_file); ++ ++ uuid_copy(backup_state.stat.uuid, uuid); ++ uuid_unparse_lower(uuid, backup_state.stat.uuid_str); ++ char *uuid_str = g_strdup(backup_state.stat.uuid_str); ++ ++ backup_state.stat.total = total; ++ backup_state.stat.transferred = 0; ++ backup_state.stat.zero_bytes = 0; ++ ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0; ++ ++ backup_state.vmaw = vmaw; ++ backup_state.pbs = pbs; ++ ++ backup_state.di_list = di_list; ++ ++ /* start all jobs (paused state) */ ++ l = di_list; ++ while (l) { ++ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; ++ l = g_list_next(l); ++ ++ // make sure target runs in same aoi_context as source ++ AioContext *aio_context = bdrv_get_aio_context(di->bs); ++ aio_context_acquire(aio_context); ++ GSList *ignore = NULL; ++ bdrv_set_aio_context_ignore(di->target, aio_context, &ignore); ++ g_slist_free(ignore); ++ aio_context_release(aio_context); ++ ++ job = backup_job_create(NULL, di->bs, di->target, backup_state.speed, MIRROR_SYNC_MODE_FULL, NULL, ++ BITMAP_SYNC_MODE_NEVER, false, NULL, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, ++ JOB_DEFAULT, pvebackup_complete_cb, di, 1, NULL, &local_err); ++ if (!job || local_err != NULL) { ++ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); ++ error_setg(&backup_state.stat.error, "backup_job_create failed"); ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ break; ++ } ++ job_start(&job->job); ++ if (di->target) { ++ bdrv_unref(di->target); ++ di->target = NULL; ++ } ++ } ++ ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ bool no_errors = !backup_state.stat.error; ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ ++ if (no_errors) { ++ pvebackup_co_run_next_job(); // run one job ++ } else { ++ pvebackup_co_cancel(NULL); ++ } ++ ++ uuid_info = g_malloc0(sizeof(*uuid_info)); ++ uuid_info->UUID = uuid_str; ++ ++ task->result = uuid_info; ++ return; ++ ++err: ++ ++ l = di_list; ++ while (l) { ++ 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); ++ ++ if (devs) { ++ g_strfreev(devs); ++ } ++ ++ if (vmaw) { ++ Error *err = NULL; ++ vma_writer_close(vmaw, &err); ++ unlink(task->backup_file); ++ } ++ ++ if (pbs) { ++ proxmox_backup_disconnect(pbs); ++ } ++ ++ if (backup_dir) { ++ rmdir(backup_dir); ++ } ++ ++ qemu_co_mutex_unlock(&backup_state.backup_mutex); ++ ++ task->result = NULL; ++ return; ++} ++ ++UuidInfo *qmp_backup( ++ const char *backup_file, ++ bool has_password, const char *password, ++ bool has_keyfile, const char *keyfile, ++ bool has_key_password, const char *key_password, ++ bool has_fingerprint, const char *fingerprint, ++ bool has_backup_id, const char *backup_id, ++ bool has_backup_time, int64_t backup_time, ++ 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) ++{ ++ QmpBackupTask task = { ++ .backup_file = backup_file, ++ .has_password = has_password, ++ .password = password, ++ .has_key_password = has_key_password, ++ .key_password = key_password, ++ .has_fingerprint = has_fingerprint, ++ .fingerprint = fingerprint, ++ .has_backup_id = has_backup_id, ++ .backup_id = backup_id, ++ .has_backup_time = has_backup_time, ++ .backup_time = backup_time, ++ .has_format = has_format, ++ .format = format, ++ .has_config_file = has_config_file, ++ .config_file = config_file, ++ .has_firewall_file = has_firewall_file, ++ .firewall_file = firewall_file, ++ .has_devlist = has_devlist, ++ .devlist = devlist, ++ .has_speed = has_speed, ++ .speed = speed, ++ .errp = errp, ++ }; ++ ++ block_on_coroutine_fn(pvebackup_co_start, &task); ++ ++ return task.result; ++} ++ ++ ++typedef struct QmpQueryBackupTask { ++ Error **errp; ++ BackupStatus *result; ++} QmpQueryBackupTask; ++ ++static void coroutine_fn pvebackup_co_query(void *opaque) ++{ ++ assert(qemu_in_coroutine()); ++ ++ QmpQueryBackupTask *task = opaque; ++ ++ BackupStatus *info = g_malloc0(sizeof(*info)); ++ ++ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); ++ ++ if (!backup_state.stat.start_time) { ++ /* not started, return {} */ ++ task->result = info; ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++ return; ++ } ++ ++ info->has_status = true; ++ info->has_start_time = true; ++ info->start_time = backup_state.stat.start_time; ++ ++ if (backup_state.stat.backup_file) { ++ info->has_backup_file = true; ++ info->backup_file = g_strdup(backup_state.stat.backup_file); ++ } ++ ++ info->has_uuid = true; ++ info->uuid = g_strdup(backup_state.stat.uuid_str); ++ ++ if (backup_state.stat.end_time) { ++ if (backup_state.stat.error) { ++ info->status = g_strdup("error"); ++ info->has_errmsg = true; ++ info->errmsg = g_strdup(error_get_pretty(backup_state.stat.error)); ++ } else { ++ info->status = g_strdup("done"); ++ } ++ info->has_end_time = true; ++ info->end_time = backup_state.stat.end_time; ++ } else { ++ info->status = g_strdup("active"); ++ } ++ ++ info->has_total = true; ++ info->total = backup_state.stat.total; ++ info->has_zero_bytes = true; ++ info->zero_bytes = backup_state.stat.zero_bytes; ++ info->has_transferred = true; ++ info->transferred = backup_state.stat.transferred; ++ ++ task->result = info; ++ ++ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); ++} ++ ++BackupStatus *qmp_query_backup(Error **errp) ++{ ++ QmpQueryBackupTask task = { ++ .errp = errp, ++ .result = NULL, ++ }; ++ ++ block_on_coroutine_fn(pvebackup_co_query, &task); ++ ++ return task.result; ++} +diff --git a/qapi/block-core.json b/qapi/block-core.json +index 783a868eb2..0b987ad6e3 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -800,6 +800,115 @@ + { 'command': 'query-block', 'returns': ['BlockInfo'] } + + ++## ++# @BackupStatus: ++# ++# Detailed backup status. ++# ++# @status: 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: error message (only returned if status is 'error') ++# ++# @total: total amount of bytes involved in the backup process ++# ++# @transferred: amount of bytes already backed up. ++# ++# @zero-bytes: amount of 'zero' bytes detected. ++# ++# @start-time: time (epoch) when backup job started. ++# ++# @end-time: time (epoch) when backup job finished. ++# ++# @backup-file: backup file name ++# ++# @uuid: 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', 'dir', 'pbs' ] } ++ ++## ++# @backup: ++# ++# Starts a VM backup. ++# ++# @backup-file: the backup file name ++# ++# @format: format of the backup file ++# ++# @config-file: a configuration file to include into ++# the backup archive. ++# ++# @speed: the maximum speed, in bytes per second ++# ++# @devlist: list of block device names (separated by ',', ';' ++# or ':'). By default the backup includes all writable block devices. ++# ++# @password: backup server passsword (required for format 'pbs') ++# ++# @keyfile: keyfile used for encryption (optional for format 'pbs') ++# ++# @key-password: password for keyfile (optional for format 'pbs') ++# ++# @fingerprint: server cert fingerprint (optional for format 'pbs') ++# ++# @backup-id: backup ID (required for format 'pbs') ++# ++# @backup-time: backup timestamp (Unix epoch, required for format 'pbs') ++# ++# Returns: the uuid of the backup job ++# ++## ++{ 'command': 'backup', 'data': { 'backup-file': 'str', ++ '*password': 'str', ++ '*keyfile': 'str', ++ '*key-password': 'str', ++ '*fingerprint': 'str', ++ '*backup-id': 'str', ++ '*backup-time': 'int', ++ '*format': 'BackupFormat', ++ '*config-file': 'str', ++ '*firewall-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' } ++ + ## + # @BlockDeviceTimedStats: + # +diff --git a/qapi/common.json b/qapi/common.json +index 7b9cbcd97b..c3b8bb7b48 100644 +--- a/qapi/common.json ++++ b/qapi/common.json +@@ -144,3 +144,16 @@ + ## + { 'enum': 'PCIELinkWidth', + 'data': [ '1', '2', '4', '8', '12', '16', '32' ] } ++ ++## ++# @UuidInfo: ++# ++# Guest UUID information (Universally Unique Identifier). ++# ++# @UUID: the UUID of the guest ++# ++# Since: 0.14.0 ++# ++# Notes: If no UUID was specified for the guest, a null UUID is returned. ++## ++{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } +diff --git a/qapi/misc.json b/qapi/misc.json +index 4c4618a574..7d506b5300 100644 +--- a/qapi/misc.json ++++ b/qapi/misc.json +@@ -270,19 +270,6 @@ + ## + { 'command': 'query-kvm', 'returns': 'KvmInfo' } + +-## +-# @UuidInfo: +-# +-# Guest UUID information (Universally Unique Identifier). +-# +-# @UUID: the UUID of the guest +-# +-# Since: 0.14.0 +-# +-# Notes: If no UUID was specified for the guest, a null UUID is returned. +-## +-{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } +- + ## + # @query-uuid: + # diff --git a/debian/patches/pve/0031-PVE-bug-fix-1071-vma-writer.c-use-correct-AioContext.patch b/debian/patches/pve/0031-PVE-bug-fix-1071-vma-writer.c-use-correct-AioContext.patch deleted file mode 100644 index ede31e0..0000000 --- a/debian/patches/pve/0031-PVE-bug-fix-1071-vma-writer.c-use-correct-AioContext.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Mon, 21 Oct 2019 11:51:57 +0200 -Subject: [PATCH] PVE bug fix #1071 - vma-writer.c: use correct AioContext - -Signed-off-by: Dietmar Maurer ---- - vma-writer.c | 16 ++++++++-------- - 1 file changed, 8 insertions(+), 8 deletions(-) - -diff --git a/vma-writer.c b/vma-writer.c -index fd9567634d..b163fa2d3a 100644 ---- a/vma-writer.c -+++ b/vma-writer.c -@@ -199,12 +199,14 @@ int vma_writer_register_stream(VmaWriter *vmaw, const char *devname, - return n; - } - --static void vma_co_continue_write(void *opaque) -+static void coroutine_fn yield_until_fd_writable(int fd) - { -- VmaWriter *vmaw = opaque; -- -- DPRINTF("vma_co_continue_write\n"); -- qemu_coroutine_enter(vmaw->co_writer); -+ assert(qemu_in_coroutine()); -+ AioContext *ctx = qemu_get_current_aio_context(); -+ aio_set_fd_handler(ctx, fd, false, NULL, (IOHandler *)qemu_coroutine_enter, -+ NULL, qemu_coroutine_self()); -+ qemu_coroutine_yield(); -+ aio_set_fd_handler(ctx, fd, false, NULL, NULL, NULL, NULL); - } - - static ssize_t coroutine_fn -@@ -224,14 +226,12 @@ vma_queue_write(VmaWriter *vmaw, const void *buf, size_t bytes) - vmaw->co_writer = qemu_coroutine_self(); - - while (done < bytes) { -- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, vma_co_continue_write, NULL, vmaw); -- qemu_coroutine_yield(); -- aio_set_fd_handler(qemu_get_aio_context(), vmaw->fd, false, NULL, NULL, NULL, NULL); - if (vmaw->status < 0) { - DPRINTF("vma_queue_write detected canceled backup\n"); - done = -1; - break; - } -+ yield_until_fd_writable(vmaw->fd); - ret = write(vmaw->fd, buf + done, bytes - done); - if (ret > 0) { - done += ret; diff --git a/debian/patches/pve/0032-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch b/debian/patches/pve/0032-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch new file mode 100644 index 0000000..792b157 --- /dev/null +++ b/debian/patches/pve/0032-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch @@ -0,0 +1,248 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dietmar Maurer +Date: Tue, 10 Mar 2020 12:55:29 +0100 +Subject: [PATCH 32/32] PVE-Backup: pbs-restore - new command to restore from + proxmox backup server + +--- + Makefile | 4 +- + pbs-restore.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 211 insertions(+), 1 deletion(-) + create mode 100644 pbs-restore.c + +diff --git a/Makefile b/Makefile +index 18d2dba2e4..469a1e3666 100644 +--- a/Makefile ++++ b/Makefile +@@ -453,7 +453,7 @@ dummy := $(call unnest-vars,, \ + + include $(SRC_PATH)/tests/Makefile.include + +-all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules $(vhost-user-json-y) ++all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) vma$(EXESUF) pbs-restore$(EXESUF) $(HELPERS-y) recurse-all modules $(vhost-user-json-y) + + qemu-version.h: FORCE + $(call quiet-command, \ +@@ -568,6 +568,8 @@ qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io + qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) + qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) + vma$(EXESUF): vma.o vma-reader.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) ++pbs-restore$(EXESUF): pbs-restore.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) ++pbs-restore$(EXESUF): LIBS += -lproxmox_backup_qemu + + qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) + +diff --git a/pbs-restore.c b/pbs-restore.c +new file mode 100644 +index 0000000000..f65de8b890 +--- /dev/null ++++ b/pbs-restore.c +@@ -0,0 +1,208 @@ ++/* ++ * Qemu image restore helper for Proxmox Backup ++ * ++ * Copyright (C) 2019 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 "qemu/osdep.h" ++#include ++#include ++#include ++ ++#include "qemu-common.h" ++#include "qemu/module.h" ++#include "qemu/error-report.h" ++#include "qemu/main-loop.h" ++#include "qemu/cutils.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "sysemu/block-backend.h" ++ ++#include ++ ++static void help(void) ++{ ++ const char *help_msg = ++ "usage: pbs-restore [--repository ] snapshot archive-name target [command options]\n" ++ ; ++ ++ printf("%s", help_msg); ++ exit(1); ++} ++ ++typedef struct CallbackData { ++ BlockBackend *target; ++ uint64_t last_offset; ++ bool skip_zero; ++} CallbackData; ++ ++static int write_callback( ++ void *callback_data_ptr, ++ uint64_t offset, ++ const unsigned char *data, ++ uint64_t data_len) ++{ ++ int res = -1; ++ ++ CallbackData *callback_data = (CallbackData *)callback_data_ptr; ++ ++ uint64_t last_offset = callback_data->last_offset; ++ if (offset > last_offset) callback_data->last_offset = offset; ++ ++ if (data == NULL) { ++ if (callback_data->skip_zero && offset > last_offset) { ++ return 0; ++ } ++ res = blk_pwrite_zeroes(callback_data->target, offset, data_len, 0); ++ } else { ++ res = blk_pwrite(callback_data->target, offset, data, data_len, 0); ++ } ++ ++ if (res < 0) { ++ fprintf(stderr, "blk_pwrite failed at offset %ld length %ld (%d) - %s\n", offset, data_len, res, strerror(-res)); ++ return res; ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char **argv) ++{ ++ Error *main_loop_err = NULL; ++ const char *format = "raw"; ++ const char *repository = NULL; ++ const char *keyfile = NULL; ++ int verbose = false; ++ bool skip_zero = false; ++ ++ error_init(argv[0]); ++ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"skip-zero", no_argument, 0, 'S'}, ++ {"verbose", no_argument, 0, 'v'}, ++ {"format", required_argument, 0, 'f'}, ++ {"repository", required_argument, 0, 'r'}, ++ {"keyfile", required_argument, 0, 'k'}, ++ {0, 0, 0, 0} ++ }; ++ int c = getopt_long(argc, argv, "hvf:r:k:", long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ fprintf(stderr, "missing argument for option '%s'", argv[optind - 1]); ++ return -1; ++ case '?': ++ fprintf(stderr, "unrecognized option '%s'", argv[optind - 1]); ++ return -1; ++ case 'f': ++ format = g_strdup(argv[optind - 1]); ++ break; ++ case 'r': ++ repository = g_strdup(argv[optind - 1]); ++ break; ++ case 'k': ++ keyfile = g_strdup(argv[optind - 1]); ++ break; ++ case 'v': ++ verbose = true; ++ break; ++ case 'S': ++ skip_zero = true; ++ break; ++ case 'h': ++ help(); ++ return 0; ++ } ++ } ++ ++ if (optind >= argc - 2) { ++ fprintf(stderr, "missing arguments\n"); ++ help(); ++ return -1; ++ } ++ ++ if (repository == NULL) { ++ repository = getenv("PBS_REPOSITORY"); ++ } ++ ++ if (repository == NULL) { ++ fprintf(stderr, "no repository specified\n"); ++ help(); ++ return -1; ++ } ++ ++ char *snapshot = argv[optind++]; ++ char *archive_name = argv[optind++]; ++ char *target = argv[optind++]; ++ ++ const char *password = getenv("PBS_PASSWORD"); ++ const char *fingerprint = getenv("PBS_FINGERPRINT"); ++ const char *key_password = getenv("PBS_ENCRYPTION_PASSWORD"); ++ ++ if (qemu_init_main_loop(&main_loop_err)) { ++ g_error("%s", error_get_pretty(main_loop_err)); ++ } ++ ++ bdrv_init(); ++ module_call_init(MODULE_INIT_QOM); ++ ++ char *pbs_error = NULL; ++ ProxmoxRestoreHandle *conn = proxmox_restore_connect( ++ repository, snapshot, password, keyfile, key_password, fingerprint, &pbs_error); ++ if (conn == NULL) { ++ fprintf(stderr, "restore failed: %s\n", pbs_error); ++ return -1; ++ } ++ ++ QDict *options = qdict_new(); ++ qdict_put_str(options, "driver", format); ++ ++ if (format) { ++ qdict_put_str(options, "driver", format); ++ } ++ ++ Error *local_err = NULL; ++ int flags = BDRV_O_RDWR; ++ ++ BlockBackend *blk = blk_new_open(target, NULL, options, flags, &local_err); ++ if (!blk) { ++ fprintf(stderr, "%s\n", error_get_pretty(local_err)); ++ return -1; ++ } ++ ++ CallbackData *callback_data = calloc(sizeof( CallbackData), 1); ++ ++ callback_data->target = blk; ++ callback_data->skip_zero = skip_zero; ++ callback_data->last_offset = 0; ++ ++ // blk_set_enable_write_cache(blk, !writethrough); ++ ++ int res = proxmox_restore_image( ++ conn, ++ archive_name, ++ write_callback, ++ callback_data, ++ &pbs_error, ++ verbose); ++ ++ proxmox_restore_disconnect(conn); ++ ++ if (res < 0) { ++ fprintf(stderr, "restore failed: %s\n", pbs_error); ++ return -1; ++ } ++ ++ return 0; ++} diff --git a/debian/patches/pve/0032-qmp_backup-run-backup-related-code-inside-coroutines.patch b/debian/patches/pve/0032-qmp_backup-run-backup-related-code-inside-coroutines.patch deleted file mode 100644 index 95ee63f..0000000 --- a/debian/patches/pve/0032-qmp_backup-run-backup-related-code-inside-coroutines.patch +++ /dev/null @@ -1,511 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 22 Oct 2019 12:48:17 +0200 -Subject: [PATCH] qmp_backup: run backup related code inside coroutines - -So that we can safely use coroutines/yield everywhere. - -Signed-off-by: Dietmar Maurer ---- - blockdev.c | 250 ++++++++++++++++++++++++++++++++++++++--------------- - 1 file changed, 180 insertions(+), 70 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index cee7952bbb..cec0f770e8 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3173,6 +3173,34 @@ out: - - /* PVE backup related function */ - -+typedef struct BlockOnCoroutineWrapper { -+ AioContext *ctx; -+ CoroutineEntry *entry; -+ void *entry_arg; -+ bool finished; -+} BlockOnCoroutineWrapper; -+ -+static void coroutine_fn block_on_coroutine_wrapper(void *opaque) -+{ -+ BlockOnCoroutineWrapper *wrapper = opaque; -+ wrapper->entry(wrapper->entry_arg); -+ wrapper->finished = true; -+} -+ -+static void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) -+{ -+ AioContext *ctx = qemu_get_current_aio_context(); -+ BlockOnCoroutineWrapper wrapper = { -+ .finished = false, -+ .entry = entry, -+ .entry_arg = entry_arg, -+ .ctx = ctx, -+ }; -+ Coroutine *wrapper_co = qemu_coroutine_create(block_on_coroutine_wrapper, &wrapper); -+ aio_co_enter(ctx, wrapper_co); -+ AIO_WAIT_WHILE(ctx, !wrapper.finished); -+} -+ - static struct PVEBackupState { - Error *error; - bool cancel; -@@ -3200,12 +3228,14 @@ typedef struct PVEBackupDevInfo { - BlockDriverState *target; - } PVEBackupDevInfo; - --static void pvebackup_run_next_job(void); -+static void pvebackup_co_run_next_job(void); - --static int pvebackup_dump_cb(void *opaque, BlockBackend *target, -+static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - uint64_t start, uint64_t bytes, - const void *pbuf) - { -+ assert(qemu_in_coroutine()); -+ - const uint64_t size = bytes; - const unsigned char *buf = pbuf; - PVEBackupDevInfo *di = opaque; -@@ -3267,8 +3297,10 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target, - return size; - } - --static void pvebackup_cleanup(void) -+static void coroutine_fn pvebackup_co_cleanup(void) - { -+ assert(qemu_in_coroutine()); -+ - qemu_mutex_lock(&backup_state.backup_mutex); - // Avoid race between block jobs and backup-cancel command: - if (!backup_state.vmaw) { -@@ -3290,18 +3322,19 @@ static void pvebackup_cleanup(void) - qemu_mutex_unlock(&backup_state.backup_mutex); - } - --static void coroutine_fn backup_close_vma_stream(void *opaque) --{ -- PVEBackupDevInfo *di = opaque; -+typedef struct PVEBackupCompeteCallbackData { -+ PVEBackupDevInfo *di; -+ int result; -+} PVEBackupCompeteCallbackData; - -- vma_writer_close_stream(backup_state.vmaw, di->dev_id); --} -- --static void pvebackup_complete_cb(void *opaque, int ret) -+static void coroutine_fn pvebackup_co_complete_cb(void *opaque) - { -- // This always runs in the main loop -+ assert(qemu_in_coroutine()); - -- PVEBackupDevInfo *di = opaque; -+ PVEBackupCompeteCallbackData *cb_data = opaque; -+ -+ PVEBackupDevInfo *di = cb_data->di; -+ int ret = cb_data->result; - - di->completed = true; - -@@ -3314,8 +3347,7 @@ static void pvebackup_complete_cb(void *opaque, int ret) - di->target = NULL; - - if (backup_state.vmaw) { -- Coroutine *co = qemu_coroutine_create(backup_close_vma_stream, di); -- qemu_coroutine_enter(co); -+ vma_writer_close_stream(backup_state.vmaw, di->dev_id); - } - - // remove self from job queue -@@ -3325,12 +3357,25 @@ static void pvebackup_complete_cb(void *opaque, int ret) - qemu_mutex_unlock(&backup_state.backup_mutex); - - if (!backup_state.cancel) { -- pvebackup_run_next_job(); -+ pvebackup_co_run_next_job(); - } - } - --static void pvebackup_cancel(void *opaque) -+static void pvebackup_complete_cb(void *opaque, int ret) - { -+ // This always called from the main loop -+ PVEBackupCompeteCallbackData cb_data = { -+ .di = opaque, -+ .result = ret, -+ }; -+ -+ block_on_coroutine_fn(pvebackup_co_complete_cb, &cb_data); -+} -+ -+static void coroutine_fn pvebackup_co_cancel(void *opaque) -+{ -+ assert(qemu_in_coroutine()); -+ - backup_state.cancel = true; - qemu_mutex_lock(&backup_state.backup_mutex); - // Avoid race between block jobs and backup-cancel command: -@@ -3365,21 +3410,16 @@ static void pvebackup_cancel(void *opaque) - } - } - -- qemu_mutex_unlock(&backup_state.backup_mutex); -- pvebackup_cleanup(); -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ pvebackup_co_cleanup(); - } - - void qmp_backup_cancel(Error **errp) - { - if (!backup_state.backup_mutex_initialized) - return; -- Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL); -- qemu_coroutine_enter(co); - -- while (backup_state.vmaw) { -- /* vma writer use main aio context */ -- aio_poll(qemu_get_aio_context(), true); -- } -+ block_on_coroutine_fn(pvebackup_co_cancel, NULL); - } - - static int config_to_vma(const char *file, BackupFormat format, -@@ -3420,8 +3460,11 @@ static int config_to_vma(const char *file, BackupFormat format, - } - - bool job_should_pause(Job *job); --static void pvebackup_run_next_job(void) -+ -+static void coroutine_fn pvebackup_co_run_next_job(void) - { -+ assert(qemu_in_coroutine()); -+ - qemu_mutex_lock(&backup_state.backup_mutex); - - GList *l = backup_state.di_list; -@@ -3447,16 +3490,33 @@ static void pvebackup_run_next_job(void) - qemu_mutex_unlock(&backup_state.backup_mutex); - - // no more jobs, run the cleanup -- pvebackup_cleanup(); -+ pvebackup_co_cleanup(); - } - --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) -+typedef struct QmpBackupTask { -+ 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; -+ UuidInfo *result; -+} QmpBackupTask; -+ -+static void coroutine_fn pvebackup_co_start(void *opaque) - { -+ assert(qemu_in_coroutine()); -+ -+ QmpBackupTask *task = opaque; -+ -+ task->result = NULL; // just to be sure -+ - BlockBackend *blk; - BlockDriverState *bs = NULL; - const char *backup_dir = NULL; -@@ -3475,16 +3535,16 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - } - - if (backup_state.di_list) { -- error_set(errp, ERROR_CLASS_GENERIC_ERROR, -+ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, - "previous backup not finished"); -- return NULL; -+ return; - } - - /* Todo: try to auto-detect format based on file name */ -- format = has_format ? format : BACKUP_FORMAT_VMA; -+ BackupFormat format = task->has_format ? task->format : BACKUP_FORMAT_VMA; - -- if (has_devlist) { -- devs = g_strsplit_set(devlist, ",;:", -1); -+ if (task->has_devlist) { -+ devs = g_strsplit_set(task->devlist, ",;:", -1); - - gchar **d = devs; - while (d && *d) { -@@ -3492,18 +3552,18 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - if (blk) { - bs = blk_bs(blk); - if (bdrv_is_read_only(bs)) { -- error_setg(errp, "Node '%s' is read only", *d); -+ error_setg(task->errp, "Node '%s' is read only", *d); - goto err; - } - if (!bdrv_is_inserted(bs)) { -- error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, *d); -+ error_setg(task->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, -+ error_set(task->errp, ERROR_CLASS_DEVICE_NOT_FOUND, - "Device '%s' not found", *d); - goto err; - } -@@ -3526,7 +3586,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - } - - if (!di_list) { -- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "empty device list"); -+ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "empty device list"); - goto err; - } - -@@ -3536,13 +3596,13 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - 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)) { -+ if (bdrv_op_is_blocked(di->bs, BLOCK_OP_TYPE_BACKUP_SOURCE, task->errp)) { - goto err; - } - - ssize_t size = bdrv_getlength(di->bs); - if (size < 0) { -- error_setg_errno(errp, -di->size, "bdrv_getlength failed"); -+ error_setg_errno(task->errp, -di->size, "bdrv_getlength failed"); - goto err; - } - di->size = size; -@@ -3552,10 +3612,10 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - uuid_generate(uuid); - - if (format == BACKUP_FORMAT_VMA) { -- vmaw = vma_writer_create(backup_file, uuid, &local_err); -+ vmaw = vma_writer_create(task->backup_file, uuid, &local_err); - if (!vmaw) { - if (local_err) { -- error_propagate(errp, local_err); -+ error_propagate(task->errp, local_err); - } - goto err; - } -@@ -3569,18 +3629,18 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - 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, -+ error_set(task->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); -+ if (mkdir(task->backup_file, 0640) != 0) { -+ error_setg_errno(task->errp, errno, "can't create directory '%s'\n", -+ task->backup_file); - goto err; - } -- backup_dir = backup_file; -+ backup_dir = task->backup_file; - - l = di_list; - while (l) { -@@ -3594,31 +3654,31 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL, - di->size, flags, false, &local_err); - if (local_err) { -- error_propagate(errp, local_err); -+ error_propagate(task->errp, local_err); - goto err; - } - - di->target = bdrv_open(di->targetfile, NULL, NULL, flags, &local_err); - if (!di->target) { -- error_propagate(errp, local_err); -+ error_propagate(task->errp, local_err); - goto err; - } - } - } else { -- error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); -+ error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); - goto err; - } - - /* add configuration file to archive */ -- if (has_config_file) { -- if (config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) { -+ if (task->has_config_file) { -+ if (config_to_vma(task->config_file, format, backup_dir, vmaw, task->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) { -+ if (task->has_firewall_file) { -+ if (config_to_vma(task->firewall_file, format, backup_dir, vmaw, task->errp) != 0) { - goto err; - } - } -@@ -3631,7 +3691,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - backup_state.error = NULL; - } - -- backup_state.speed = (has_speed && speed > 0) ? speed : 0; -+ backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0; - - backup_state.start_time = time(NULL); - backup_state.end_time = 0; -@@ -3639,7 +3699,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - if (backup_state.backup_file) { - g_free(backup_state.backup_file); - } -- backup_state.backup_file = g_strdup(backup_file); -+ backup_state.backup_file = g_strdup(task->backup_file); - - backup_state.vmaw = vmaw; - -@@ -3658,14 +3718,13 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - while (l) { - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); -- job = backup_job_create(NULL, di->bs, di->target, speed, MIRROR_SYNC_MODE_FULL, NULL, -+ job = backup_job_create(NULL, di->bs, di->target, backup_state.speed, MIRROR_SYNC_MODE_FULL, NULL, - false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, -- JOB_DEFAULT, -- pvebackup_dump_cb, pvebackup_complete_cb, di, -+ JOB_DEFAULT, pvebackup_co_dump_cb, pvebackup_complete_cb, di, - 1, NULL, &local_err); - if (!job || local_err != NULL) { - error_setg(&backup_state.error, "backup_job_create failed"); -- pvebackup_cancel(NULL); -+ pvebackup_co_cancel(NULL); - } else { - job_start(&job->job); - } -@@ -3678,13 +3737,14 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - qemu_mutex_unlock(&backup_state.backup_mutex); - - if (!backup_state.error) { -- pvebackup_run_next_job(); // run one job -+ pvebackup_co_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; -+ task->result = uuid_info; -+ return; - - err: - -@@ -3711,23 +3771,61 @@ err: - if (vmaw) { - Error *err = NULL; - vma_writer_close(vmaw, &err); -- unlink(backup_file); -+ unlink(task->backup_file); - } - - if (backup_dir) { - rmdir(backup_dir); - } - -- return NULL; -+ task->result = NULL; -+ return; - } - --BackupStatus *qmp_query_backup(Error **errp) -+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) -+{ -+ QmpBackupTask task = { -+ .backup_file = backup_file, -+ .has_format = has_format, -+ .format = format, -+ .has_config_file = has_config_file, -+ .config_file = config_file, -+ .has_firewall_file = has_firewall_file, -+ .firewall_file = firewall_file, -+ .has_devlist = has_devlist, -+ .devlist = devlist, -+ .has_speed = has_speed, -+ .speed = speed, -+ .errp = errp, -+ }; -+ -+ block_on_coroutine_fn(pvebackup_co_start, &task); -+ return task.result; -+} -+ -+ -+typedef struct QmpQueryBackupTask { -+ Error **errp; -+ BackupStatus *result; -+} QmpQueryBackupTask; -+ -+static void coroutine_fn pvebackup_co_query(void *opaque) - { -+ assert(qemu_in_coroutine()); -+ -+ QmpQueryBackupTask *task = opaque; -+ - BackupStatus *info = g_malloc0(sizeof(*info)); - - if (!backup_state.start_time) { - /* not started, return {} */ -- return info; -+ task->result = info; -+ return; - } - - info->has_status = true; -@@ -3763,7 +3861,19 @@ BackupStatus *qmp_query_backup(Error **errp) - info->has_transferred = true; - info->transferred = backup_state.transferred; - -- return info; -+ task->result = info; -+} -+ -+BackupStatus *qmp_query_backup(Error **errp) -+{ -+ QmpQueryBackupTask task = { -+ .errp = errp, -+ .result = NULL, -+ }; -+ -+ block_on_coroutine_fn(pvebackup_co_query, &task); -+ -+ return task.result; - } - - void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, diff --git a/debian/patches/pve/0033-qmp_backup-use-a-CoMutex-to-protect-access-to-backup.patch b/debian/patches/pve/0033-qmp_backup-use-a-CoMutex-to-protect-access-to-backup.patch deleted file mode 100644 index 6a08930..0000000 --- a/debian/patches/pve/0033-qmp_backup-use-a-CoMutex-to-protect-access-to-backup.patch +++ /dev/null @@ -1,274 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 22 Oct 2019 12:48:18 +0200 -Subject: [PATCH] qmp_backup: use a CoMutex to protect access to backup_state - -And use job_cancel instead of job_cancel_sync (which hangs sometimes) -Also avoid calling pvebackup_co_cleanup if not necessary. If there are -running jobs, this is called from the block job completion callbacks -anyways. - -Signed-off-by: Dietmar Maurer ---- - blockdev.c | 74 ++++++++++++++++++++++++++++++++++++++---------------- - 1 file changed, 52 insertions(+), 22 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index cec0f770e8..29196c18d8 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3215,7 +3215,7 @@ static struct PVEBackupState { - size_t total; - size_t transferred; - size_t zero_bytes; -- QemuMutex backup_mutex; -+ CoMutex backup_mutex; - bool backup_mutex_initialized; - } backup_state; - -@@ -3240,7 +3240,10 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - const unsigned char *buf = pbuf; - PVEBackupDevInfo *di = opaque; - -+ qemu_co_mutex_lock(&backup_state.backup_mutex); -+ - if (backup_state.cancel) { -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - return size; // return success - } - -@@ -3251,6 +3254,7 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - "got unaligned write inside backup dump " - "callback (sector %ld)", start); - } -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - return -1; // not aligned to cluster size - } - -@@ -3292,6 +3296,8 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - backup_state.transferred += size; - } - -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ - // Note: always return success, because we want that writes succeed anyways. - - return size; -@@ -3301,10 +3307,9 @@ static void coroutine_fn pvebackup_co_cleanup(void) - { - assert(qemu_in_coroutine()); - -- qemu_mutex_lock(&backup_state.backup_mutex); -- // Avoid race between block jobs and backup-cancel command: -+ qemu_co_mutex_lock(&backup_state.backup_mutex); - if (!backup_state.vmaw) { -- qemu_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - return; - } - -@@ -3319,7 +3324,7 @@ static void coroutine_fn pvebackup_co_cleanup(void) - - g_list_free(backup_state.di_list); - backup_state.di_list = NULL; -- qemu_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - } - - typedef struct PVEBackupCompeteCallbackData { -@@ -3333,6 +3338,8 @@ static void coroutine_fn pvebackup_co_complete_cb(void *opaque) - - PVEBackupCompeteCallbackData *cb_data = opaque; - -+ qemu_co_mutex_lock(&backup_state.backup_mutex); -+ - PVEBackupDevInfo *di = cb_data->di; - int ret = cb_data->result; - -@@ -3351,12 +3358,14 @@ static void coroutine_fn pvebackup_co_complete_cb(void *opaque) - } - - // remove self from job queue -- qemu_mutex_lock(&backup_state.backup_mutex); - backup_state.di_list = g_list_remove(backup_state.di_list, di); - g_free(di); -- qemu_mutex_unlock(&backup_state.backup_mutex); - -- if (!backup_state.cancel) { -+ bool cancel = backup_state.cancel; -+ -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ -+ if (!cancel) { - pvebackup_co_run_next_job(); - } - } -@@ -3376,11 +3385,13 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - { - assert(qemu_in_coroutine()); - -+ qemu_co_mutex_lock(&backup_state.backup_mutex); -+ - backup_state.cancel = true; -- qemu_mutex_lock(&backup_state.backup_mutex); -+ - // Avoid race between block jobs and backup-cancel command: - if (!backup_state.vmaw) { -- qemu_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - return; - } - -@@ -3393,6 +3404,7 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - vma_writer_set_error(backup_state.vmaw, "backup cancelled"); - } - -+ bool running_jobs = 0; - GList *l = backup_state.di_list; - while (l) { - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -@@ -3403,6 +3415,7 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - AioContext *aio_context = blk_get_aio_context(job->blk); - aio_context_acquire(aio_context); - if (!di->completed) { -+ running_jobs += 1; - job_cancel(&job->job, false); - } - aio_context_release(aio_context); -@@ -3411,7 +3424,8 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - } - - qemu_co_mutex_unlock(&backup_state.backup_mutex); -- pvebackup_co_cleanup(); -+ -+ if (running_jobs == 0) pvebackup_co_cleanup(); // else job will call completion handler - } - - void qmp_backup_cancel(Error **errp) -@@ -3465,7 +3479,7 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - { - assert(qemu_in_coroutine()); - -- qemu_mutex_lock(&backup_state.backup_mutex); -+ qemu_co_mutex_lock(&backup_state.backup_mutex); - - GList *l = backup_state.di_list; - while (l) { -@@ -3474,11 +3488,13 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - if (!di->completed && di->bs && di->bs->job) { - BlockJob *job = di->bs->job; - AioContext *aio_context = blk_get_aio_context(job->blk); -+ bool cancel_job = backup_state.error || backup_state.cancel; -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ - aio_context_acquire(aio_context); -- qemu_mutex_unlock(&backup_state.backup_mutex); - if (job_should_pause(&job->job)) { -- if (backup_state.error || backup_state.cancel) { -- job_cancel_sync(&job->job); -+ if (cancel_job) { -+ job_cancel(&job->job, false); - } else { - job_resume(&job->job); - } -@@ -3487,7 +3503,7 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - return; - } - } -- qemu_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - - // no more jobs, run the cleanup - pvebackup_co_cleanup(); -@@ -3530,11 +3546,14 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - BlockJob *job; - - if (!backup_state.backup_mutex_initialized) { -- qemu_mutex_init(&backup_state.backup_mutex); -+ qemu_co_mutex_init(&backup_state.backup_mutex); - backup_state.backup_mutex_initialized = true; - } - -+ qemu_co_mutex_lock(&backup_state.backup_mutex); -+ - if (backup_state.di_list) { -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - error_set(task->errp, ERROR_CLASS_GENERIC_ERROR, - "previous backup not finished"); - return; -@@ -3706,7 +3725,6 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - uuid_copy(backup_state.uuid, uuid); - uuid_unparse_lower(uuid, backup_state.uuid_str); - -- qemu_mutex_lock(&backup_state.backup_mutex); - backup_state.di_list = di_list; - - backup_state.total = total; -@@ -3724,20 +3742,21 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - 1, NULL, &local_err); - if (!job || local_err != NULL) { - error_setg(&backup_state.error, "backup_job_create failed"); -- pvebackup_co_cancel(NULL); -- } else { -- job_start(&job->job); -+ break; - } -+ job_start(&job->job); - if (di->target) { - bdrv_unref(di->target); - di->target = NULL; - } - } - -- qemu_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - - if (!backup_state.error) { - pvebackup_co_run_next_job(); // run one job -+ } else { -+ pvebackup_co_cancel(NULL); - } - - uuid_info = g_malloc0(sizeof(*uuid_info)); -@@ -3778,6 +3797,8 @@ err: - rmdir(backup_dir); - } - -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ - task->result = NULL; - return; - } -@@ -3805,6 +3826,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - }; - - block_on_coroutine_fn(pvebackup_co_start, &task); -+ - return task.result; - } - -@@ -3822,9 +3844,15 @@ static void coroutine_fn pvebackup_co_query(void *opaque) - - BackupStatus *info = g_malloc0(sizeof(*info)); - -+ if (!backup_state.backup_mutex_initialized) -+ return; -+ -+ qemu_co_mutex_lock(&backup_state.backup_mutex); -+ - if (!backup_state.start_time) { - /* not started, return {} */ - task->result = info; -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - return; - } - -@@ -3862,6 +3890,8 @@ static void coroutine_fn pvebackup_co_query(void *opaque) - info->transferred = backup_state.transferred; - - task->result = info; -+ -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); - } - - BackupStatus *qmp_query_backup(Error **errp) diff --git a/debian/patches/pve/0034-vma_writer_close-avoid-call-to-aio_poll-acquire-flus.patch b/debian/patches/pve/0034-vma_writer_close-avoid-call-to-aio_poll-acquire-flus.patch deleted file mode 100644 index 5300262..0000000 --- a/debian/patches/pve/0034-vma_writer_close-avoid-call-to-aio_poll-acquire-flus.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Tue, 22 Oct 2019 12:48:19 +0200 -Subject: [PATCH] vma_writer_close: avoid call to aio_poll (acquire flush_lock - instead) - -Signed-off-by: Dietmar Maurer ---- - vma-writer.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/vma-writer.c b/vma-writer.c -index b163fa2d3a..fe86b18a60 100644 ---- a/vma-writer.c -+++ b/vma-writer.c -@@ -705,9 +705,7 @@ int vma_writer_close(VmaWriter *vmaw, Error **errp) - - int i; - -- while (vmaw->co_writer) { -- aio_poll(qemu_get_aio_context(), true); -- } -+ qemu_co_mutex_lock(&vmaw->flush_lock); // wait for pending writes - - assert(vmaw->co_writer == NULL); - -@@ -748,6 +746,8 @@ int vma_writer_close(VmaWriter *vmaw, Error **errp) - error_setg(errp, "%s", vmaw->errmsg); - } - -+ qemu_co_mutex_unlock(&vmaw->flush_lock); -+ - return vmaw->status; - } - diff --git a/debian/patches/pve/0035-backup_job_create-pass-cluster-size-for-dump.patch b/debian/patches/pve/0035-backup_job_create-pass-cluster-size-for-dump.patch deleted file mode 100644 index ba78610..0000000 --- a/debian/patches/pve/0035-backup_job_create-pass-cluster-size-for-dump.patch +++ /dev/null @@ -1,123 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Thu, 24 Oct 2019 08:06:50 +0200 -Subject: [PATCH] backup_job_create: pass cluster size for dump - -Signed-off-by: Dietmar Maurer ---- - block/backup.c | 8 +++++++- - block/replication.c | 2 +- - blockdev.c | 10 ++++++---- - include/block/block_int.h | 4 ++++ - 4 files changed, 18 insertions(+), 6 deletions(-) - -diff --git a/block/backup.c b/block/backup.c -index fdcfb0529c..2398a4d602 100644 ---- a/block/backup.c -+++ b/block/backup.c -@@ -566,6 +566,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - BlockdevOnError on_target_error, - int creation_flags, - BackupDumpFunc *dump_cb, -+ int dump_cb_block_size, - BlockCompletionFunc *cb, void *opaque, - int pause_count, - JobTxn *txn, Error **errp) -@@ -636,7 +637,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - goto error; - } - -- cluster_size = backup_calculate_cluster_size(target ? target : bs, errp); -+ if (target) { -+ cluster_size = backup_calculate_cluster_size(target, errp); -+ } else { -+ cluster_size = dump_cb_block_size; -+ } -+ - if (cluster_size < 0) { - goto error; - } -diff --git a/block/replication.c b/block/replication.c -index f060755713..b9465c3587 100644 ---- a/block/replication.c -+++ b/block/replication.c -@@ -546,7 +546,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, - 0, MIRROR_SYNC_MODE_NONE, NULL, false, - BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, -- NULL, -+ NULL, 0, - backup_job_completed, bs, 0, NULL, &local_err); - if (local_err) { - error_propagate(errp, local_err); -diff --git a/blockdev.c b/blockdev.c -index 29196c18d8..a95beb823e 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3544,6 +3544,7 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - GList *l; - UuidInfo *uuid_info; - BlockJob *job; -+ int dump_cb_block_size = -1; - - if (!backup_state.backup_mutex_initialized) { - qemu_co_mutex_init(&backup_state.backup_mutex); -@@ -3631,6 +3632,7 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - uuid_generate(uuid); - - if (format == BACKUP_FORMAT_VMA) { -+ dump_cb_block_size = VMA_CLUSTER_SIZE; - vmaw = vma_writer_create(task->backup_file, uuid, &local_err); - if (!vmaw) { - if (local_err) { -@@ -3738,8 +3740,8 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - l = g_list_next(l); - job = backup_job_create(NULL, di->bs, di->target, backup_state.speed, MIRROR_SYNC_MODE_FULL, NULL, - false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, -- JOB_DEFAULT, pvebackup_co_dump_cb, pvebackup_complete_cb, di, -- 1, NULL, &local_err); -+ JOB_DEFAULT, pvebackup_co_dump_cb, dump_cb_block_size, -+ pvebackup_complete_cb, di, 1, NULL, &local_err); - if (!job || local_err != NULL) { - error_setg(&backup_state.error, "backup_job_create failed"); - break; -@@ -4312,7 +4314,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, - job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, bmap, backup->compress, - backup->on_source_error, backup->on_target_error, -- job_flags, NULL, NULL, NULL, 0, txn, &local_err); -+ job_flags, NULL, 0, NULL, NULL, 0, txn, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - goto unref; -@@ -4417,7 +4419,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, - job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, bmap, backup->compress, - backup->on_source_error, backup->on_target_error, -- job_flags, NULL, NULL, NULL, 0, txn, &local_err); -+ job_flags, NULL, 0, NULL, NULL, 0, txn, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - } -diff --git a/include/block/block_int.h b/include/block/block_int.h -index 5a8b2e06c1..e7ca823058 100644 ---- a/include/block/block_int.h -+++ b/include/block/block_int.h -@@ -1160,6 +1160,9 @@ void mirror_start(const char *job_id, BlockDriverState *bs, - * @on_target_error: The action to take upon error writing to the target. - * @creation_flags: Flags that control the behavior of the Job lifetime. - * See @BlockJobCreateFlags -+ * @dump_cb: Callback for PVE backup code. Called for each data block when -+ * target is NULL. -+ * @dump_cb_block_size: The minimum block size expected by dump_cb. - * @cb: Completion function for the job. - * @opaque: Opaque pointer value passed to @cb. - * @txn: Transaction that this job is part of (may be NULL). -@@ -1176,6 +1179,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - BlockdevOnError on_target_error, - int creation_flags, - BackupDumpFunc *dump_cb, -+ int dump_cb_block_size, - BlockCompletionFunc *cb, void *opaque, - int pause_count, - JobTxn *txn, Error **errp); diff --git a/debian/patches/pve/0036-avoid-calling-dump_cb-with-NULL-data-pointer-for-sma.patch b/debian/patches/pve/0036-avoid-calling-dump_cb-with-NULL-data-pointer-for-sma.patch deleted file mode 100644 index a9d8b1a..0000000 --- a/debian/patches/pve/0036-avoid-calling-dump_cb-with-NULL-data-pointer-for-sma.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Thu, 24 Oct 2019 08:06:51 +0200 -Subject: [PATCH] avoid calling dump_cb with NULL data pointer for small/last - cluster - -The last block of a backup may be smaller than cluster_size. - -Signed-off-by: Dietmar Maurer ---- - block/backup.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/block/backup.c b/block/backup.c -index 2398a4d602..c682c99f7a 100644 ---- a/block/backup.c -+++ b/block/backup.c -@@ -131,7 +131,12 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, - - if (buffer_is_zero(*bounce_buffer, nbytes)) { - if (job->dump_cb) { -- ret = job->dump_cb(job->common.job.opaque, job->target, start, nbytes, NULL); -+ if (nbytes == job->cluster_size) { -+ // Note: pass NULL to indicate that we want to write [0u8; cluster_size] -+ ret = job->dump_cb(job->common.job.opaque, job->target, start, nbytes, NULL); -+ } else { -+ ret = job->dump_cb(job->common.job.opaque, job->target, start, nbytes, *bounce_buffer); -+ } - } else { - ret = blk_co_pwrite_zeroes(job->target, start, - nbytes, write_flags | BDRV_REQ_MAY_UNMAP); diff --git a/debian/patches/pve/0037-rename-config_to_vma-into-pvebackup_co_add_config.patch b/debian/patches/pve/0037-rename-config_to_vma-into-pvebackup_co_add_config.patch deleted file mode 100644 index e7d277d..0000000 --- a/debian/patches/pve/0037-rename-config_to_vma-into-pvebackup_co_add_config.patch +++ /dev/null @@ -1,109 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Thu, 24 Oct 2019 08:06:52 +0200 -Subject: [PATCH] rename config_to_vma into pvebackup_co_add_config - -- mark it with coroutine_fn -- add an additional parameter 'name' -- return -1 on error (instead of 1) -- code cleanup - -Signed-off-by: Dietmar Maurer ---- - blockdev.c | 40 ++++++++++++++++++++++++++-------------- - 1 file changed, 26 insertions(+), 14 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index a95beb823e..530b76c82f 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3436,10 +3436,16 @@ void qmp_backup_cancel(Error **errp) - block_on_coroutine_fn(pvebackup_co_cancel, NULL); - } - --static int config_to_vma(const char *file, BackupFormat format, -- const char *backup_dir, VmaWriter *vmaw, -- Error **errp) -+static int coroutine_fn pvebackup_co_add_config( -+ const char *file, -+ const char *name, -+ BackupFormat format, -+ const char *backup_dir, -+ VmaWriter *vmaw, -+ Error **errp) - { -+ int res = 0; -+ - char *cdata = NULL; - gsize clen = 0; - GError *err = NULL; -@@ -3449,28 +3455,30 @@ static int config_to_vma(const char *file, BackupFormat format, - } - - char *basename = g_path_get_basename(file); -+ if (name == NULL) name = basename; - - if (format == BACKUP_FORMAT_VMA) { -- if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -+ if (vma_writer_add_config(vmaw, name, cdata, clen) != 0) { - error_setg(errp, "unable to add %s config data to vma archive", file); -- g_free(cdata); -- g_free(basename); -- return 1; -+ goto err; - } - } else if (format == BACKUP_FORMAT_DIR) { - char config_path[PATH_MAX]; -- snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); -+ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, name); - 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; -+ goto err; - } - } - -+ out: - g_free(basename); - g_free(cdata); -- return 0; -+ return res; -+ -+ err: -+ res = -1; -+ goto out; - } - - bool job_should_pause(Job *job); -@@ -3546,6 +3554,9 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - BlockJob *job; - int dump_cb_block_size = -1; - -+ const char *config_name = "qemu-server.conf"; -+ const char *firewall_name = "qemu-server.fw"; -+ - if (!backup_state.backup_mutex_initialized) { - qemu_co_mutex_init(&backup_state.backup_mutex); - backup_state.backup_mutex_initialized = true; -@@ -3690,16 +3701,17 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - goto err; - } - -+ - /* add configuration file to archive */ - if (task->has_config_file) { -- if (config_to_vma(task->config_file, format, backup_dir, vmaw, task->errp) != 0) { -+ if (pvebackup_co_add_config(task->config_file, config_name, format, backup_dir, vmaw, task->errp) != 0) { - goto err; - } - } - - /* add firewall file to archive */ - if (task->has_firewall_file) { -- if (config_to_vma(task->firewall_file, format, backup_dir, vmaw, task->errp) != 0) { -+ if (pvebackup_co_add_config(task->firewall_file, firewall_name, format, backup_dir, vmaw, task->errp) != 0) { - goto err; - } - } diff --git a/debian/patches/pve/0038-pvebackup_co_dump_cb-do-not-call-job-cancel.patch b/debian/patches/pve/0038-pvebackup_co_dump_cb-do-not-call-job-cancel.patch deleted file mode 100644 index 84282e2..0000000 --- a/debian/patches/pve/0038-pvebackup_co_dump_cb-do-not-call-job-cancel.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Wed, 30 Oct 2019 12:15:44 +0100 -Subject: [PATCH] pvebackup_co_dump_cb: do not call job->cancel() - -The backup loop will automatically abort if we return an error. ---- - blockdev.c | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 530b76c82f..568f71fdb4 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3274,10 +3274,8 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - if (!backup_state.error) { - vma_writer_error_propagate(backup_state.vmaw, &backup_state.error); - } -- if (di->bs && di->bs->job) { -- job_cancel(&di->bs->job->job, true); -- } -- break; -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ return ret; - } else { - backup_state.zero_bytes += zero_bytes; - if (remaining >= VMA_CLUSTER_SIZE) { diff --git a/debian/patches/pve/0039-fix-backup-job-completion.patch b/debian/patches/pve/0039-fix-backup-job-completion.patch deleted file mode 100644 index 8b4a9ce..0000000 --- a/debian/patches/pve/0039-fix-backup-job-completion.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Wed, 30 Oct 2019 12:15:45 +0100 -Subject: [PATCH] fix backup job completion - -With recent changes, pvebackup_co_run_next_job cancels the job async, -so we need to run pvebackup_co_cleanup in the completion handler -instead. We call pvebackup_co_run_next as long as there are -jobs in the list. ---- - blockdev.c | 9 ++++----- - 1 file changed, 4 insertions(+), 5 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 568f71fdb4..66f2711185 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3359,12 +3359,14 @@ static void coroutine_fn pvebackup_co_complete_cb(void *opaque) - backup_state.di_list = g_list_remove(backup_state.di_list, di); - g_free(di); - -- bool cancel = backup_state.cancel; -+ int pending_jobs = g_list_length(backup_state.di_list); - - qemu_co_mutex_unlock(&backup_state.backup_mutex); - -- if (!cancel) { -+ if (pending_jobs > 0) { - pvebackup_co_run_next_job(); -+ } else { -+ pvebackup_co_cleanup(); - } - } - -@@ -3510,9 +3512,6 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - } - } - qemu_co_mutex_unlock(&backup_state.backup_mutex); -- -- // no more jobs, run the cleanup -- pvebackup_co_cleanup(); - } - - typedef struct QmpBackupTask { diff --git a/debian/patches/pve/0040-pvebackup_complete_cb-avoid-poll-loop-if-already-ins.patch b/debian/patches/pve/0040-pvebackup_complete_cb-avoid-poll-loop-if-already-ins.patch deleted file mode 100644 index 9650476..0000000 --- a/debian/patches/pve/0040-pvebackup_complete_cb-avoid-poll-loop-if-already-ins.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Wed, 30 Oct 2019 12:15:46 +0100 -Subject: [PATCH] pvebackup_complete_cb: avoid poll loop if already inside - coroutine - ---- - blockdev.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 66f2711185..083ada6c8e 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3189,6 +3189,8 @@ static void coroutine_fn block_on_coroutine_wrapper(void *opaque) - - static void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) - { -+ assert(!qemu_in_coroutine()); -+ - AioContext *ctx = qemu_get_current_aio_context(); - BlockOnCoroutineWrapper wrapper = { - .finished = false, -@@ -3372,13 +3374,17 @@ static void coroutine_fn pvebackup_co_complete_cb(void *opaque) - - static void pvebackup_complete_cb(void *opaque, int ret) - { -- // This always called from the main loop -+ // This can be called from the main loop, or from a coroutine - PVEBackupCompeteCallbackData cb_data = { - .di = opaque, - .result = ret, - }; - -- block_on_coroutine_fn(pvebackup_co_complete_cb, &cb_data); -+ if (qemu_in_coroutine()) { -+ pvebackup_co_complete_cb(&cb_data); -+ } else { -+ block_on_coroutine_fn(pvebackup_co_complete_cb, &cb_data); -+ } - } - - static void coroutine_fn pvebackup_co_cancel(void *opaque) diff --git a/debian/patches/pve/0041-PVE-backup-consider-source-cluster-size-as-well.patch b/debian/patches/pve/0041-PVE-backup-consider-source-cluster-size-as-well.patch deleted file mode 100644 index b72cb76..0000000 --- a/debian/patches/pve/0041-PVE-backup-consider-source-cluster-size-as-well.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Mon, 4 Nov 2019 14:18:40 +0100 -Subject: [PATCH] PVE: backup: consider source cluster size as well - -Signed-off-by: Wolfgang Bumiller ---- - block/backup.c | 7 ++----- - 1 file changed, 2 insertions(+), 5 deletions(-) - -diff --git a/block/backup.c b/block/backup.c -index c682c99f7a..556367f8ba 100644 ---- a/block/backup.c -+++ b/block/backup.c -@@ -642,11 +642,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - goto error; - } - -- if (target) { -- cluster_size = backup_calculate_cluster_size(target, errp); -- } else { -- cluster_size = dump_cb_block_size; -- } -+ cluster_size = backup_calculate_cluster_size(target ? target : bs, errp); -+ cluster_size = MAX(cluster_size, dump_cb_block_size); - - if (cluster_size < 0) { - goto error; diff --git a/debian/patches/pve/0042-PVE-fixup-vma-tool.patch b/debian/patches/pve/0042-PVE-fixup-vma-tool.patch deleted file mode 100644 index 828a7fe..0000000 --- a/debian/patches/pve/0042-PVE-fixup-vma-tool.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Thomas Lamprecht -Date: Mon, 26 Aug 2019 18:40:10 +0200 -Subject: [PATCH] PVE: fixup: vma tool - -Signed-off-by: Thomas Lamprecht ---- - vma.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/vma.c b/vma.c -index 3289fd722f..a82752448a 100644 ---- a/vma.c -+++ b/vma.c -@@ -16,6 +16,7 @@ - - #include "vma.h" - #include "qemu-common.h" -+#include "qemu/module.h" - #include "qemu/error-report.h" - #include "qemu/main-loop.h" - #include "qemu/cutils.h" -@@ -800,7 +801,9 @@ int main(int argc, char **argv) - const char *cmdname; - Error *main_loop_err = NULL; - -- error_set_progname(argv[0]); -+ error_init(argv[0]); -+ module_call_init(MODULE_INIT_TRACE); -+ qemu_init_exec_dir(argv[0]); - - if (qemu_init_main_loop(&main_loop_err)) { - g_error("%s", error_get_pretty(main_loop_err)); diff --git a/debian/patches/pve/0043-PVE-fixup-blockdev-pvebackup-integration-fix-blockjo.patch b/debian/patches/pve/0043-PVE-fixup-blockdev-pvebackup-integration-fix-blockjo.patch deleted file mode 100644 index 7f0b28a..0000000 --- a/debian/patches/pve/0043-PVE-fixup-blockdev-pvebackup-integration-fix-blockjo.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Thomas Lamprecht -Date: Mon, 26 Aug 2019 18:40:44 +0200 -Subject: [PATCH] PVE: fixup: blockdev pvebackup integration: fix blockjob - -Signed-off-by: Thomas Lamprecht -Signed-off-by: Stefan Reiter ---- - blockdev.c | 49 +++++++++++++++++++++++++++---------------------- - 1 file changed, 27 insertions(+), 22 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 083ada6c8e..46e8a2780a 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3416,15 +3416,17 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); - if (!di->completed && di->bs) { -- BlockJob *job = di->bs->job; -- if (job) { -- AioContext *aio_context = blk_get_aio_context(job->blk); -- aio_context_acquire(aio_context); -- if (!di->completed) { -- running_jobs += 1; -- job_cancel(&job->job, false); -+ for (BlockJob *job = block_job_next(NULL); job; job = block_job_next(job)) { -+ if (block_job_has_bdrv(job, di->bs)) { -+ AioContext *aio_context = job->job.aio_context; -+ aio_context_acquire(aio_context); -+ -+ if (!di->completed) { -+ running_jobs += 1; -+ job_cancel(&job->job, false); -+ } -+ aio_context_release(aio_context); - } -- aio_context_release(aio_context); - } - } - } -@@ -3499,22 +3501,25 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - 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; -- AioContext *aio_context = blk_get_aio_context(job->blk); -- bool cancel_job = backup_state.error || backup_state.cancel; -- qemu_co_mutex_unlock(&backup_state.backup_mutex); -- -- aio_context_acquire(aio_context); -- if (job_should_pause(&job->job)) { -- if (cancel_job) { -- job_cancel(&job->job, false); -- } else { -- job_resume(&job->job); -+ if (!di->completed && di->bs) { -+ for (BlockJob *job = block_job_next(NULL); job; job = block_job_next(job)) { -+ if (block_job_has_bdrv(job, di->bs)) { -+ AioContext *aio_context = job->job.aio_context; -+ qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ aio_context_acquire(aio_context); -+ -+ -+ if (job_should_pause(&job->job)) { -+ if (backup_state.error || backup_state.cancel) { -+ job_cancel(&job->job, false); -+ } else { -+ job_resume(&job->job); -+ } -+ } -+ aio_context_release(aio_context); -+ return; - } - } -- aio_context_release(aio_context); -- return; - } - } - qemu_co_mutex_unlock(&backup_state.backup_mutex); diff --git a/debian/patches/pve/0047-PVE-fix-hmp-info-backup-cmd-for-not-initialized-back.patch b/debian/patches/pve/0047-PVE-fix-hmp-info-backup-cmd-for-not-initialized-back.patch deleted file mode 100644 index c739b77..0000000 --- a/debian/patches/pve/0047-PVE-fix-hmp-info-backup-cmd-for-not-initialized-back.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Tim Marx -Date: Wed, 22 Jan 2020 16:22:14 +0100 -Subject: [PATCH] PVE: fix hmp info backup cmd for not initialized - backup_state.backup_mutex - -Signed-off-by: Tim Marx ---- - monitor/hmp-cmds.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c -index bc9ca346f7..fb820410de 100644 ---- a/monitor/hmp-cmds.c -+++ b/monitor/hmp-cmds.c -@@ -201,6 +201,12 @@ void hmp_info_backup(Monitor *mon, const QDict *qdict) - BackupStatus *info; - - info = qmp_query_backup(NULL); -+ -+ if (!info) { -+ monitor_printf(mon, "Backup status: not initialized\n"); -+ return; -+ } -+ - if (info->has_status) { - if (info->has_errmsg) { - monitor_printf(mon, "Backup status: %s - %s\n", --- -2.20.1 - diff --git a/debian/patches/pve/0048-PVE-backup-use-separate-CoRwlock-for-data-accessed-b.patch b/debian/patches/pve/0048-PVE-backup-use-separate-CoRwlock-for-data-accessed-b.patch deleted file mode 100644 index f8b368e..0000000 --- a/debian/patches/pve/0048-PVE-backup-use-separate-CoRwlock-for-data-accessed-b.patch +++ /dev/null @@ -1,365 +0,0 @@ -From 4c2e4e498716b8a556fccec20fb1348555c2b189 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Fri, 14 Feb 2020 10:21:37 +0100 -Subject: [PATCH qemu 1/7] PVE backup: use separate CoRwlock for data accessed - by qmp query_backup - -This should minimize contention between query_backup and running backup jobs. ---- - blockdev.c | 178 +++++++++++++++++++++++++++++++++-------------------- - 1 file changed, 112 insertions(+), 66 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 46e8a2780a..4367eaf2a7 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3203,22 +3203,26 @@ static void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) - AIO_WAIT_WHILE(ctx, !wrapper.finished); - } - -+ - static struct PVEBackupState { -- Error *error; -+ struct { -+ CoRwlock rwlock; -+ Error *error; -+ time_t start_time; -+ time_t end_time; -+ char *backup_file; -+ uuid_t uuid; -+ char uuid_str[37]; -+ size_t total; -+ size_t transferred; -+ size_t zero_bytes; -+ } stat; - 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; - CoMutex backup_mutex; -- bool backup_mutex_initialized; -+ bool mutex_initialized; - } backup_state; - - typedef struct PVEBackupDevInfo { -@@ -3251,11 +3255,14 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - - uint64_t cluster_num = start / VMA_CLUSTER_SIZE; - if ((cluster_num * VMA_CLUSTER_SIZE) != start) { -- if (!backup_state.error) { -- error_setg(&backup_state.error, -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ if (!backup_state.stat.error) { -+ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); -+ error_setg(&backup_state.stat.error, - "got unaligned write inside backup dump " - "callback (sector %ld)", start); - } -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - qemu_co_mutex_unlock(&backup_state.backup_mutex); - return -1; // not aligned to cluster size - } -@@ -3273,27 +3280,35 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - buf += VMA_CLUSTER_SIZE; - } - if (ret < 0) { -- if (!backup_state.error) { -- vma_writer_error_propagate(backup_state.vmaw, &backup_state.error); -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ if (!backup_state.stat.error) { -+ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); -+ vma_writer_error_propagate(backup_state.vmaw, &backup_state.stat.error); - } -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); -+ - qemu_co_mutex_unlock(&backup_state.backup_mutex); - return ret; - } else { -- backup_state.zero_bytes += zero_bytes; -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); -+ backup_state.stat.zero_bytes += zero_bytes; - if (remaining >= VMA_CLUSTER_SIZE) { -- backup_state.transferred += VMA_CLUSTER_SIZE; -+ backup_state.stat.transferred += VMA_CLUSTER_SIZE; - remaining -= VMA_CLUSTER_SIZE; - } else { -- backup_state.transferred += remaining; -+ backup_state.stat.transferred += remaining; - remaining = 0; - } -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - } - } - } else { -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); - if (!buf) { -- backup_state.zero_bytes += size; -+ backup_state.stat.zero_bytes += size; - } -- backup_state.transferred += size; -+ backup_state.stat.transferred += size; -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - } - - qemu_co_mutex_unlock(&backup_state.backup_mutex); -@@ -3313,12 +3328,20 @@ static void coroutine_fn pvebackup_co_cleanup(void) - return; - } - -- backup_state.end_time = time(NULL); -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); -+ backup_state.stat.end_time = time(NULL); -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - - if (backup_state.vmaw) { - Error *local_err = NULL; - vma_writer_close(backup_state.vmaw, &local_err); -- error_propagate(&backup_state.error, local_err); -+ -+ if (local_err != NULL) { -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); -+ error_propagate(&backup_state.stat.error, local_err); -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); -+ } -+ - backup_state.vmaw = NULL; - } - -@@ -3345,10 +3368,14 @@ static void coroutine_fn pvebackup_co_complete_cb(void *opaque) - - di->completed = true; - -- if (ret < 0 && !backup_state.error) { -- error_setg(&backup_state.error, "job failed with err %d - %s", -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ -+ if (ret < 0 && !backup_state.stat.error) { -+ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); -+ error_setg(&backup_state.stat.error, "job failed with err %d - %s", - ret, strerror(-ret)); - } -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - - di->bs = NULL; - di->target = NULL; -@@ -3401,9 +3428,12 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - return; - } - -- if (!backup_state.error) { -- error_setg(&backup_state.error, "backup cancelled"); -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ if (!backup_state.stat.error) { -+ qemu_co_rwlock_upgrade(&backup_state.stat.rwlock); -+ error_setg(&backup_state.stat.error, "backup cancelled"); - } -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - - if (backup_state.vmaw) { - /* make sure vma writer does not block anymore */ -@@ -3438,7 +3468,7 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - - void qmp_backup_cancel(Error **errp) - { -- if (!backup_state.backup_mutex_initialized) -+ if (!backup_state.mutex_initialized) - return; - - block_on_coroutine_fn(pvebackup_co_cancel, NULL); -@@ -3508,9 +3538,13 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - qemu_co_mutex_unlock(&backup_state.backup_mutex); - aio_context_acquire(aio_context); - -- -+ - if (job_should_pause(&job->job)) { -- if (backup_state.error || backup_state.cancel) { -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ bool error_or_canceled = backup_state.stat.error || backup_state.cancel; -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); -+ -+ if (error_or_canceled) { - job_cancel(&job->job, false); - } else { - job_resume(&job->job); -@@ -3565,9 +3599,10 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - const char *config_name = "qemu-server.conf"; - const char *firewall_name = "qemu-server.fw"; - -- if (!backup_state.backup_mutex_initialized) { -+ if (!backup_state.mutex_initialized) { -+ qemu_co_rwlock_init(&backup_state.stat.rwlock); - qemu_co_mutex_init(&backup_state.backup_mutex); -- backup_state.backup_mutex_initialized = true; -+ backup_state.mutex_initialized = true; - } - - qemu_co_mutex_lock(&backup_state.backup_mutex); -@@ -3727,31 +3762,36 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - - backup_state.cancel = false; - -- if (backup_state.error) { -- error_free(backup_state.error); -- backup_state.error = NULL; -- } -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); - -- backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0; -+ if (backup_state.stat.error) { -+ error_free(backup_state.stat.error); -+ backup_state.stat.error = NULL; -+ } - -- backup_state.start_time = time(NULL); -- backup_state.end_time = 0; -+ backup_state.stat.start_time = time(NULL); -+ backup_state.stat.end_time = 0; - -- if (backup_state.backup_file) { -- g_free(backup_state.backup_file); -+ if (backup_state.stat.backup_file) { -+ g_free(backup_state.stat.backup_file); - } -- backup_state.backup_file = g_strdup(task->backup_file); -+ backup_state.stat.backup_file = g_strdup(task->backup_file); - -- backup_state.vmaw = vmaw; -+ uuid_copy(backup_state.stat.uuid, uuid); -+ uuid_unparse_lower(uuid, backup_state.stat.uuid_str); -+ char *uuid_str = g_strdup(backup_state.stat.uuid_str); - -- uuid_copy(backup_state.uuid, uuid); -- uuid_unparse_lower(uuid, backup_state.uuid_str); -+ backup_state.stat.total = total; -+ backup_state.stat.transferred = 0; -+ backup_state.stat.zero_bytes = 0; - -- backup_state.di_list = di_list; -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); -+ -+ backup_state.speed = (task->has_speed && task->speed > 0) ? task->speed : 0; -+ -+ backup_state.vmaw = vmaw; - -- backup_state.total = total; -- backup_state.transferred = 0; -- backup_state.zero_bytes = 0; -+ backup_state.di_list = di_list; - - /* start all jobs (paused state) */ - l = di_list; -@@ -3763,7 +3803,9 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - JOB_DEFAULT, pvebackup_co_dump_cb, dump_cb_block_size, - pvebackup_complete_cb, di, 1, NULL, &local_err); - if (!job || local_err != NULL) { -- error_setg(&backup_state.error, "backup_job_create failed"); -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); -+ error_setg(&backup_state.stat.error, "backup_job_create failed"); -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - break; - } - job_start(&job->job); -@@ -3775,14 +3817,18 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - - qemu_co_mutex_unlock(&backup_state.backup_mutex); - -- if (!backup_state.error) { -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ bool no_errors = !backup_state.stat.error; -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); -+ -+ if (no_errors) { - pvebackup_co_run_next_job(); // run one job - } else { - pvebackup_co_cancel(NULL); - } - - uuid_info = g_malloc0(sizeof(*uuid_info)); -- uuid_info->UUID = g_strdup(backup_state.uuid_str); -+ uuid_info->UUID = uuid_str; - - task->result = uuid_info; - return; -@@ -3866,54 +3912,54 @@ static void coroutine_fn pvebackup_co_query(void *opaque) - - BackupStatus *info = g_malloc0(sizeof(*info)); - -- if (!backup_state.backup_mutex_initialized) -+ if (!backup_state.mutex_initialized) - return; - -- qemu_co_mutex_lock(&backup_state.backup_mutex); -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); - -- if (!backup_state.start_time) { -+ if (!backup_state.stat.start_time) { - /* not started, return {} */ - task->result = info; -- qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - return; - } - - info->has_status = true; - info->has_start_time = true; -- info->start_time = backup_state.start_time; -+ info->start_time = backup_state.stat.start_time; - -- if (backup_state.backup_file) { -+ if (backup_state.stat.backup_file) { - info->has_backup_file = true; -- info->backup_file = g_strdup(backup_state.backup_file); -+ info->backup_file = g_strdup(backup_state.stat.backup_file); - } - - info->has_uuid = true; -- info->uuid = g_strdup(backup_state.uuid_str); -+ info->uuid = g_strdup(backup_state.stat.uuid_str); - -- if (backup_state.end_time) { -- if (backup_state.error) { -+ if (backup_state.stat.end_time) { -+ if (backup_state.stat.error) { - info->status = g_strdup("error"); - info->has_errmsg = true; -- info->errmsg = g_strdup(error_get_pretty(backup_state.error)); -+ info->errmsg = g_strdup(error_get_pretty(backup_state.stat.error)); - } else { - info->status = g_strdup("done"); - } - info->has_end_time = true; -- info->end_time = backup_state.end_time; -+ info->end_time = backup_state.stat.end_time; - } else { - info->status = g_strdup("active"); - } - - info->has_total = true; -- info->total = backup_state.total; -+ info->total = backup_state.stat.total; - info->has_zero_bytes = true; -- info->zero_bytes = backup_state.zero_bytes; -+ info->zero_bytes = backup_state.stat.zero_bytes; - info->has_transferred = true; -- info->transferred = backup_state.transferred; -+ info->transferred = backup_state.stat.transferred; - - task->result = info; - -- qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - } - - BackupStatus *qmp_query_backup(Error **errp) --- -2.20.1 - diff --git a/debian/patches/pve/0049-PVE-backup-block_on_coroutine_wrapper-call-aio_wait.patch b/debian/patches/pve/0049-PVE-backup-block_on_coroutine_wrapper-call-aio_wait.patch deleted file mode 100644 index c1c7b24..0000000 --- a/debian/patches/pve/0049-PVE-backup-block_on_coroutine_wrapper-call-aio_wait.patch +++ /dev/null @@ -1,25 +0,0 @@ -From ca384caae9a0dbbbbab2174ec09935702ac7a067 Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Fri, 14 Feb 2020 11:51:02 +0100 -Subject: [PATCH qemu 2/7] PVE backup - block_on_coroutine_wrapper: call - aio_wait_kick() as described in block/aio-wait.h - ---- - blockdev.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/blockdev.c b/blockdev.c -index 4367eaf2a7..2f84f8832d 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3185,6 +3185,7 @@ static void coroutine_fn block_on_coroutine_wrapper(void *opaque) - BlockOnCoroutineWrapper *wrapper = opaque; - wrapper->entry(wrapper->entry_arg); - wrapper->finished = true; -+ aio_wait_kick(); - } - - static void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) --- -2.20.1 - diff --git a/debian/patches/pve/0050-PVE-backup-move-backup_state.cancel-to-backup_state.patch b/debian/patches/pve/0050-PVE-backup-move-backup_state.cancel-to-backup_state.patch deleted file mode 100644 index 7841169..0000000 --- a/debian/patches/pve/0050-PVE-backup-move-backup_state.cancel-to-backup_state.patch +++ /dev/null @@ -1,95 +0,0 @@ -From c8856d5cbcf3d427cd02c9556f845486ab5362bc Mon Sep 17 00:00:00 2001 -From: Dietmar Maurer -Date: Fri, 14 Feb 2020 12:37:27 +0100 -Subject: [PATCH qemu 3/7] PVE backup: move backup_state.cancel to - backup_state.stat.cancel - -In order to avoid lock contention with qmp_backup_cancel. ---- - blockdev.c | 25 +++++++++++++++---------- - 1 file changed, 15 insertions(+), 10 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 2f84f8832d..a4b5b98224 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -3204,9 +3204,9 @@ static void block_on_coroutine_fn(CoroutineEntry *entry, void *entry_arg) - AIO_WAIT_WHILE(ctx, !wrapper.finished); - } - -- - static struct PVEBackupState { - struct { -+ // Everithing accessed from qmp command, protected using rwlock - CoRwlock rwlock; - Error *error; - time_t start_time; -@@ -3217,8 +3217,8 @@ static struct PVEBackupState { - size_t total; - size_t transferred; - size_t zero_bytes; -+ bool cancel; - } stat; -- bool cancel; - int64_t speed; - VmaWriter *vmaw; - GList *di_list; -@@ -3247,13 +3247,16 @@ static int coroutine_fn pvebackup_co_dump_cb(void *opaque, BlockBackend *target, - const unsigned char *buf = pbuf; - PVEBackupDevInfo *di = opaque; - -- qemu_co_mutex_lock(&backup_state.backup_mutex); -+ qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -+ bool cancel = backup_state.stat.cancel; -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - -- if (backup_state.cancel) { -- qemu_co_mutex_unlock(&backup_state.backup_mutex); -+ if (cancel) { - return size; // return success - } - -+ qemu_co_mutex_lock(&backup_state.backup_mutex); -+ - uint64_t cluster_num = start / VMA_CLUSTER_SIZE; - if ((cluster_num * VMA_CLUSTER_SIZE) != start) { - qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -@@ -3419,9 +3422,11 @@ static void coroutine_fn pvebackup_co_cancel(void *opaque) - { - assert(qemu_in_coroutine()); - -- qemu_co_mutex_lock(&backup_state.backup_mutex); -+ qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); -+ backup_state.stat.cancel = true; -+ qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - -- backup_state.cancel = true; -+ qemu_co_mutex_lock(&backup_state.backup_mutex); - - // Avoid race between block jobs and backup-cancel command: - if (!backup_state.vmaw) { -@@ -3542,7 +3547,7 @@ static void coroutine_fn pvebackup_co_run_next_job(void) - - if (job_should_pause(&job->job)) { - qemu_co_rwlock_rdlock(&backup_state.stat.rwlock); -- bool error_or_canceled = backup_state.stat.error || backup_state.cancel; -+ bool error_or_canceled = backup_state.stat.error || backup_state.stat.cancel; - qemu_co_rwlock_unlock(&backup_state.stat.rwlock); - - if (error_or_canceled) { -@@ -3761,10 +3766,10 @@ static void coroutine_fn pvebackup_co_start(void *opaque) - } - /* initialize global backup_state now */ - -- backup_state.cancel = false; -- - qemu_co_rwlock_wrlock(&backup_state.stat.rwlock); - -+ backup_state.stat.cancel = false; -+ - if (backup_state.stat.error) { - error_free(backup_state.stat.error); - backup_state.stat.error = NULL; --- -2.20.1 - diff --git a/debian/patches/series b/debian/patches/series index 6f5dcd0..651c609 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,8 +1,3 @@ -extra/0001-monitor-qmp-resume-monitor-when-clearing-its-queue.patch -extra/0002-virtio-blk-schedule-virtio_notify_config-to-run-on-m.patch -extra/0003-vnc-fix-memory-leak-when-vnc-disconnect.patch -extra/0004-util-add-slirp_fmt-helpers.patch -extra/0005-tcp_emu-fix-unsafe-snprintf-usages.patch pve/0001-PVE-Config-block-file-change-locking-default-to-off.patch pve/0002-PVE-Config-Adjust-network-script-path-to-etc-kvm.patch pve/0003-PVE-Config-set-the-CPU-model-to-kvm64-32-instead-of-.patch @@ -22,34 +17,16 @@ pve/0016-PVE-qapi-modify-spice-query.patch pve/0017-PVE-internal-snapshot-async.patch pve/0018-PVE-block-add-the-zeroinit-block-driver-filter.patch pve/0019-PVE-backup-modify-job-api.patch -pve/0020-PVE-backup-introduce-vma-archive-format.patch -pve/0021-PVE-Deprecated-adding-old-vma-files.patch -pve/0022-PVE-vma-add-throttling-options-to-drive-mapping-fifo.patch -pve/0023-PVE-vma-add-cache-option-to-device-map.patch -pve/0024-PVE-vma-remove-forced-NO_FLUSH-option.patch -pve/0025-PVE-Add-dummy-id-command-line-parameter.patch -pve/0026-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch -pve/0027-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch -pve/0028-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch -pve/0029-PVE-move-snapshot-cleanup-into-bottom-half.patch -pve/0030-PVE-monitor-disable-oob-capability.patch -pve/0031-PVE-bug-fix-1071-vma-writer.c-use-correct-AioContext.patch -pve/0032-qmp_backup-run-backup-related-code-inside-coroutines.patch -pve/0033-qmp_backup-use-a-CoMutex-to-protect-access-to-backup.patch -pve/0034-vma_writer_close-avoid-call-to-aio_poll-acquire-flus.patch -pve/0035-backup_job_create-pass-cluster-size-for-dump.patch -pve/0036-avoid-calling-dump_cb-with-NULL-data-pointer-for-sma.patch -pve/0037-rename-config_to_vma-into-pvebackup_co_add_config.patch -pve/0038-pvebackup_co_dump_cb-do-not-call-job-cancel.patch -pve/0039-fix-backup-job-completion.patch -pve/0040-pvebackup_complete_cb-avoid-poll-loop-if-already-ins.patch -pve/0041-PVE-backup-consider-source-cluster-size-as-well.patch -pve/0042-PVE-fixup-vma-tool.patch -pve/0043-PVE-fixup-blockdev-pvebackup-integration-fix-blockjo.patch -pve/0044-Acquire-aio_context-before-calling-block_job_add_bdr.patch -pve/0045-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch -pve/0046-PVE-Allow-version-code-in-machine-type.patch -pve/0047-PVE-fix-hmp-info-backup-cmd-for-not-initialized-back.patch -pve/0048-PVE-backup-use-separate-CoRwlock-for-data-accessed-b.patch -pve/0049-PVE-backup-block_on_coroutine_wrapper-call-aio_wait.patch -pve/0050-PVE-backup-move-backup_state.cancel-to-backup_state.patch +pve/0020-PVE-Add-dummy-id-command-line-parameter.patch +pve/0021-PVE-Config-Revert-target-i386-disable-LINT0-after-re.patch +pve/0022-PVE-Up-Config-file-posix-make-locking-optiono-on-cre.patch +pve/0023-PVE-savevm-async-kick-AIO-wait-on-block-state-write.patch +pve/0024-PVE-move-snapshot-cleanup-into-bottom-half.patch +pve/0025-PVE-monitor-disable-oob-capability.patch +pve/0026-PVE-Acquire-aio_context-before-calling-block_job_add.patch +pve/0027-PVE-Compat-4.0-used-balloon-qemu-4-0-config-size-fal.patch +pve/0028-PVE-Allow-version-code-in-machine-type.patch +pve/0029-PVE-Backup-add-vma-backup-format-code.patch +pve/0030-PVE-Backup-add-backup-dump-block-driver.patch +pve/0031-PVE-Backup-proxmox-backup-patches-for-qemu.patch +pve/0032-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch diff --git a/debian/rules b/debian/rules index dad78dd..2cb0773 100755 --- a/debian/rules +++ b/debian/rules @@ -120,7 +120,6 @@ install: build rm $(destdir)/usr/share/kvm/s390-netboot.img rm $(destdir)/usr/share/kvm/qemu_vga.ndrv rm $(destdir)/usr/share/kvm/slof.bin - rm $(destdir)/usr/share/kvm/spapr-rtas.bin rm $(destdir)/usr/share/kvm/u-boot.e500 # remove Aplha files rm $(destdir)/usr/share/kvm/palcode-clipper diff --git a/qemu b/qemu index 99c5874..b0ca999 160000 --- a/qemu +++ b/qemu @@ -1 +1 @@ -Subproject commit 99c5874a9b6c9f70aef285d6eff85d4f46de3c52 +Subproject commit b0ca999a43a22b38158a222233d3f5881648bb4f