@@ -47,6 +47,7 @@ typedef struct BackupBlockJob {
uint64_t sectors_read;
unsigned long *done_bitmap;
int64_t cluster_size;
+ bool compress;
NotifierWithReturn before_write;
QLIST_HEAD(, CowRequest) inflight_reqs;
} BackupBlockJob;
@@ -154,7 +155,8 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwritev(job->target, start * job->cluster_size,
- bounce_qiov.size, &bounce_qiov, 0);
+ bounce_qiov.size, &bounce_qiov,
+ job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
}
if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret);
@@ -477,6 +479,7 @@ static void coroutine_fn backup_run(void *opaque)
void backup_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
+ bool compress,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque,
@@ -507,6 +510,12 @@ void backup_start(const char *job_id, BlockDriverState *bs,
return;
}
+ if (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;
+ }
+
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
return;
}
@@ -555,6 +564,7 @@ void backup_start(const char *job_id, BlockDriverState *bs,
job->sync_mode = sync_mode;
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
sync_bitmap : NULL;
+ job->compress = compress;
/* If there is no backing file on the target, we cannot rely on COW if our
* backup cluster size is smaller than the target cluster size. Even for
@@ -3132,6 +3132,9 @@ static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp)
if (!backup->has_job_id) {
backup->job_id = NULL;
}
+ if (!backup->has_compress) {
+ backup->compress = false;
+ }
bs = qmp_get_root_bs(backup->device, errp);
if (!bs) {
@@ -3210,8 +3213,8 @@ static void do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, Error **errp)
}
backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
- bmap, backup->on_source_error, backup->on_target_error,
- block_job_cb, bs, txn, &local_err);
+ bmap, backup->compress, backup->on_source_error,
+ backup->on_target_error, block_job_cb, bs, txn, &local_err);
bdrv_unref(target_bs);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -3277,7 +3280,7 @@ void do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error **errp)
}
}
backup_start(backup->job_id, bs, target_bs, backup->speed, backup->sync,
- NULL, backup->on_source_error, backup->on_target_error,
+ NULL, false, backup->on_source_error, backup->on_target_error,
block_job_cb, bs, txn, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@@ -1182,8 +1182,8 @@ ETEXI
{
.name = "drive_backup",
- .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?",
- .params = "[-n] [-f] device target [format]",
+ .args_type = "reuse:-n,full:-f,compress:-c,device:B,target:s,format:s?",
+ .params = "[-n] [-f] [-c] device target [format]",
.help = "initiates a point-in-time\n\t\t\t"
"copy for a device. The device's contents are\n\t\t\t"
"copied to the new image file, excluding data that\n\t\t\t"
@@ -1191,7 +1191,9 @@ ETEXI
"The -n flag requests QEMU to reuse the image found\n\t\t\t"
"in new-image-file, instead of recreating it from scratch.\n\t\t\t"
"The -f flag requests QEMU to copy the whole disk,\n\t\t\t"
- "so that the result does not need a backing file.\n\t\t\t",
+ "so that the result does not need a backing file.\n\t\t\t"
+ "The -c flag requests QEMU to compress backup data\n\t\t\t"
+ "(if the target format supports it).\n\t\t\t",
.mhandler.cmd = hmp_drive_backup,
},
STEXI
@@ -1109,6 +1109,7 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
const char *format = qdict_get_try_str(qdict, "format");
bool reuse = qdict_get_try_bool(qdict, "reuse", false);
bool full = qdict_get_try_bool(qdict, "full", false);
+ bool compress = qdict_get_try_bool(qdict, "compress", false);
Error *err = NULL;
DriveBackup backup = {
.device = (char *)device,
@@ -1118,6 +1119,8 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
.sync = full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
.has_mode = true,
.mode = reuse ? NEW_IMAGE_MODE_EXISTING : NEW_IMAGE_MODE_ABSOLUTE_PATHS,
+ .has_compress = !!compress,
+ .compress = compress,
};
if (!filename) {
@@ -765,6 +765,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
void backup_start(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
+ bool compress,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockCompletionFunc *cb, void *opaque,
@@ -898,6 +898,9 @@
# Must be present if sync is "incremental", must NOT be present
# otherwise. (Since 2.4)
#
+# @compress: #optional true to compress data, if the target format supports it.
+# (default: false) (since 2.7)
+#
# @on-source-error: #optional the action to take on an error on the source,
# default 'report'. 'stop' and 'enospc' can only be used
# if the block device supports io-status (see BlockInfo).
@@ -915,7 +918,7 @@
{ 'struct': 'DriveBackup',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
'*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
- '*speed': 'int', '*bitmap': 'str',
+ '*speed': 'int', '*bitmap': 'str', '*compress': 'bool',
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError' } }
@@ -1217,7 +1217,8 @@ EQMP
{
.name = "drive-backup",
.args_type = "job-id:s?,sync:s,device:B,target:s,speed:i?,mode:s?,"
- "format:s?,bitmap:s?,on-source-error:s?,on-target-error:s?",
+ "format:s?,bitmap:s?,compress:b?,"
+ "on-source-error:s?,on-target-error:s?",
.mhandler.cmd_new = qmp_marshal_drive_backup,
},
@@ -1253,6 +1254,8 @@ Arguments:
- "mode": whether and how QEMU should create a new image
(NewImageMode, optional, default 'absolute-paths')
- "speed": the maximum speed, in bytes per second (json-int, optional)
+- "compress": true to compress data, if the target format supports it.
+ (json-bool, optional, default false)
- "on-source-error": the action to take on an error on the source, default
'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status.