}
}
-static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp)
+static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
{
- bool bps_flag;
- bool iops_flag;
-
- assert(io_limits);
-
- bps_flag = (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] != 0)
- && ((io_limits->bps[BLOCK_IO_LIMIT_READ] != 0)
- || (io_limits->bps[BLOCK_IO_LIMIT_WRITE] != 0));
- iops_flag = (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] != 0)
- && ((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");
+ if (throttle_conflicting(cfg)) {
+ error_setg(errp, "bps/iops/max total values and read/write values"
+ " 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");
+ if (!throttle_is_valid(cfg)) {
+ error_setg(errp, "bps/iops/maxs values must be 0 or greater");
return false;
}
return true;
}
-DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
+static DriveInfo *blockdev_init(QemuOpts *all_opts,
+ BlockInterfaceType block_default_type)
{
const char *buf;
const char *file = NULL;
enum { MEDIA_DISK, MEDIA_CDROM } media;
int bus_id, unit_id;
int cyls, heads, secs, translation;
- BlockDriver *drv = NULL;
int max_devs;
int index;
int ro = 0;
int on_read_error, on_write_error;
const char *devaddr;
DriveInfo *dinfo;
- BlockIOLimit io_limits;
+ ThrottleConfig cfg;
int snapshot = 0;
bool copy_on_read;
int ret;
QemuOpts *opts;
QDict *bs_opts;
const char *id;
+ bool has_driver_specific_opts;
+ BlockDriver *drv = NULL;
translation = BIOS_ATA_TRANSLATION_AUTO;
media = MEDIA_DISK;
qdict_del(bs_opts, "id");
}
+ has_driver_specific_opts = !!qdict_size(bs_opts);
+
/* extract parameters */
bus_id = qemu_opt_get_number(opts, "bus", 0);
unit_id = qemu_opt_get_number(opts, "unit", -1);
secs = qemu_opt_get_number(opts, "secs", 0);
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
- ro = qemu_opt_get_bool(opts, "readonly", 0);
+ ro = qemu_opt_get_bool(opts, "read-only", 0);
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
file = qemu_opt_get(opts, "file");
}
}
- bdrv_flags |= BDRV_O_CACHE_WB;
- if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
- if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
- error_report("invalid cache option");
- return NULL;
- }
+ if (qemu_opt_get_bool(opts, "cache.writeback", true)) {
+ bdrv_flags |= BDRV_O_CACHE_WB;
+ }
+ if (qemu_opt_get_bool(opts, "cache.direct", false)) {
+ bdrv_flags |= BDRV_O_NOCACHE;
+ }
+ if (qemu_opt_get_bool(opts, "cache.no-flush", true)) {
+ bdrv_flags |= BDRV_O_NO_FLUSH;
}
#ifdef CONFIG_LINUX_AIO
error_printf("\n");
return NULL;
}
+
drv = bdrv_find_whitelisted_format(buf, ro);
if (!drv) {
- error_report("'%s' invalid format", buf);
+ if (!ro && bdrv_find_whitelisted_format(buf, !ro)) {
+ error_report("'%s' can be only used as read-only device.", buf);
+ } else {
+ error_report("'%s' invalid format", buf);
+ }
return NULL;
}
}
/* disk I/O throttling */
- io_limits.bps[BLOCK_IO_LIMIT_TOTAL] =
- qemu_opt_get_number(opts, "bps", 0);
- io_limits.bps[BLOCK_IO_LIMIT_READ] =
- qemu_opt_get_number(opts, "bps_rd", 0);
- io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
- qemu_opt_get_number(opts, "bps_wr", 0);
- io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
- qemu_opt_get_number(opts, "iops", 0);
- io_limits.iops[BLOCK_IO_LIMIT_READ] =
- qemu_opt_get_number(opts, "iops_rd", 0);
- io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
- qemu_opt_get_number(opts, "iops_wr", 0);
-
- if (!do_check_io_limits(&io_limits, &error)) {
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buckets[THROTTLE_BPS_TOTAL].avg =
+ qemu_opt_get_number(opts, "throttling.bps-total", 0);
+ cfg.buckets[THROTTLE_BPS_READ].avg =
+ qemu_opt_get_number(opts, "throttling.bps-read", 0);
+ cfg.buckets[THROTTLE_BPS_WRITE].avg =
+ qemu_opt_get_number(opts, "throttling.bps-write", 0);
+ cfg.buckets[THROTTLE_OPS_TOTAL].avg =
+ qemu_opt_get_number(opts, "throttling.iops-total", 0);
+ cfg.buckets[THROTTLE_OPS_READ].avg =
+ qemu_opt_get_number(opts, "throttling.iops-read", 0);
+ cfg.buckets[THROTTLE_OPS_WRITE].avg =
+ qemu_opt_get_number(opts, "throttling.iops-write", 0);
+
+ cfg.buckets[THROTTLE_BPS_TOTAL].max =
+ qemu_opt_get_number(opts, "throttling.bps-total-max", 0);
+ cfg.buckets[THROTTLE_BPS_READ].max =
+ qemu_opt_get_number(opts, "throttling.bps-read-max", 0);
+ cfg.buckets[THROTTLE_BPS_WRITE].max =
+ qemu_opt_get_number(opts, "throttling.bps-write-max", 0);
+ cfg.buckets[THROTTLE_OPS_TOTAL].max =
+ qemu_opt_get_number(opts, "throttling.iops-total-max", 0);
+ cfg.buckets[THROTTLE_OPS_READ].max =
+ qemu_opt_get_number(opts, "throttling.iops-read-max", 0);
+ cfg.buckets[THROTTLE_OPS_WRITE].max =
+ qemu_opt_get_number(opts, "throttling.iops-write-max", 0);
+
+ cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
+
+ if (!check_throttle_config(&cfg, &error)) {
error_report("%s", error_get_pretty(error));
error_free(error);
return NULL;
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
/* disk I/O throttling */
- bdrv_set_io_limits(dinfo->bdrv, &io_limits);
+ if (throttle_enabled(&cfg)) {
+ bdrv_io_limits_enable(dinfo->bdrv);
+ bdrv_set_io_limits(dinfo->bdrv, &cfg);
+ }
switch(type) {
case IF_IDE:
abort();
}
if (!file || !*file) {
- if (qdict_size(bs_opts)) {
+ if (has_driver_specific_opts) {
file = NULL;
} else {
return dinfo;
} else if (ro == 1) {
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
type != IF_NONE && type != IF_PFLASH) {
- error_report("readonly not supported by this bus type");
+ error_report("read-only not supported by this bus type");
goto err;
}
}
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
if (ro && copy_on_read) {
- error_report("warning: disabling copy_on_read on readonly drive");
+ error_report("warning: disabling copy_on_read on read-only drive");
}
+ QINCREF(bs_opts);
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv);
- bs_opts = NULL;
if (ret < 0) {
if (ret == -EMEDIUMTYPE) {
error_report("could not open disk image %s: not in %s format",
- file ?: dinfo->id, drv->format_name);
+ file ?: dinfo->id, drv ? drv->format_name :
+ qdict_get_str(bs_opts, "driver"));
} else {
error_report("could not open disk image %s: %s",
file ?: dinfo->id, strerror(-ret));
if (bdrv_key_required(dinfo->bdrv))
autostart = 0;
+ QDECREF(bs_opts);
qemu_opts_del(opts);
return dinfo;
return NULL;
}
+static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to)
+{
+ const char *value;
+
+ value = qemu_opt_get(opts, from);
+ if (value) {
+ qemu_opt_set(opts, to, value);
+ qemu_opt_unset(opts, from);
+ }
+}
+
+DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
+{
+ const char *value;
+
+ /* Change legacy command line options into QMP ones */
+ qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
+ qemu_opt_rename(all_opts, "iops_rd", "throttling.iops-read");
+ qemu_opt_rename(all_opts, "iops_wr", "throttling.iops-write");
+
+ qemu_opt_rename(all_opts, "bps", "throttling.bps-total");
+ qemu_opt_rename(all_opts, "bps_rd", "throttling.bps-read");
+ qemu_opt_rename(all_opts, "bps_wr", "throttling.bps-write");
+
+ qemu_opt_rename(all_opts, "iops_max", "throttling.iops-total-max");
+ qemu_opt_rename(all_opts, "iops_rd_max", "throttling.iops-read-max");
+ qemu_opt_rename(all_opts, "iops_wr_max", "throttling.iops-write-max");
+
+ qemu_opt_rename(all_opts, "bps_max", "throttling.bps-total-max");
+ qemu_opt_rename(all_opts, "bps_rd_max", "throttling.bps-read-max");
+ qemu_opt_rename(all_opts, "bps_wr_max", "throttling.bps-write-max");
+
+ qemu_opt_rename(all_opts,
+ "iops_size", "throttling.iops-size");
+
+ qemu_opt_rename(all_opts, "readonly", "read-only");
+
+ value = qemu_opt_get(all_opts, "cache");
+ if (value) {
+ int flags = 0;
+
+ if (bdrv_parse_cache_flags(value, &flags) != 0) {
+ error_report("invalid cache option");
+ return NULL;
+ }
+
+ /* Specific options take precedence */
+ if (!qemu_opt_get(all_opts, "cache.writeback")) {
+ qemu_opt_set_bool(all_opts, "cache.writeback",
+ !!(flags & BDRV_O_CACHE_WB));
+ }
+ if (!qemu_opt_get(all_opts, "cache.direct")) {
+ qemu_opt_set_bool(all_opts, "cache.direct",
+ !!(flags & BDRV_O_NOCACHE));
+ }
+ if (!qemu_opt_get(all_opts, "cache.no-flush")) {
+ qemu_opt_set_bool(all_opts, "cache.no-flush",
+ !!(flags & BDRV_O_NO_FLUSH));
+ }
+ qemu_opt_unset(all_opts, "cache");
+ }
+
+ return blockdev_init(all_opts, block_default_type);
+}
+
void do_commit(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
qmp_drive_backup(backup->device, backup->target,
backup->has_format, backup->format,
+ backup->sync,
backup->has_mode, backup->mode,
backup->has_speed, backup->speed,
backup->has_on_source_error, backup->on_source_error,
static void abort_commit(BlkTransactionState *common)
{
- assert(false); /* this action never succeeds */
+ g_assert_not_reached(); /* this action never succeeds */
}
static const BdrvActionOps actions[] = {
/* throttling disk I/O limits */
void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
- int64_t bps_wr, int64_t iops, int64_t iops_rd,
- int64_t iops_wr, Error **errp)
+ int64_t bps_wr,
+ int64_t iops,
+ int64_t iops_rd,
+ int64_t iops_wr,
+ bool has_bps_max,
+ int64_t bps_max,
+ bool has_bps_rd_max,
+ int64_t bps_rd_max,
+ bool has_bps_wr_max,
+ int64_t bps_wr_max,
+ bool has_iops_max,
+ int64_t iops_max,
+ bool has_iops_rd_max,
+ int64_t iops_rd_max,
+ bool has_iops_wr_max,
+ int64_t iops_wr_max,
+ bool has_iops_size,
+ int64_t iops_size, Error **errp)
{
- BlockIOLimit io_limits;
+ ThrottleConfig cfg;
BlockDriverState *bs;
bs = bdrv_find(device);
return;
}
- io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = bps;
- io_limits.bps[BLOCK_IO_LIMIT_READ] = bps_rd;
- io_limits.bps[BLOCK_IO_LIMIT_WRITE] = bps_wr;
- io_limits.iops[BLOCK_IO_LIMIT_TOTAL]= iops;
- io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
- io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
+ cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
+ cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
- if (!do_check_io_limits(&io_limits, errp)) {
- return;
+ cfg.buckets[THROTTLE_OPS_TOTAL].avg = iops;
+ cfg.buckets[THROTTLE_OPS_READ].avg = iops_rd;
+ cfg.buckets[THROTTLE_OPS_WRITE].avg = iops_wr;
+
+ if (has_bps_max) {
+ cfg.buckets[THROTTLE_BPS_TOTAL].max = bps_max;
+ }
+ if (has_bps_rd_max) {
+ cfg.buckets[THROTTLE_BPS_READ].max = bps_rd_max;
+ }
+ if (has_bps_wr_max) {
+ cfg.buckets[THROTTLE_BPS_WRITE].max = bps_wr_max;
+ }
+ if (has_iops_max) {
+ cfg.buckets[THROTTLE_OPS_TOTAL].max = iops_max;
+ }
+ if (has_iops_rd_max) {
+ cfg.buckets[THROTTLE_OPS_READ].max = iops_rd_max;
+ }
+ if (has_iops_wr_max) {
+ cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
}
- bs->io_limits = io_limits;
+ if (has_iops_size) {
+ cfg.op_size = iops_size;
+ }
- if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) {
+ if (!check_throttle_config(&cfg, errp)) {
+ return;
+ }
+
+ if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
bdrv_io_limits_enable(bs);
- } else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) {
+ } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
bdrv_io_limits_disable(bs);
- } else {
- if (bs->block_timer) {
- qemu_mod_timer(bs->block_timer, qemu_get_clock_ns(vm_clock));
- }
+ }
+
+ if (bs->io_limits_enabled) {
+ bdrv_set_io_limits(bs, &cfg);
}
}
void qmp_drive_backup(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_on_source_error, BlockdevOnError on_source_error,
{
BlockDriverState *bs;
BlockDriverState *target_bs;
+ BlockDriverState *source = NULL;
BlockDriver *drv = NULL;
Error *local_err = NULL;
int flags;
flags = bs->open_flags | BDRV_O_RDWR;
+ /* See if we have a backing HD we can use to create our new image
+ * on top of. */
+ if (sync == MIRROR_SYNC_MODE_TOP) {
+ source = bs->backing_hd;
+ if (!source) {
+ sync = MIRROR_SYNC_MODE_FULL;
+ }
+ }
+ if (sync == MIRROR_SYNC_MODE_NONE) {
+ source = bs;
+ }
+
size = bdrv_getlength(bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
if (mode != NEW_IMAGE_MODE_EXISTING) {
assert(format && drv);
- bdrv_img_create(target, format,
- NULL, NULL, NULL, size, flags, &local_err, false);
+ if (source) {
+ bdrv_img_create(target, format, source->filename,
+ source->drv->format_name, NULL,
+ size, flags, &local_err, false);
+ } else {
+ bdrv_img_create(target, format, NULL, NULL, NULL,
+ size, flags, &local_err, false);
+ }
}
if (error_is_set(&local_err)) {
return;
}
- backup_start(bs, target_bs, speed, on_source_error, on_target_error,
+ backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
block_job_cb, bs, &local_err);
if (local_err != NULL) {
bdrv_delete(target_bs);
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
},{
- .name = "cache",
- .type = QEMU_OPT_STRING,
- .help = "host cache usage (none, writeback, writethrough, "
- "directsync, unsafe)",
+ .name = "cache.writeback",
+ .type = QEMU_OPT_BOOL,
+ .help = "enables writeback mode for any caches",
+ },{
+ .name = "cache.direct",
+ .type = QEMU_OPT_BOOL,
+ .help = "enables use of O_DIRECT (bypass the host page cache)",
+ },{
+ .name = "cache.no-flush",
+ .type = QEMU_OPT_BOOL,
+ .help = "ignore any flush requests for the device",
},{
.name = "aio",
.type = QEMU_OPT_STRING,
.type = QEMU_OPT_STRING,
.help = "pci address (virtio only)",
},{
- .name = "readonly",
+ .name = "read-only",
.type = QEMU_OPT_BOOL,
.help = "open drive file as read-only",
},{
- .name = "iops",
+ .name = "throttling.iops-total",
.type = QEMU_OPT_NUMBER,
.help = "limit total I/O operations per second",
},{
- .name = "iops_rd",
+ .name = "throttling.iops-read",
.type = QEMU_OPT_NUMBER,
.help = "limit read operations per second",
},{
- .name = "iops_wr",
+ .name = "throttling.iops-write",
.type = QEMU_OPT_NUMBER,
.help = "limit write operations per second",
},{
- .name = "bps",
+ .name = "throttling.bps-total",
.type = QEMU_OPT_NUMBER,
.help = "limit total bytes per second",
},{
- .name = "bps_rd",
+ .name = "throttling.bps-read",
.type = QEMU_OPT_NUMBER,
.help = "limit read bytes per second",
},{
- .name = "bps_wr",
+ .name = "throttling.bps-write",
.type = QEMU_OPT_NUMBER,
.help = "limit write bytes per second",
+ },{
+ .name = "throttling.iops-total-max",
+ .type = QEMU_OPT_NUMBER,
+ .help = "I/O operations burst",
+ },{
+ .name = "throttling.iops-read-max",
+ .type = QEMU_OPT_NUMBER,
+ .help = "I/O operations read burst",
+ },{
+ .name = "throttling.iops-write-max",
+ .type = QEMU_OPT_NUMBER,
+ .help = "I/O operations write burst",
+ },{
+ .name = "throttling.bps-total-max",
+ .type = QEMU_OPT_NUMBER,
+ .help = "total bytes burst",
+ },{
+ .name = "throttling.bps-read-max",
+ .type = QEMU_OPT_NUMBER,
+ .help = "total bytes read burst",
+ },{
+ .name = "throttling.bps-write-max",
+ .type = QEMU_OPT_NUMBER,
+ .help = "total bytes write burst",
+ },{
+ .name = "throttling.iops-size",
+ .type = QEMU_OPT_NUMBER,
+ .help = "when limiting by iops max size of an I/O in bytes",
},{
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,