X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=blockdev.c;h=86e6bffdc493a1113c0984a7e9d4c35b87ddcc2f;hb=8b7acc79b9adb4dda6cc867b90e3a1e873f4f7e8;hp=453486480237b24e221374ac0367f86518ec70dd;hpb=405c97c3a5950d8a49b90cb977e33b6b3f9a8f95;p=qemu.git diff --git a/blockdev.c b/blockdev.c index 453486480..86e6bffdc 100644 --- a/blockdev.c +++ b/blockdev.c @@ -38,6 +38,8 @@ #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" @@ -89,6 +91,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); } @@ -211,8 +217,11 @@ static void bdrv_format_print(void *opaque, const char *name) static void drive_uninit(DriveInfo *dinfo) { - qemu_opts_del(dinfo->opts); - bdrv_delete(dinfo->bdrv); + if (dinfo->opts) { + qemu_opts_del(dinfo->opts); + } + + bdrv_unref(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); g_free(dinfo->serial); @@ -234,36 +243,36 @@ void drive_get_ref(DriveInfo *dinfo) typedef struct { QEMUBH *bh; - DriveInfo *dinfo; -} DrivePutRefBH; + BlockDriverState *bs; +} BDRVPutRefBH; -static void drive_put_ref_bh(void *opaque) +static void bdrv_put_ref_bh(void *opaque) { - DrivePutRefBH *s = opaque; + BDRVPutRefBH *s = opaque; - drive_put_ref(s->dinfo); + bdrv_unref(s->bs); qemu_bh_delete(s->bh); g_free(s); } /* - * Release a drive reference in a BH + * Release a BDS reference in a BH * - * It is not possible to use drive_put_ref() from a callback function when the - * callers still need the drive. In such cases we schedule a BH to release the - * reference. + * It is not safe to use bdrv_unref() from a callback function when the callers + * still need the BlockDriverState. In such cases we schedule a BH to release + * the reference. */ -static void drive_put_ref_bh_schedule(DriveInfo *dinfo) +static void bdrv_put_ref_bh_schedule(BlockDriverState *bs) { - DrivePutRefBH *s; + BDRVPutRefBH *s; - s = g_new(DrivePutRefBH, 1); - s->bh = qemu_bh_new(drive_put_ref_bh, s); - s->dinfo = dinfo; + s = g_new(BDRVPutRefBH, 1); + s->bh = qemu_bh_new(bdrv_put_ref_bh, s); + s->bs = 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,92 +283,65 @@ 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 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; } -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(QDict *bs_opts, + BlockInterfaceType type, + 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; - BlockIOLimit io_limits; + ThrottleConfig cfg; int snapshot = 0; bool copy_on_read; int ret; Error *error = NULL; QemuOpts *opts; - QDict *bs_opts; const char *id; bool has_driver_specific_opts; + 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); + error_propagate(errp, error); return NULL; } - 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; + error_propagate(errp, error); + goto early_err; } if (id) { @@ -369,14 +351,6 @@ 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); @@ -384,82 +358,20 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, 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; } } - bdrv_flags = 0; 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)) { + if (qemu_opt_get_bool(opts, "cache.no-flush", false)) { bdrv_flags |= BDRV_O_NO_FLUSH; } @@ -470,8 +382,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 @@ -481,48 +393,62 @@ 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; } - qdict_put(bs_opts, "driver", qstring_from_str(buf)); + drv = bdrv_find_format(buf); + if (!drv) { + error_setg(errp, "'%s' invalid format", buf); + goto early_err; + } } /* disk I/O throttling */ - io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = + memset(&cfg, 0, sizeof(cfg)); + cfg.buckets[THROTTLE_BPS_TOTAL].avg = qemu_opt_get_number(opts, "throttling.bps-total", 0); - io_limits.bps[BLOCK_IO_LIMIT_READ] = + cfg.buckets[THROTTLE_BPS_READ].avg = qemu_opt_get_number(opts, "throttling.bps-read", 0); - io_limits.bps[BLOCK_IO_LIMIT_WRITE] = + cfg.buckets[THROTTLE_BPS_WRITE].avg = qemu_opt_get_number(opts, "throttling.bps-write", 0); - io_limits.iops[BLOCK_IO_LIMIT_TOTAL] = + cfg.buckets[THROTTLE_OPS_TOTAL].avg = qemu_opt_get_number(opts, "throttling.iops-total", 0); - io_limits.iops[BLOCK_IO_LIMIT_READ] = + cfg.buckets[THROTTLE_OPS_READ].avg = qemu_opt_get_number(opts, "throttling.iops-read", 0); - io_limits.iops[BLOCK_IO_LIMIT_WRITE] = + cfg.buckets[THROTTLE_OPS_WRITE].avg = qemu_opt_get_number(opts, "throttling.iops-write", 0); - if (!do_check_io_limits(&io_limits, &error)) { - error_report("%s", error_get_pretty(error)); - error_free(error); - return NULL; - } + 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); - 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"); + cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); + + if (!check_throttle_config(&cfg, &error)) { + 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; + error_setg(errp, "werror is not supported by this bus type"); + goto early_err; } - 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_is_set(&error)) { + error_propagate(errp, error); + goto early_err; } } @@ -530,95 +456,23 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, 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; - } - } - - if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) { - if (type != IF_VIRTIO) { - error_report("addr is not supported by this bus type"); - return NULL; + 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; + on_read_error = parse_block_error_action(buf, 1, &error); + if (error_is_set(&error)) { + error_propagate(errp, error); + goto early_err; } - 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; } /* 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->id = g_strdup(qemu_opts_id(opts)); dinfo->bdrv = bdrv_new(dinfo->id); 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); @@ -628,42 +482,17 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); /* disk I/O throttling */ - bdrv_set_io_limits(dinfo->bdrv, &io_limits); - - 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 (throttle_enabled(&cfg)) { + bdrv_io_limits_enable(dinfo->bdrv); + bdrv_set_io_limits(dinfo->bdrv, &cfg); } + if (!file || !*file) { if (has_driver_specific_opts) { file = NULL; } else { + QDECREF(bs_opts); + qemu_opts_del(opts); return dinfo; } } @@ -681,34 +510,15 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, bdrv_flags |= BDRV_O_INCOMING; } - 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; - } - } - bdrv_flags |= ro ? 0 : BDRV_O_RDWR; - if (ro && copy_on_read) { - error_report("warning: disabling copy_on_read on read-only drive"); - } - QINCREF(bs_opts); - ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, NULL); + ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error); if (ret < 0) { - if (ret == -EMEDIUMTYPE) { - error_report("could not open disk image %s: not in %s format", - file ?: dinfo->id, qdict_get_str(bs_opts, "driver")); - } else { - error_report("could not open disk image %s: %s", - file ?: dinfo->id, strerror(-ret)); - } + error_setg(errp, "could not open disk image %s: %s", + file ?: dinfo->id, error_get_pretty(error)); + error_free(error); goto err; } @@ -721,12 +531,13 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts, return dinfo; err: - qemu_opts_del(opts); - QDECREF(bs_opts); - bdrv_delete(dinfo->bdrv); + bdrv_unref(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); g_free(dinfo); +early_err: + QDECREF(bs_opts); + qemu_opts_del(opts); return NULL; } @@ -741,9 +552,85 @@ static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to) } } +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)", + }, + + /* 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 = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + }, + + { /* end of list */ } + }, +}; + DriveInfo *drive_init(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; + bool read_only = false; + bool copy_on_read; + Error *local_err = NULL; /* Change legacy command line options into QMP ones */ qemu_opt_rename(all_opts, "iops", "throttling.iops-total"); @@ -754,34 +641,270 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) 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; + 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_nofail(&qemu_legacy_drive_opts); + qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err); + if (error_is_set(&local_err)) { + qerror_report_err(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, "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 (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; + } + + /* 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); + } - /* 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)); + /* 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 (type == IF_VIRTIO) { + 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"); } - if (!qemu_opt_get(all_opts, "cache.direct")) { - qemu_opt_set_bool(all_opts, "cache.direct", - !!(flags & BDRV_O_NOCACHE)); + qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id")); + if (devaddr) { + qemu_opt_set(devopts, "addr", devaddr); } - if (!qemu_opt_get(all_opts, "cache.no-flush")) { - qemu_opt_set_bool(all_opts, "cache.no-flush", - !!(flags & BDRV_O_NO_FLUSH)); + } + + /* Actual block device init: Functionality shared with blockdev-add */ + dinfo = blockdev_init(bs_opts, type, &local_err); + if (dinfo == NULL) { + if (error_is_set(&local_err)) { + qerror_report_err(local_err); + error_free(local_err); } - qemu_opt_unset(all_opts, "cache"); + goto fail; + } else { + assert(!error_is_set(&local_err)); + } + + /* Set legacy DriveInfo fields */ + dinfo->enable_auto_del = true; + dinfo->opts = all_opts; + + dinfo->cyls = cyls; + dinfo->heads = heads; + dinfo->secs = secs; + dinfo->trans = translation; + + dinfo->bus = bus_id; + dinfo->unit = unit_id; + dinfo->devaddr = devaddr; + + 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); + return dinfo; } void do_commit(Monitor *mon, const QDict *qdict) @@ -835,6 +958,80 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file, &snapshot, errp); } +void qmp_blockdev_snapshot_internal_sync(const char *device, + const char *name, + Error **errp) +{ + BlockdevSnapshotInternal snapshot = { + .device = (char *) device, + .name = (char *) name + }; + + blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, + &snapshot, errp); +} + +SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, + bool has_id, + const char *id, + bool has_name, + const char *name, + Error **errp) +{ + BlockDriverState *bs = bdrv_find(device); + QEMUSnapshotInfo sn; + Error *local_err = NULL; + SnapshotInfo *info = NULL; + int ret; + + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return NULL; + } + + if (!has_id) { + id = NULL; + } + + if (!has_name) { + name = NULL; + } + + if (!id && !name) { + error_setg(errp, "Name or id must be provided"); + return NULL; + } + + ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return NULL; + } + if (!ret) { + error_setg(errp, + "Snapshot with id '%s' and name '%s' does not exist on " + "device '%s'", + STR_OR_NULL(id), STR_OR_NULL(name), device); + return NULL; + } + + bdrv_snapshot_delete(bs, id, name, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return NULL; + } + + info = g_malloc0(sizeof(SnapshotInfo)); + info->id = g_strdup(sn.id_str); + info->name = g_strdup(sn.name); + info->date_nsec = sn.date_nsec; + info->date_sec = sn.date_sec; + info->vm_state_size = sn.vm_state_size; + info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000; + info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; + + return info; +} /* New and old BlockDriverState structs for group snapshots */ @@ -866,6 +1063,117 @@ struct BlkTransactionState { QSIMPLEQ_ENTRY(BlkTransactionState) entry; }; +/* internal snapshot private data */ +typedef struct InternalSnapshotState { + BlkTransactionState common; + BlockDriverState *bs; + QEMUSnapshotInfo sn; +} InternalSnapshotState; + +static void internal_snapshot_prepare(BlkTransactionState *common, + Error **errp) +{ + const char *device; + const char *name; + BlockDriverState *bs; + QEMUSnapshotInfo old_sn, *sn; + bool ret; + qemu_timeval tv; + BlockdevSnapshotInternal *internal; + InternalSnapshotState *state; + int ret1; + + g_assert(common->action->kind == + TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC); + internal = common->action->blockdev_snapshot_internal_sync; + state = DO_UPCAST(InternalSnapshotState, common, common); + + /* 1. parse input */ + device = internal->device; + name = internal->name; + + /* 2. check for validation */ + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } + + if (bdrv_is_read_only(bs)) { + error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); + return; + } + + if (!bdrv_can_snapshot(bs)) { + error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + bs->drv->format_name, device, "internal snapshot"); + return; + } + + if (!strlen(name)) { + error_setg(errp, "Name is empty"); + return; + } + + /* 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)) { + return; + } else if (ret) { + error_setg(errp, + "Snapshot with name '%s' already exists on device '%s'", + name, device); + return; + } + + /* 3. take the snapshot */ + sn = &state->sn; + pstrcpy(sn->name, sizeof(sn->name), name); + qemu_gettimeofday(&tv); + sn->date_sec = tv.tv_sec; + sn->date_nsec = tv.tv_usec * 1000; + sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + ret1 = bdrv_snapshot_create(bs, sn); + if (ret1 < 0) { + error_setg_errno(errp, -ret1, + "Failed to create snapshot '%s' on device '%s'", + name, device); + return; + } + + /* 4. succeed, mark a snapshot is created */ + state->bs = bs; +} + +static void internal_snapshot_abort(BlkTransactionState *common) +{ + InternalSnapshotState *state = + DO_UPCAST(InternalSnapshotState, common, common); + BlockDriverState *bs = state->bs; + QEMUSnapshotInfo *sn = &state->sn; + Error *local_error = NULL; + + if (!bs) { + return; + } + + if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { + error_report("Failed to delete snapshot with id '%s' and name '%s' on " + "device '%s' in abort: %s", + sn->id_str, + sn->name, + bdrv_get_device_name(bs), + error_get_pretty(local_error)); + error_free(local_error); + } +} + /* external snapshot private data */ typedef struct ExternalSnapshotState { BlkTransactionState common; @@ -929,6 +1237,11 @@ static void external_snapshot_prepare(BlkTransactionState *common, } } + if (bdrv_check_ext_snapshot(state->old_bs) != EXT_SNAPSHOT_ALLOWED) { + error_set(errp, QERR_FEATURE_DISABLED, "snapshot"); + return; + } + flags = state->old_bs->open_flags; /* create new image w/backing file */ @@ -948,9 +1261,9 @@ static void external_snapshot_prepare(BlkTransactionState *common, /* TODO Inherit bs->options or only take explicit options with an * extended QMP command? */ ret = bdrv_open(state->new_bs, new_image_file, NULL, - flags | BDRV_O_NO_BACKING, drv); + flags | BDRV_O_NO_BACKING, drv, &local_err); if (ret != 0) { - error_setg_file_open(errp, -ret, new_image_file); + error_propagate(errp, local_err); } } @@ -973,7 +1286,7 @@ static void external_snapshot_abort(BlkTransactionState *common) ExternalSnapshotState *state = DO_UPCAST(ExternalSnapshotState, common, common); if (state->new_bs) { - bdrv_delete(state->new_bs); + bdrv_unref(state->new_bs); } } @@ -1029,7 +1342,7 @@ static void abort_prepare(BlkTransactionState *common, Error **errp) static void abort_commit(BlkTransactionState *common) { - assert(false); /* this action never succeeds */ + g_assert_not_reached(); /* this action never succeeds */ } static const BdrvActionOps actions[] = { @@ -1049,6 +1362,11 @@ static const BdrvActionOps actions[] = { .prepare = abort_prepare, .commit = abort_commit, }, + [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = { + .instance_size = sizeof(InternalSnapshotState), + .prepare = internal_snapshot_prepare, + .abort = internal_snapshot_abort, + }, }; /* @@ -1079,6 +1397,8 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) assert(dev_info->kind < ARRAY_SIZE(actions)); ops = &actions[dev_info->kind]; + assert(ops->instance_size > 0); + state = g_malloc0(ops->instance_size); state->ops = ops; state->action = dev_info; @@ -1180,11 +1500,12 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, int bdrv_flags, BlockDriver *drv, const char *password, Error **errp) { + Error *local_err = NULL; int ret; - ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv); + ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err); if (ret < 0) { - error_setg_file_open(errp, -ret, filename); + error_propagate(errp, local_err); return; } @@ -1238,10 +1559,26 @@ void qmp_change_blockdev(const char *device, const char *filename, /* 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); @@ -1250,27 +1587,50 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, 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 (!check_throttle_config(&cfg, errp)) { + return; + } - if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) { + 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); } } @@ -1374,7 +1734,7 @@ static void block_job_cb(void *opaque, int ret) } qobject_decref(obj); - drive_put_ref_bh_schedule(drive_get_by_blockdev(bs)); + bdrv_put_ref_bh_schedule(bs); } void qmp_block_stream(const char *device, bool has_base, @@ -1411,11 +1771,6 @@ void qmp_block_stream(const char *device, bool has_base, return; } - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); - trace_qmp_block_stream(bs, bs->job); } @@ -1472,10 +1827,6 @@ void qmp_block_commit(const char *device, error_propagate(errp, local_err); return; } - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); } void qmp_drive_backup(const char *device, const char *target, @@ -1574,25 +1925,20 @@ void qmp_drive_backup(const char *device, const char *target, } target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, NULL, flags, drv); + ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err); if (ret < 0) { - bdrv_delete(target_bs); - error_setg_file_open(errp, -ret, target); + bdrv_unref(target_bs); + error_propagate(errp, local_err); return; } 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); + bdrv_unref(target_bs); error_propagate(errp, local_err); return; } - - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); } #define DEFAULT_MIRROR_BUF_SIZE (10 << 20) @@ -1691,7 +2037,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 */ @@ -1714,10 +2059,11 @@ void qmp_drive_mirror(const char *device, const char *target, * file. */ target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv); + ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv, + &local_err); if (ret < 0) { - bdrv_delete(target_bs); - error_setg_file_open(errp, -ret, target); + bdrv_unref(target_bs); + error_propagate(errp, local_err); return; } @@ -1725,15 +2071,10 @@ void qmp_drive_mirror(const char *device, const char *target, on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { - bdrv_delete(target_bs); + bdrv_unref(target_bs); error_propagate(errp, local_err); return; } - - /* Grab a reference so hotplug does not delete the BlockDriverState from - * underneath us. - */ - drive_get_ref(drive_get_by_blockdev(bs)); } static BlockJob *find_block_job(const char *device) @@ -1820,6 +2161,54 @@ 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(); + 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_init: Reject aio=native with + * cache.direct=false instead of silently switching to aio=threads, except + * if called from drive_init. + * + * For now, simply forbidding the combination for all drivers will do. */ + if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) { + bool direct = options->cache->has_direct && options->cache->direct; + if (!options->has_cache && !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 (error_is_set(&local_err)) { + error_propagate(errp, local_err); + goto fail; + } + + obj = qmp_output_get_qobject(ov); + qdict = qobject_to_qdict(obj); + + qdict_flatten(qdict); + + blockdev_init(qdict, IF_NONE, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + goto fail; + } + +fail: + qmp_output_visitor_cleanup(ov); +} + static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs) { BlockJobInfoList **prev = opaque; @@ -1847,42 +2236,6 @@ 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", @@ -1926,10 +2279,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, @@ -1958,14 +2307,38 @@ QemuOptsList qemu_common_drive_opts = { .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, .help = "copy read data from backing file into image file", - },{ - .name = "boot", - .type = QEMU_OPT_BOOL, - .help = "(deprecated, ignored)", }, { /* end of list */ } },