X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=blockdev.c;h=32b04b478c73873cd54ad2d93a189ecdeeda268c;hb=4ecd4d16a0af714ff7d9a1ad2559c621bf27649f;hp=b9c1c0cc1aaa17ee526f4dd6499181ce9b38c1b6;hpb=5efde22aa781d37df58f0060430f459491dcfd62;p=mirror_qemu.git diff --git a/blockdev.c b/blockdev.c index b9c1c0cc1a..32b04b478c 100644 --- a/blockdev.c +++ b/blockdev.c @@ -34,11 +34,14 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" +#include "block/throttle-groups.h" #include "monitor/monitor.h" +#include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/config-file.h" #include "qapi/qmp/types.h" #include "qapi-visit.h" +#include "qapi/qmp/qerror.h" #include "qapi/qmp-output-visitor.h" #include "qapi/util.h" #include "sysemu/sysemu.h" @@ -173,7 +176,7 @@ static int drive_index_to_unit_id(BlockInterfaceType type, int index) QemuOpts *drive_def(const char *optstr) { - return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); + return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false); } QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, @@ -334,6 +337,12 @@ static bool check_throttle_config(ThrottleConfig *cfg, Error **errp) return false; } + if (throttle_max_is_missing_limit(cfg)) { + error_setg(errp, "bps_max/iops_max require corresponding" + " bps/iops values"); + return false; + } + return true; } @@ -357,6 +366,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, const char *id; bool has_driver_specific_opts; BlockdevDetectZeroesOptions detect_zeroes; + const char *throttling_group; /* Check common options by copying from bs_opts to opts, all other options * stay in bs_opts for processing by bdrv_open(). */ @@ -391,13 +401,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, } } - if (qemu_opt_get_bool(opts, "cache.writeback", true)) { + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) { bdrv_flags |= BDRV_O_CACHE_WB; } - if (qemu_opt_get_bool(opts, "cache.direct", false)) { + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) { bdrv_flags |= BDRV_O_NOCACHE; } - if (qemu_opt_get_bool(opts, "cache.no-flush", false)) { + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { bdrv_flags |= BDRV_O_NO_FLUSH; } @@ -459,6 +469,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); + throttling_group = qemu_opt_get(opts, "throttling.group"); + if (!check_throttle_config(&cfg, &error)) { error_propagate(errp, error); goto early_err; @@ -547,7 +559,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, /* disk I/O throttling */ if (throttle_enabled(&cfg)) { - bdrv_io_limits_enable(bs); + if (!throttling_group) { + throttling_group = blk_name(blk); + } + bdrv_io_limits_enable(bs, throttling_group); bdrv_set_io_limits(bs, &cfg); } @@ -711,6 +726,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) { "iops_size", "throttling.iops-size" }, + { "group", "throttling.group" }, + { "readonly", "read-only" }, }; @@ -733,16 +750,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } /* Specific options take precedence */ - if (!qemu_opt_get(all_opts, "cache.writeback")) { - qemu_opt_set_bool(all_opts, "cache.writeback", + if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) { + qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB, !!(flags & BDRV_O_CACHE_WB), &error_abort); } - if (!qemu_opt_get(all_opts, "cache.direct")) { - qemu_opt_set_bool(all_opts, "cache.direct", + if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) { + qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT, !!(flags & BDRV_O_NOCACHE), &error_abort); } - if (!qemu_opt_get(all_opts, "cache.no-flush")) { - qemu_opt_set_bool(all_opts, "cache.no-flush", + if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) { + qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH, !!(flags & BDRV_O_NO_FLUSH), &error_abort); } qemu_opt_unset(all_opts, "cache"); @@ -933,7 +950,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) 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", &error_abort); + qemu_opt_set(devopts, "driver", "virtio-blk-ccw", &error_abort); } else { qemu_opt_set(devopts, "driver", "virtio-blk-pci", &error_abort); } @@ -1016,18 +1033,18 @@ fail: void hmp_commit(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); - BlockDriverState *bs; + BlockBackend *blk; int ret; if (!strcmp(device, "all")) { ret = bdrv_commit_all(); } else { - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { monitor_printf(mon, "Device '%s' not found\n", device); return; } - ret = bdrv_commit(bs); + ret = bdrv_commit(blk_bs(blk)); } if (ret < 0) { monitor_printf(mon, "'commit' error for '%s': %s\n", device, @@ -1092,17 +1109,21 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, const char *name, Error **errp) { - BlockDriverState *bs = bdrv_find(device); + BlockDriverState *bs; + BlockBackend *blk; AioContext *aio_context; QEMUSnapshotInfo sn; Error *local_err = NULL; SnapshotInfo *info = NULL; int ret; - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return NULL; } + bs = blk_bs(blk); if (!has_id) { id = NULL; @@ -1161,6 +1182,68 @@ out_aio_context: return NULL; } +/** + * block_dirty_bitmap_lookup: + * Return a dirty bitmap (if present), after validating + * the node reference and bitmap names. + * + * @node: The name of the BDS node to search for bitmaps + * @name: The name of the bitmap to search for + * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. + * @paio: Output pointer for aio_context acquisition, if desired. Can be NULL. + * @errp: Output pointer for error information. Can be NULL. + * + * @return: A bitmap object on success, or NULL on failure. + */ +static BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, + const char *name, + BlockDriverState **pbs, + AioContext **paio, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + AioContext *aio_context; + + if (!node) { + error_setg(errp, "Node cannot be NULL"); + return NULL; + } + if (!name) { + error_setg(errp, "Bitmap name cannot be NULL"); + return NULL; + } + bs = bdrv_lookup_bs(node, node, NULL); + if (!bs) { + error_setg(errp, "Node '%s' not found", node); + return NULL; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + bitmap = bdrv_find_dirty_bitmap(bs, name); + if (!bitmap) { + error_setg(errp, "Dirty bitmap '%s' not found", name); + goto fail; + } + + if (pbs) { + *pbs = bs; + } + if (paio) { + *paio = aio_context; + } else { + aio_context_release(aio_context); + } + + return bitmap; + + fail: + aio_context_release(aio_context); + return NULL; +} + /* New and old BlockDriverState structs for atomic group operations */ typedef struct BlkTransactionState BlkTransactionState; @@ -1205,6 +1288,7 @@ static void internal_snapshot_prepare(BlkTransactionState *common, Error *local_err = NULL; const char *device; const char *name; + BlockBackend *blk; BlockDriverState *bs; QEMUSnapshotInfo old_sn, *sn; bool ret; @@ -1223,18 +1307,20 @@ static void internal_snapshot_prepare(BlkTransactionState *common, name = internal->name; /* 2. check for validation */ - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); /* AioContext is released in .clean() */ state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(state->aio_context); if (!bdrv_is_inserted(bs)) { - error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; } @@ -1243,13 +1329,14 @@ static void internal_snapshot_prepare(BlkTransactionState *common, } if (bdrv_is_read_only(bs)) { - error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); + error_setg(errp, "Device '%s' 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"); + error_setg(errp, "Block format '%s' used by device '%s' " + "does not support internal snapshots", + bs->drv->format_name, device); return; } @@ -1335,9 +1422,8 @@ typedef struct ExternalSnapshotState { static void external_snapshot_prepare(BlkTransactionState *common, Error **errp) { - BlockDriver *drv; int flags, ret; - QDict *options = NULL; + QDict *options; Error *local_err = NULL; bool has_device = false; const char *device; @@ -1372,12 +1458,6 @@ static void external_snapshot_prepare(BlkTransactionState *common, } /* start processing */ - drv = bdrv_find_format(format); - if (!drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; - } - state->old_bs = bdrv_lookup_bs(has_device ? device : NULL, has_node_name ? node_name : NULL, &local_err); @@ -1401,7 +1481,7 @@ static void external_snapshot_prepare(BlkTransactionState *common, aio_context_acquire(state->aio_context); if (!bdrv_is_inserted(state->old_bs)) { - error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); return; } @@ -1412,13 +1492,13 @@ static void external_snapshot_prepare(BlkTransactionState *common, if (!bdrv_is_read_only(state->old_bs)) { if (bdrv_flush(state->old_bs)) { - error_set(errp, QERR_IO_ERROR); + error_setg(errp, QERR_IO_ERROR); return; } } if (!bdrv_is_first_non_filter(state->old_bs)) { - error_set(errp, QERR_FEATURE_DISABLED, "snapshot"); + error_setg(errp, QERR_FEATURE_DISABLED, "snapshot"); return; } @@ -1436,17 +1516,18 @@ static void external_snapshot_prepare(BlkTransactionState *common, } } + options = qdict_new(); if (has_snapshot_node_name) { - options = qdict_new(); qdict_put(options, "node-name", qstring_from_str(snapshot_node_name)); } + qdict_put(options, "driver", qstring_from_str(format)); /* TODO Inherit bs->options or only take explicit options with an * extended QMP command? */ assert(state->new_bs == NULL); ret = bdrv_open(&state->new_bs, new_image_file, NULL, options, - flags | BDRV_O_NO_BACKING, drv, &local_err); + flags | BDRV_O_NO_BACKING, &local_err); /* We will manually add the backing_hd field to the bs later */ if (ret != 0) { error_propagate(errp, local_err); @@ -1494,17 +1575,20 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) { DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); BlockDriverState *bs; + BlockBackend *blk; DriveBackup *backup; Error *local_err = NULL; assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); backup = common->action->drive_backup; - bs = bdrv_find(backup->device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); + blk = blk_by_name(backup->device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", backup->device); return; } + bs = blk_bs(blk); /* AioContext is released in .clean() */ state->aio_context = bdrv_get_aio_context(bs); @@ -1515,6 +1599,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp) backup->sync, backup->has_mode, backup->mode, backup->has_speed, backup->speed, + backup->has_bitmap, backup->bitmap, backup->has_on_source_error, backup->on_source_error, backup->has_on_target_error, backup->on_target_error, &local_err); @@ -1559,22 +1644,25 @@ static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackup *backup; BlockDriverState *bs, *target; + BlockBackend *blk; Error *local_err = NULL; assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); backup = common->action->blockdev_backup; - bs = bdrv_find(backup->device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); + blk = blk_by_name(backup->device); + if (!blk) { + error_setg(errp, "Device '%s' not found", backup->device); return; } + bs = blk_bs(blk); - target = bdrv_find(backup->target); - if (!target) { - error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target); + blk = blk_by_name(backup->target); + if (!blk) { + error_setg(errp, "Device '%s' not found", backup->target); return; } + target = blk_bs(blk); /* AioContext is released in .clean() */ state->aio_context = bdrv_get_aio_context(bs); @@ -1767,7 +1855,8 @@ void qmp_eject(const char *device, bool has_force, bool force, Error **errp) blk = blk_by_name(device); if (!blk) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } @@ -1800,13 +1889,19 @@ void qmp_block_passwd(bool has_device, const char *device, /* Assumes AioContext is held */ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, - int bdrv_flags, BlockDriver *drv, + int bdrv_flags, const char *format, const char *password, Error **errp) { Error *local_err = NULL; + QDict *options = NULL; int ret; - ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err); + if (format) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(format)); + } + + ret = bdrv_open(&bs, filename, NULL, options, bdrv_flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); return; @@ -1821,13 +1916,13 @@ void qmp_change_blockdev(const char *device, const char *filename, BlockBackend *blk; BlockDriverState *bs; AioContext *aio_context; - BlockDriver *drv = NULL; int bdrv_flags; Error *err = NULL; blk = blk_by_name(device); if (!blk) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } bs = blk_bs(blk); @@ -1835,14 +1930,6 @@ void qmp_change_blockdev(const char *device, const char *filename, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (format) { - drv = bdrv_find_whitelisted_format(format, bs->read_only); - if (!drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } - eject_device(blk, 0, &err); if (err) { error_propagate(errp, err); @@ -1852,7 +1939,7 @@ void qmp_change_blockdev(const char *device, const char *filename, bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; - qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp); + qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, format, NULL, errp); out: aio_context_release(aio_context); @@ -1877,17 +1964,22 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, bool has_iops_wr_max, int64_t iops_wr_max, bool has_iops_size, - int64_t iops_size, Error **errp) + int64_t iops_size, + bool has_group, + const char *group, Error **errp) { ThrottleConfig cfg; BlockDriverState *bs; + BlockBackend *blk; AioContext *aio_context; - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); memset(&cfg, 0, sizeof(cfg)); cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps; @@ -1928,20 +2020,121 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, 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)) { + if (throttle_enabled(&cfg)) { + /* Enable I/O limits if they're not enabled yet, otherwise + * just update the throttling group. */ + if (!bs->io_limits_enabled) { + bdrv_io_limits_enable(bs, has_group ? group : device); + } else if (has_group) { + bdrv_io_limits_update_group(bs, group); + } + /* Set the new throttling configuration */ + bdrv_set_io_limits(bs, &cfg); + } else if (bs->io_limits_enabled) { + /* If all throttling settings are set to 0, disable I/O limits */ bdrv_io_limits_disable(bs); } - if (bs->io_limits_enabled) { - bdrv_set_io_limits(bs, &cfg); + aio_context_release(aio_context); +} + +void qmp_block_dirty_bitmap_add(const char *node, const char *name, + bool has_granularity, uint32_t granularity, + Error **errp) +{ + AioContext *aio_context; + BlockDriverState *bs; + + if (!name || name[0] == '\0') { + error_setg(errp, "Bitmap name cannot be empty"); + return; } + bs = bdrv_lookup_bs(node, node, errp); + if (!bs) { + return; + } + + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + + if (has_granularity) { + if (granularity < 512 || !is_power_of_2(granularity)) { + error_setg(errp, "Granularity must be power of 2 " + "and at least 512"); + goto out; + } + } else { + /* Default to cluster size, if available: */ + granularity = bdrv_get_default_bitmap_granularity(bs); + } + + bdrv_create_dirty_bitmap(bs, granularity, name, errp); + + out: aio_context_release(aio_context); } -int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_block_dirty_bitmap_remove(const char *node, const char *name, + Error **errp) +{ + AioContext *aio_context; + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp); + if (!bitmap || !bs) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be removed", + name); + goto out; + } + bdrv_dirty_bitmap_make_anon(bitmap); + bdrv_release_dirty_bitmap(bs, bitmap); + + out: + aio_context_release(aio_context); +} + +/** + * Completely clear a bitmap, for the purposes of synchronizing a bitmap + * immediately after a full backup operation. + */ +void qmp_block_dirty_bitmap_clear(const char *node, const char *name, + Error **errp) +{ + AioContext *aio_context; + BdrvDirtyBitmap *bitmap; + BlockDriverState *bs; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, &aio_context, errp); + if (!bitmap || !bs) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be modified", + name); + goto out; + } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently disabled and cannot be cleared", + name); + goto out; + } + + bdrv_clear_dirty_bitmap(bitmap); + + out: + aio_context_release(aio_context); +} + +void hmp_drive_del(Monitor *mon, const QDict *qdict) { const char *id = qdict_get_str(qdict, "id"); BlockBackend *blk; @@ -1952,14 +2145,14 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) blk = blk_by_name(id); if (!blk) { error_report("Device '%s' not found", id); - return -1; + return; } bs = blk_bs(blk); if (!blk_legacy_dinfo(blk)) { error_report("Deleting device added with blockdev-add" " is not supported"); - return -1; + return; } aio_context = bdrv_get_aio_context(bs); @@ -1968,12 +2161,9 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { error_report_err(local_err); aio_context_release(aio_context); - return -1; + return; } - /* quiesce block driver; prevent further io */ - bdrv_drain_all(); - bdrv_flush(bs); bdrv_close(bs); /* if we have a device attached to this BlockDriverState @@ -1991,7 +2181,6 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) } aio_context_release(aio_context); - return 0; } void qmp_block_resize(bool has_device, const char *device, @@ -2015,17 +2204,17 @@ void qmp_block_resize(bool has_device, const char *device, aio_context_acquire(aio_context); if (!bdrv_is_first_non_filter(bs)) { - error_set(errp, QERR_FEATURE_DISABLED, "resize"); + error_setg(errp, QERR_FEATURE_DISABLED, "resize"); goto out; } if (size < 0) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); goto out; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) { - error_set(errp, QERR_DEVICE_IN_USE, device); + error_setg(errp, QERR_DEVICE_IN_USE, device); goto out; } @@ -2037,16 +2226,16 @@ void qmp_block_resize(bool has_device, const char *device, case 0: break; case -ENOMEDIUM: - error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); break; case -ENOTSUP: - error_set(errp, QERR_UNSUPPORTED); + error_setg(errp, QERR_UNSUPPORTED); break; case -EACCES: - error_set(errp, QERR_DEVICE_IS_READ_ONLY, device); + error_setg(errp, "Device '%s' is read only", device); break; case -EBUSY: - error_set(errp, QERR_DEVICE_IN_USE, device); + error_setg(errp, QERR_DEVICE_IN_USE, device); break; default: error_setg_errno(errp, -ret, "Could not resize"); @@ -2091,6 +2280,7 @@ void qmp_block_stream(const char *device, bool has_on_error, BlockdevOnError on_error, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *base_bs = NULL; AioContext *aio_context; @@ -2101,11 +2291,13 @@ void qmp_block_stream(const char *device, on_error = BLOCKDEV_ON_ERROR_REPORT; } - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -2117,7 +2309,7 @@ void qmp_block_stream(const char *device, if (has_base) { base_bs = bdrv_find_backing_image(bs, base); if (base_bs == NULL) { - error_set(errp, QERR_BASE_NOT_FOUND, base); + error_setg(errp, QERR_BASE_NOT_FOUND, base); goto out; } assert(bdrv_get_aio_context(base_bs) == aio_context); @@ -2155,6 +2347,7 @@ void qmp_block_commit(const char *device, bool has_speed, int64_t speed, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *base_bs, *top_bs; AioContext *aio_context; @@ -2173,18 +2366,17 @@ void qmp_block_commit(const char *device, * live commit feature versions; for this to work, we must make sure to * perform the device lookup before any generic errors that may occur in a * scenario in which all optional arguments are omitted. */ - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - /* drain all i/o before commits */ - bdrv_drain_all(); - if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { goto out; } @@ -2212,7 +2404,7 @@ void qmp_block_commit(const char *device, } if (base_bs == NULL) { - error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL"); + error_setg(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL"); goto out; } @@ -2254,15 +2446,18 @@ void qmp_drive_backup(const char *device, const char *target, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, + bool has_bitmap, const char *bitmap, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *target_bs; BlockDriverState *source = NULL; + BdrvDirtyBitmap *bmap = NULL; AioContext *aio_context; - BlockDriver *drv = NULL; + QDict *options = NULL; Error *local_err = NULL; int flags; int64_t size; @@ -2281,11 +2476,13 @@ void qmp_drive_backup(const char *device, const char *target, mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -2293,20 +2490,13 @@ void qmp_drive_backup(const char *device, const char *target, /* Although backup_run has this check too, we need to use bs->drv below, so * do an early check redundantly. */ if (!bdrv_is_inserted(bs)) { - error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; } if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; } - if (format) { - drv = bdrv_find_format(format); - if (!drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } /* Early check to avoid creating target */ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { @@ -2334,7 +2524,7 @@ void qmp_drive_backup(const char *device, const char *target, } if (mode != NEW_IMAGE_MODE_EXISTING) { - assert(format && drv); + assert(format); if (source) { bdrv_img_create(target, format, source->filename, source->drv->format_name, NULL, @@ -2350,8 +2540,13 @@ void qmp_drive_backup(const char *device, const char *target, goto out; } + if (format) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(format)); + } + target_bs = NULL; - ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err); + ret = bdrv_open(&target_bs, target, NULL, options, flags, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -2359,7 +2554,16 @@ void qmp_drive_backup(const char *device, const char *target, bdrv_set_aio_context(target_bs, aio_context); - backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, + if (has_bitmap) { + bmap = bdrv_find_dirty_bitmap(bs, bitmap); + if (!bmap) { + error_setg(errp, "Bitmap '%s' could not be found", bitmap); + goto out; + } + } + + backup_start(bs, target_bs, speed, sync, bmap, + on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); @@ -2373,7 +2577,7 @@ out: BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) { - return bdrv_named_nodes_list(); + return bdrv_named_nodes_list(errp); } void qmp_blockdev_backup(const char *device, const char *target, @@ -2385,6 +2589,7 @@ void qmp_blockdev_backup(const char *device, const char *target, BlockdevOnError on_target_error, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *target_bs; Error *local_err = NULL; @@ -2400,25 +2605,27 @@ void qmp_blockdev_backup(const char *device, const char *target, on_target_error = BLOCKDEV_ON_ERROR_REPORT; } - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_setg(errp, "Device '%s' not found", device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - target_bs = bdrv_find(target); - if (!target_bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, target); + blk = blk_by_name(target); + if (!blk) { + error_setg(errp, "Device '%s' not found", target); goto out; } + target_bs = blk_bs(blk); bdrv_ref(target_bs); bdrv_set_aio_context(target_bs, aio_context); - backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error, - block_job_cb, bs, &local_err); + backup_start(bs, target_bs, speed, sync, NULL, on_source_error, + on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -2427,8 +2634,6 @@ out: aio_context_release(aio_context); } -#define DEFAULT_MIRROR_BUF_SIZE (10 << 20) - void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, bool has_node_name, const char *node_name, @@ -2440,14 +2645,15 @@ void qmp_drive_mirror(const char *device, const char *target, bool has_buf_size, int64_t buf_size, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, + bool has_unmap, bool unmap, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; BlockDriverState *source, *target_bs; AioContext *aio_context; - BlockDriver *drv = NULL; Error *local_err = NULL; - QDict *options = NULL; + QDict *options; int flags; int64_t size; int ret; @@ -2468,43 +2674,42 @@ void qmp_drive_mirror(const char *device, const char *target, granularity = 0; } if (!has_buf_size) { - buf_size = DEFAULT_MIRROR_BUF_SIZE; + buf_size = 0; + } + if (!has_unmap) { + unmap = true; } if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", - "a value in range [512B, 64MB]"); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", + "a value in range [512B, 64MB]"); return; } if (granularity & (granularity - 1)) { - error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", "power of 2"); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", + "power of 2"); return; } - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); if (!bdrv_is_inserted(bs)) { - error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); goto out; } if (!has_format) { format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name; } - if (format) { - drv = bdrv_find_format(format); - if (!drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - goto out; - } - } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) { goto out; @@ -2536,7 +2741,7 @@ void qmp_drive_mirror(const char *device, const char *target, goto out; } - to_replace_bs = check_to_replace_node(replaces, &local_err); + to_replace_bs = check_to_replace_node(bs, replaces, &local_err); if (!to_replace_bs) { error_propagate(errp, local_err); @@ -2559,7 +2764,7 @@ void qmp_drive_mirror(const char *device, const char *target, && mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ - assert(format && drv); + assert(format); bdrv_img_create(target, format, NULL, NULL, NULL, size, flags, &local_err, false); } else { @@ -2583,17 +2788,20 @@ void qmp_drive_mirror(const char *device, const char *target, goto out; } + options = qdict_new(); if (has_node_name) { - options = qdict_new(); qdict_put(options, "node-name", qstring_from_str(node_name)); } + if (format) { + qdict_put(options, "driver", qstring_from_str(format)); + } /* Mirroring takes care of copy-on-write using the source's backing * file. */ target_bs = NULL; ret = bdrv_open(&target_bs, target, NULL, options, - flags | BDRV_O_NO_BACKING, drv, &local_err); + flags | BDRV_O_NO_BACKING, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; @@ -2608,6 +2816,7 @@ void qmp_drive_mirror(const char *device, const char *target, has_replaces ? replaces : NULL, speed, granularity, buf_size, sync, on_source_error, on_target_error, + unmap, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_unref(target_bs); @@ -2623,12 +2832,14 @@ out: static BlockJob *find_block_job(const char *device, AioContext **aio_context, Error **errp) { + BlockBackend *blk; BlockDriverState *bs; - bs = bdrv_find(device); - if (!bs) { + blk = blk_by_name(device); + if (!blk) { goto notfound; } + bs = blk_bs(blk); *aio_context = bdrv_get_aio_context(bs); aio_context_acquire(*aio_context); @@ -2674,7 +2885,7 @@ void qmp_block_job_cancel(const char *device, force = false; } - if (job->paused && !force) { + if (job->user_paused && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); goto out; @@ -2691,10 +2902,11 @@ void qmp_block_job_pause(const char *device, Error **errp) AioContext *aio_context; BlockJob *job = find_block_job(device, &aio_context, errp); - if (!job) { + if (!job || job->user_paused) { return; } + job->user_paused = true; trace_qmp_block_job_pause(job); block_job_pause(job); aio_context_release(aio_context); @@ -2705,10 +2917,11 @@ void qmp_block_job_resume(const char *device, Error **errp) AioContext *aio_context; BlockJob *job = find_block_job(device, &aio_context, errp); - if (!job) { + if (!job || !job->user_paused) { return; } + job->user_paused = false; trace_qmp_block_job_resume(job); block_job_resume(job); aio_context_release(aio_context); @@ -2733,6 +2946,7 @@ void qmp_change_backing_file(const char *device, const char *backing_file, Error **errp) { + BlockBackend *blk; BlockDriverState *bs = NULL; AioContext *aio_context; BlockDriverState *image_bs = NULL; @@ -2741,12 +2955,13 @@ void qmp_change_backing_file(const char *device, int open_flags; int ret; - /* find the top layer BDS of the chain */ - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); + blk = blk_by_name(device); + if (!blk) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", device); return; } + bs = blk_bs(blk); aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); @@ -2907,15 +3122,15 @@ QemuOptsList qemu_common_drive_opts = { .type = QEMU_OPT_STRING, .help = "discard operation (ignore/off, unmap/on)", },{ - .name = "cache.writeback", + .name = BDRV_OPT_CACHE_WB, .type = QEMU_OPT_BOOL, .help = "enables writeback mode for any caches", },{ - .name = "cache.direct", + .name = BDRV_OPT_CACHE_DIRECT, .type = QEMU_OPT_BOOL, .help = "enables use of O_DIRECT (bypass the host page cache)", },{ - .name = "cache.no-flush", + .name = BDRV_OPT_CACHE_NO_FLUSH, .type = QEMU_OPT_BOOL, .help = "ignore any flush requests for the device", },{ @@ -2990,6 +3205,10 @@ QemuOptsList qemu_common_drive_opts = { .name = "throttling.iops-size", .type = QEMU_OPT_NUMBER, .help = "when limiting by iops max size of an I/O in bytes", + },{ + .name = "throttling.group", + .type = QEMU_OPT_STRING, + .help = "name of the block throttling group", },{ .name = "copy-on-read", .type = QEMU_OPT_BOOL,