2020-06-30 11:28:24 +03:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Stefan Reiter <s.reiter@proxmox.com>
|
|
|
|
Date: Mon, 29 Jun 2020 11:06:03 +0200
|
|
|
|
Subject: [PATCH] PVE-Backup: Add dirty-bitmap tracking for incremental backups
|
|
|
|
|
|
|
|
Uses QEMU's existing MIRROR_SYNC_MODE_BITMAP and a dirty-bitmap on top
|
|
|
|
of all backed-up drives. This will only execute the data-write callback
|
|
|
|
for any changed chunks, the PBS rust code will reuse chunks from the
|
|
|
|
previous index for everything it doesn't receive if reuse_index is true.
|
|
|
|
|
|
|
|
On error or cancellation, remove all dirty bitmaps to ensure
|
|
|
|
consistency.
|
|
|
|
|
2020-07-02 14:03:44 +03:00
|
|
|
Add PBS/incremental specific information to query backup info QMP and
|
|
|
|
HMP commands.
|
|
|
|
|
2020-06-30 11:28:24 +03:00
|
|
|
Only supported for PBS backups.
|
|
|
|
|
|
|
|
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
|
|
|
|
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
|
2020-07-02 14:03:44 +03:00
|
|
|
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
|
2020-06-30 11:28:24 +03:00
|
|
|
---
|
2021-03-03 12:56:02 +03:00
|
|
|
block/monitor/block-hmp-cmds.c | 1 +
|
|
|
|
monitor/hmp-cmds.c | 45 ++++++++++----
|
|
|
|
proxmox-backup-client.c | 3 +-
|
|
|
|
proxmox-backup-client.h | 1 +
|
|
|
|
pve-backup.c | 103 ++++++++++++++++++++++++++++++---
|
|
|
|
qapi/block-core.json | 12 +++-
|
|
|
|
6 files changed, 142 insertions(+), 23 deletions(-)
|
2020-06-30 11:28:24 +03:00
|
|
|
|
|
|
|
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
|
2022-12-14 17:16:32 +03:00
|
|
|
index 477044c54a..556af25861 100644
|
2020-06-30 11:28:24 +03:00
|
|
|
--- a/block/monitor/block-hmp-cmds.c
|
|
|
|
+++ b/block/monitor/block-hmp-cmds.c
|
2021-10-11 14:55:34 +03:00
|
|
|
@@ -1042,6 +1042,7 @@ void hmp_backup(Monitor *mon, const QDict *qdict)
|
2020-06-30 11:28:24 +03:00
|
|
|
false, NULL, // PBS fingerprint
|
|
|
|
false, NULL, // PBS backup-id
|
|
|
|
false, 0, // PBS backup-time
|
|
|
|
+ false, false, // PBS incremental
|
|
|
|
true, dir ? BACKUP_FORMAT_DIR : BACKUP_FORMAT_VMA,
|
|
|
|
false, NULL, false, NULL, !!devlist,
|
|
|
|
devlist, qdict_haskey(qdict, "speed"), speed, &error);
|
2020-07-02 14:03:44 +03:00
|
|
|
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
|
2022-12-14 17:16:32 +03:00
|
|
|
index a40b25e906..670f783515 100644
|
2020-07-02 14:03:44 +03:00
|
|
|
--- a/monitor/hmp-cmds.c
|
|
|
|
+++ b/monitor/hmp-cmds.c
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -225,19 +225,42 @@ void hmp_info_backup(Monitor *mon, const QDict *qdict)
|
2020-07-02 14:03:44 +03:00
|
|
|
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);
|
|
|
|
+
|
|
|
|
+ if (!(info->has_total && info->total)) {
|
|
|
|
+ // this should not happen normally
|
|
|
|
+ monitor_printf(mon, "Total size: %d\n", 0);
|
|
|
|
+ } else {
|
|
|
|
+ bool incremental = false;
|
|
|
|
+ size_t total_or_dirty = info->total;
|
|
|
|
+ if (info->has_transferred) {
|
|
|
|
+ if (info->has_dirty && info->dirty) {
|
|
|
|
+ if (info->dirty < info->total) {
|
|
|
|
+ total_or_dirty = info->dirty;
|
|
|
|
+ incremental = true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int per = (info->transferred * 100)/total_or_dirty;
|
|
|
|
+
|
|
|
|
+ monitor_printf(mon, "Backup mode: %s\n", incremental ? "incremental" : "full");
|
|
|
|
+
|
|
|
|
+ int zero_per = (info->has_zero_bytes && info->zero_bytes) ?
|
|
|
|
+ (info->zero_bytes * 100)/info->total : 0;
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ if (info->has_reused) {
|
|
|
|
+ int reused_per = (info->reused * 100)/total_or_dirty;
|
|
|
|
+ monitor_printf(mon, "Reused bytes: %zd (%d%%)\n",
|
|
|
|
+ info->reused, reused_per);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
qapi_free_BackupStatus(info);
|
2020-06-30 11:28:24 +03:00
|
|
|
diff --git a/proxmox-backup-client.c b/proxmox-backup-client.c
|
2020-10-29 16:10:36 +03:00
|
|
|
index a8f6653a81..4ce7bc0b5e 100644
|
2020-06-30 11:28:24 +03:00
|
|
|
--- a/proxmox-backup-client.c
|
|
|
|
+++ b/proxmox-backup-client.c
|
2020-10-29 16:10:36 +03:00
|
|
|
@@ -89,6 +89,7 @@ proxmox_backup_co_register_image(
|
2020-06-30 11:28:24 +03:00
|
|
|
ProxmoxBackupHandle *pbs,
|
|
|
|
const char *device_name,
|
|
|
|
uint64_t size,
|
|
|
|
+ bool incremental,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
Coroutine *co = qemu_coroutine_self();
|
2020-10-29 16:10:36 +03:00
|
|
|
@@ -98,7 +99,7 @@ proxmox_backup_co_register_image(
|
2020-06-30 11:28:24 +03:00
|
|
|
int pbs_res = -1;
|
|
|
|
|
|
|
|
proxmox_backup_register_image_async(
|
|
|
|
- pbs, device_name, size ,proxmox_backup_schedule_wake, &waker, &pbs_res, &pbs_err);
|
|
|
|
+ pbs, device_name, size, incremental, 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");
|
|
|
|
diff --git a/proxmox-backup-client.h b/proxmox-backup-client.h
|
2020-10-29 16:10:36 +03:00
|
|
|
index 1dda8b7d8f..8cbf645b2c 100644
|
2020-06-30 11:28:24 +03:00
|
|
|
--- a/proxmox-backup-client.h
|
|
|
|
+++ b/proxmox-backup-client.h
|
2020-10-29 16:10:36 +03:00
|
|
|
@@ -32,6 +32,7 @@ proxmox_backup_co_register_image(
|
2020-06-30 11:28:24 +03:00
|
|
|
ProxmoxBackupHandle *pbs,
|
|
|
|
const char *device_name,
|
|
|
|
uint64_t size,
|
|
|
|
+ bool incremental,
|
|
|
|
Error **errp);
|
|
|
|
|
|
|
|
|
|
|
|
diff --git a/pve-backup.c b/pve-backup.c
|
2022-12-14 17:16:32 +03:00
|
|
|
index 3d28975eaa..abd7062afe 100644
|
2020-06-30 11:28:24 +03:00
|
|
|
--- a/pve-backup.c
|
|
|
|
+++ b/pve-backup.c
|
|
|
|
@@ -28,6 +28,8 @@
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
+const char *PBS_BITMAP_NAME = "pbs-incremental-dirty-bitmap";
|
|
|
|
+
|
|
|
|
static struct PVEBackupState {
|
|
|
|
struct {
|
|
|
|
// Everithing accessed from qmp_backup_query command is protected using lock
|
2020-07-02 14:03:44 +03:00
|
|
|
@@ -39,7 +41,9 @@ static struct PVEBackupState {
|
|
|
|
uuid_t uuid;
|
|
|
|
char uuid_str[37];
|
|
|
|
size_t total;
|
|
|
|
+ size_t dirty;
|
|
|
|
size_t transferred;
|
|
|
|
+ size_t reused;
|
|
|
|
size_t zero_bytes;
|
|
|
|
} stat;
|
|
|
|
int64_t speed;
|
|
|
|
@@ -66,6 +70,7 @@ typedef struct PVEBackupDevInfo {
|
2020-06-30 11:28:24 +03:00
|
|
|
uint8_t dev_id;
|
|
|
|
bool completed;
|
|
|
|
char targetfile[PATH_MAX];
|
|
|
|
+ BdrvDirtyBitmap *bitmap;
|
|
|
|
BlockDriverState *target;
|
|
|
|
} PVEBackupDevInfo;
|
|
|
|
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -107,11 +112,12 @@ static bool pvebackup_error_or_canceled(void)
|
2020-07-02 14:03:44 +03:00
|
|
|
return error_or_canceled;
|
|
|
|
}
|
|
|
|
|
|
|
|
-static void pvebackup_add_transfered_bytes(size_t transferred, size_t zero_bytes)
|
|
|
|
+static void pvebackup_add_transfered_bytes(size_t transferred, size_t zero_bytes, size_t reused)
|
|
|
|
{
|
|
|
|
qemu_mutex_lock(&backup_state.stat.lock);
|
|
|
|
backup_state.stat.zero_bytes += zero_bytes;
|
|
|
|
backup_state.stat.transferred += transferred;
|
|
|
|
+ backup_state.stat.reused += reused;
|
|
|
|
qemu_mutex_unlock(&backup_state.stat.lock);
|
|
|
|
}
|
|
|
|
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -150,7 +156,8 @@ pvebackup_co_dump_pbs_cb(
|
2020-07-02 14:03:44 +03:00
|
|
|
pvebackup_propagate_error(local_err);
|
|
|
|
return pbs_res;
|
|
|
|
} else {
|
|
|
|
- pvebackup_add_transfered_bytes(size, !buf ? size : 0);
|
|
|
|
+ size_t reused = (pbs_res == 0) ? size : 0;
|
|
|
|
+ pvebackup_add_transfered_bytes(size, !buf ? size : 0, reused);
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -210,11 +217,11 @@ pvebackup_co_dump_vma_cb(
|
2020-07-02 14:03:44 +03:00
|
|
|
} else {
|
|
|
|
if (remaining >= VMA_CLUSTER_SIZE) {
|
|
|
|
assert(ret == VMA_CLUSTER_SIZE);
|
|
|
|
- pvebackup_add_transfered_bytes(VMA_CLUSTER_SIZE, zero_bytes);
|
|
|
|
+ pvebackup_add_transfered_bytes(VMA_CLUSTER_SIZE, zero_bytes, 0);
|
|
|
|
remaining -= VMA_CLUSTER_SIZE;
|
|
|
|
} else {
|
|
|
|
assert(ret == remaining);
|
|
|
|
- pvebackup_add_transfered_bytes(remaining, zero_bytes);
|
|
|
|
+ pvebackup_add_transfered_bytes(remaining, zero_bytes, 0);
|
|
|
|
remaining = 0;
|
|
|
|
}
|
|
|
|
}
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -250,6 +257,18 @@ static void coroutine_fn pvebackup_co_cleanup(void *unused)
|
2020-06-30 11:28:24 +03:00
|
|
|
if (local_err != NULL) {
|
|
|
|
pvebackup_propagate_error(local_err);
|
|
|
|
}
|
|
|
|
+ } else {
|
|
|
|
+ // on error or cancel we cannot ensure synchronization of dirty
|
|
|
|
+ // bitmaps with backup server, so remove all and do full backup next
|
|
|
|
+ GList *l = backup_state.di_list;
|
|
|
|
+ while (l) {
|
|
|
|
+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
|
|
|
|
+ l = g_list_next(l);
|
|
|
|
+
|
|
|
|
+ if (di->bitmap) {
|
|
|
|
+ bdrv_release_dirty_bitmap(di->bitmap);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
|
|
|
|
proxmox_backup_disconnect(backup_state.pbs);
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -305,6 +324,12 @@ static void pvebackup_complete_cb(void *opaque, int ret)
|
2020-07-02 14:03:44 +03:00
|
|
|
// remove self from job queue
|
|
|
|
backup_state.di_list = g_list_remove(backup_state.di_list, di);
|
|
|
|
|
|
|
|
+ if (di->bitmap && ret < 0) {
|
|
|
|
+ // on error or cancel we cannot ensure synchronization of dirty
|
|
|
|
+ // bitmaps with backup server, so remove all and do full backup next
|
|
|
|
+ bdrv_release_dirty_bitmap(di->bitmap);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
g_free(di);
|
|
|
|
|
|
|
|
qemu_mutex_unlock(&backup_state.backup_mutex);
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -469,12 +494,18 @@ static bool create_backup_jobs(void) {
|
2020-06-30 11:28:24 +03:00
|
|
|
|
|
|
|
assert(di->target != NULL);
|
|
|
|
|
|
|
|
+ MirrorSyncMode sync_mode = MIRROR_SYNC_MODE_FULL;
|
|
|
|
+ BitmapSyncMode bitmap_mode = BITMAP_SYNC_MODE_NEVER;
|
|
|
|
+ if (di->bitmap) {
|
|
|
|
+ sync_mode = MIRROR_SYNC_MODE_BITMAP;
|
|
|
|
+ bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS;
|
|
|
|
+ }
|
|
|
|
AioContext *aio_context = bdrv_get_aio_context(di->bs);
|
|
|
|
aio_context_acquire(aio_context);
|
|
|
|
|
|
|
|
BlockJob *job = backup_job_create(
|
|
|
|
- NULL, di->bs, di->target, backup_state.speed, MIRROR_SYNC_MODE_FULL, NULL,
|
2021-05-27 13:43:32 +03:00
|
|
|
- BITMAP_SYNC_MODE_NEVER, false, NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
|
2020-06-30 11:28:24 +03:00
|
|
|
+ NULL, di->bs, di->target, backup_state.speed, sync_mode, di->bitmap,
|
2021-05-27 13:43:32 +03:00
|
|
|
+ bitmap_mode, false, NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
|
|
|
|
JOB_DEFAULT, pvebackup_complete_cb, di, NULL, &local_err);
|
2020-06-30 11:28:24 +03:00
|
|
|
|
|
|
|
aio_context_release(aio_context);
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -525,6 +556,8 @@ typedef struct QmpBackupTask {
|
2020-06-30 11:28:24 +03:00
|
|
|
const char *fingerprint;
|
|
|
|
bool has_fingerprint;
|
|
|
|
int64_t backup_time;
|
2021-03-03 12:56:02 +03:00
|
|
|
+ bool has_use_dirty_bitmap;
|
|
|
|
+ bool use_dirty_bitmap;
|
2020-06-30 11:28:24 +03:00
|
|
|
bool has_format;
|
|
|
|
BackupFormat format;
|
|
|
|
bool has_config_file;
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -616,6 +649,7 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-07-02 14:03:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t total = 0;
|
|
|
|
+ size_t dirty = 0;
|
|
|
|
|
|
|
|
l = di_list;
|
|
|
|
while (l) {
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -653,6 +687,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-06-30 11:28:24 +03:00
|
|
|
int dump_cb_block_size = PROXMOX_BACKUP_DEFAULT_CHUNK_SIZE; // Hardcoded (4M)
|
|
|
|
firewall_name = "fw.conf";
|
|
|
|
|
2021-03-03 12:56:02 +03:00
|
|
|
+ bool use_dirty_bitmap = task->has_use_dirty_bitmap && task->use_dirty_bitmap;
|
2020-06-30 11:28:24 +03:00
|
|
|
+
|
|
|
|
char *pbs_err = NULL;
|
|
|
|
pbs = proxmox_backup_new(
|
|
|
|
task->backup_file,
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -672,7 +708,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-06-30 11:28:24 +03:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
- if (proxmox_backup_co_connect(pbs, task->errp) < 0)
|
|
|
|
+ int connect_result = proxmox_backup_co_connect(pbs, task->errp);
|
|
|
|
+ if (connect_result < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* register all devices */
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -683,9 +720,40 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-06-30 11:28:24 +03:00
|
|
|
|
|
|
|
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)
|
|
|
|
+ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(di->bs, PBS_BITMAP_NAME);
|
2021-03-03 12:56:02 +03:00
|
|
|
+ bool expect_only_dirty = false;
|
2020-06-30 11:28:24 +03:00
|
|
|
+
|
2021-03-03 12:56:02 +03:00
|
|
|
+ if (use_dirty_bitmap) {
|
2020-06-30 11:28:24 +03:00
|
|
|
+ if (bitmap == NULL) {
|
|
|
|
+ bitmap = bdrv_create_dirty_bitmap(di->bs, dump_cb_block_size, PBS_BITMAP_NAME, task->errp);
|
|
|
|
+ if (!bitmap) {
|
|
|
|
+ goto err;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
2021-03-03 12:56:02 +03:00
|
|
|
+ expect_only_dirty = proxmox_backup_check_incremental(pbs, devname, di->size) != 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (expect_only_dirty) {
|
2020-07-02 14:03:44 +03:00
|
|
|
+ dirty += bdrv_get_dirty_count(bitmap);
|
2021-03-03 12:56:02 +03:00
|
|
|
+ } else {
|
|
|
|
+ /* mark entire bitmap as dirty to make full backup */
|
|
|
|
+ bdrv_set_dirty_bitmap(bitmap, 0, di->size);
|
|
|
|
+ dirty += di->size;
|
2020-06-30 11:28:24 +03:00
|
|
|
+ }
|
|
|
|
+ di->bitmap = bitmap;
|
2021-03-03 12:56:02 +03:00
|
|
|
+ } else {
|
2020-07-02 14:03:44 +03:00
|
|
|
+ dirty += di->size;
|
2021-03-03 12:56:02 +03:00
|
|
|
+
|
|
|
|
+ /* after a full backup the old dirty bitmap is invalid anyway */
|
|
|
|
+ if (bitmap != NULL) {
|
|
|
|
+ bdrv_release_dirty_bitmap(bitmap);
|
|
|
|
+ }
|
2020-06-30 11:28:24 +03:00
|
|
|
+ }
|
|
|
|
+
|
2021-03-03 12:56:02 +03:00
|
|
|
+ int dev_id = proxmox_backup_co_register_image(pbs, devname, di->size, expect_only_dirty, task->errp);
|
2020-06-30 11:28:24 +03:00
|
|
|
+ if (dev_id < 0) {
|
|
|
|
goto err;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (!(di->target = bdrv_backup_dump_create(dump_cb_block_size, di->size, pvebackup_co_dump_pbs_cb, di, task->errp))) {
|
|
|
|
goto err;
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -694,6 +762,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-07-02 14:03:44 +03:00
|
|
|
di->dev_id = dev_id;
|
|
|
|
}
|
|
|
|
} else if (format == BACKUP_FORMAT_VMA) {
|
|
|
|
+ dirty = total;
|
|
|
|
+
|
|
|
|
vmaw = vma_writer_create(task->backup_file, uuid, &local_err);
|
|
|
|
if (!vmaw) {
|
|
|
|
if (local_err) {
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -721,6 +791,8 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-07-02 14:03:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (format == BACKUP_FORMAT_DIR) {
|
|
|
|
+ dirty = total;
|
|
|
|
+
|
|
|
|
if (mkdir(task->backup_file, 0640) != 0) {
|
|
|
|
error_setg_errno(task->errp, errno, "can't create directory '%s'\n",
|
|
|
|
task->backup_file);
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -793,8 +865,10 @@ static void coroutine_fn pvebackup_co_prepare(void *opaque)
|
2020-07-02 14:03:44 +03:00
|
|
|
char *uuid_str = g_strdup(backup_state.stat.uuid_str);
|
|
|
|
|
|
|
|
backup_state.stat.total = total;
|
|
|
|
+ backup_state.stat.dirty = dirty;
|
|
|
|
backup_state.stat.transferred = 0;
|
|
|
|
backup_state.stat.zero_bytes = 0;
|
2021-03-03 12:56:02 +03:00
|
|
|
+ backup_state.stat.reused = format == BACKUP_FORMAT_PBS && dirty >= total ? 0 : total - dirty;
|
2020-07-02 14:03:44 +03:00
|
|
|
|
|
|
|
qemu_mutex_unlock(&backup_state.stat.lock);
|
|
|
|
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -818,6 +892,10 @@ err:
|
2020-06-30 11:28:24 +03:00
|
|
|
PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data;
|
|
|
|
l = g_list_next(l);
|
|
|
|
|
|
|
|
+ if (di->bitmap) {
|
|
|
|
+ bdrv_release_dirty_bitmap(di->bitmap);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
if (di->target) {
|
|
|
|
bdrv_unref(di->target);
|
|
|
|
}
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -859,6 +937,7 @@ UuidInfo *qmp_backup(
|
2020-06-30 11:28:24 +03:00
|
|
|
bool has_fingerprint, const char *fingerprint,
|
|
|
|
bool has_backup_id, const char *backup_id,
|
|
|
|
bool has_backup_time, int64_t backup_time,
|
2021-03-03 12:56:02 +03:00
|
|
|
+ bool has_use_dirty_bitmap, bool use_dirty_bitmap,
|
2020-06-30 11:28:24 +03:00
|
|
|
bool has_format, BackupFormat format,
|
|
|
|
bool has_config_file, const char *config_file,
|
|
|
|
bool has_firewall_file, const char *firewall_file,
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -877,6 +956,8 @@ UuidInfo *qmp_backup(
|
2020-06-30 11:28:24 +03:00
|
|
|
.backup_id = backup_id,
|
|
|
|
.has_backup_time = has_backup_time,
|
|
|
|
.backup_time = backup_time,
|
2021-03-03 12:56:02 +03:00
|
|
|
+ .has_use_dirty_bitmap = has_use_dirty_bitmap,
|
|
|
|
+ .use_dirty_bitmap = use_dirty_bitmap,
|
2020-06-30 11:28:24 +03:00
|
|
|
.has_format = has_format,
|
|
|
|
.format = format,
|
|
|
|
.has_config_file = has_config_file,
|
2022-12-14 17:16:32 +03:00
|
|
|
@@ -945,10 +1026,14 @@ BackupStatus *qmp_query_backup(Error **errp)
|
2020-07-02 14:03:44 +03:00
|
|
|
|
|
|
|
info->has_total = true;
|
|
|
|
info->total = backup_state.stat.total;
|
|
|
|
+ info->has_dirty = true;
|
|
|
|
+ info->dirty = backup_state.stat.dirty;
|
|
|
|
info->has_zero_bytes = true;
|
|
|
|
info->zero_bytes = backup_state.stat.zero_bytes;
|
|
|
|
info->has_transferred = true;
|
|
|
|
info->transferred = backup_state.stat.transferred;
|
|
|
|
+ info->has_reused = true;
|
|
|
|
+ info->reused = backup_state.stat.reused;
|
|
|
|
|
|
|
|
qemu_mutex_unlock(&backup_state.stat.lock);
|
|
|
|
|
2020-06-30 11:28:24 +03:00
|
|
|
diff --git a/qapi/block-core.json b/qapi/block-core.json
|
2022-12-14 17:16:32 +03:00
|
|
|
index c3b6b93472..992e6c1e3f 100644
|
2020-06-30 11:28:24 +03:00
|
|
|
--- a/qapi/block-core.json
|
|
|
|
+++ b/qapi/block-core.json
|
update submodule and patches to 7.1.0
Notable changes:
* The only big change is the switch to using a custom QIOChannel for
savevm-async, because the previously used QEMUFileOps was dropped.
Changes to the current implementation:
* Switch to vector based methods as required for an IO channel. For
short reads the passed-in IO vector is stuffed with zeroes at the
end, just to be sure.
* For reading: The documentation in include/io/channel.h states that
at least one byte should be read, so also error out when whe are
at the very end instead of returning 0.
* For reading: Fix off-by-one error when request goes beyond end.
The wrong code piece was:
if ((pos + size) > maxlen) {
size = maxlen - pos - 1;
}
Previously, the last byte would not be read. It's actually
possible to get a snapshot .raw file that has content all the way
up the final 512 byte (= BDRV_SECTOR_SIZE) boundary without any
trailing zero bytes (I wrote a script to do it).
Luckily, it didn't cause a real issue, because qemu_loadvm_state()
is not interested in the final (i.e. QEMU_VM_VMDESCRIPTION)
section. The buffer for reading it is simply freed up afterwards
and the function will assume that it read the whole section, even
if that's not the case.
* For writing: Make use of the generated blk_pwritev() wrapper
instead of manually wrapping the coroutine to simplify and save a
few lines.
* Adapt to changed interfaces for blk_{pread,pwrite}:
* a9262f551e ("block: Change blk_{pread,pwrite}() param order")
* 3b35d4542c ("block: Add a 'flags' param to blk_pread()")
* bf5b16fa40 ("block: Make blk_{pread,pwrite}() return 0 on success")
Those changes especially affected the qemu-img dd patches, because
the context also changed, but also some of our block drivers used
the functions.
* Drop qemu-common.h include: it got renamed after essentially
everything was moved to other headers. The only remaining user I
could find for things dropped from the header between 7.0 and 7.1
was qemu_get_vm_name() in the iscsi-initiatorname patch, but it
already includes the header to which the function was moved.
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-10-14 15:07:13 +03:00
|
|
|
@@ -753,8 +753,13 @@
|
2020-07-02 14:03:44 +03:00
|
|
|
#
|
|
|
|
# @total: total amount of bytes involved in the backup process
|
|
|
|
#
|
2021-03-03 12:56:02 +03:00
|
|
|
+# @dirty: with incremental mode (PBS) this is the amount of bytes involved
|
2020-07-02 14:03:44 +03:00
|
|
|
+# in the backup process which are marked dirty.
|
|
|
|
+#
|
|
|
|
# @transferred: amount of bytes already backed up.
|
|
|
|
#
|
|
|
|
+# @reused: amount of bytes reused due to deduplication.
|
|
|
|
+#
|
|
|
|
# @zero-bytes: amount of 'zero' bytes detected.
|
|
|
|
#
|
|
|
|
# @start-time: time (epoch) when backup job started.
|
update submodule and patches to 7.1.0
Notable changes:
* The only big change is the switch to using a custom QIOChannel for
savevm-async, because the previously used QEMUFileOps was dropped.
Changes to the current implementation:
* Switch to vector based methods as required for an IO channel. For
short reads the passed-in IO vector is stuffed with zeroes at the
end, just to be sure.
* For reading: The documentation in include/io/channel.h states that
at least one byte should be read, so also error out when whe are
at the very end instead of returning 0.
* For reading: Fix off-by-one error when request goes beyond end.
The wrong code piece was:
if ((pos + size) > maxlen) {
size = maxlen - pos - 1;
}
Previously, the last byte would not be read. It's actually
possible to get a snapshot .raw file that has content all the way
up the final 512 byte (= BDRV_SECTOR_SIZE) boundary without any
trailing zero bytes (I wrote a script to do it).
Luckily, it didn't cause a real issue, because qemu_loadvm_state()
is not interested in the final (i.e. QEMU_VM_VMDESCRIPTION)
section. The buffer for reading it is simply freed up afterwards
and the function will assume that it read the whole section, even
if that's not the case.
* For writing: Make use of the generated blk_pwritev() wrapper
instead of manually wrapping the coroutine to simplify and save a
few lines.
* Adapt to changed interfaces for blk_{pread,pwrite}:
* a9262f551e ("block: Change blk_{pread,pwrite}() param order")
* 3b35d4542c ("block: Add a 'flags' param to blk_pread()")
* bf5b16fa40 ("block: Make blk_{pread,pwrite}() return 0 on success")
Those changes especially affected the qemu-img dd patches, because
the context also changed, but also some of our block drivers used
the functions.
* Drop qemu-common.h include: it got renamed after essentially
everything was moved to other headers. The only remaining user I
could find for things dropped from the header between 7.0 and 7.1
was qemu_get_vm_name() in the iscsi-initiatorname patch, but it
already includes the header to which the function was moved.
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-10-14 15:07:13 +03:00
|
|
|
@@ -767,8 +772,8 @@
|
2020-07-02 14:03:44 +03:00
|
|
|
#
|
|
|
|
##
|
|
|
|
{ 'struct': 'BackupStatus',
|
|
|
|
- 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int',
|
|
|
|
- '*transferred': 'int', '*zero-bytes': 'int',
|
|
|
|
+ 'data': {'*status': 'str', '*errmsg': 'str', '*total': 'int', '*dirty': 'int',
|
|
|
|
+ '*transferred': 'int', '*zero-bytes': 'int', '*reused': 'int',
|
|
|
|
'*start-time': 'int', '*end-time': 'int',
|
|
|
|
'*backup-file': 'str', '*uuid': 'str' } }
|
|
|
|
|
update submodule and patches to 7.1.0
Notable changes:
* The only big change is the switch to using a custom QIOChannel for
savevm-async, because the previously used QEMUFileOps was dropped.
Changes to the current implementation:
* Switch to vector based methods as required for an IO channel. For
short reads the passed-in IO vector is stuffed with zeroes at the
end, just to be sure.
* For reading: The documentation in include/io/channel.h states that
at least one byte should be read, so also error out when whe are
at the very end instead of returning 0.
* For reading: Fix off-by-one error when request goes beyond end.
The wrong code piece was:
if ((pos + size) > maxlen) {
size = maxlen - pos - 1;
}
Previously, the last byte would not be read. It's actually
possible to get a snapshot .raw file that has content all the way
up the final 512 byte (= BDRV_SECTOR_SIZE) boundary without any
trailing zero bytes (I wrote a script to do it).
Luckily, it didn't cause a real issue, because qemu_loadvm_state()
is not interested in the final (i.e. QEMU_VM_VMDESCRIPTION)
section. The buffer for reading it is simply freed up afterwards
and the function will assume that it read the whole section, even
if that's not the case.
* For writing: Make use of the generated blk_pwritev() wrapper
instead of manually wrapping the coroutine to simplify and save a
few lines.
* Adapt to changed interfaces for blk_{pread,pwrite}:
* a9262f551e ("block: Change blk_{pread,pwrite}() param order")
* 3b35d4542c ("block: Add a 'flags' param to blk_pread()")
* bf5b16fa40 ("block: Make blk_{pread,pwrite}() return 0 on success")
Those changes especially affected the qemu-img dd patches, because
the context also changed, but also some of our block drivers used
the functions.
* Drop qemu-common.h include: it got renamed after essentially
everything was moved to other headers. The only remaining user I
could find for things dropped from the header between 7.0 and 7.1
was qemu_get_vm_name() in the iscsi-initiatorname patch, but it
already includes the header to which the function was moved.
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-10-14 15:07:13 +03:00
|
|
|
@@ -811,6 +816,8 @@
|
2020-06-30 11:28:24 +03:00
|
|
|
#
|
|
|
|
# @backup-time: backup timestamp (Unix epoch, required for format 'pbs')
|
|
|
|
#
|
2021-03-03 12:56:02 +03:00
|
|
|
+# @use-dirty-bitmap: use dirty bitmap to detect incremental changes since last job (optional for format 'pbs')
|
2020-06-30 11:28:24 +03:00
|
|
|
+#
|
|
|
|
# Returns: the uuid of the backup job
|
|
|
|
#
|
|
|
|
##
|
update submodule and patches to 7.1.0
Notable changes:
* The only big change is the switch to using a custom QIOChannel for
savevm-async, because the previously used QEMUFileOps was dropped.
Changes to the current implementation:
* Switch to vector based methods as required for an IO channel. For
short reads the passed-in IO vector is stuffed with zeroes at the
end, just to be sure.
* For reading: The documentation in include/io/channel.h states that
at least one byte should be read, so also error out when whe are
at the very end instead of returning 0.
* For reading: Fix off-by-one error when request goes beyond end.
The wrong code piece was:
if ((pos + size) > maxlen) {
size = maxlen - pos - 1;
}
Previously, the last byte would not be read. It's actually
possible to get a snapshot .raw file that has content all the way
up the final 512 byte (= BDRV_SECTOR_SIZE) boundary without any
trailing zero bytes (I wrote a script to do it).
Luckily, it didn't cause a real issue, because qemu_loadvm_state()
is not interested in the final (i.e. QEMU_VM_VMDESCRIPTION)
section. The buffer for reading it is simply freed up afterwards
and the function will assume that it read the whole section, even
if that's not the case.
* For writing: Make use of the generated blk_pwritev() wrapper
instead of manually wrapping the coroutine to simplify and save a
few lines.
* Adapt to changed interfaces for blk_{pread,pwrite}:
* a9262f551e ("block: Change blk_{pread,pwrite}() param order")
* 3b35d4542c ("block: Add a 'flags' param to blk_pread()")
* bf5b16fa40 ("block: Make blk_{pread,pwrite}() return 0 on success")
Those changes especially affected the qemu-img dd patches, because
the context also changed, but also some of our block drivers used
the functions.
* Drop qemu-common.h include: it got renamed after essentially
everything was moved to other headers. The only remaining user I
could find for things dropped from the header between 7.0 and 7.1
was qemu_get_vm_name() in the iscsi-initiatorname patch, but it
already includes the header to which the function was moved.
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2022-10-14 15:07:13 +03:00
|
|
|
@@ -821,6 +828,7 @@
|
2020-06-30 11:28:24 +03:00
|
|
|
'*fingerprint': 'str',
|
|
|
|
'*backup-id': 'str',
|
|
|
|
'*backup-time': 'int',
|
2021-03-03 12:56:02 +03:00
|
|
|
+ '*use-dirty-bitmap': 'bool',
|
2020-06-30 11:28:24 +03:00
|
|
|
'*format': 'BackupFormat',
|
|
|
|
'*config-file': 'str',
|
|
|
|
'*firewall-file': 'str',
|