]> Git Repo - qemu.git/blobdiff - blockdev.c
vl.c: extend -m option to support options for memory hotplug
[qemu.git] / blockdev.c
index 80605a2bac38be4fc90ed3d449af53168d3a39ec..9b0f8ac45ea0c6f6cd5e79c3101f51d96f4a9c0d 100644 (file)
 #include "hw/block/block.h"
 #include "block/blockjob.h"
 #include "monitor/monitor.h"
-#include "qapi/qmp/qerror.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
 #include "qapi/qmp/types.h"
+#include "qapi-visit.h"
+#include "qapi/qmp-output-visitor.h"
 #include "sysemu/sysemu.h"
 #include "block/block_int.h"
 #include "qmp-commands.h"
@@ -45,7 +46,6 @@
 #include "sysemu/arch_init.h"
 
 static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
-extern QemuOptsList qemu_common_drive_opts;
 
 static const char *const if_name[IF_COUNT] = {
     [IF_NONE] = "none",
@@ -89,6 +89,10 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
 {
     DriveInfo *dinfo = drive_get_by_blockdev(bs);
 
+    if (dinfo && !dinfo->enable_auto_del) {
+        return;
+    }
+
     if (bs->job) {
         block_job_cancel(bs->job);
     }
@@ -102,7 +106,7 @@ void blockdev_auto_del(BlockDriverState *bs)
     DriveInfo *dinfo = drive_get_by_blockdev(bs);
 
     if (dinfo && dinfo->auto_del) {
-        drive_put_ref(dinfo);
+        drive_del(dinfo);
     }
 }
 
@@ -209,9 +213,12 @@ static void bdrv_format_print(void *opaque, const char *name)
     error_printf(" %s", name);
 }
 
-static void drive_uninit(DriveInfo *dinfo)
+void drive_del(DriveInfo *dinfo)
 {
-    qemu_opts_del(dinfo->opts);
+    if (dinfo->opts) {
+        qemu_opts_del(dinfo->opts);
+    }
+
     bdrv_unref(dinfo->bdrv);
     g_free(dinfo->id);
     QTAILQ_REMOVE(&drives, dinfo, next);
@@ -219,19 +226,6 @@ static void drive_uninit(DriveInfo *dinfo)
     g_free(dinfo);
 }
 
-void drive_put_ref(DriveInfo *dinfo)
-{
-    assert(dinfo->refcount);
-    if (--dinfo->refcount == 0) {
-        drive_uninit(dinfo);
-    }
-}
-
-void drive_get_ref(DriveInfo *dinfo)
-{
-    dinfo->refcount++;
-}
-
 typedef struct {
     QEMUBH *bh;
     BlockDriverState *bs;
@@ -263,7 +257,7 @@ static void bdrv_put_ref_bh_schedule(BlockDriverState *bs)
     qemu_bh_schedule(s->bh);
 }
 
-static int parse_block_error_action(const char *buf, bool is_read)
+static int parse_block_error_action(const char *buf, bool is_read, Error **errp)
 {
     if (!strcmp(buf, "ignore")) {
         return BLOCKDEV_ON_ERROR_IGNORE;
@@ -274,12 +268,31 @@ static int parse_block_error_action(const char *buf, bool is_read)
     } else if (!strcmp(buf, "report")) {
         return BLOCKDEV_ON_ERROR_REPORT;
     } else {
-        error_report("'%s' invalid %s error action",
-                     buf, is_read ? "read" : "write");
+        error_setg(errp, "'%s' invalid %s error action",
+                   buf, is_read ? "read" : "write");
         return -1;
     }
 }
 
+static inline int parse_enum_option(const char *lookup[], const char *buf,
+                                    int max, int def, Error **errp)
+{
+    int i;
+
+    if (!buf) {
+        return def;
+    }
+
+    for (i = 0; i < max; i++) {
+        if (!strcmp(buf, lookup[i])) {
+            return i;
+        }
+    }
+
+    error_setg(errp, "invalid parameter value: %s", buf);
+    return def;
+}
+
 static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
 {
     if (throttle_conflicting(cfg)) {
@@ -296,23 +309,16 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
     return true;
 }
 
-static DriveInfo *blockdev_init(QemuOpts *all_opts,
-                                BlockInterfaceType block_default_type)
+typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType;
+
+/* Takes the ownership of bs_opts */
+static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
+                                Error **errp)
 {
     const char *buf;
-    const char *file = NULL;
-    const char *serial;
-    const char *mediastr = "";
-    BlockInterfaceType type;
-    enum { MEDIA_DISK, MEDIA_CDROM } media;
-    int bus_id, unit_id;
-    int cyls, heads, secs, translation;
-    int max_devs;
-    int index;
     int ro = 0;
     int bdrv_flags = 0;
     int on_read_error, on_write_error;
-    const char *devaddr;
     DriveInfo *dinfo;
     ThrottleConfig cfg;
     int snapshot = 0;
@@ -320,31 +326,24 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
     int ret;
     Error *error = NULL;
     QemuOpts *opts;
-    QDict *bs_opts;
     const char *id;
     bool has_driver_specific_opts;
+    BlockdevDetectZeroesOptions detect_zeroes;
     BlockDriver *drv = NULL;
 
-    translation = BIOS_ATA_TRANSLATION_AUTO;
-    media = MEDIA_DISK;
-
-    /* Check common options by copying from all_opts to opts, all other options
-     * are stored in bs_opts. */
-    id = qemu_opts_id(all_opts);
+    /* Check common options by copying from bs_opts to opts, all other options
+     * stay in bs_opts for processing by bdrv_open(). */
+    id = qdict_get_try_str(bs_opts, "id");
     opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
-    if (error_is_set(&error)) {
-        qerror_report_err(error);
-        error_free(error);
-        return NULL;
+    if (error) {
+        error_propagate(errp, error);
+        goto err_no_opts;
     }
 
-    bs_opts = qdict_new();
-    qemu_opts_to_qdict(all_opts, bs_opts);
     qemu_opts_absorb_qdict(opts, bs_opts, &error);
-    if (error_is_set(&error)) {
-        qerror_report_err(error);
-        error_free(error);
-        return NULL;
+    if (error) {
+        error_propagate(errp, error);
+        goto early_err;
     }
 
     if (id) {
@@ -354,86 +353,14 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
     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);
-    index   = qemu_opt_get_number(opts, "index", -1);
-
-    cyls  = qemu_opt_get_number(opts, "cyls", 0);
-    heads = qemu_opt_get_number(opts, "heads", 0);
-    secs  = qemu_opt_get_number(opts, "secs", 0);
-
     snapshot = qemu_opt_get_bool(opts, "snapshot", 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");
-    serial = qemu_opt_get(opts, "serial");
-
-    if ((buf = qemu_opt_get(opts, "if")) != NULL) {
-        for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
-            ;
-        if (type == IF_COUNT) {
-            error_report("unsupported bus type '%s'", buf);
-            return NULL;
-       }
-    } else {
-        type = block_default_type;
-    }
-
-    max_devs = if_max_devs[type];
-
-    if (cyls || heads || secs) {
-        if (cyls < 1) {
-            error_report("invalid physical cyls number");
-           return NULL;
-       }
-        if (heads < 1) {
-            error_report("invalid physical heads number");
-           return NULL;
-       }
-        if (secs < 1) {
-            error_report("invalid physical secs number");
-           return NULL;
-       }
-    }
-
-    if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
-        if (!cyls) {
-            error_report("'%s' trans must be used with cyls, heads and secs",
-                         buf);
-            return NULL;
-        }
-        if (!strcmp(buf, "none"))
-            translation = BIOS_ATA_TRANSLATION_NONE;
-        else if (!strcmp(buf, "lba"))
-            translation = BIOS_ATA_TRANSLATION_LBA;
-        else if (!strcmp(buf, "auto"))
-            translation = BIOS_ATA_TRANSLATION_AUTO;
-       else {
-            error_report("'%s' invalid translation type", buf);
-           return NULL;
-       }
-    }
-
-    if ((buf = qemu_opt_get(opts, "media")) != NULL) {
-        if (!strcmp(buf, "disk")) {
-           media = MEDIA_DISK;
-       } else if (!strcmp(buf, "cdrom")) {
-            if (cyls || secs || heads) {
-                error_report("CHS can't be set with media=%s", buf);
-               return NULL;
-            }
-           media = MEDIA_CDROM;
-       } else {
-           error_report("'%s' invalid media", buf);
-           return NULL;
-       }
-    }
-
     if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
         if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
-            error_report("invalid discard option");
-            return NULL;
+            error_setg(errp, "invalid discard option");
+            goto early_err;
         }
     }
 
@@ -443,7 +370,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
     if (qemu_opt_get_bool(opts, "cache.direct", false)) {
         bdrv_flags |= BDRV_O_NOCACHE;
     }
-    if (qemu_opt_get_bool(opts, "cache.no-flush", true)) {
+    if (qemu_opt_get_bool(opts, "cache.no-flush", false)) {
         bdrv_flags |= BDRV_O_NO_FLUSH;
     }
 
@@ -454,8 +381,8 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
         } else if (!strcmp(buf, "threads")) {
             /* this is the default */
         } else {
-           error_report("invalid aio option");
-           return NULL;
+           error_setg(errp, "invalid aio option");
+           goto early_err;
         }
     }
 #endif
@@ -465,17 +392,13 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
             error_printf("Supported formats:");
             bdrv_iterate_format(bdrv_format_print, NULL);
             error_printf("\n");
-            return NULL;
+            goto early_err;
         }
 
-        drv = bdrv_find_whitelisted_format(buf, ro);
+        drv = bdrv_find_format(buf);
         if (!drv) {
-            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;
+            error_setg(errp, "'%s' invalid format", buf);
+            goto early_err;
         }
     }
 
@@ -510,127 +433,57 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
     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;
-    }
-
-    if (qemu_opt_get(opts, "boot") != NULL) {
-        fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
-                "ignored. Future versions will reject this parameter. Please "
-                "update your scripts.\n");
+        error_propagate(errp, error);
+        goto early_err;
     }
 
     on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
     if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
-        if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
-            error_report("werror is not supported by this bus type");
-            return NULL;
-        }
-
-        on_write_error = parse_block_error_action(buf, 0);
-        if (on_write_error < 0) {
-            return NULL;
+        on_write_error = parse_block_error_action(buf, 0, &error);
+        if (error) {
+            error_propagate(errp, error);
+            goto early_err;
         }
     }
 
     on_read_error = BLOCKDEV_ON_ERROR_REPORT;
     if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
-        if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
-            error_report("rerror is not supported by this bus type");
-            return NULL;
-        }
-
-        on_read_error = parse_block_error_action(buf, 1);
-        if (on_read_error < 0) {
-            return NULL;
+        on_read_error = parse_block_error_action(buf, 1, &error);
+        if (error) {
+            error_propagate(errp, error);
+            goto early_err;
         }
     }
 
-    if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
-        if (type != IF_VIRTIO) {
-            error_report("addr is not supported by this bus type");
-            return NULL;
-        }
+    detect_zeroes =
+        parse_enum_option(BlockdevDetectZeroesOptions_lookup,
+                          qemu_opt_get(opts, "detect-zeroes"),
+                          BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
+                          BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
+                          &error);
+    if (error) {
+        error_propagate(errp, error);
+        goto early_err;
     }
 
-    /* compute bus and unit according index */
-
-    if (index != -1) {
-        if (bus_id != 0 || unit_id != -1) {
-            error_report("index cannot be used with bus and unit");
-            return NULL;
-        }
-        bus_id = drive_index_to_bus_id(type, index);
-        unit_id = drive_index_to_unit_id(type, index);
-    }
-
-    /* if user doesn't specify a unit_id,
-     * try to find the first free
-     */
-
-    if (unit_id == -1) {
-       unit_id = 0;
-       while (drive_get(type, bus_id, unit_id) != NULL) {
-           unit_id++;
-           if (max_devs && unit_id >= max_devs) {
-               unit_id -= max_devs;
-               bus_id++;
-           }
-       }
-    }
-
-    /* check unit id */
-
-    if (max_devs && unit_id >= max_devs) {
-        error_report("unit %d too big (max is %d)",
-                     unit_id, max_devs - 1);
-        return NULL;
-    }
-
-    /*
-     * catch multiple definitions
-     */
-
-    if (drive_get(type, bus_id, unit_id) != NULL) {
-        error_report("drive with bus=%d, unit=%d (index=%d) exists",
-                     bus_id, unit_id, index);
-        return NULL;
+    if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
+        !(bdrv_flags & BDRV_O_UNMAP)) {
+        error_setg(errp, "setting detect-zeroes to unmap is not allowed "
+                         "without setting discard operation to unmap");
+        goto early_err;
     }
 
     /* init */
-
     dinfo = g_malloc0(sizeof(*dinfo));
-    if ((buf = qemu_opts_id(opts)) != NULL) {
-        dinfo->id = g_strdup(buf);
-    } else {
-        /* no id supplied -> create one */
-        dinfo->id = g_malloc0(32);
-        if (type == IF_IDE || type == IF_SCSI)
-            mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
-        if (max_devs)
-            snprintf(dinfo->id, 32, "%s%i%s%i",
-                     if_name[type], bus_id, mediastr, unit_id);
-        else
-            snprintf(dinfo->id, 32, "%s%s%i",
-                     if_name[type], mediastr, unit_id);
-    }
-    dinfo->bdrv = bdrv_new(dinfo->id);
+    dinfo->id = g_strdup(qemu_opts_id(opts));
+    dinfo->bdrv = bdrv_new(dinfo->id, &error);
+    if (error) {
+        error_propagate(errp, error);
+        goto bdrv_new_err;
+    }
     dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
     dinfo->bdrv->read_only = ro;
-    dinfo->devaddr = devaddr;
-    dinfo->type = type;
-    dinfo->bus = bus_id;
-    dinfo->unit = unit_id;
-    dinfo->cyls = cyls;
-    dinfo->heads = heads;
-    dinfo->secs = secs;
-    dinfo->trans = translation;
-    dinfo->opts = all_opts;
-    dinfo->refcount = 1;
-    if (serial != NULL) {
-        dinfo->serial = g_strdup(serial);
-    }
+    dinfo->bdrv->detect_zeroes = detect_zeroes;
     QTAILQ_INSERT_TAIL(&drives, dinfo, next);
 
     bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
@@ -641,40 +494,12 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
         bdrv_set_io_limits(dinfo->bdrv, &cfg);
     }
 
-    switch(type) {
-    case IF_IDE:
-    case IF_SCSI:
-    case IF_XEN:
-    case IF_NONE:
-        dinfo->media_cd = media == MEDIA_CDROM;
-        break;
-    case IF_SD:
-    case IF_FLOPPY:
-    case IF_PFLASH:
-    case IF_MTD:
-        break;
-    case IF_VIRTIO:
-    {
-        /* add virtio block device */
-        QemuOpts *devopts;
-        devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
-        if (arch_type == QEMU_ARCH_S390X) {
-            qemu_opt_set(devopts, "driver", "virtio-blk-s390");
-        } else {
-            qemu_opt_set(devopts, "driver", "virtio-blk-pci");
-        }
-        qemu_opt_set(devopts, "drive", dinfo->id);
-        if (devaddr)
-            qemu_opt_set(devopts, "addr", devaddr);
-        break;
-    }
-    default:
-        abort();
-    }
     if (!file || !*file) {
         if (has_driver_specific_opts) {
             file = NULL;
         } else {
+            QDECREF(bs_opts);
+            qemu_opts_del(opts);
             return dinfo;
         }
     }
@@ -688,117 +513,463 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
         bdrv_flags |= BDRV_O_COPY_ON_READ;
     }
 
-    if (runstate_check(RUN_STATE_INMIGRATE)) {
-        bdrv_flags |= BDRV_O_INCOMING;
+    if (runstate_check(RUN_STATE_INMIGRATE)) {
+        bdrv_flags |= BDRV_O_INCOMING;
+    }
+
+    bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
+
+    QINCREF(bs_opts);
+    ret = bdrv_open(&dinfo->bdrv, file, NULL, bs_opts, bdrv_flags, drv, &error);
+
+    if (ret < 0) {
+        error_setg(errp, "could not open disk image %s: %s",
+                   file ?: dinfo->id, error_get_pretty(error));
+        error_free(error);
+        goto err;
+    }
+
+    if (bdrv_key_required(dinfo->bdrv))
+        autostart = 0;
+
+    QDECREF(bs_opts);
+    qemu_opts_del(opts);
+
+    return dinfo;
+
+err:
+    bdrv_unref(dinfo->bdrv);
+    QTAILQ_REMOVE(&drives, dinfo, next);
+bdrv_new_err:
+    g_free(dinfo->id);
+    g_free(dinfo);
+early_err:
+    qemu_opts_del(opts);
+err_no_opts:
+    QDECREF(bs_opts);
+    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);
+    }
+}
+
+QemuOptsList qemu_legacy_drive_opts = {
+    .name = "drive",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_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 = "index",
+            .type = QEMU_OPT_NUMBER,
+            .help = "index number",
+        },{
+            .name = "media",
+            .type = QEMU_OPT_STRING,
+            .help = "media type (disk, cdrom)",
+        },{
+            .name = "if",
+            .type = QEMU_OPT_STRING,
+            .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
+        },{
+            .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 = "boot",
+            .type = QEMU_OPT_BOOL,
+            .help = "(deprecated, ignored)",
+        },{
+            .name = "addr",
+            .type = QEMU_OPT_STRING,
+            .help = "pci address (virtio only)",
+        },{
+            .name = "serial",
+            .type = QEMU_OPT_STRING,
+            .help = "disk serial number",
+        },{
+            .name = "file",
+            .type = QEMU_OPT_STRING,
+            .help = "file name",
+        },
+
+        /* Options that are passed on, but have special semantics with -drive */
+        {
+            .name = "read-only",
+            .type = QEMU_OPT_BOOL,
+            .help = "open drive file as read-only",
+        },{
+            .name = "rerror",
+            .type = QEMU_OPT_STRING,
+            .help = "read error action",
+        },{
+            .name = "werror",
+            .type = QEMU_OPT_STRING,
+            .help = "write error action",
+        },{
+            .name = "copy-on-read",
+            .type = QEMU_OPT_BOOL,
+            .help = "copy read data from backing file into image file",
+        },
+
+        { /* end of list */ }
+    },
+};
+
+DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
+{
+    const char *value;
+    DriveInfo *dinfo = NULL;
+    QDict *bs_opts;
+    QemuOpts *legacy_opts;
+    DriveMediaType media = MEDIA_DISK;
+    BlockInterfaceType type;
+    int cyls, heads, secs, translation;
+    int max_devs, bus_id, unit_id, index;
+    const char *devaddr;
+    const char *werror, *rerror;
+    bool read_only = false;
+    bool copy_on_read;
+    const char *serial;
+    const char *filename;
+    Error *local_err = NULL;
+
+    /* 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");
+    }
+
+    /* Get a QDict for processing the options */
+    bs_opts = qdict_new();
+    qemu_opts_to_qdict(all_opts, bs_opts);
+
+    legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0,
+                                   &error_abort);
+    qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
+    if (local_err) {
+        error_report("%s", error_get_pretty(local_err));
+        error_free(local_err);
+        goto fail;
+    }
+
+    /* Deprecated option boot=[on|off] */
+    if (qemu_opt_get(legacy_opts, "boot") != NULL) {
+        fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
+                "ignored. Future versions will reject this parameter. Please "
+                "update your scripts.\n");
+    }
+
+    /* Media type */
+    value = qemu_opt_get(legacy_opts, "media");
+    if (value) {
+        if (!strcmp(value, "disk")) {
+            media = MEDIA_DISK;
+        } else if (!strcmp(value, "cdrom")) {
+            media = MEDIA_CDROM;
+            read_only = true;
+        } else {
+            error_report("'%s' invalid media", value);
+            goto fail;
+        }
+    }
+
+    /* copy-on-read is disabled with a warning for read-only devices */
+    read_only |= qemu_opt_get_bool(legacy_opts, "read-only", false);
+    copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false);
+
+    if (read_only && copy_on_read) {
+        error_report("warning: disabling copy-on-read on read-only drive");
+        copy_on_read = false;
+    }
+
+    qdict_put(bs_opts, "read-only",
+              qstring_from_str(read_only ? "on" : "off"));
+    qdict_put(bs_opts, "copy-on-read",
+              qstring_from_str(copy_on_read ? "on" :"off"));
+
+    /* Controller type */
+    value = qemu_opt_get(legacy_opts, "if");
+    if (value) {
+        for (type = 0;
+             type < IF_COUNT && strcmp(value, if_name[type]);
+             type++) {
+        }
+        if (type == IF_COUNT) {
+            error_report("unsupported bus type '%s'", value);
+            goto fail;
+        }
+    } else {
+        type = block_default_type;
+    }
+
+    /* Geometry */
+    cyls  = qemu_opt_get_number(legacy_opts, "cyls", 0);
+    heads = qemu_opt_get_number(legacy_opts, "heads", 0);
+    secs  = qemu_opt_get_number(legacy_opts, "secs", 0);
+
+    if (cyls || heads || secs) {
+        if (cyls < 1) {
+            error_report("invalid physical cyls number");
+            goto fail;
+        }
+        if (heads < 1) {
+            error_report("invalid physical heads number");
+            goto fail;
+        }
+        if (secs < 1) {
+            error_report("invalid physical secs number");
+            goto fail;
+        }
+    }
+
+    translation = BIOS_ATA_TRANSLATION_AUTO;
+    value = qemu_opt_get(legacy_opts, "trans");
+    if (value != NULL) {
+        if (!cyls) {
+            error_report("'%s' trans must be used with cyls, heads and secs",
+                         value);
+            goto fail;
+        }
+        if (!strcmp(value, "none")) {
+            translation = BIOS_ATA_TRANSLATION_NONE;
+        } else if (!strcmp(value, "lba")) {
+            translation = BIOS_ATA_TRANSLATION_LBA;
+        } else if (!strcmp(value, "large")) {
+            translation = BIOS_ATA_TRANSLATION_LARGE;
+        } else if (!strcmp(value, "rechs")) {
+            translation = BIOS_ATA_TRANSLATION_RECHS;
+        } else if (!strcmp(value, "auto")) {
+            translation = BIOS_ATA_TRANSLATION_AUTO;
+        } else {
+            error_report("'%s' invalid translation type", value);
+            goto fail;
+        }
+    }
+
+    if (media == MEDIA_CDROM) {
+        if (cyls || secs || heads) {
+            error_report("CHS can't be set with media=cdrom");
+            goto fail;
+        }
+    }
+
+    /* Device address specified by bus/unit or index.
+     * If none was specified, try to find the first free one. */
+    bus_id  = qemu_opt_get_number(legacy_opts, "bus", 0);
+    unit_id = qemu_opt_get_number(legacy_opts, "unit", -1);
+    index   = qemu_opt_get_number(legacy_opts, "index", -1);
+
+    max_devs = if_max_devs[type];
+
+    if (index != -1) {
+        if (bus_id != 0 || unit_id != -1) {
+            error_report("index cannot be used with bus and unit");
+            goto fail;
+        }
+        bus_id = drive_index_to_bus_id(type, index);
+        unit_id = drive_index_to_unit_id(type, index);
+    }
+
+    if (unit_id == -1) {
+       unit_id = 0;
+       while (drive_get(type, bus_id, unit_id) != NULL) {
+           unit_id++;
+           if (max_devs && unit_id >= max_devs) {
+               unit_id -= max_devs;
+               bus_id++;
+           }
+       }
+    }
+
+    if (max_devs && unit_id >= max_devs) {
+        error_report("unit %d too big (max is %d)", unit_id, max_devs - 1);
+        goto fail;
     }
 
-    if (media == MEDIA_CDROM) {
-        /* CDROM is fine for any interface, don't check.  */
-        ro = 1;
-    } else if (ro == 1) {
-        if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
-            type != IF_NONE && type != IF_PFLASH) {
-            error_report("read-only not supported by this bus type");
-            goto err;
-        }
+    if (drive_get(type, bus_id, unit_id) != NULL) {
+        error_report("drive with bus=%d, unit=%d (index=%d) exists",
+                     bus_id, unit_id, index);
+        goto fail;
     }
 
-    bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
+    /* Serial number */
+    serial = qemu_opt_get(legacy_opts, "serial");
 
-    if (ro && copy_on_read) {
-        error_report("warning: disabling copy_on_read on read-only drive");
+    /* no id supplied -> create one */
+    if (qemu_opts_id(all_opts) == NULL) {
+        char *new_id;
+        const char *mediastr = "";
+        if (type == IF_IDE || type == IF_SCSI) {
+            mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
+        }
+        if (max_devs) {
+            new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id,
+                                     mediastr, unit_id);
+        } else {
+            new_id = g_strdup_printf("%s%s%i", if_name[type],
+                                     mediastr, unit_id);
+        }
+        qdict_put(bs_opts, "id", qstring_from_str(new_id));
+        g_free(new_id);
     }
 
-    QINCREF(bs_opts);
-    ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
-
-    if (ret < 0) {
-        error_report("could not open disk image %s: %s",
-                     file ?: dinfo->id, error_get_pretty(error));
-        goto err;
+    /* Add virtio block device */
+    devaddr = qemu_opt_get(legacy_opts, "addr");
+    if (devaddr && type != IF_VIRTIO) {
+        error_report("addr is not supported by this bus type");
+        goto fail;
     }
 
-    if (bdrv_key_required(dinfo->bdrv))
-        autostart = 0;
-
-    QDECREF(bs_opts);
-    qemu_opts_del(opts);
-
-    return dinfo;
-
-err:
-    qemu_opts_del(opts);
-    QDECREF(bs_opts);
-    bdrv_unref(dinfo->bdrv);
-    g_free(dinfo->id);
-    QTAILQ_REMOVE(&drives, dinfo, next);
-    g_free(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);
+    if (type == IF_VIRTIO) {
+        QemuOpts *devopts;
+        devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+                                   &error_abort);
+        if (arch_type == QEMU_ARCH_S390X) {
+            qemu_opt_set(devopts, "driver", "virtio-blk-s390");
+        } else {
+            qemu_opt_set(devopts, "driver", "virtio-blk-pci");
+        }
+        qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"));
+        if (devaddr) {
+            qemu_opt_set(devopts, "addr", devaddr);
+        }
     }
-}
-
-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");
+    filename = qemu_opt_get(legacy_opts, "file");
 
-    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");
+    /* Check werror/rerror compatibility with if=... */
+    werror = qemu_opt_get(legacy_opts, "werror");
+    if (werror != NULL) {
+        if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO &&
+            type != IF_NONE) {
+            error_report("werror is not supported by this bus type");
+            goto fail;
+        }
+        qdict_put(bs_opts, "werror", qstring_from_str(werror));
+    }
 
-    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");
+    rerror = qemu_opt_get(legacy_opts, "rerror");
+    if (rerror != NULL) {
+        if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI &&
+            type != IF_NONE) {
+            error_report("rerror is not supported by this bus type");
+            goto fail;
+        }
+        qdict_put(bs_opts, "rerror", qstring_from_str(rerror));
+    }
 
-    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");
+    /* Actual block device init: Functionality shared with blockdev-add */
+    dinfo = blockdev_init(filename, bs_opts, &local_err);
+    bs_opts = NULL;
+    if (dinfo == NULL) {
+        if (local_err) {
+            error_report("%s", error_get_pretty(local_err));
+            error_free(local_err);
+        }
+        goto fail;
+    } else {
+        assert(!local_err);
+    }
 
-    qemu_opt_rename(all_opts,
-                    "iops_size", "throttling.iops-size");
+    /* Set legacy DriveInfo fields */
+    dinfo->enable_auto_del = true;
+    dinfo->opts = all_opts;
 
-    qemu_opt_rename(all_opts, "readonly", "read-only");
+    dinfo->cyls = cyls;
+    dinfo->heads = heads;
+    dinfo->secs = secs;
+    dinfo->trans = translation;
 
-    value = qemu_opt_get(all_opts, "cache");
-    if (value) {
-        int flags = 0;
+    dinfo->type = type;
+    dinfo->bus = bus_id;
+    dinfo->unit = unit_id;
+    dinfo->devaddr = devaddr;
 
-        if (bdrv_parse_cache_flags(value, &flags) != 0) {
-            error_report("invalid cache option");
-            return NULL;
-        }
+    dinfo->serial = g_strdup(serial);
 
-        /* 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");
+    switch(type) {
+    case IF_IDE:
+    case IF_SCSI:
+    case IF_XEN:
+    case IF_NONE:
+        dinfo->media_cd = media == MEDIA_CDROM;
+        break;
+    default:
+        break;
     }
 
-    return blockdev_init(all_opts, block_default_type);
+fail:
+    qemu_opts_del(legacy_opts);
+    QDECREF(bs_opts);
+    return dinfo;
 }
 
 void do_commit(Monitor *mon, const QDict *qdict)
@@ -835,14 +1006,22 @@ static void blockdev_do_action(int kind, void *data, Error **errp)
     qmp_transaction(&list, errp);
 }
 
-void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
+void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
+                                bool has_node_name, const char *node_name,
+                                const char *snapshot_file,
+                                bool has_snapshot_node_name,
+                                const char *snapshot_node_name,
                                 bool has_format, const char *format,
-                                bool has_mode, enum NewImageMode mode,
-                                Error **errp)
+                                bool has_mode, NewImageMode mode, Error **errp)
 {
     BlockdevSnapshot snapshot = {
+        .has_device = has_device,
         .device = (char *) device,
+        .has_node_name = has_node_name,
+        .node_name = (char *) node_name,
         .snapshot_file = (char *) snapshot_file,
+        .has_snapshot_node_name = has_snapshot_node_name,
+        .snapshot_node_name = (char *) snapshot_node_name,
         .has_format = has_format,
         .format = (char *) format,
         .has_mode = has_mode,
@@ -897,7 +1076,7 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
     }
 
     ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
-    if (error_is_set(&local_err)) {
+    if (local_err) {
         error_propagate(errp, local_err);
         return NULL;
     }
@@ -910,7 +1089,7 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
     }
 
     bdrv_snapshot_delete(bs, id, name, &local_err);
-    if (error_is_set(&local_err)) {
+    if (local_err) {
         error_propagate(errp, local_err);
         return NULL;
     }
@@ -967,6 +1146,7 @@ typedef struct InternalSnapshotState {
 static void internal_snapshot_prepare(BlkTransactionState *common,
                                       Error **errp)
 {
+    Error *local_err = NULL;
     const char *device;
     const char *name;
     BlockDriverState *bs;
@@ -1015,8 +1195,10 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
     }
 
     /* check whether a snapshot with name exist */
-    ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, errp);
-    if (error_is_set(errp)) {
+    ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn,
+                                            &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
         return;
     } else if (ret) {
         error_setg(errp,
@@ -1080,8 +1262,14 @@ static void external_snapshot_prepare(BlkTransactionState *common,
 {
     BlockDriver *drv;
     int flags, ret;
+    QDict *options = NULL;
     Error *local_err = NULL;
+    bool has_device = false;
     const char *device;
+    bool has_node_name = false;
+    const char *node_name;
+    bool has_snapshot_node_name = false;
+    const char *snapshot_node_name;
     const char *new_image_file;
     const char *format = "qcow2";
     enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
@@ -1092,7 +1280,14 @@ static void external_snapshot_prepare(BlkTransactionState *common,
     /* get parameters */
     g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
 
+    has_device = action->blockdev_snapshot_sync->has_device;
     device = action->blockdev_snapshot_sync->device;
+    has_node_name = action->blockdev_snapshot_sync->has_node_name;
+    node_name = action->blockdev_snapshot_sync->node_name;
+    has_snapshot_node_name =
+        action->blockdev_snapshot_sync->has_snapshot_node_name;
+    snapshot_node_name = action->blockdev_snapshot_sync->snapshot_node_name;
+
     new_image_file = action->blockdev_snapshot_sync->snapshot_file;
     if (action->blockdev_snapshot_sync->has_format) {
         format = action->blockdev_snapshot_sync->format;
@@ -1108,9 +1303,21 @@ static void external_snapshot_prepare(BlkTransactionState *common,
         return;
     }
 
-    state->old_bs = bdrv_find(device);
-    if (!state->old_bs) {
-        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+    state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
+                                   has_node_name ? node_name : NULL,
+                                   &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    if (has_node_name && !has_snapshot_node_name) {
+        error_setg(errp, "New snapshot node name missing");
+        return;
+    }
+
+    if (has_snapshot_node_name && bdrv_find_node(snapshot_node_name)) {
+        error_setg(errp, "New snapshot node name already existing");
         return;
     }
 
@@ -1119,8 +1326,8 @@ static void external_snapshot_prepare(BlkTransactionState *common,
         return;
     }
 
-    if (bdrv_in_use(state->old_bs)) {
-        error_set(errp, QERR_DEVICE_IN_USE, device);
+    if (bdrv_op_is_blocked(state->old_bs,
+                           BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
         return;
     }
 
@@ -1131,6 +1338,11 @@ static void external_snapshot_prepare(BlkTransactionState *common,
         }
     }
 
+    if (!bdrv_is_first_non_filter(state->old_bs)) {
+        error_set(errp, QERR_FEATURE_DISABLED, "snapshot");
+        return;
+    }
+
     flags = state->old_bs->open_flags;
 
     /* create new image w/backing file */
@@ -1139,18 +1351,24 @@ static void external_snapshot_prepare(BlkTransactionState *common,
                         state->old_bs->filename,
                         state->old_bs->drv->format_name,
                         NULL, -1, flags, &local_err, false);
-        if (error_is_set(&local_err)) {
+        if (local_err) {
             error_propagate(errp, local_err);
             return;
         }
     }
 
-    /* We will manually add the backing_hd field to the bs later */
-    state->new_bs = bdrv_new("");
+    if (has_snapshot_node_name) {
+        options = qdict_new();
+        qdict_put(options, "node-name",
+                  qstring_from_str(snapshot_node_name));
+    }
+
     /* TODO Inherit bs->options or only take explicit options with an
      * extended QMP command? */
-    ret = bdrv_open(state->new_bs, new_image_file, NULL,
+    assert(state->new_bs == NULL);
+    ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
                     flags | BDRV_O_NO_BACKING, drv, &local_err);
+    /* We will manually add the backing_hd field to the bs later */
     if (ret != 0) {
         error_propagate(errp, local_err);
     }
@@ -1202,7 +1420,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
                      backup->has_on_source_error, backup->on_source_error,
                      backup->has_on_target_error, backup->on_target_error,
                      &local_err);
-    if (error_is_set(&local_err)) {
+    if (local_err) {
         error_propagate(errp, local_err);
         state->bs = NULL;
         state->job = NULL;
@@ -1294,7 +1512,7 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp)
         QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
 
         state->ops->prepare(state, &local_err);
-        if (error_is_set(&local_err)) {
+        if (local_err) {
             error_propagate(errp, local_err);
             goto delete_and_fail;
         }
@@ -1331,19 +1549,20 @@ exit:
 
 static void eject_device(BlockDriverState *bs, int force, Error **errp)
 {
-    if (bdrv_in_use(bs)) {
-        error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
         return;
     }
     if (!bdrv_dev_has_removable_media(bs)) {
-        error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
+        error_setg(errp, "Device '%s' is not removable",
+                   bdrv_get_device_name(bs));
         return;
     }
 
     if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
         bdrv_dev_eject_request(bs, force);
         if (!force) {
-            error_set(errp, QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
+            error_setg(errp, "Device '%s' is locked",
+                       bdrv_get_device_name(bs));
             return;
         }
     }
@@ -1364,14 +1583,19 @@ void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
     eject_device(bs, force, errp);
 }
 
-void qmp_block_passwd(const char *device, const char *password, Error **errp)
+void qmp_block_passwd(bool has_device, const char *device,
+                      bool has_node_name, const char *node_name,
+                      const char *password, Error **errp)
 {
+    Error *local_err = NULL;
     BlockDriverState *bs;
     int err;
 
-    bs = bdrv_find(device);
-    if (!bs) {
-        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+    bs = bdrv_lookup_bs(has_device ? device : NULL,
+                        has_node_name ? node_name : NULL,
+                        &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
         return;
     }
 
@@ -1392,7 +1616,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
     Error *local_err = NULL;
     int ret;
 
-    ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err);
+    ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err);
     if (ret < 0) {
         error_propagate(errp, local_err);
         return;
@@ -1413,7 +1637,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
 }
 
 void qmp_change_blockdev(const char *device, const char *filename,
-                         bool has_format, const char *format, Error **errp)
+                         const char *format, Error **errp)
 {
     BlockDriverState *bs;
     BlockDriver *drv = NULL;
@@ -1435,7 +1659,7 @@ void qmp_change_blockdev(const char *device, const char *filename,
     }
 
     eject_device(bs, 0, &err);
-    if (error_is_set(&err)) {
+    if (err) {
         error_propagate(errp, err);
         return;
     }
@@ -1469,6 +1693,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
 {
     ThrottleConfig cfg;
     BlockDriverState *bs;
+    AioContext *aio_context;
 
     bs = bdrv_find(device);
     if (!bs) {
@@ -1512,6 +1737,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
         return;
     }
 
+    aio_context = bdrv_get_aio_context(bs);
+    aio_context_acquire(aio_context);
+
     if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
         bdrv_io_limits_enable(bs);
     } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
@@ -1521,20 +1749,24 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
     if (bs->io_limits_enabled) {
         bdrv_set_io_limits(bs, &cfg);
     }
+
+    aio_context_release(aio_context);
 }
 
 int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
     const char *id = qdict_get_str(qdict, "id");
     BlockDriverState *bs;
+    Error *local_err = NULL;
 
     bs = bdrv_find(id);
     if (!bs) {
-        qerror_report(QERR_DEVICE_NOT_FOUND, id);
+        error_report("Device '%s' not found", id);
         return -1;
     }
-    if (bdrv_in_use(bs)) {
-        qerror_report(QERR_DEVICE_IN_USE, id);
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
+        error_report("%s", error_get_pretty(local_err));
+        error_free(local_err);
         return -1;
     }
 
@@ -1555,20 +1787,30 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
         bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
                           BLOCKDEV_ON_ERROR_REPORT);
     } else {
-        drive_uninit(drive_get_by_blockdev(bs));
+        drive_del(drive_get_by_blockdev(bs));
     }
 
     return 0;
 }
 
-void qmp_block_resize(const char *device, int64_t size, Error **errp)
+void qmp_block_resize(bool has_device, const char *device,
+                      bool has_node_name, const char *node_name,
+                      int64_t size, Error **errp)
 {
+    Error *local_err = NULL;
     BlockDriverState *bs;
     int ret;
 
-    bs = bdrv_find(device);
-    if (!bs) {
-        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+    bs = bdrv_lookup_bs(has_device ? device : NULL,
+                        has_node_name ? node_name : NULL,
+                        &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    if (!bdrv_is_first_non_filter(bs)) {
+        error_set(errp, QERR_FEATURE_DISABLED, "resize");
         return;
     }
 
@@ -1645,6 +1887,10 @@ void qmp_block_stream(const char *device, bool has_base,
         return;
     }
 
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
+        return;
+    }
+
     if (base) {
         base_bs = bdrv_find_backing_image(bs, base);
         if (base_bs == NULL) {
@@ -1655,7 +1901,7 @@ void qmp_block_stream(const char *device, bool has_base,
 
     stream_start(bs, base_bs, base, has_speed ? speed : 0,
                  on_error, block_job_cb, bs, &local_err);
-    if (error_is_set(&local_err)) {
+    if (local_err) {
         error_propagate(errp, local_err);
         return;
     }
@@ -1676,6 +1922,10 @@ void qmp_block_commit(const char *device,
      */
     BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
 
+    if (!has_speed) {
+        speed = 0;
+    }
+
     /* drain all i/o before commits */
     bdrv_drain_all();
 
@@ -1685,6 +1935,10 @@ void qmp_block_commit(const char *device,
         return;
     }
 
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) {
+        return;
+    }
+
     /* default top_bs is the active layer */
     top_bs = bs;
 
@@ -1710,8 +1964,13 @@ void qmp_block_commit(const char *device,
         return;
     }
 
-    commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
-                &local_err);
+    if (top_bs == bs) {
+        commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
+                            bs, &local_err);
+    } else {
+        commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
+                    &local_err);
+    }
     if (local_err != NULL) {
         error_propagate(errp, local_err);
         return;
@@ -1771,8 +2030,7 @@ void qmp_drive_backup(const char *device, const char *target,
         }
     }
 
-    if (bdrv_in_use(bs)) {
-        error_set(errp, QERR_DEVICE_IN_USE, device);
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
         return;
     }
 
@@ -1808,15 +2066,14 @@ void qmp_drive_backup(const char *device, const char *target,
         }
     }
 
-    if (error_is_set(&local_err)) {
+    if (local_err) {
         error_propagate(errp, local_err);
         return;
     }
 
-    target_bs = bdrv_new("");
-    ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err);
+    target_bs = NULL;
+    ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err);
     if (ret < 0) {
-        bdrv_unref(target_bs);
         error_propagate(errp, local_err);
         return;
     }
@@ -1830,6 +2087,11 @@ void qmp_drive_backup(const char *device, const char *target,
     }
 }
 
+BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
+{
+    return bdrv_named_nodes_list();
+}
+
 #define DEFAULT_MIRROR_BUF_SIZE   (10 << 20)
 
 void qmp_drive_mirror(const char *device, const char *target,
@@ -1901,8 +2163,7 @@ void qmp_drive_mirror(const char *device, const char *target,
         }
     }
 
-    if (bdrv_in_use(bs)) {
-        error_set(errp, QERR_DEVICE_IN_USE, device);
+    if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
         return;
     }
 
@@ -1911,6 +2172,9 @@ void qmp_drive_mirror(const char *device, const char *target,
     if (!source && sync == MIRROR_SYNC_MODE_TOP) {
         sync = MIRROR_SYNC_MODE_FULL;
     }
+    if (sync == MIRROR_SYNC_MODE_NONE) {
+        source = bs;
+    }
 
     size = bdrv_getlength(bs);
     if (size < 0) {
@@ -1918,7 +2182,9 @@ void qmp_drive_mirror(const char *device, const char *target,
         return;
     }
 
-    if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) {
+    if ((sync == MIRROR_SYNC_MODE_FULL || !source)
+        && mode != NEW_IMAGE_MODE_EXISTING)
+    {
         /* create new image w/o backing file */
         assert(format && drv);
         bdrv_img_create(target, format,
@@ -1926,7 +2192,6 @@ void qmp_drive_mirror(const char *device, const char *target,
     } else {
         switch (mode) {
         case NEW_IMAGE_MODE_EXISTING:
-            ret = 0;
             break;
         case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
             /* create new image with backing file */
@@ -1940,7 +2205,7 @@ void qmp_drive_mirror(const char *device, const char *target,
         }
     }
 
-    if (error_is_set(&local_err)) {
+    if (local_err) {
         error_propagate(errp, local_err);
         return;
     }
@@ -1948,11 +2213,10 @@ void qmp_drive_mirror(const char *device, const char *target,
     /* Mirroring takes care of copy-on-write using the source's backing
      * file.
      */
-    target_bs = bdrv_new("");
-    ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv,
-                    &local_err);
+    target_bs = NULL;
+    ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING,
+                    drv, &local_err);
     if (ret < 0) {
-        bdrv_unref(target_bs);
         error_propagate(errp, local_err);
         return;
     }
@@ -2004,7 +2268,8 @@ void qmp_block_job_cancel(const char *device,
         return;
     }
     if (job->paused && !force) {
-        error_set(errp, QERR_BLOCK_JOB_PAUSED, device);
+        error_setg(errp, "The block job for device '%s' is currently paused",
+                   device);
         return;
     }
 
@@ -2051,6 +2316,63 @@ void qmp_block_job_complete(const char *device, Error **errp)
     block_job_complete(job, errp);
 }
 
+void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
+{
+    QmpOutputVisitor *ov = qmp_output_visitor_new();
+    DriveInfo *dinfo;
+    QObject *obj;
+    QDict *qdict;
+    Error *local_err = NULL;
+
+    /* Require an ID in the top level */
+    if (!options->has_id) {
+        error_setg(errp, "Block device needs an ID");
+        goto fail;
+    }
+
+    /* TODO Sort it out in raw-posix and drive_new(): Reject aio=native with
+     * cache.direct=false instead of silently switching to aio=threads, except
+     * when called from drive_new().
+     *
+     * For now, simply forbidding the combination for all drivers will do. */
+    if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) {
+        bool direct = options->has_cache &&
+                      options->cache->has_direct &&
+                      options->cache->direct;
+        if (!direct) {
+            error_setg(errp, "aio=native requires cache.direct=true");
+            goto fail;
+        }
+    }
+
+    visit_type_BlockdevOptions(qmp_output_get_visitor(ov),
+                               &options, NULL, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto fail;
+    }
+
+    obj = qmp_output_get_qobject(ov);
+    qdict = qobject_to_qdict(obj);
+
+    qdict_flatten(qdict);
+
+    dinfo = blockdev_init(NULL, qdict, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        goto fail;
+    }
+
+    if (bdrv_key_required(dinfo->bdrv)) {
+        drive_del(dinfo);
+        error_setg(errp, "blockdev-add doesn't support encrypted devices");
+        goto fail;
+    }
+
+fail:
+    qmp_output_visitor_cleanup(ov);
+}
+
 static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
 {
     BlockJobInfoList **prev = opaque;
@@ -2078,49 +2400,9 @@ QemuOptsList qemu_common_drive_opts = {
     .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,
@@ -2145,10 +2427,6 @@ QemuOptsList qemu_common_drive_opts = {
             .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,
@@ -2157,10 +2435,6 @@ QemuOptsList qemu_common_drive_opts = {
             .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,
@@ -2222,9 +2496,9 @@ QemuOptsList qemu_common_drive_opts = {
             .type = QEMU_OPT_BOOL,
             .help = "copy read data from backing file into image file",
         },{
-            .name = "boot",
-            .type = QEMU_OPT_BOOL,
-            .help = "(deprecated, ignored)",
+            .name = "detect-zeroes",
+            .type = QEMU_OPT_STRING,
+            .help = "try to optimize zero writes (off, on, unmap)",
         },
         { /* end of list */ }
     },
This page took 0.070959 seconds and 4 git commands to generate.