static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
extern QemuOptsList qemu_common_drive_opts;
+extern QemuOptsList qemu_old_drive_opts;
static const char *const if_name[IF_COUNT] = {
[IF_NONE] = "none",
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;
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);
+ qemu_opt_get_number(opts, "throttling.bps-total", 0);
io_limits.bps[BLOCK_IO_LIMIT_READ] =
- qemu_opt_get_number(opts, "bps_rd", 0);
+ qemu_opt_get_number(opts, "throttling.bps-read", 0);
io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
- qemu_opt_get_number(opts, "bps_wr", 0);
+ qemu_opt_get_number(opts, "throttling.bps-write", 0);
io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
- qemu_opt_get_number(opts, "iops", 0);
+ qemu_opt_get_number(opts, "throttling.iops-total", 0);
io_limits.iops[BLOCK_IO_LIMIT_READ] =
- qemu_opt_get_number(opts, "iops_rd", 0);
+ qemu_opt_get_number(opts, "throttling.iops-read", 0);
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
- qemu_opt_get_number(opts, "iops_wr", 0);
+ qemu_opt_get_number(opts, "throttling.iops-write", 0);
if (!do_check_io_limits(&io_limits, &error)) {
error_report("%s", error_get_pretty(error));
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;
+
+ /*
+ * Check that only old options are used by copying into a QemuOpts with
+ * stricter checks. Going through a QDict seems to be the easiest way to
+ * achieve this...
+ */
+ QemuOpts* check_opts;
+ QDict *qdict;
+ Error *local_err = NULL;
+
+ qdict = qemu_opts_to_qdict(all_opts, NULL);
+ check_opts = qemu_opts_from_qdict(&qemu_old_drive_opts, qdict, &local_err);
+ QDECREF(qdict);
+
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return NULL;
+ }
+ qemu_opts_del(check_opts);
+
+ /* 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, "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");
static void abort_commit(BlkTransactionState *common)
{
- assert(false); /* this action never succeeds */
+ g_assert_not_reached(); /* this action never succeeds */
}
static const BdrvActionOps actions[] = {
bdrv_io_limits_disable(bs);
} else {
if (bs->block_timer) {
- qemu_mod_timer(bs->block_timer, qemu_get_clock_ns(vm_clock));
+ timer_mod(bs->block_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
}
}
}
{
BlockDriverState *bs;
BlockDriverState *target_bs;
+ BlockDriverState *source = NULL;
BlockDriver *drv = NULL;
Error *local_err = NULL;
int flags;
int64_t size;
int ret;
- if (sync != MIRROR_SYNC_MODE_FULL) {
- error_setg(errp, "only sync mode 'full' is currently supported");
- return;
- }
if (!has_speed) {
speed = 0;
}
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);
QemuOptsList qemu_common_drive_opts = {
.name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
+ .desc = {
+ {
+ .name = "bus",
+ .type = QEMU_OPT_NUMBER,
+ .help = "bus number",
+ },{
+ .name = "unit",
+ .type = QEMU_OPT_NUMBER,
+ .help = "unit number (i.e. lun for scsi)",
+ },{
+ .name = "if",
+ .type = QEMU_OPT_STRING,
+ .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
+ },{
+ .name = "index",
+ .type = QEMU_OPT_NUMBER,
+ .help = "index number",
+ },{
+ .name = "cyls",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of cylinders (ide disk geometry)",
+ },{
+ .name = "heads",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of heads (ide disk geometry)",
+ },{
+ .name = "secs",
+ .type = QEMU_OPT_NUMBER,
+ .help = "number of sectors (ide disk geometry)",
+ },{
+ .name = "trans",
+ .type = QEMU_OPT_STRING,
+ .help = "chs translation (auto, lba. none)",
+ },{
+ .name = "media",
+ .type = QEMU_OPT_STRING,
+ .help = "media type (disk, cdrom)",
+ },{
+ .name = "snapshot",
+ .type = QEMU_OPT_BOOL,
+ .help = "enable/disable snapshot mode",
+ },{
+ .name = "file",
+ .type = QEMU_OPT_STRING,
+ .help = "disk image",
+ },{
+ .name = "discard",
+ .type = QEMU_OPT_STRING,
+ .help = "discard operation (ignore/off, unmap/on)",
+ },{
+ .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,
+ .help = "host AIO implementation (threads, native)",
+ },{
+ .name = "format",
+ .type = QEMU_OPT_STRING,
+ .help = "disk format (raw, qcow2, ...)",
+ },{
+ .name = "serial",
+ .type = QEMU_OPT_STRING,
+ .help = "disk serial number",
+ },{
+ .name = "rerror",
+ .type = QEMU_OPT_STRING,
+ .help = "read error action",
+ },{
+ .name = "werror",
+ .type = QEMU_OPT_STRING,
+ .help = "write error action",
+ },{
+ .name = "addr",
+ .type = QEMU_OPT_STRING,
+ .help = "pci address (virtio only)",
+ },{
+ .name = "read-only",
+ .type = QEMU_OPT_BOOL,
+ .help = "open drive file as read-only",
+ },{
+ .name = "throttling.iops-total",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit total I/O operations per second",
+ },{
+ .name = "throttling.iops-read",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit read operations per second",
+ },{
+ .name = "throttling.iops-write",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit write operations per second",
+ },{
+ .name = "throttling.bps-total",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit total bytes per second",
+ },{
+ .name = "throttling.bps-read",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit read bytes per second",
+ },{
+ .name = "throttling.bps-write",
+ .type = QEMU_OPT_NUMBER,
+ .help = "limit write bytes per second",
+ },{
+ .name = "copy-on-read",
+ .type = QEMU_OPT_BOOL,
+ .help = "copy read data from backing file into image file",
+ },{
+ .name = "boot",
+ .type = QEMU_OPT_BOOL,
+ .help = "(deprecated, ignored)",
+ },
+ { /* end of list */ }
+ },
+};
+
+QemuOptsList qemu_old_drive_opts = {
+ .name = "drive",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_old_drive_opts.head),
.desc = {
{
.name = "bus",