}
}
-static bool do_check_io_limits(BlockIOLimit *io_limits)
+static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp)
{
bool bps_flag;
bool iops_flag;
&& ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
|| (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
if (bps_flag || iops_flag) {
+ error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
+ "cannot be used at the same time");
+ return false;
+ }
+
+ if (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] < 0 ||
+ io_limits->bps[BLOCK_IO_LIMIT_WRITE] < 0 ||
+ io_limits->bps[BLOCK_IO_LIMIT_READ] < 0 ||
+ io_limits->iops[BLOCK_IO_LIMIT_TOTAL] < 0 ||
+ io_limits->iops[BLOCK_IO_LIMIT_WRITE] < 0 ||
+ io_limits->iops[BLOCK_IO_LIMIT_READ] < 0) {
+ error_setg(errp, "bps and iops values must be 0 or greater");
return false;
}
int snapshot = 0;
bool copy_on_read;
int ret;
+ Error *error = NULL;
translation = BIOS_ATA_TRANSLATION_AUTO;
media = MEDIA_DISK;
}
}
+ if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
+ if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
+ error_report("invalid discard option");
+ return NULL;
+ }
+ }
+
bdrv_flags |= BDRV_O_CACHE_WB;
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
qemu_opt_get_number(opts, "iops_wr", 0);
- if (!do_check_io_limits(&io_limits)) {
- error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
- "cannot be used at the same time");
+ if (!do_check_io_limits(&io_limits, &error)) {
+ error_report("%s", error_get_pretty(error));
+ error_free(error);
return NULL;
}
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
if (ret < 0) {
- error_report("could not open disk image %s: %s",
- file, strerror(-ret));
+ if (ret == -EMEDIUMTYPE) {
+ error_report("could not open disk image %s: not in %s format",
+ file, drv->format_name);
+ } else {
+ error_report("could not open disk image %s: %s",
+ file, strerror(-ret));
+ }
goto err;
}
bdrv_img_create(new_image_file, format,
states->old_bs->filename,
states->old_bs->drv->format_name,
- NULL, -1, flags, &local_err);
+ NULL, -1, flags, &local_err, false);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto delete_and_fail;
io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
- if (!do_check_io_limits(&io_limits)) {
- error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
+ if (!do_check_io_limits(&io_limits, errp)) {
return;
}
drive_get_ref(drive_get_by_blockdev(bs));
}
+#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
+
void qmp_drive_mirror(const char *device, const char *target,
bool has_format, const char *format,
enum MirrorSyncMode sync,
bool has_mode, enum NewImageMode mode,
bool has_speed, int64_t speed,
+ bool has_granularity, uint32_t granularity,
+ bool has_buf_size, int64_t buf_size,
bool has_on_source_error, BlockdevOnError on_source_error,
bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp)
{
- BlockDriverInfo bdi;
BlockDriverState *bs;
BlockDriverState *source, *target_bs;
BlockDriver *proto_drv;
if (!has_mode) {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
+ if (!has_granularity) {
+ granularity = 0;
+ }
+ if (!has_buf_size) {
+ buf_size = DEFAULT_MIRROR_BUF_SIZE;
+ }
+
+ if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
+ error_set(errp, QERR_INVALID_PARAMETER, device);
+ return;
+ }
+ if (granularity & (granularity - 1)) {
+ error_set(errp, QERR_INVALID_PARAMETER, device);
+ return;
+ }
bs = bdrv_find(device);
if (!bs) {
return;
}
+ bdrv_get_geometry(bs, &size);
+ size *= 512;
if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) {
/* create new image w/o backing file */
assert(format && drv);
- bdrv_get_geometry(bs, &size);
- size *= 512;
bdrv_img_create(target, format,
- NULL, NULL, NULL, size, flags, &local_err);
+ NULL, NULL, NULL, size, flags, &local_err, false);
} else {
switch (mode) {
case NEW_IMAGE_MODE_EXISTING:
bdrv_img_create(target, format,
source->filename,
source->drv->format_name,
- NULL, -1, flags, &local_err);
+ NULL, size, flags, &local_err, false);
break;
default:
abort();
return;
}
+ /* Mirroring takes care of copy-on-write using the source's backing
+ * file.
+ */
target_bs = bdrv_new("");
ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv);
return;
}
- /* We need a backing file if we will copy parts of a cluster. */
- if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 &&
- bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) {
- ret = bdrv_open_backing_file(target_bs);
- if (ret < 0) {
- bdrv_delete(target_bs);
- error_set(errp, QERR_OPEN_FILE_FAILED, target);
- return;
- }
- }
-
- mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
+ mirror_start(bs, target_bs, speed, granularity, buf_size, sync,
+ on_source_error, on_target_error,
block_job_cb, bs, &local_err);
if (local_err != NULL) {
bdrv_delete(target_bs);
.name = "file",
.type = QEMU_OPT_STRING,
.help = "disk image",
+ },{
+ .name = "discard",
+ .type = QEMU_OPT_STRING,
+ .help = "discard operation (ignore/off, unmap/on)",
},{
.name = "cache",
.type = QEMU_OPT_STRING,