X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=blockdev.c;h=7c9d8dd0acfe7f136f4601cebad92157cee7d5ae;hb=554ecc5774badd181458fe3d6a1624c2ede9f04e;hp=030070b607d107385498e1c427dc959257a64d1f;hpb=58513bde833804bc9395d79fd81aae631b97c348;p=qemu.git diff --git a/blockdev.c b/blockdev.c index 030070b60..7c9d8dd0a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -5,10 +5,33 @@ * * This work is licensed under the terms of the GNU GPL, version 2 or * later. See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include "sysemu/blockdev.h" -#include "hw/block-common.h" +#include "hw/block/block.h" #include "block/blockjob.h" #include "monitor/monitor.h" #include "qapi/qmp/qerror.h" @@ -22,6 +45,7 @@ #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", @@ -191,6 +215,7 @@ static void drive_uninit(DriveInfo *dinfo) bdrv_delete(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); + g_free(dinfo->serial); g_free(dinfo); } @@ -255,7 +280,7 @@ static int parse_block_error_action(const char *buf, bool is_read) } } -static bool do_check_io_limits(BlockIOLimit *io_limits) +static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp) { bool bps_flag; bool iops_flag; @@ -269,13 +294,25 @@ static bool do_check_io_limits(BlockIOLimit *io_limits) && ((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"); + 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"); return false; } return true; } -DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) +DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) { const char *buf; const char *file = NULL; @@ -297,10 +334,37 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) int snapshot = 0; bool copy_on_read; int ret; + Error *error = NULL; + QemuOpts *opts; + QDict *bs_opts; + const char *id; 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); + 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; + } + + 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 (id) { + qdict_del(bs_opts, "id"); + } + /* extract parameters */ bus_id = qemu_opt_get_number(opts, "bus", 0); unit_id = qemu_opt_get_number(opts, "unit", -1); @@ -378,6 +442,13 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) } } + if ((buf = qemu_opt_get(opts, "discard")) != NULL) { + if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { + error_report("invalid discard option"); + return NULL; + } + } + bdrv_flags |= BDRV_O_CACHE_WB; if ((buf = qemu_opt_get(opts, "cache")) != NULL) { if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) { @@ -427,9 +498,9 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) io_limits.iops[BLOCK_IO_LIMIT_WRITE] = qemu_opt_get_number(opts, "iops_wr", 0); - if (!do_check_io_limits(&io_limits)) { - error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) " - "cannot be used at the same time"); + if (!do_check_io_limits(&io_limits, &error)) { + error_report("%s", error_get_pretty(error)); + error_free(error); return NULL; } @@ -544,9 +615,11 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) dinfo->heads = heads; dinfo->secs = secs; dinfo->trans = translation; - dinfo->opts = opts; + dinfo->opts = all_opts; dinfo->refcount = 1; - dinfo->serial = serial; + if (serial != NULL) { + dinfo->serial = g_strdup(serial); + } QTAILQ_INSERT_TAIL(&drives, dinfo, next); bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); @@ -567,22 +640,29 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) case IF_MTD: break; case IF_VIRTIO: + { /* add virtio block device */ - opts = qemu_opts_create_nofail(qemu_find_opts("device")); + QemuOpts *devopts; + devopts = qemu_opts_create_nofail(qemu_find_opts("device")); if (arch_type == QEMU_ARCH_S390X) { - qemu_opt_set(opts, "driver", "virtio-blk-s390"); + qemu_opt_set(devopts, "driver", "virtio-blk-s390"); } else { - qemu_opt_set(opts, "driver", "virtio-blk-pci"); + qemu_opt_set(devopts, "driver", "virtio-blk-pci"); } - qemu_opt_set(opts, "drive", dinfo->id); + qemu_opt_set(devopts, "drive", dinfo->id); if (devaddr) - qemu_opt_set(opts, "addr", devaddr); + qemu_opt_set(devopts, "addr", devaddr); break; + } default: abort(); } if (!file || !*file) { - return dinfo; + if (qdict_size(bs_opts)) { + file = NULL; + } else { + return dinfo; + } } if (snapshot) { /* always use cache=unsafe with snapshot */ @@ -615,18 +695,30 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type) error_report("warning: disabling copy_on_read on readonly drive"); } - ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); + ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv); + bs_opts = NULL; + if (ret < 0) { - error_report("could not open disk image %s: %s", - file, strerror(-ret)); + if (ret == -EMEDIUMTYPE) { + error_report("could not open disk image %s: not in %s format", + file ?: dinfo->id, drv->format_name); + } else { + error_report("could not open disk image %s: %s", + file ?: dinfo->id, strerror(-ret)); + } goto err; } if (bdrv_key_required(dinfo->bdrv)) autostart = 0; + + qemu_opts_del(opts); + return dinfo; err: + qemu_opts_del(opts); + QDECREF(bs_opts); bdrv_delete(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); @@ -786,7 +878,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp) bdrv_img_create(new_image_file, format, states->old_bs->filename, states->old_bs->drv->format_name, - NULL, -1, flags, &local_err); + NULL, -1, flags, &local_err, false); if (error_is_set(&local_err)) { error_propagate(errp, local_err); goto delete_and_fail; @@ -795,7 +887,9 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp) /* We will manually add the backing_hd field to the bs later */ states->new_bs = bdrv_new(""); - ret = bdrv_open(states->new_bs, new_image_file, + /* TODO Inherit bs->options or only take explicit options with an + * extended QMP command? */ + ret = bdrv_open(states->new_bs, new_image_file, NULL, flags | BDRV_O_NO_BACKING, drv); if (ret != 0) { error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); @@ -896,7 +990,7 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename, int bdrv_flags, BlockDriver *drv, const char *password, Error **errp) { - if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) { + if (bdrv_open(bs, filename, NULL, bdrv_flags, drv) < 0) { error_set(errp, QERR_OPEN_FILE_FAILED, filename); return; } @@ -970,13 +1064,11 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd; io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr; - if (!do_check_io_limits(&io_limits)) { - error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); + if (!do_check_io_limits(&io_limits, errp)) { return; } bs->io_limits = io_limits; - bs->slice_time = BLOCK_IO_SLICE_TIME; if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) { bdrv_io_limits_enable(bs); @@ -1026,6 +1118,7 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) void qmp_block_resize(const char *device, int64_t size, Error **errp) { BlockDriverState *bs; + int ret; bs = bdrv_find(device); if (!bs) { @@ -1038,7 +1131,11 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp) return; } - switch (bdrv_truncate(bs, size)) { + /* complete all in-flight operations before resizing the device */ + bdrv_drain_all(); + + ret = bdrv_truncate(bs, size); + switch (ret) { case 0: break; case -ENOMEDIUM: @@ -1054,7 +1151,7 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp) error_set(errp, QERR_DEVICE_IN_USE, device); break; default: - error_set(errp, QERR_UNDEFINED_ERROR); + error_setg_errno(errp, -ret, "Could not resize"); break; } } @@ -1184,16 +1281,19 @@ void qmp_block_commit(const char *device, drive_get_ref(drive_get_by_blockdev(bs)); } +#define DEFAULT_MIRROR_BUF_SIZE (10 << 20) + void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, + bool has_granularity, uint32_t granularity, + 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, Error **errp) { - BlockDriverInfo bdi; BlockDriverState *bs; BlockDriverState *source, *target_bs; BlockDriver *proto_drv; @@ -1215,6 +1315,21 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_mode) { mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; } + if (!has_granularity) { + granularity = 0; + } + if (!has_buf_size) { + buf_size = DEFAULT_MIRROR_BUF_SIZE; + } + + if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { + error_set(errp, QERR_INVALID_PARAMETER, device); + return; + } + if (granularity & (granularity - 1)) { + error_set(errp, QERR_INVALID_PARAMETER, device); + return; + } bs = bdrv_find(device); if (!bs) { @@ -1255,13 +1370,13 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + bdrv_get_geometry(bs, &size); + size *= 512; if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) { /* create new image w/o backing file */ assert(format && drv); - bdrv_get_geometry(bs, &size); - size *= 512; bdrv_img_create(target, format, - NULL, NULL, NULL, size, flags, &local_err); + NULL, NULL, NULL, size, flags, &local_err, false); } else { switch (mode) { case NEW_IMAGE_MODE_EXISTING: @@ -1272,7 +1387,7 @@ void qmp_drive_mirror(const char *device, const char *target, bdrv_img_create(target, format, source->filename, source->drv->format_name, - NULL, -1, flags, &local_err); + NULL, size, flags, &local_err, false); break; default: abort(); @@ -1284,8 +1399,11 @@ void qmp_drive_mirror(const char *device, const char *target, return; } + /* Mirroring takes care of copy-on-write using the source's backing + * file. + */ target_bs = bdrv_new(""); - ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv); + ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv); if (ret < 0) { bdrv_delete(target_bs); @@ -1293,18 +1411,8 @@ void qmp_drive_mirror(const char *device, const char *target, return; } - /* We need a backing file if we will copy parts of a cluster. */ - if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 && - bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) { - ret = bdrv_open_backing_file(target_bs); - if (ret < 0) { - bdrv_delete(target_bs); - error_set(errp, QERR_OPEN_FILE_FAILED, target); - return; - } - } - - mirror_start(bs, target_bs, speed, sync, on_source_error, on_target_error, + mirror_start(bs, target_bs, speed, granularity, buf_size, sync, + on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { bdrv_delete(target_bs); @@ -1424,6 +1532,128 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) return dummy.next; } +QemuOptsList qemu_common_drive_opts = { + .name = "drive", + .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), + .desc = { + { + .name = "bus", + .type = QEMU_OPT_NUMBER, + .help = "bus number", + },{ + .name = "unit", + .type = QEMU_OPT_NUMBER, + .help = "unit number (i.e. lun for scsi)", + },{ + .name = "if", + .type = QEMU_OPT_STRING, + .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", + },{ + .name = "index", + .type = QEMU_OPT_NUMBER, + .help = "index number", + },{ + .name = "cyls", + .type = QEMU_OPT_NUMBER, + .help = "number of cylinders (ide disk geometry)", + },{ + .name = "heads", + .type = QEMU_OPT_NUMBER, + .help = "number of heads (ide disk geometry)", + },{ + .name = "secs", + .type = QEMU_OPT_NUMBER, + .help = "number of sectors (ide disk geometry)", + },{ + .name = "trans", + .type = QEMU_OPT_STRING, + .help = "chs translation (auto, lba. none)", + },{ + .name = "media", + .type = QEMU_OPT_STRING, + .help = "media type (disk, cdrom)", + },{ + .name = "snapshot", + .type = QEMU_OPT_BOOL, + .help = "enable/disable snapshot mode", + },{ + .name = "file", + .type = QEMU_OPT_STRING, + .help = "disk image", + },{ + .name = "discard", + .type = QEMU_OPT_STRING, + .help = "discard operation (ignore/off, unmap/on)", + },{ + .name = "cache", + .type = QEMU_OPT_STRING, + .help = "host cache usage (none, writeback, writethrough, " + "directsync, unsafe)", + },{ + .name = "aio", + .type = QEMU_OPT_STRING, + .help = "host AIO implementation (threads, native)", + },{ + .name = "format", + .type = QEMU_OPT_STRING, + .help = "disk format (raw, qcow2, ...)", + },{ + .name = "serial", + .type = QEMU_OPT_STRING, + .help = "disk serial number", + },{ + .name = "rerror", + .type = QEMU_OPT_STRING, + .help = "read error action", + },{ + .name = "werror", + .type = QEMU_OPT_STRING, + .help = "write error action", + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + .help = "pci address (virtio only)", + },{ + .name = "readonly", + .type = QEMU_OPT_BOOL, + .help = "open drive file as read-only", + },{ + .name = "iops", + .type = QEMU_OPT_NUMBER, + .help = "limit total I/O operations per second", + },{ + .name = "iops_rd", + .type = QEMU_OPT_NUMBER, + .help = "limit read operations per second", + },{ + .name = "iops_wr", + .type = QEMU_OPT_NUMBER, + .help = "limit write operations per second", + },{ + .name = "bps", + .type = QEMU_OPT_NUMBER, + .help = "limit total bytes per second", + },{ + .name = "bps_rd", + .type = QEMU_OPT_NUMBER, + .help = "limit read bytes per second", + },{ + .name = "bps_wr", + .type = QEMU_OPT_NUMBER, + .help = "limit write bytes per second", + },{ + .name = "copy-on-read", + .type = QEMU_OPT_BOOL, + .help = "copy read data from backing file into image file", + },{ + .name = "boot", + .type = QEMU_OPT_BOOL, + .help = "(deprecated, ignored)", + }, + { /* end of list */ } + }, +}; + QemuOptsList qemu_drive_opts = { .name = "drive", .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), @@ -1472,6 +1702,10 @@ QemuOptsList qemu_drive_opts = { .name = "file", .type = QEMU_OPT_STRING, .help = "disk image", + },{ + .name = "discard", + .type = QEMU_OPT_STRING, + .help = "discard operation (ignore/off, unmap/on)", },{ .name = "cache", .type = QEMU_OPT_STRING,