2010-06-02 16:48:27 +00:00
|
|
|
/*
|
|
|
|
* QEMU host block devices
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or
|
|
|
|
* later. See the COPYING file in the top-level directory.
|
2013-03-14 12:59:53 +00:00
|
|
|
*
|
|
|
|
* 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.
|
2010-06-02 16:48:27 +00:00
|
|
|
*/
|
|
|
|
|
2012-12-17 17:20:04 +00:00
|
|
|
#include "sysemu/blockdev.h"
|
2013-02-05 16:06:20 +00:00
|
|
|
#include "hw/block/block.h"
|
2012-12-17 17:19:44 +00:00
|
|
|
#include "block/blockjob.h"
|
2012-12-17 17:19:49 +00:00
|
|
|
#include "monitor/monitor.h"
|
2012-12-17 17:19:43 +00:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2012-12-17 17:20:00 +00:00
|
|
|
#include "qemu/option.h"
|
|
|
|
#include "qemu/config-file.h"
|
2012-12-17 17:19:43 +00:00
|
|
|
#include "qapi/qmp/types.h"
|
2012-12-17 17:20:04 +00:00
|
|
|
#include "sysemu/sysemu.h"
|
2012-12-17 17:19:44 +00:00
|
|
|
#include "block/block_int.h"
|
2011-11-23 15:28:21 +00:00
|
|
|
#include "qmp-commands.h"
|
2012-01-18 14:40:46 +00:00
|
|
|
#include "trace.h"
|
2012-12-17 17:20:04 +00:00
|
|
|
#include "sysemu/arch_init.h"
|
2010-06-02 16:48:27 +00:00
|
|
|
|
2010-06-02 16:55:22 +00:00
|
|
|
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
2013-03-15 09:35:07 +00:00
|
|
|
extern QemuOptsList qemu_common_drive_opts;
|
2013-07-30 12:58:27 +00:00
|
|
|
extern QemuOptsList qemu_old_drive_opts;
|
2010-06-02 16:48:27 +00:00
|
|
|
|
2011-01-28 10:21:39 +00:00
|
|
|
static const char *const if_name[IF_COUNT] = {
|
|
|
|
[IF_NONE] = "none",
|
|
|
|
[IF_IDE] = "ide",
|
|
|
|
[IF_SCSI] = "scsi",
|
|
|
|
[IF_FLOPPY] = "floppy",
|
|
|
|
[IF_PFLASH] = "pflash",
|
|
|
|
[IF_MTD] = "mtd",
|
|
|
|
[IF_SD] = "sd",
|
|
|
|
[IF_VIRTIO] = "virtio",
|
|
|
|
[IF_XEN] = "xen",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int if_max_devs[IF_COUNT] = {
|
2011-01-28 10:21:40 +00:00
|
|
|
/*
|
|
|
|
* Do not change these numbers! They govern how drive option
|
|
|
|
* index maps to unit and bus. That mapping is ABI.
|
|
|
|
*
|
|
|
|
* All controllers used to imlement if=T drives need to support
|
|
|
|
* if_max_devs[T] units, for any T with if_max_devs[T] != 0.
|
|
|
|
* Otherwise, some index values map to "impossible" bus, unit
|
|
|
|
* values.
|
|
|
|
*
|
|
|
|
* For instance, if you change [IF_SCSI] to 255, -drive
|
|
|
|
* if=scsi,index=12 no longer means bus=1,unit=5, but
|
|
|
|
* bus=0,unit=12. With an lsi53c895a controller (7 units max),
|
|
|
|
* the drive can't be set up. Regression.
|
|
|
|
*/
|
|
|
|
[IF_IDE] = 2,
|
|
|
|
[IF_SCSI] = 7,
|
2011-01-28 10:21:39 +00:00
|
|
|
};
|
|
|
|
|
2010-06-25 06:09:10 +00:00
|
|
|
/*
|
|
|
|
* We automatically delete the drive when a device using it gets
|
|
|
|
* unplugged. Questionable feature, but we can't just drop it.
|
|
|
|
* Device models call blockdev_mark_auto_del() to schedule the
|
|
|
|
* automatic deletion, and generic qdev code calls blockdev_auto_del()
|
|
|
|
* when deletion is actually safe.
|
|
|
|
*/
|
|
|
|
void blockdev_mark_auto_del(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
|
|
|
|
2012-03-30 11:17:10 +00:00
|
|
|
if (bs->job) {
|
|
|
|
block_job_cancel(bs->job);
|
|
|
|
}
|
2010-12-08 16:05:00 +00:00
|
|
|
if (dinfo) {
|
|
|
|
dinfo->auto_del = 1;
|
|
|
|
}
|
2010-06-25 06:09:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void blockdev_auto_del(BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
DriveInfo *dinfo = drive_get_by_blockdev(bs);
|
|
|
|
|
2010-12-08 16:05:00 +00:00
|
|
|
if (dinfo && dinfo->auto_del) {
|
2011-01-26 14:12:32 +00:00
|
|
|
drive_put_ref(dinfo);
|
2010-06-25 06:09:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-28 10:21:43 +00:00
|
|
|
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
|
|
|
|
{
|
|
|
|
int max_devs = if_max_devs[type];
|
|
|
|
return max_devs ? index / max_devs : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int drive_index_to_unit_id(BlockInterfaceType type, int index)
|
|
|
|
{
|
|
|
|
int max_devs = if_max_devs[type];
|
|
|
|
return max_devs ? index % max_devs : index;
|
|
|
|
}
|
|
|
|
|
2011-01-28 10:21:41 +00:00
|
|
|
QemuOpts *drive_def(const char *optstr)
|
|
|
|
{
|
|
|
|
return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
2011-01-31 10:50:09 +00:00
|
|
|
const char *optstr)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
QemuOpts *opts;
|
2011-01-28 10:21:41 +00:00
|
|
|
char buf[32];
|
2010-06-02 16:48:27 +00:00
|
|
|
|
2011-01-28 10:21:41 +00:00
|
|
|
opts = drive_def(optstr);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (!opts) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-28 10:21:41 +00:00
|
|
|
if (type != IF_DEFAULT) {
|
|
|
|
qemu_opt_set(opts, "if", if_name[type]);
|
|
|
|
}
|
|
|
|
if (index >= 0) {
|
|
|
|
snprintf(buf, sizeof(buf), "%d", index);
|
|
|
|
qemu_opt_set(opts, "index", buf);
|
|
|
|
}
|
2010-06-02 16:48:27 +00:00
|
|
|
if (file)
|
|
|
|
qemu_opt_set(opts, "file", file);
|
|
|
|
return opts;
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
|
|
|
|
{
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
/* seek interface, bus and unit */
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
|
|
|
if (dinfo->type == type &&
|
|
|
|
dinfo->bus == bus &&
|
|
|
|
dinfo->unit == unit)
|
|
|
|
return dinfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-28 10:21:44 +00:00
|
|
|
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index)
|
|
|
|
{
|
|
|
|
return drive_get(type,
|
|
|
|
drive_index_to_bus_id(type, index),
|
|
|
|
drive_index_to_unit_id(type, index));
|
|
|
|
}
|
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
int drive_get_max_bus(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
int max_bus;
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
max_bus = -1;
|
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
|
|
|
if(dinfo->type == type &&
|
|
|
|
dinfo->bus > max_bus)
|
|
|
|
max_bus = dinfo->bus;
|
|
|
|
}
|
|
|
|
return max_bus;
|
|
|
|
}
|
|
|
|
|
2011-01-28 10:21:37 +00:00
|
|
|
/* Get a block device. This should only be used for single-drive devices
|
|
|
|
(e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
|
|
|
|
appropriate bus. */
|
|
|
|
DriveInfo *drive_get_next(BlockInterfaceType type)
|
|
|
|
{
|
|
|
|
static int next_block_unit[IF_COUNT];
|
|
|
|
|
|
|
|
return drive_get(type, 0, next_block_unit[type]++);
|
|
|
|
}
|
|
|
|
|
2010-06-24 15:25:32 +00:00
|
|
|
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(dinfo, &drives, next) {
|
2010-06-24 15:25:32 +00:00
|
|
|
if (dinfo->bdrv == bs) {
|
|
|
|
return dinfo;
|
|
|
|
}
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2010-06-24 15:25:32 +00:00
|
|
|
return NULL;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bdrv_format_print(void *opaque, const char *name)
|
|
|
|
{
|
2011-01-17 18:31:27 +00:00
|
|
|
error_printf(" %s", name);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2011-01-26 14:12:32 +00:00
|
|
|
static void drive_uninit(DriveInfo *dinfo)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
qemu_opts_del(dinfo->opts);
|
|
|
|
bdrv_delete(dinfo->bdrv);
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(dinfo->id);
|
2010-06-02 16:48:27 +00:00
|
|
|
QTAILQ_REMOVE(&drives, dinfo, next);
|
2013-03-15 09:35:06 +00:00
|
|
|
g_free(dinfo->serial);
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(dinfo);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2011-01-26 14:12:32 +00:00
|
|
|
void drive_put_ref(DriveInfo *dinfo)
|
|
|
|
{
|
|
|
|
assert(dinfo->refcount);
|
|
|
|
if (--dinfo->refcount == 0) {
|
|
|
|
drive_uninit(dinfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void drive_get_ref(DriveInfo *dinfo)
|
|
|
|
{
|
|
|
|
dinfo->refcount++;
|
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:50 +00:00
|
|
|
typedef struct {
|
|
|
|
QEMUBH *bh;
|
|
|
|
DriveInfo *dinfo;
|
|
|
|
} DrivePutRefBH;
|
|
|
|
|
|
|
|
static void drive_put_ref_bh(void *opaque)
|
|
|
|
{
|
|
|
|
DrivePutRefBH *s = opaque;
|
|
|
|
|
|
|
|
drive_put_ref(s->dinfo);
|
|
|
|
qemu_bh_delete(s->bh);
|
|
|
|
g_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release a drive 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.
|
|
|
|
*/
|
|
|
|
static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
|
|
|
|
{
|
|
|
|
DrivePutRefBH *s;
|
|
|
|
|
|
|
|
s = g_new(DrivePutRefBH, 1);
|
|
|
|
s->bh = qemu_bh_new(drive_put_ref_bh, s);
|
|
|
|
s->dinfo = dinfo;
|
|
|
|
qemu_bh_schedule(s->bh);
|
|
|
|
}
|
|
|
|
|
2012-09-28 15:22:56 +00:00
|
|
|
static int parse_block_error_action(const char *buf, bool is_read)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
if (!strcmp(buf, "ignore")) {
|
2012-09-28 15:22:55 +00:00
|
|
|
return BLOCKDEV_ON_ERROR_IGNORE;
|
2010-06-02 16:48:27 +00:00
|
|
|
} else if (!is_read && !strcmp(buf, "enospc")) {
|
2012-09-28 15:22:55 +00:00
|
|
|
return BLOCKDEV_ON_ERROR_ENOSPC;
|
2010-06-02 16:48:27 +00:00
|
|
|
} else if (!strcmp(buf, "stop")) {
|
2012-09-28 15:22:55 +00:00
|
|
|
return BLOCKDEV_ON_ERROR_STOP;
|
2010-06-02 16:48:27 +00:00
|
|
|
} else if (!strcmp(buf, "report")) {
|
2012-09-28 15:22:55 +00:00
|
|
|
return BLOCKDEV_ON_ERROR_REPORT;
|
2010-06-02 16:48:27 +00:00
|
|
|
} else {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("'%s' invalid %s error action",
|
|
|
|
buf, is_read ? "read" : "write");
|
2010-06-02 16:48:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-13 15:53:42 +00:00
|
|
|
static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp)
|
2011-11-03 08:57:25 +00:00
|
|
|
{
|
|
|
|
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) {
|
2013-02-13 15:53:42 +00:00
|
|
|
error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
|
|
|
|
"cannot be used at the same time");
|
2011-11-03 08:57:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-13 15:53:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-11-03 08:57:25 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-17 12:41:54 +00:00
|
|
|
static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|
|
|
BlockInterfaceType block_default_type)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
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;
|
2011-11-03 08:57:25 +00:00
|
|
|
BlockIOLimit io_limits;
|
2010-06-02 16:48:27 +00:00
|
|
|
int snapshot = 0;
|
2011-11-17 13:40:32 +00:00
|
|
|
bool copy_on_read;
|
2010-06-02 16:48:27 +00:00
|
|
|
int ret;
|
2013-02-13 15:53:42 +00:00
|
|
|
Error *error = NULL;
|
2013-03-15 09:35:07 +00:00
|
|
|
QemuOpts *opts;
|
|
|
|
QDict *bs_opts;
|
|
|
|
const char *id;
|
2013-07-09 09:09:02 +00:00
|
|
|
bool has_driver_specific_opts;
|
2013-08-08 14:45:16 +00:00
|
|
|
BlockDriver *drv = NULL;
|
2010-06-02 16:48:27 +00:00
|
|
|
|
|
|
|
translation = BIOS_ATA_TRANSLATION_AUTO;
|
|
|
|
media = MEDIA_DISK;
|
|
|
|
|
2013-03-15 09:35:07 +00:00
|
|
|
/* 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");
|
|
|
|
}
|
|
|
|
|
2013-07-09 09:09:02 +00:00
|
|
|
has_driver_specific_opts = !!qdict_size(bs_opts);
|
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
/* 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);
|
2013-07-19 18:07:29 +00:00
|
|
|
ro = qemu_opt_get_bool(opts, "read-only", 0);
|
2011-11-17 13:40:32 +00:00
|
|
|
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
|
2010-06-02 16:48:27 +00:00
|
|
|
|
|
|
|
file = qemu_opt_get(opts, "file");
|
|
|
|
serial = qemu_opt_get(opts, "serial");
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "if")) != NULL) {
|
2011-01-28 10:21:39 +00:00
|
|
|
for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
|
|
|
|
;
|
|
|
|
if (type == IF_COUNT) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("unsupported bus type '%s'", buf);
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-07-01 13:46:12 +00:00
|
|
|
} else {
|
2012-11-20 14:30:34 +00:00
|
|
|
type = block_default_type;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2011-07-01 13:46:12 +00:00
|
|
|
|
2011-01-28 10:21:39 +00:00
|
|
|
max_devs = if_max_devs[type];
|
2010-06-02 16:48:27 +00:00
|
|
|
|
|
|
|
if (cyls || heads || secs) {
|
2012-07-10 09:12:54 +00:00
|
|
|
if (cyls < 1) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("invalid physical cyls number");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2012-07-10 09:12:54 +00:00
|
|
|
if (heads < 1) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("invalid physical heads number");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2012-07-10 09:12:54 +00:00
|
|
|
if (secs < 1) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("invalid physical secs number");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
|
|
|
|
if (!cyls) {
|
2011-06-22 12:03:57 +00:00
|
|
|
error_report("'%s' trans must be used with cyls, heads and secs",
|
2011-01-17 18:31:27 +00:00
|
|
|
buf);
|
2010-06-02 16:48:27 +00:00
|
|
|
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 {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("'%s' invalid translation type", buf);
|
2010-06-02 16:48:27 +00:00
|
|
|
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) {
|
2011-07-01 13:46:13 +00:00
|
|
|
error_report("CHS can't be set with media=%s", buf);
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
media = MEDIA_CDROM;
|
|
|
|
} else {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("'%s' invalid media", buf);
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 13:06:12 +00:00
|
|
|
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
|
|
|
|
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
|
|
|
|
error_report("invalid discard option");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-18 14:31:25 +00:00
|
|
|
if (qemu_opt_get_bool(opts, "cache.writeback", true)) {
|
|
|
|
bdrv_flags |= BDRV_O_CACHE_WB;
|
|
|
|
}
|
|
|
|
if (qemu_opt_get_bool(opts, "cache.direct", false)) {
|
|
|
|
bdrv_flags |= BDRV_O_NOCACHE;
|
|
|
|
}
|
|
|
|
if (qemu_opt_get_bool(opts, "cache.no-flush", true)) {
|
|
|
|
bdrv_flags |= BDRV_O_NO_FLUSH;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_LINUX_AIO
|
|
|
|
if ((buf = qemu_opt_get(opts, "aio")) != NULL) {
|
|
|
|
if (!strcmp(buf, "native")) {
|
|
|
|
bdrv_flags |= BDRV_O_NATIVE_AIO;
|
|
|
|
} else if (!strcmp(buf, "threads")) {
|
|
|
|
/* this is the default */
|
|
|
|
} else {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("invalid aio option");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
|
2012-08-02 12:45:54 +00:00
|
|
|
if (is_help_option(buf)) {
|
|
|
|
error_printf("Supported formats:");
|
|
|
|
bdrv_iterate_format(bdrv_format_print, NULL);
|
|
|
|
error_printf("\n");
|
|
|
|
return NULL;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2013-07-09 09:09:02 +00:00
|
|
|
|
2013-08-08 14:45:16 +00:00
|
|
|
drv = bdrv_find_whitelisted_format(buf, ro);
|
|
|
|
if (!drv) {
|
|
|
|
error_report("'%s' invalid format", buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2011-11-03 08:57:25 +00:00
|
|
|
/* disk I/O throttling */
|
|
|
|
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] =
|
2013-07-17 12:41:54 +00:00
|
|
|
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
2011-11-03 08:57:25 +00:00
|
|
|
io_limits.bps[BLOCK_IO_LIMIT_READ] =
|
2013-07-17 12:41:54 +00:00
|
|
|
qemu_opt_get_number(opts, "throttling.bps-read", 0);
|
2011-11-03 08:57:25 +00:00
|
|
|
io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
|
2013-07-17 12:41:54 +00:00
|
|
|
qemu_opt_get_number(opts, "throttling.bps-write", 0);
|
2011-11-03 08:57:25 +00:00
|
|
|
io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
|
2013-07-17 12:41:54 +00:00
|
|
|
qemu_opt_get_number(opts, "throttling.iops-total", 0);
|
2011-11-03 08:57:25 +00:00
|
|
|
io_limits.iops[BLOCK_IO_LIMIT_READ] =
|
2013-07-17 12:41:54 +00:00
|
|
|
qemu_opt_get_number(opts, "throttling.iops-read", 0);
|
2011-11-03 08:57:25 +00:00
|
|
|
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
|
2013-07-17 12:41:54 +00:00
|
|
|
qemu_opt_get_number(opts, "throttling.iops-write", 0);
|
2011-11-03 08:57:25 +00:00
|
|
|
|
2013-02-13 15:53:42 +00:00
|
|
|
if (!do_check_io_limits(&io_limits, &error)) {
|
|
|
|
error_report("%s", error_get_pretty(error));
|
|
|
|
error_free(error);
|
2011-11-03 08:57:25 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-05 17:51:43 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2012-09-28 15:22:55 +00:00
|
|
|
on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;
|
2010-06-02 16:48:27 +00:00
|
|
|
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
|
|
|
|
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("werror is not supported by this bus type");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
on_write_error = parse_block_error_action(buf, 0);
|
|
|
|
if (on_write_error < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-28 15:22:55 +00:00
|
|
|
on_read_error = BLOCKDEV_ON_ERROR_REPORT;
|
2010-06-02 16:48:27 +00:00
|
|
|
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
|
2010-10-25 12:52:21 +00:00
|
|
|
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("rerror is not supported by this bus type");
|
2010-06-02 16:48:27 +00:00
|
|
|
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) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("addr is not supported by this bus type");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* compute bus and unit according index */
|
|
|
|
|
|
|
|
if (index != -1) {
|
|
|
|
if (bus_id != 0 || unit_id != -1) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("index cannot be used with bus and unit");
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-01-28 10:21:43 +00:00
|
|
|
bus_id = drive_index_to_bus_id(type, index);
|
|
|
|
unit_id = drive_index_to_unit_id(type, index);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
2011-01-17 18:31:27 +00:00
|
|
|
error_report("unit %d too big (max is %d)",
|
|
|
|
unit_id, max_devs - 1);
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
blockdev: Reject multiple definitions for the same drive
We silently ignore multiple definitions for the same drive:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=ide,index=1,file=tmp.qcow2 -drive if=ide,index=1,file=nonexistant
QEMU 0.13.50 monitor - type 'help' for more information
(qemu) info block
ide0-hd1: type=hd removable=0 file=tmp.qcow2 backing_file=tmp.img ro=0 drv=qcow2 encrypted=0
With if=none, this can become quite confusing:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=none,index=1,file=tmp.qcow2,id=eins -drive if=none,index=1,file=nonexistant,id=zwei -device ide-drive,drive=eins -device ide-drive,drive=zwei
qemu-system-x86_64: -device ide-drive,drive=zwei: Property 'ide-drive.drive' can't find value 'zwei'
The second -device fails, because it refers to drive zwei, which got
silently ignored.
Make multiple drive definitions fail cleanly.
Unfortunately, there's code that relies on multiple drive definitions
being silently ignored: main() merrily adds default drives even when
the user already defined these drives. Fix that up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-01-28 10:21:45 +00:00
|
|
|
* catch multiple definitions
|
2010-06-02 16:48:27 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
if (drive_get(type, bus_id, unit_id) != NULL) {
|
blockdev: Reject multiple definitions for the same drive
We silently ignore multiple definitions for the same drive:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=ide,index=1,file=tmp.qcow2 -drive if=ide,index=1,file=nonexistant
QEMU 0.13.50 monitor - type 'help' for more information
(qemu) info block
ide0-hd1: type=hd removable=0 file=tmp.qcow2 backing_file=tmp.img ro=0 drv=qcow2 encrypted=0
With if=none, this can become quite confusing:
$ qemu-system-x86_64 -nodefaults -vnc :1 -S -monitor stdio -drive if=none,index=1,file=tmp.qcow2,id=eins -drive if=none,index=1,file=nonexistant,id=zwei -device ide-drive,drive=eins -device ide-drive,drive=zwei
qemu-system-x86_64: -device ide-drive,drive=zwei: Property 'ide-drive.drive' can't find value 'zwei'
The second -device fails, because it refers to drive zwei, which got
silently ignored.
Make multiple drive definitions fail cleanly.
Unfortunately, there's code that relies on multiple drive definitions
being silently ignored: main() merrily adds default drives even when
the user already defined these drives. Fix that up.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-01-28 10:21:45 +00:00
|
|
|
error_report("drive with bus=%d, unit=%d (index=%d) exists",
|
|
|
|
bus_id, unit_id, index);
|
2010-06-02 16:48:27 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init */
|
|
|
|
|
2011-08-21 03:09:37 +00:00
|
|
|
dinfo = g_malloc0(sizeof(*dinfo));
|
2010-06-02 16:48:27 +00:00
|
|
|
if ((buf = qemu_opts_id(opts)) != NULL) {
|
2011-08-21 03:09:37 +00:00
|
|
|
dinfo->id = g_strdup(buf);
|
2010-06-02 16:48:27 +00:00
|
|
|
} else {
|
|
|
|
/* no id supplied -> create one */
|
2011-08-21 03:09:37 +00:00
|
|
|
dinfo->id = g_malloc0(32);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (type == IF_IDE || type == IF_SCSI)
|
|
|
|
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
|
|
|
|
if (max_devs)
|
|
|
|
snprintf(dinfo->id, 32, "%s%i%s%i",
|
2012-06-05 12:52:53 +00:00
|
|
|
if_name[type], bus_id, mediastr, unit_id);
|
2010-06-02 16:48:27 +00:00
|
|
|
else
|
|
|
|
snprintf(dinfo->id, 32, "%s%s%i",
|
2012-06-05 12:52:53 +00:00
|
|
|
if_name[type], mediastr, unit_id);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
dinfo->bdrv = bdrv_new(dinfo->id);
|
2012-09-20 23:20:22 +00:00
|
|
|
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
|
|
|
|
dinfo->bdrv->read_only = ro;
|
2010-06-02 16:48:27 +00:00
|
|
|
dinfo->devaddr = devaddr;
|
|
|
|
dinfo->type = type;
|
|
|
|
dinfo->bus = bus_id;
|
|
|
|
dinfo->unit = unit_id;
|
2012-07-10 09:12:39 +00:00
|
|
|
dinfo->cyls = cyls;
|
|
|
|
dinfo->heads = heads;
|
|
|
|
dinfo->secs = secs;
|
|
|
|
dinfo->trans = translation;
|
2013-03-15 09:35:07 +00:00
|
|
|
dinfo->opts = all_opts;
|
2011-01-26 14:12:32 +00:00
|
|
|
dinfo->refcount = 1;
|
2013-03-15 09:35:06 +00:00
|
|
|
if (serial != NULL) {
|
|
|
|
dinfo->serial = g_strdup(serial);
|
|
|
|
}
|
2010-06-02 16:48:27 +00:00
|
|
|
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
|
|
|
|
|
2010-06-02 16:55:17 +00:00
|
|
|
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
|
|
|
|
2011-11-03 08:57:25 +00:00
|
|
|
/* disk I/O throttling */
|
|
|
|
bdrv_set_io_limits(dinfo->bdrv, &io_limits);
|
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
switch(type) {
|
|
|
|
case IF_IDE:
|
|
|
|
case IF_SCSI:
|
|
|
|
case IF_XEN:
|
|
|
|
case IF_NONE:
|
block: Geometry and translation hints are now useless, purge them
There are two producers of these hints: drive_init() on behalf of
-drive, and hd_geometry_guess().
The only consumer of the hint is hd_geometry_guess().
The callers of hd_geometry_guess() call it only when drive_init()
didn't set the hints. Therefore, drive_init()'s hints are never used.
Thus, hd_geometry_guess() only ever sees hints it produced itself in a
prior call. Only the first call computes something, subsequent calls
just repeat the first call's results. However, hd_geometry_guess() is
never called more than once: the device models don't, and the block
device is destroyed on unplug. Thus, dropping the repeat feature
doesn't break anything now.
If a block device wasn't destroyed on unplug and could be reused with
a new device, then repeating old results would be wrong. Thus,
dropping the repeat feature prevents future breakage.
This renders the hints unused. Purge them from the block layer.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-07-10 09:12:50 +00:00
|
|
|
dinfo->media_cd = media == MEDIA_CDROM;
|
2010-06-02 16:48:27 +00:00
|
|
|
break;
|
|
|
|
case IF_SD:
|
|
|
|
case IF_FLOPPY:
|
|
|
|
case IF_PFLASH:
|
|
|
|
case IF_MTD:
|
|
|
|
break;
|
|
|
|
case IF_VIRTIO:
|
2013-03-15 09:35:07 +00:00
|
|
|
{
|
2010-06-02 16:48:27 +00:00
|
|
|
/* add virtio block device */
|
2013-03-15 09:35:07 +00:00
|
|
|
QemuOpts *devopts;
|
|
|
|
devopts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
2012-02-03 17:13:30 +00:00
|
|
|
if (arch_type == QEMU_ARCH_S390X) {
|
2013-03-15 09:35:07 +00:00
|
|
|
qemu_opt_set(devopts, "driver", "virtio-blk-s390");
|
2012-02-03 17:13:30 +00:00
|
|
|
} else {
|
2013-03-15 09:35:07 +00:00
|
|
|
qemu_opt_set(devopts, "driver", "virtio-blk-pci");
|
2012-02-03 17:13:30 +00:00
|
|
|
}
|
2013-03-15 09:35:07 +00:00
|
|
|
qemu_opt_set(devopts, "drive", dinfo->id);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (devaddr)
|
2013-03-15 09:35:07 +00:00
|
|
|
qemu_opt_set(devopts, "addr", devaddr);
|
2010-06-02 16:48:27 +00:00
|
|
|
break;
|
2013-03-15 09:35:07 +00:00
|
|
|
}
|
2011-01-28 10:21:41 +00:00
|
|
|
default:
|
2010-06-02 16:48:27 +00:00
|
|
|
abort();
|
|
|
|
}
|
2010-05-11 13:36:46 +00:00
|
|
|
if (!file || !*file) {
|
2013-07-09 09:09:02 +00:00
|
|
|
if (has_driver_specific_opts) {
|
2013-03-18 15:40:51 +00:00
|
|
|
file = NULL;
|
|
|
|
} else {
|
|
|
|
return dinfo;
|
|
|
|
}
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
if (snapshot) {
|
|
|
|
/* always use cache=unsafe with snapshot */
|
|
|
|
bdrv_flags &= ~BDRV_O_CACHE_MASK;
|
|
|
|
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
|
|
|
|
}
|
|
|
|
|
2011-11-17 13:40:32 +00:00
|
|
|
if (copy_on_read) {
|
|
|
|
bdrv_flags |= BDRV_O_COPY_ON_READ;
|
|
|
|
}
|
|
|
|
|
2012-03-23 07:36:50 +00:00
|
|
|
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
|
|
|
bdrv_flags |= BDRV_O_INCOMING;
|
|
|
|
}
|
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
if (media == MEDIA_CDROM) {
|
|
|
|
/* CDROM is fine for any interface, don't check. */
|
|
|
|
ro = 1;
|
|
|
|
} else if (ro == 1) {
|
2012-02-22 07:18:48 +00:00
|
|
|
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
|
|
|
|
type != IF_NONE && type != IF_PFLASH) {
|
2013-07-19 18:07:29 +00:00
|
|
|
error_report("read-only not supported by this bus type");
|
2011-02-08 14:12:39 +00:00
|
|
|
goto err;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
|
|
|
|
|
2012-06-27 17:03:13 +00:00
|
|
|
if (ro && copy_on_read) {
|
2013-07-19 18:07:29 +00:00
|
|
|
error_report("warning: disabling copy_on_read on read-only drive");
|
2012-06-27 17:03:13 +00:00
|
|
|
}
|
|
|
|
|
2013-07-09 09:09:02 +00:00
|
|
|
QINCREF(bs_opts);
|
2013-08-08 14:45:16 +00:00
|
|
|
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv);
|
2013-03-15 09:35:07 +00:00
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
if (ret < 0) {
|
2013-01-17 20:45:24 +00:00
|
|
|
if (ret == -EMEDIUMTYPE) {
|
|
|
|
error_report("could not open disk image %s: not in %s format",
|
2013-08-08 14:45:16 +00:00
|
|
|
file ?: dinfo->id, drv ? drv->format_name :
|
|
|
|
qdict_get_str(bs_opts, "driver"));
|
2013-01-17 20:45:24 +00:00
|
|
|
} else {
|
|
|
|
error_report("could not open disk image %s: %s",
|
2013-03-18 15:40:51 +00:00
|
|
|
file ?: dinfo->id, strerror(-ret));
|
2013-01-17 20:45:24 +00:00
|
|
|
}
|
2011-02-08 14:12:39 +00:00
|
|
|
goto err;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_key_required(dinfo->bdrv))
|
|
|
|
autostart = 0;
|
2013-03-15 09:35:07 +00:00
|
|
|
|
2013-07-09 09:09:02 +00:00
|
|
|
QDECREF(bs_opts);
|
2013-03-15 09:35:07 +00:00
|
|
|
qemu_opts_del(opts);
|
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
return dinfo;
|
2011-02-08 14:12:39 +00:00
|
|
|
|
|
|
|
err:
|
2013-03-15 09:35:07 +00:00
|
|
|
qemu_opts_del(opts);
|
|
|
|
QDECREF(bs_opts);
|
2011-02-08 14:12:39 +00:00
|
|
|
bdrv_delete(dinfo->bdrv);
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(dinfo->id);
|
2011-02-08 14:12:39 +00:00
|
|
|
QTAILQ_REMOVE(&drives, dinfo, next);
|
2011-08-21 03:09:37 +00:00
|
|
|
g_free(dinfo);
|
2011-02-08 14:12:39 +00:00
|
|
|
return NULL;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2013-07-17 12:41:54 +00:00
|
|
|
static void qemu_opt_rename(QemuOpts *opts, const char *from, const char *to)
|
|
|
|
{
|
|
|
|
const char *value;
|
|
|
|
|
|
|
|
value = qemu_opt_get(opts, from);
|
|
|
|
if (value) {
|
|
|
|
qemu_opt_set(opts, to, value);
|
|
|
|
qemu_opt_unset(opts, from);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|
|
|
{
|
2013-07-18 14:31:25 +00:00
|
|
|
const char *value;
|
|
|
|
|
2013-07-30 12:58:27 +00:00
|
|
|
/*
|
|
|
|
* Check that only old options are used by copying into a QemuOpts with
|
|
|
|
* stricter checks. Going through a QDict seems to be the easiest way to
|
|
|
|
* achieve this...
|
|
|
|
*/
|
|
|
|
QemuOpts* check_opts;
|
|
|
|
QDict *qdict;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
qdict = qemu_opts_to_qdict(all_opts, NULL);
|
|
|
|
check_opts = qemu_opts_from_qdict(&qemu_old_drive_opts, qdict, &local_err);
|
|
|
|
QDECREF(qdict);
|
|
|
|
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
qemu_opts_del(check_opts);
|
|
|
|
|
2013-07-17 12:41:54 +00:00
|
|
|
/* Change legacy command line options into QMP ones */
|
|
|
|
qemu_opt_rename(all_opts, "iops", "throttling.iops-total");
|
|
|
|
qemu_opt_rename(all_opts, "iops_rd", "throttling.iops-read");
|
|
|
|
qemu_opt_rename(all_opts, "iops_wr", "throttling.iops-write");
|
|
|
|
|
|
|
|
qemu_opt_rename(all_opts, "bps", "throttling.bps-total");
|
|
|
|
qemu_opt_rename(all_opts, "bps_rd", "throttling.bps-read");
|
|
|
|
qemu_opt_rename(all_opts, "bps_wr", "throttling.bps-write");
|
|
|
|
|
2013-07-19 18:07:29 +00:00
|
|
|
qemu_opt_rename(all_opts, "readonly", "read-only");
|
|
|
|
|
2013-07-18 14:31:25 +00:00
|
|
|
value = qemu_opt_get(all_opts, "cache");
|
|
|
|
if (value) {
|
|
|
|
int flags = 0;
|
|
|
|
|
|
|
|
if (bdrv_parse_cache_flags(value, &flags) != 0) {
|
|
|
|
error_report("invalid cache option");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Specific options take precedence */
|
|
|
|
if (!qemu_opt_get(all_opts, "cache.writeback")) {
|
|
|
|
qemu_opt_set_bool(all_opts, "cache.writeback",
|
|
|
|
!!(flags & BDRV_O_CACHE_WB));
|
|
|
|
}
|
|
|
|
if (!qemu_opt_get(all_opts, "cache.direct")) {
|
|
|
|
qemu_opt_set_bool(all_opts, "cache.direct",
|
|
|
|
!!(flags & BDRV_O_NOCACHE));
|
|
|
|
}
|
|
|
|
if (!qemu_opt_get(all_opts, "cache.no-flush")) {
|
|
|
|
qemu_opt_set_bool(all_opts, "cache.no-flush",
|
|
|
|
!!(flags & BDRV_O_NO_FLUSH));
|
|
|
|
}
|
|
|
|
qemu_opt_unset(all_opts, "cache");
|
|
|
|
}
|
|
|
|
|
2013-07-17 12:41:54 +00:00
|
|
|
return blockdev_init(all_opts, block_default_type);
|
|
|
|
}
|
|
|
|
|
2010-06-02 16:48:27 +00:00
|
|
|
void do_commit(Monitor *mon, const QDict *qdict)
|
|
|
|
{
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2010-06-02 16:55:18 +00:00
|
|
|
BlockDriverState *bs;
|
2012-03-05 18:10:11 +00:00
|
|
|
int ret;
|
2010-06-02 16:48:27 +00:00
|
|
|
|
2010-06-02 16:55:18 +00:00
|
|
|
if (!strcmp(device, "all")) {
|
2012-03-05 18:10:11 +00:00
|
|
|
ret = bdrv_commit_all();
|
2010-06-02 16:55:18 +00:00
|
|
|
} else {
|
|
|
|
bs = bdrv_find(device);
|
2010-06-02 16:55:19 +00:00
|
|
|
if (!bs) {
|
2013-01-18 17:45:35 +00:00
|
|
|
monitor_printf(mon, "Device '%s' not found\n", device);
|
2010-06-02 16:55:19 +00:00
|
|
|
return;
|
2010-06-02 16:55:18 +00:00
|
|
|
}
|
2012-01-18 14:40:41 +00:00
|
|
|
ret = bdrv_commit(bs);
|
2013-01-18 17:45:35 +00:00
|
|
|
}
|
|
|
|
if (ret < 0) {
|
|
|
|
monitor_printf(mon, "'commit' error for '%s': %s\n", device,
|
|
|
|
strerror(-ret));
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-06 17:55:59 +00:00
|
|
|
static void blockdev_do_action(int kind, void *data, Error **errp)
|
|
|
|
{
|
2013-05-13 08:43:43 +00:00
|
|
|
TransactionAction action;
|
|
|
|
TransactionActionList list;
|
2012-03-06 17:55:59 +00:00
|
|
|
|
|
|
|
action.kind = kind;
|
|
|
|
action.data = data;
|
|
|
|
list.value = &action;
|
|
|
|
list.next = NULL;
|
|
|
|
qmp_transaction(&list, errp);
|
|
|
|
}
|
|
|
|
|
2011-11-25 18:15:19 +00:00
|
|
|
void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
|
|
|
|
bool has_format, const char *format,
|
2012-03-06 17:55:59 +00:00
|
|
|
bool has_mode, enum NewImageMode mode,
|
2011-11-25 18:15:19 +00:00
|
|
|
Error **errp)
|
2010-12-16 12:52:16 +00:00
|
|
|
{
|
2012-03-06 17:55:59 +00:00
|
|
|
BlockdevSnapshot snapshot = {
|
|
|
|
.device = (char *) device,
|
|
|
|
.snapshot_file = (char *) snapshot_file,
|
|
|
|
.has_format = has_format,
|
|
|
|
.format = (char *) format,
|
|
|
|
.has_mode = has_mode,
|
|
|
|
.mode = mode,
|
|
|
|
};
|
2013-05-13 08:43:43 +00:00
|
|
|
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
|
|
|
|
&snapshot, errp);
|
2010-12-16 12:52:16 +00:00
|
|
|
}
|
|
|
|
|
2012-02-28 20:54:06 +00:00
|
|
|
|
|
|
|
/* New and old BlockDriverState structs for group snapshots */
|
2013-05-08 10:25:16 +00:00
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
typedef struct BlkTransactionState BlkTransactionState;
|
2013-05-08 10:25:16 +00:00
|
|
|
|
|
|
|
/* Only prepare() may fail. In a single transaction, only one of commit() or
|
|
|
|
abort() will be called, clean() will always be called if it present. */
|
|
|
|
typedef struct BdrvActionOps {
|
|
|
|
/* Size of state struct, in bytes. */
|
|
|
|
size_t instance_size;
|
|
|
|
/* Prepare the work, must NOT be NULL. */
|
2013-06-24 15:13:15 +00:00
|
|
|
void (*prepare)(BlkTransactionState *common, Error **errp);
|
2013-06-24 15:13:16 +00:00
|
|
|
/* Commit the changes, can be NULL. */
|
2013-06-24 15:13:15 +00:00
|
|
|
void (*commit)(BlkTransactionState *common);
|
2013-05-08 10:25:16 +00:00
|
|
|
/* Abort the changes on fail, can be NULL. */
|
2013-06-24 15:13:15 +00:00
|
|
|
void (*abort)(BlkTransactionState *common);
|
2013-05-08 10:25:16 +00:00
|
|
|
/* Clean up resource in the end, can be NULL. */
|
2013-06-24 15:13:15 +00:00
|
|
|
void (*clean)(BlkTransactionState *common);
|
2013-05-08 10:25:16 +00:00
|
|
|
} BdrvActionOps;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This structure must be arranged as first member in child type, assuming
|
|
|
|
* that compiler will also arrange it to the same address with parent instance.
|
|
|
|
* Later it will be used in free().
|
|
|
|
*/
|
2013-06-24 15:13:15 +00:00
|
|
|
struct BlkTransactionState {
|
2013-05-13 08:43:43 +00:00
|
|
|
TransactionAction *action;
|
2013-05-08 10:25:16 +00:00
|
|
|
const BdrvActionOps *ops;
|
2013-06-24 15:13:15 +00:00
|
|
|
QSIMPLEQ_ENTRY(BlkTransactionState) entry;
|
2013-05-08 10:25:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* external snapshot private data */
|
2013-06-24 15:13:15 +00:00
|
|
|
typedef struct ExternalSnapshotState {
|
|
|
|
BlkTransactionState common;
|
2012-02-28 20:54:06 +00:00
|
|
|
BlockDriverState *old_bs;
|
|
|
|
BlockDriverState *new_bs;
|
2013-06-24 15:13:15 +00:00
|
|
|
} ExternalSnapshotState;
|
2012-02-28 20:54:06 +00:00
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
static void external_snapshot_prepare(BlkTransactionState *common,
|
2013-05-08 10:25:12 +00:00
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriver *drv;
|
|
|
|
int flags, ret;
|
|
|
|
Error *local_err = NULL;
|
2013-05-08 10:25:13 +00:00
|
|
|
const char *device;
|
|
|
|
const char *new_image_file;
|
|
|
|
const char *format = "qcow2";
|
|
|
|
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
2013-06-24 15:13:15 +00:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2013-05-13 08:43:43 +00:00
|
|
|
TransactionAction *action = common->action;
|
2013-05-08 10:25:12 +00:00
|
|
|
|
2013-05-08 10:25:13 +00:00
|
|
|
/* get parameters */
|
2013-05-13 08:43:43 +00:00
|
|
|
g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
|
2013-05-08 10:25:13 +00:00
|
|
|
|
|
|
|
device = action->blockdev_snapshot_sync->device;
|
|
|
|
new_image_file = action->blockdev_snapshot_sync->snapshot_file;
|
|
|
|
if (action->blockdev_snapshot_sync->has_format) {
|
|
|
|
format = action->blockdev_snapshot_sync->format;
|
|
|
|
}
|
|
|
|
if (action->blockdev_snapshot_sync->has_mode) {
|
|
|
|
mode = action->blockdev_snapshot_sync->mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start processing */
|
2013-05-08 10:25:12 +00:00
|
|
|
drv = bdrv_find_format(format);
|
|
|
|
if (!drv) {
|
|
|
|
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
state->old_bs = bdrv_find(device);
|
|
|
|
if (!state->old_bs) {
|
2013-05-08 10:25:12 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
if (!bdrv_is_inserted(state->old_bs)) {
|
2013-05-08 10:25:12 +00:00
|
|
|
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
if (bdrv_in_use(state->old_bs)) {
|
2013-05-08 10:25:12 +00:00
|
|
|
error_set(errp, QERR_DEVICE_IN_USE, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
if (!bdrv_is_read_only(state->old_bs)) {
|
|
|
|
if (bdrv_flush(state->old_bs)) {
|
2013-05-08 10:25:12 +00:00
|
|
|
error_set(errp, QERR_IO_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
flags = state->old_bs->open_flags;
|
2013-05-08 10:25:12 +00:00
|
|
|
|
|
|
|
/* create new image w/backing file */
|
|
|
|
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
|
|
|
bdrv_img_create(new_image_file, format,
|
2013-06-24 15:13:15 +00:00
|
|
|
state->old_bs->filename,
|
|
|
|
state->old_bs->drv->format_name,
|
2013-05-08 10:25:12 +00:00
|
|
|
NULL, -1, flags, &local_err, false);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We will manually add the backing_hd field to the bs later */
|
2013-06-24 15:13:15 +00:00
|
|
|
state->new_bs = bdrv_new("");
|
2013-05-08 10:25:12 +00:00
|
|
|
/* TODO Inherit bs->options or only take explicit options with an
|
|
|
|
* extended QMP command? */
|
2013-06-24 15:13:15 +00:00
|
|
|
ret = bdrv_open(state->new_bs, new_image_file, NULL,
|
2013-05-08 10:25:12 +00:00
|
|
|
flags | BDRV_O_NO_BACKING, drv);
|
|
|
|
if (ret != 0) {
|
2013-06-07 18:33:48 +00:00
|
|
|
error_setg_file_open(errp, -ret, new_image_file);
|
2013-05-08 10:25:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
static void external_snapshot_commit(BlkTransactionState *common)
|
2013-05-08 10:25:14 +00:00
|
|
|
{
|
2013-06-24 15:13:15 +00:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
2013-05-08 10:25:16 +00:00
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
/* This removes our old bs and adds the new bs */
|
|
|
|
bdrv_append(state->new_bs, state->old_bs);
|
2013-05-08 10:25:14 +00:00
|
|
|
/* We don't need (or want) to use the transactional
|
|
|
|
* bdrv_reopen_multiple() across all the entries at once, because we
|
|
|
|
* don't want to abort all of them if one of them fails the reopen */
|
2013-06-24 15:13:15 +00:00
|
|
|
bdrv_reopen(state->new_bs, state->new_bs->open_flags & ~BDRV_O_RDWR,
|
2013-05-08 10:25:14 +00:00
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
static void external_snapshot_abort(BlkTransactionState *common)
|
2013-05-08 10:25:15 +00:00
|
|
|
{
|
2013-06-24 15:13:15 +00:00
|
|
|
ExternalSnapshotState *state =
|
|
|
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
|
|
|
if (state->new_bs) {
|
|
|
|
bdrv_delete(state->new_bs);
|
2013-05-08 10:25:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
blockdev: add DriveBackup transaction
This patch adds a transactional version of the drive-backup QMP command.
It allows atomic snapshots of multiple drives along with automatic
cleanup if there is a failure to start one of the backup jobs.
Note that QMP events are emitted for block job completion/cancellation
and the block job will be listed by query-block-jobs.
@device: the name of the device whose writes should be mirrored.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:17 +00:00
|
|
|
typedef struct DriveBackupState {
|
|
|
|
BlkTransactionState common;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockJob *job;
|
|
|
|
} DriveBackupState;
|
|
|
|
|
|
|
|
static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
|
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
|
|
|
DriveBackup *backup;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
|
|
|
|
backup = common->action->drive_backup;
|
|
|
|
|
|
|
|
qmp_drive_backup(backup->device, backup->target,
|
|
|
|
backup->has_format, backup->format,
|
2013-06-26 12:11:57 +00:00
|
|
|
backup->sync,
|
blockdev: add DriveBackup transaction
This patch adds a transactional version of the drive-backup QMP command.
It allows atomic snapshots of multiple drives along with automatic
cleanup if there is a failure to start one of the backup jobs.
Note that QMP events are emitted for block job completion/cancellation
and the block job will be listed by query-block-jobs.
@device: the name of the device whose writes should be mirrored.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:17 +00:00
|
|
|
backup->has_mode, backup->mode,
|
|
|
|
backup->has_speed, backup->speed,
|
|
|
|
backup->has_on_source_error, backup->on_source_error,
|
|
|
|
backup->has_on_target_error, backup->on_target_error,
|
|
|
|
&local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
state->bs = NULL;
|
|
|
|
state->job = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->bs = bdrv_find(backup->device);
|
|
|
|
state->job = state->bs->job;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void drive_backup_abort(BlkTransactionState *common)
|
|
|
|
{
|
|
|
|
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
|
|
|
BlockDriverState *bs = state->bs;
|
|
|
|
|
|
|
|
/* Only cancel if it's the job we started */
|
|
|
|
if (bs && bs->job && bs->job == state->job) {
|
|
|
|
block_job_cancel_sync(bs->job);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:18 +00:00
|
|
|
static void abort_prepare(BlkTransactionState *common, Error **errp)
|
|
|
|
{
|
|
|
|
error_setg(errp, "Transaction aborted using Abort action");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void abort_commit(BlkTransactionState *common)
|
|
|
|
{
|
2013-07-25 16:21:28 +00:00
|
|
|
g_assert_not_reached(); /* this action never succeeds */
|
2013-06-24 15:13:18 +00:00
|
|
|
}
|
|
|
|
|
2013-05-08 10:25:16 +00:00
|
|
|
static const BdrvActionOps actions[] = {
|
2013-05-13 08:43:43 +00:00
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
|
2013-06-24 15:13:15 +00:00
|
|
|
.instance_size = sizeof(ExternalSnapshotState),
|
2013-05-08 10:25:16 +00:00
|
|
|
.prepare = external_snapshot_prepare,
|
|
|
|
.commit = external_snapshot_commit,
|
|
|
|
.abort = external_snapshot_abort,
|
|
|
|
},
|
blockdev: add DriveBackup transaction
This patch adds a transactional version of the drive-backup QMP command.
It allows atomic snapshots of multiple drives along with automatic
cleanup if there is a failure to start one of the backup jobs.
Note that QMP events are emitted for block job completion/cancellation
and the block job will be listed by query-block-jobs.
@device: the name of the device whose writes should be mirrored.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:17 +00:00
|
|
|
[TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
|
|
|
|
.instance_size = sizeof(DriveBackupState),
|
|
|
|
.prepare = drive_backup_prepare,
|
|
|
|
.abort = drive_backup_abort,
|
|
|
|
},
|
2013-06-24 15:13:18 +00:00
|
|
|
[TRANSACTION_ACTION_KIND_ABORT] = {
|
|
|
|
.instance_size = sizeof(BlkTransactionState),
|
|
|
|
.prepare = abort_prepare,
|
|
|
|
.commit = abort_commit,
|
|
|
|
},
|
2013-05-08 10:25:16 +00:00
|
|
|
};
|
|
|
|
|
2012-02-28 20:54:06 +00:00
|
|
|
/*
|
|
|
|
* 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail
|
|
|
|
* then we do not pivot any of the devices in the group, and abandon the
|
|
|
|
* snapshots
|
|
|
|
*/
|
2013-05-13 08:43:43 +00:00
|
|
|
void qmp_transaction(TransactionActionList *dev_list, Error **errp)
|
2012-02-28 20:54:06 +00:00
|
|
|
{
|
2013-05-13 08:43:43 +00:00
|
|
|
TransactionActionList *dev_entry = dev_list;
|
2013-06-24 15:13:15 +00:00
|
|
|
BlkTransactionState *state, *next;
|
2012-11-30 12:52:07 +00:00
|
|
|
Error *local_err = NULL;
|
2012-02-28 20:54:06 +00:00
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionState) snap_bdrv_states;
|
2012-02-28 20:54:06 +00:00
|
|
|
QSIMPLEQ_INIT(&snap_bdrv_states);
|
|
|
|
|
|
|
|
/* drain all i/o before any snapshots */
|
|
|
|
bdrv_drain_all();
|
|
|
|
|
|
|
|
/* We don't do anything in this loop that commits us to the snapshot */
|
|
|
|
while (NULL != dev_entry) {
|
2013-05-13 08:43:43 +00:00
|
|
|
TransactionAction *dev_info = NULL;
|
2013-05-08 10:25:16 +00:00
|
|
|
const BdrvActionOps *ops;
|
2012-03-06 17:55:57 +00:00
|
|
|
|
2012-02-28 20:54:06 +00:00
|
|
|
dev_info = dev_entry->value;
|
|
|
|
dev_entry = dev_entry->next;
|
|
|
|
|
2013-05-08 10:25:16 +00:00
|
|
|
assert(dev_info->kind < ARRAY_SIZE(actions));
|
|
|
|
|
|
|
|
ops = &actions[dev_info->kind];
|
2013-06-24 15:13:15 +00:00
|
|
|
state = g_malloc0(ops->instance_size);
|
|
|
|
state->ops = ops;
|
|
|
|
state->action = dev_info;
|
|
|
|
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
|
2012-02-28 20:54:06 +00:00
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
state->ops->prepare(state, &local_err);
|
2013-05-08 10:25:16 +00:00
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
goto delete_and_fail;
|
2012-02-28 20:54:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:15 +00:00
|
|
|
QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
|
2013-06-24 15:13:16 +00:00
|
|
|
if (state->ops->commit) {
|
|
|
|
state->ops->commit(state);
|
|
|
|
}
|
2012-02-28 20:54:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
delete_and_fail:
|
|
|
|
/*
|
|
|
|
* failure, and it is all-or-none; abandon each new bs, and keep using
|
|
|
|
* the original bs for all images
|
|
|
|
*/
|
2013-06-24 15:13:15 +00:00
|
|
|
QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
|
|
|
|
if (state->ops->abort) {
|
|
|
|
state->ops->abort(state);
|
2013-05-08 10:25:16 +00:00
|
|
|
}
|
2012-02-28 20:54:06 +00:00
|
|
|
}
|
|
|
|
exit:
|
2013-06-24 15:13:15 +00:00
|
|
|
QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
|
|
|
|
if (state->ops->clean) {
|
|
|
|
state->ops->clean(state);
|
2013-05-08 10:25:16 +00:00
|
|
|
}
|
2013-06-24 15:13:15 +00:00
|
|
|
g_free(state);
|
2012-02-28 20:54:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-07 17:47:23 +00:00
|
|
|
static void eject_device(BlockDriverState *bs, int force, Error **errp)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
2012-01-18 14:40:41 +00:00
|
|
|
if (bdrv_in_use(bs)) {
|
|
|
|
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
|
|
|
return;
|
|
|
|
}
|
block: Clean up remaining users of "removable"
BlockDriverState member removable is a confused mess. It is true when
an ide-cd, scsi-cd or floppy qdev is attached, or when the
BlockDriverState was created with -drive if={floppy,sd} or -drive
if={ide,scsi,xen,none},media=cdrom ("created removable"), except when
an ide-hd, scsi-hd, scsi-generic or virtio-blk qdev is attached.
Three users remain:
1. eject_device(), via bdrv_is_removable() uses it to determine
whether a block device can eject media.
2. bdrv_info() is monitor command "info block". QMP documentation
says "true if the device is removable, false otherwise". From the
monitor user's point of view, the only sensible interpretation of
"is removable" is "can eject media with monitor commands eject and
change".
A block device can eject media unless a device is attached that
doesn't support it. Switch the two users over to new
bdrv_dev_has_removable_media() that returns exactly that.
3. bdrv_getlength() uses to suppress its length cache when media can
change (see commit 46a4e4e6). Media change is either monitor
command change (updates the length cache), monitor command eject
(doesn't update the length cache, easily fixable), or physical
media change (invalidates length cache, not so easily fixable).
I'm refraining from improving anything here, because this series is
long enough already. Instead, I simply switch it over to
bdrv_dev_has_removable_media() as well.
This changes the behavior of the length cache and of monitor commands
eject and change in two cases:
a. drive not created removable, no device attached
The commit makes the drive removable, and defeats the length cache.
Example: -drive if=none
b. drive created removable, but the attached drive is non-removable,
and doesn't call bdrv_set_removable(..., 0) (most devices don't)
The commit makes the drive non-removable, and enables the length
cache.
Example: -drive if=xen,media=cdrom -M xenpv
The other non-removable devices that don't call
bdrv_set_removable() can't currently use a drive created removable,
either because they aren't qdevified, or because they lack a drive
property. Won't stay that way.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-09-06 16:58:51 +00:00
|
|
|
if (!bdrv_dev_has_removable_media(bs)) {
|
2011-12-07 17:47:23 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
|
|
|
|
return;
|
2011-07-20 16:23:35 +00:00
|
|
|
}
|
2011-12-07 17:47:23 +00:00
|
|
|
|
2011-11-07 16:50:13 +00:00
|
|
|
if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
|
|
|
|
bdrv_dev_eject_request(bs, force);
|
|
|
|
if (!force) {
|
2011-12-07 17:47:23 +00:00
|
|
|
error_set(errp, QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
|
|
|
|
return;
|
2011-11-07 16:50:13 +00:00
|
|
|
}
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2011-12-07 17:47:23 +00:00
|
|
|
|
2010-06-01 22:12:19 +00:00
|
|
|
bdrv_close(bs);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2011-12-07 18:02:36 +00:00
|
|
|
void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2011-12-07 18:02:36 +00:00
|
|
|
bs = bdrv_find(device);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (!bs) {
|
2011-12-07 18:02:36 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
2011-12-07 17:47:23 +00:00
|
|
|
}
|
|
|
|
|
2011-12-07 18:02:36 +00:00
|
|
|
eject_device(bs, force, errp);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2011-11-23 15:28:21 +00:00
|
|
|
void qmp_block_passwd(const char *device, const char *password, Error **errp)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
int err;
|
|
|
|
|
2011-11-23 15:28:21 +00:00
|
|
|
bs = bdrv_find(device);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (!bs) {
|
2011-11-23 15:28:21 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
|
2011-11-23 15:28:21 +00:00
|
|
|
err = bdrv_set_key(bs, password);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (err == -EINVAL) {
|
2011-11-23 15:28:21 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
|
|
|
|
return;
|
2010-06-02 16:48:27 +00:00
|
|
|
} else if (err < 0) {
|
2011-11-23 15:28:21 +00:00
|
|
|
error_set(errp, QERR_INVALID_PASSWORD);
|
|
|
|
return;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 13:13:50 +00:00
|
|
|
static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
|
|
|
|
int bdrv_flags, BlockDriver *drv,
|
|
|
|
const char *password, Error **errp)
|
|
|
|
{
|
2013-06-07 18:33:48 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv);
|
|
|
|
if (ret < 0) {
|
|
|
|
error_setg_file_open(errp, -ret, filename);
|
2011-12-08 13:13:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_key_required(bs)) {
|
|
|
|
if (password) {
|
|
|
|
if (bdrv_set_key(bs, password) < 0) {
|
|
|
|
error_set(errp, QERR_INVALID_PASSWORD);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error_set(errp, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs),
|
|
|
|
bdrv_get_encrypted_filename(bs));
|
|
|
|
}
|
|
|
|
} else if (password) {
|
|
|
|
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_change_blockdev(const char *device, const char *filename,
|
|
|
|
bool has_format, const char *format, Error **errp)
|
2010-06-02 16:48:27 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriver *drv = NULL;
|
|
|
|
int bdrv_flags;
|
2011-12-07 17:47:23 +00:00
|
|
|
Error *err = NULL;
|
2010-06-02 16:48:27 +00:00
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
2011-12-08 13:13:50 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2011-12-08 13:13:50 +00:00
|
|
|
|
|
|
|
if (format) {
|
2013-05-29 11:35:40 +00:00
|
|
|
drv = bdrv_find_whitelisted_format(format, bs->read_only);
|
2010-06-02 16:48:27 +00:00
|
|
|
if (!drv) {
|
2011-12-08 13:13:50 +00:00
|
|
|
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
|
|
|
return;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
|
|
|
}
|
2011-12-08 13:13:50 +00:00
|
|
|
|
2011-12-07 17:47:23 +00:00
|
|
|
eject_device(bs, 0, &err);
|
|
|
|
if (error_is_set(&err)) {
|
2011-12-08 13:13:50 +00:00
|
|
|
error_propagate(errp, err);
|
|
|
|
return;
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2011-12-08 13:13:50 +00:00
|
|
|
|
2010-06-25 13:26:48 +00:00
|
|
|
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
|
2010-07-25 20:49:34 +00:00
|
|
|
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
|
2011-12-08 13:13:50 +00:00
|
|
|
|
|
|
|
qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp);
|
2010-06-02 16:48:27 +00:00
|
|
|
}
|
2010-11-12 17:07:13 +00:00
|
|
|
|
2011-11-08 05:00:31 +00:00
|
|
|
/* throttling disk I/O limits */
|
2011-12-14 18:49:14 +00:00
|
|
|
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)
|
2011-11-08 05:00:31 +00:00
|
|
|
{
|
|
|
|
BlockIOLimit io_limits;
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
2011-12-14 18:49:14 +00:00
|
|
|
bs = bdrv_find(device);
|
2011-11-08 05:00:31 +00:00
|
|
|
if (!bs) {
|
2011-12-14 18:49:14 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
2011-11-08 05:00:31 +00:00
|
|
|
}
|
|
|
|
|
2011-12-14 18:49:14 +00:00
|
|
|
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;
|
2011-11-08 05:00:31 +00:00
|
|
|
|
2013-02-13 15:53:42 +00:00
|
|
|
if (!do_check_io_limits(&io_limits, errp)) {
|
2011-12-14 18:49:14 +00:00
|
|
|
return;
|
2011-11-08 05:00:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bs->io_limits = io_limits;
|
|
|
|
|
|
|
|
if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) {
|
|
|
|
bdrv_io_limits_enable(bs);
|
|
|
|
} else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) {
|
|
|
|
bdrv_io_limits_disable(bs);
|
|
|
|
} else {
|
|
|
|
if (bs->block_timer) {
|
|
|
|
qemu_mod_timer(bs->block_timer, qemu_get_clock_ns(vm_clock));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-12 17:07:13 +00:00
|
|
|
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
|
|
|
|
{
|
|
|
|
const char *id = qdict_get_str(qdict, "id");
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find(id);
|
|
|
|
if (!bs) {
|
|
|
|
qerror_report(QERR_DEVICE_NOT_FOUND, id);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-01-26 14:12:35 +00:00
|
|
|
if (bdrv_in_use(bs)) {
|
|
|
|
qerror_report(QERR_DEVICE_IN_USE, id);
|
|
|
|
return -1;
|
|
|
|
}
|
2010-11-12 17:07:13 +00:00
|
|
|
|
|
|
|
/* quiesce block driver; prevent further io */
|
2011-11-30 12:23:43 +00:00
|
|
|
bdrv_drain_all();
|
2010-11-12 17:07:13 +00:00
|
|
|
bdrv_flush(bs);
|
|
|
|
bdrv_close(bs);
|
|
|
|
|
2011-08-03 13:07:40 +00:00
|
|
|
/* if we have a device attached to this BlockDriverState
|
Do not delete BlockDriverState when deleting the drive
When removing a drive from the host-side via drive_del we currently have
the following path:
drive_del
qemu_aio_flush()
bdrv_close() // zaps bs->drv, which makes any subsequent I/O get
// dropped. Works as designed
drive_uninit()
bdrv_delete() // frees the bs. Since the device is still connected to
// bs, any subsequent I/O is a use-after-free.
The value of bs->drv becomes unpredictable on free. As long as it
remains null, I/O still gets dropped, however it could become non-null
at any point after the free resulting SEGVs or other QEMU state
corruption.
To resolve this issue as simply as possible, we can chose to not
actually delete the BlockDriverState pointer. Since bdrv_close()
handles setting the drv pointer to NULL, we just need to remove the
BlockDriverState from the QLIST that is used to enumerate the block
devices. This is currently handled within bdrv_delete, so move this
into its own function, bdrv_make_anon().
The result is that we can now invoke drive_del, this closes the file
descriptors and sets BlockDriverState->drv to NULL which prevents futher
IO to the device, and since we do not free BlockDriverState, we don't
have to worry about the copy retained in the block devices.
We also don't attempt to remove the qdev property since we are no longer
deleting the BlockDriverState on drives with associated drives. This
also allows for removing Drives with no devices associated either.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-03-30 01:51:47 +00:00
|
|
|
* then we need to make the drive anonymous until the device
|
|
|
|
* can be removed. If this is a drive with no device backing
|
|
|
|
* then we can just get rid of the block driver state right here.
|
|
|
|
*/
|
2011-08-03 13:07:40 +00:00
|
|
|
if (bdrv_get_attached_dev(bs)) {
|
Do not delete BlockDriverState when deleting the drive
When removing a drive from the host-side via drive_del we currently have
the following path:
drive_del
qemu_aio_flush()
bdrv_close() // zaps bs->drv, which makes any subsequent I/O get
// dropped. Works as designed
drive_uninit()
bdrv_delete() // frees the bs. Since the device is still connected to
// bs, any subsequent I/O is a use-after-free.
The value of bs->drv becomes unpredictable on free. As long as it
remains null, I/O still gets dropped, however it could become non-null
at any point after the free resulting SEGVs or other QEMU state
corruption.
To resolve this issue as simply as possible, we can chose to not
actually delete the BlockDriverState pointer. Since bdrv_close()
handles setting the drv pointer to NULL, we just need to remove the
BlockDriverState from the QLIST that is used to enumerate the block
devices. This is currently handled within bdrv_delete, so move this
into its own function, bdrv_make_anon().
The result is that we can now invoke drive_del, this closes the file
descriptors and sets BlockDriverState->drv to NULL which prevents futher
IO to the device, and since we do not free BlockDriverState, we don't
have to worry about the copy retained in the block devices.
We also don't attempt to remove the qdev property since we are no longer
deleting the BlockDriverState on drives with associated drives. This
also allows for removing Drives with no devices associated either.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-03-30 01:51:47 +00:00
|
|
|
bdrv_make_anon(bs);
|
2013-06-05 08:33:14 +00:00
|
|
|
|
|
|
|
/* Further I/O must not pause the guest */
|
|
|
|
bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
|
|
|
|
BLOCKDEV_ON_ERROR_REPORT);
|
Do not delete BlockDriverState when deleting the drive
When removing a drive from the host-side via drive_del we currently have
the following path:
drive_del
qemu_aio_flush()
bdrv_close() // zaps bs->drv, which makes any subsequent I/O get
// dropped. Works as designed
drive_uninit()
bdrv_delete() // frees the bs. Since the device is still connected to
// bs, any subsequent I/O is a use-after-free.
The value of bs->drv becomes unpredictable on free. As long as it
remains null, I/O still gets dropped, however it could become non-null
at any point after the free resulting SEGVs or other QEMU state
corruption.
To resolve this issue as simply as possible, we can chose to not
actually delete the BlockDriverState pointer. Since bdrv_close()
handles setting the drv pointer to NULL, we just need to remove the
BlockDriverState from the QLIST that is used to enumerate the block
devices. This is currently handled within bdrv_delete, so move this
into its own function, bdrv_make_anon().
The result is that we can now invoke drive_del, this closes the file
descriptors and sets BlockDriverState->drv to NULL which prevents futher
IO to the device, and since we do not free BlockDriverState, we don't
have to worry about the copy retained in the block devices.
We also don't attempt to remove the qdev property since we are no longer
deleting the BlockDriverState on drives with associated drives. This
also allows for removing Drives with no devices associated either.
Reported-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2011-03-30 01:51:47 +00:00
|
|
|
} else {
|
|
|
|
drive_uninit(drive_get_by_blockdev(bs));
|
2010-11-12 17:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-24 12:32:33 +00:00
|
|
|
|
2011-11-25 16:57:10 +00:00
|
|
|
void qmp_block_resize(const char *device, int64_t size, Error **errp)
|
2011-01-24 12:32:33 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2013-05-02 13:32:55 +00:00
|
|
|
int ret;
|
2011-01-24 12:32:33 +00:00
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
2011-11-25 16:57:10 +00:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
2011-01-24 12:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size < 0) {
|
2012-01-04 22:23:34 +00:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
|
2011-11-25 16:57:10 +00:00
|
|
|
return;
|
2011-01-24 12:32:33 +00:00
|
|
|
}
|
|
|
|
|
2013-03-11 10:04:24 +00:00
|
|
|
/* complete all in-flight operations before resizing the device */
|
|
|
|
bdrv_drain_all();
|
|
|
|
|
2013-05-02 13:32:55 +00:00
|
|
|
ret = bdrv_truncate(bs, size);
|
|
|
|
switch (ret) {
|
2012-01-04 22:23:34 +00:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -ENOMEDIUM:
|
|
|
|
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
|
|
|
break;
|
|
|
|
case -ENOTSUP:
|
|
|
|
error_set(errp, QERR_UNSUPPORTED);
|
|
|
|
break;
|
|
|
|
case -EACCES:
|
|
|
|
error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
|
|
|
|
break;
|
|
|
|
case -EBUSY:
|
|
|
|
error_set(errp, QERR_DEVICE_IN_USE, device);
|
|
|
|
break;
|
|
|
|
default:
|
2013-05-02 13:32:55 +00:00
|
|
|
error_setg_errno(errp, -ret, "Could not resize");
|
2012-01-04 22:23:34 +00:00
|
|
|
break;
|
2011-01-24 12:32:33 +00:00
|
|
|
}
|
|
|
|
}
|
2012-01-18 14:40:46 +00:00
|
|
|
|
2012-09-27 17:29:14 +00:00
|
|
|
static void block_job_cb(void *opaque, int ret)
|
2012-01-18 14:40:46 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs = opaque;
|
|
|
|
QObject *obj;
|
|
|
|
|
2012-09-27 17:29:14 +00:00
|
|
|
trace_block_job_cb(bs, bs->job, ret);
|
2012-01-18 14:40:46 +00:00
|
|
|
|
|
|
|
assert(bs->job);
|
|
|
|
obj = qobject_from_block_job(bs->job);
|
|
|
|
if (ret < 0) {
|
|
|
|
QDict *dict = qobject_to_qdict(obj);
|
|
|
|
qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
|
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:48 +00:00
|
|
|
if (block_job_is_cancelled(bs->job)) {
|
|
|
|
monitor_protocol_event(QEVENT_BLOCK_JOB_CANCELLED, obj);
|
|
|
|
} else {
|
|
|
|
monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj);
|
|
|
|
}
|
2012-01-18 14:40:46 +00:00
|
|
|
qobject_decref(obj);
|
2012-01-18 14:40:50 +00:00
|
|
|
|
|
|
|
drive_put_ref_bh_schedule(drive_get_by_blockdev(bs));
|
2012-01-18 14:40:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_block_stream(const char *device, bool has_base,
|
2012-09-28 15:22:59 +00:00
|
|
|
const char *base, bool has_speed, int64_t speed,
|
|
|
|
bool has_on_error, BlockdevOnError on_error,
|
|
|
|
Error **errp)
|
2012-01-18 14:40:46 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
2012-01-18 14:40:53 +00:00
|
|
|
BlockDriverState *base_bs = NULL;
|
2012-04-25 15:51:00 +00:00
|
|
|
Error *local_err = NULL;
|
2012-01-18 14:40:46 +00:00
|
|
|
|
2012-09-28 15:22:59 +00:00
|
|
|
if (!has_on_error) {
|
|
|
|
on_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:46 +00:00
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (base) {
|
2012-01-18 14:40:53 +00:00
|
|
|
base_bs = bdrv_find_backing_image(bs, base);
|
|
|
|
if (base_bs == NULL) {
|
|
|
|
error_set(errp, QERR_BASE_NOT_FOUND, base);
|
|
|
|
return;
|
|
|
|
}
|
2012-01-18 14:40:46 +00:00
|
|
|
}
|
|
|
|
|
2012-04-25 15:51:03 +00:00
|
|
|
stream_start(bs, base_bs, base, has_speed ? speed : 0,
|
2012-09-28 15:22:59 +00:00
|
|
|
on_error, block_job_cb, bs, &local_err);
|
2012-04-25 15:51:00 +00:00
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
2012-01-18 14:40:46 +00:00
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:50 +00:00
|
|
|
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
|
|
|
* underneath us.
|
|
|
|
*/
|
|
|
|
drive_get_ref(drive_get_by_blockdev(bs));
|
|
|
|
|
2012-01-18 14:40:46 +00:00
|
|
|
trace_qmp_block_stream(bs, bs->job);
|
|
|
|
}
|
2012-01-18 14:40:47 +00:00
|
|
|
|
QAPI: add command for live block commit, 'block-commit'
The command for live block commit is added, which has the following
arguments:
device: the block device to perform the commit on (mandatory)
base: the base image to commit into; optional (if not specified,
it is the underlying original image)
top: the top image of the commit - all data from inside top down
to base will be committed into base (mandatory for now; see
note, below)
speed: maximum speed, in bytes/sec
Note: Eventually this command will support merging down the active layer,
but that code is not yet complete. If the active layer is passed
in as top, then an error will be returned. Once merging down the
active layer is supported, the 'top' argument may become optional,
and default to the active layer.
The is done as a block job, so upon completion a BLOCK_JOB_COMPLETED will
be emitted.
Signed-off-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-09-27 17:29:16 +00:00
|
|
|
void qmp_block_commit(const char *device,
|
|
|
|
bool has_base, const char *base, const char *top,
|
|
|
|
bool has_speed, int64_t speed,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *base_bs, *top_bs;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
/* This will be part of the QMP command, if/when the
|
|
|
|
* BlockdevOnError change for blkmirror makes it in
|
|
|
|
*/
|
2012-09-28 15:22:55 +00:00
|
|
|
BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
|
QAPI: add command for live block commit, 'block-commit'
The command for live block commit is added, which has the following
arguments:
device: the block device to perform the commit on (mandatory)
base: the base image to commit into; optional (if not specified,
it is the underlying original image)
top: the top image of the commit - all data from inside top down
to base will be committed into base (mandatory for now; see
note, below)
speed: maximum speed, in bytes/sec
Note: Eventually this command will support merging down the active layer,
but that code is not yet complete. If the active layer is passed
in as top, then an error will be returned. Once merging down the
active layer is supported, the 'top' argument may become optional,
and default to the active layer.
The is done as a block job, so upon completion a BLOCK_JOB_COMPLETED will
be emitted.
Signed-off-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-09-27 17:29:16 +00:00
|
|
|
|
|
|
|
/* drain all i/o before commits */
|
|
|
|
bdrv_drain_all();
|
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs) {
|
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* default top_bs is the active layer */
|
|
|
|
top_bs = bs;
|
|
|
|
|
|
|
|
if (top) {
|
|
|
|
if (strcmp(bs->filename, top) != 0) {
|
|
|
|
top_bs = bdrv_find_backing_image(bs, top);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top_bs == NULL) {
|
|
|
|
error_setg(errp, "Top image file %s not found", top ? top : "NULL");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-16 19:49:10 +00:00
|
|
|
if (has_base && base) {
|
|
|
|
base_bs = bdrv_find_backing_image(top_bs, base);
|
|
|
|
} else {
|
|
|
|
base_bs = bdrv_find_base(top_bs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (base_bs == NULL) {
|
|
|
|
error_set(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
QAPI: add command for live block commit, 'block-commit'
The command for live block commit is added, which has the following
arguments:
device: the block device to perform the commit on (mandatory)
base: the base image to commit into; optional (if not specified,
it is the underlying original image)
top: the top image of the commit - all data from inside top down
to base will be committed into base (mandatory for now; see
note, below)
speed: maximum speed, in bytes/sec
Note: Eventually this command will support merging down the active layer,
but that code is not yet complete. If the active layer is passed
in as top, then an error will be returned. Once merging down the
active layer is supported, the 'top' argument may become optional,
and default to the active layer.
The is done as a block job, so upon completion a BLOCK_JOB_COMPLETED will
be emitted.
Signed-off-by: Jeff Cody <jcody@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-09-27 17:29:16 +00:00
|
|
|
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
|
|
|
|
&local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
|
|
|
* underneath us.
|
|
|
|
*/
|
|
|
|
drive_get_ref(drive_get_by_blockdev(bs));
|
|
|
|
}
|
|
|
|
|
block: add drive-backup QMP command
@drive-backup
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
@device: the name of the device which should be copied.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.
Returns: nothing on success
If @device is not a valid block device, DeviceNotFound
Since 1.6
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:14 +00:00
|
|
|
void qmp_drive_backup(const char *device, const char *target,
|
|
|
|
bool has_format, const char *format,
|
2013-06-26 12:11:57 +00:00
|
|
|
enum MirrorSyncMode sync,
|
block: add drive-backup QMP command
@drive-backup
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
@device: the name of the device which should be copied.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.
Returns: nothing on success
If @device is not a valid block device, DeviceNotFound
Since 1.6
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:14 +00:00
|
|
|
bool has_mode, enum NewImageMode mode,
|
|
|
|
bool has_speed, int64_t speed,
|
|
|
|
bool has_on_source_error, BlockdevOnError on_source_error,
|
|
|
|
bool has_on_target_error, BlockdevOnError on_target_error,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *target_bs;
|
2013-07-26 18:39:04 +00:00
|
|
|
BlockDriverState *source = NULL;
|
block: add drive-backup QMP command
@drive-backup
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
@device: the name of the device which should be copied.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.
Returns: nothing on success
If @device is not a valid block device, DeviceNotFound
Since 1.6
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:14 +00:00
|
|
|
BlockDriver *drv = NULL;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
int flags;
|
|
|
|
int64_t size;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!has_speed) {
|
|
|
|
speed = 0;
|
|
|
|
}
|
|
|
|
if (!has_on_source_error) {
|
|
|
|
on_source_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
if (!has_on_target_error) {
|
|
|
|
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
if (!has_mode) {
|
|
|
|
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (!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);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_in_use(bs)) {
|
|
|
|
error_set(errp, QERR_DEVICE_IN_USE, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = bs->open_flags | BDRV_O_RDWR;
|
|
|
|
|
2013-07-26 18:39:04 +00:00
|
|
|
/* See if we have a backing HD we can use to create our new image
|
|
|
|
* on top of. */
|
|
|
|
if (sync == MIRROR_SYNC_MODE_TOP) {
|
|
|
|
source = bs->backing_hd;
|
|
|
|
if (!source) {
|
|
|
|
sync = MIRROR_SYNC_MODE_FULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sync == MIRROR_SYNC_MODE_NONE) {
|
|
|
|
source = bs;
|
|
|
|
}
|
|
|
|
|
block: add drive-backup QMP command
@drive-backup
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
@device: the name of the device which should be copied.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.
Returns: nothing on success
If @device is not a valid block device, DeviceNotFound
Since 1.6
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:14 +00:00
|
|
|
size = bdrv_getlength(bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
|
|
|
assert(format && drv);
|
2013-07-26 18:39:04 +00:00
|
|
|
if (source) {
|
|
|
|
bdrv_img_create(target, format, source->filename,
|
|
|
|
source->drv->format_name, NULL,
|
|
|
|
size, flags, &local_err, false);
|
|
|
|
} else {
|
|
|
|
bdrv_img_create(target, format, NULL, NULL, NULL,
|
|
|
|
size, flags, &local_err, false);
|
|
|
|
}
|
block: add drive-backup QMP command
@drive-backup
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
@device: the name of the device which should be copied.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.
Returns: nothing on success
If @device is not a valid block device, DeviceNotFound
Since 1.6
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
target_bs = bdrv_new("");
|
|
|
|
ret = bdrv_open(target_bs, target, NULL, flags, drv);
|
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_delete(target_bs);
|
|
|
|
error_setg_file_open(errp, -ret, target);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-07-26 18:39:04 +00:00
|
|
|
backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
|
block: add drive-backup QMP command
@drive-backup
Start a point-in-time copy of a block device to a new destination. The
status of ongoing drive-backup operations can be checked with
query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
The operation can be stopped before it has completed using the
block-job-cancel command.
@device: the name of the device which should be copied.
@target: the target of the new image. If the file exists, or if it
is a device, the existing file/device will be used as the new
destination. If it does not exist, a new file will be created.
@format: #optional the format of the new destination, default is to
probe if @mode is 'existing', else the format of the source
@mode: #optional whether and how QEMU should create a new image, default is
'absolute-paths'.
@speed: #optional the maximum speed, in bytes per second
@on-source-error: #optional the action to take on an error on the source,
default 'report'. 'stop' and 'enospc' can only be used
if the block device supports io-status (see BlockInfo).
@on-target-error: #optional the action to take on an error on the target,
default 'report' (no limitations, since this applies to
a different block device than @device).
Note that @on-source-error and @on-target-error only affect background I/O.
If an error occurs during a guest write request, the device's rerror/werror
actions will be used.
Returns: nothing on success
If @device is not a valid block device, DeviceNotFound
Since 1.6
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2013-06-24 15:13:14 +00:00
|
|
|
block_job_cb, bs, &local_err);
|
|
|
|
if (local_err != NULL) {
|
|
|
|
bdrv_delete(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));
|
|
|
|
}
|
|
|
|
|
2013-01-22 08:03:13 +00:00
|
|
|
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
|
|
|
|
|
2012-10-18 14:49:24 +00:00
|
|
|
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,
|
2012-10-18 14:49:28 +00:00
|
|
|
bool has_speed, int64_t speed,
|
2013-01-21 16:09:46 +00:00
|
|
|
bool has_granularity, uint32_t granularity,
|
2013-01-22 08:03:13 +00:00
|
|
|
bool has_buf_size, int64_t buf_size,
|
2012-10-18 14:49:28 +00:00
|
|
|
bool has_on_source_error, BlockdevOnError on_source_error,
|
|
|
|
bool has_on_target_error, BlockdevOnError on_target_error,
|
|
|
|
Error **errp)
|
2012-10-18 14:49:24 +00:00
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
BlockDriverState *source, *target_bs;
|
|
|
|
BlockDriver *drv = NULL;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
int flags;
|
2013-06-24 15:13:13 +00:00
|
|
|
int64_t size;
|
2012-10-18 14:49:24 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!has_speed) {
|
|
|
|
speed = 0;
|
|
|
|
}
|
2012-10-18 14:49:28 +00:00
|
|
|
if (!has_on_source_error) {
|
|
|
|
on_source_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
|
|
|
if (!has_on_target_error) {
|
|
|
|
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
|
|
|
|
}
|
2012-10-18 14:49:24 +00:00
|
|
|
if (!has_mode) {
|
|
|
|
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
|
|
|
}
|
2013-01-21 16:09:46 +00:00
|
|
|
if (!has_granularity) {
|
|
|
|
granularity = 0;
|
|
|
|
}
|
2013-01-22 08:03:13 +00:00
|
|
|
if (!has_buf_size) {
|
|
|
|
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
|
|
|
}
|
|
|
|
|
2013-01-21 16:09:46 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-10-18 14:49:24 +00:00
|
|
|
|
|
|
|
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 (!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);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bdrv_in_use(bs)) {
|
|
|
|
error_set(errp, QERR_DEVICE_IN_USE, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = bs->open_flags | BDRV_O_RDWR;
|
|
|
|
source = bs->backing_hd;
|
|
|
|
if (!source && sync == MIRROR_SYNC_MODE_TOP) {
|
|
|
|
sync = MIRROR_SYNC_MODE_FULL;
|
|
|
|
}
|
|
|
|
|
2013-06-24 15:13:13 +00:00
|
|
|
size = bdrv_getlength(bs);
|
|
|
|
if (size < 0) {
|
|
|
|
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-18 14:49:24 +00:00
|
|
|
if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) {
|
|
|
|
/* create new image w/o backing file */
|
|
|
|
assert(format && drv);
|
2012-11-30 12:52:08 +00:00
|
|
|
bdrv_img_create(target, format,
|
2013-02-13 08:09:40 +00:00
|
|
|
NULL, NULL, NULL, size, flags, &local_err, false);
|
2012-10-18 14:49:24 +00:00
|
|
|
} else {
|
|
|
|
switch (mode) {
|
|
|
|
case NEW_IMAGE_MODE_EXISTING:
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
|
|
|
|
/* create new image with backing file */
|
2012-11-30 12:52:08 +00:00
|
|
|
bdrv_img_create(target, format,
|
|
|
|
source->filename,
|
|
|
|
source->drv->format_name,
|
2013-02-13 08:09:40 +00:00
|
|
|
NULL, size, flags, &local_err, false);
|
2012-10-18 14:49:24 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-30 12:52:08 +00:00
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
error_propagate(errp, local_err);
|
2012-10-18 14:49:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-21 16:09:43 +00:00
|
|
|
/* Mirroring takes care of copy-on-write using the source's backing
|
|
|
|
* file.
|
|
|
|
*/
|
2012-10-18 14:49:24 +00:00
|
|
|
target_bs = bdrv_new("");
|
2013-03-15 09:35:02 +00:00
|
|
|
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv);
|
2012-10-18 14:49:24 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
bdrv_delete(target_bs);
|
2013-06-07 18:33:48 +00:00
|
|
|
error_setg_file_open(errp, -ret, target);
|
2012-10-18 14:49:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-22 08:03:13 +00:00
|
|
|
mirror_start(bs, target_bs, speed, granularity, buf_size, sync,
|
2013-01-21 16:09:46 +00:00
|
|
|
on_source_error, on_target_error,
|
2012-10-18 14:49:28 +00:00
|
|
|
block_job_cb, bs, &local_err);
|
2012-10-18 14:49:24 +00:00
|
|
|
if (local_err != NULL) {
|
|
|
|
bdrv_delete(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));
|
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:47 +00:00
|
|
|
static BlockJob *find_block_job(const char *device)
|
|
|
|
{
|
|
|
|
BlockDriverState *bs;
|
|
|
|
|
|
|
|
bs = bdrv_find(device);
|
|
|
|
if (!bs || !bs->job) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return bs->job;
|
|
|
|
}
|
|
|
|
|
2012-04-25 15:51:02 +00:00
|
|
|
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
|
2012-01-18 14:40:47 +00:00
|
|
|
{
|
|
|
|
BlockJob *job = find_block_job(device);
|
|
|
|
|
|
|
|
if (!job) {
|
2012-09-28 15:22:44 +00:00
|
|
|
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
2012-01-18 14:40:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-25 15:51:02 +00:00
|
|
|
block_job_set_speed(job, speed, errp);
|
2012-01-18 14:40:47 +00:00
|
|
|
}
|
2012-01-18 14:40:48 +00:00
|
|
|
|
2012-09-28 15:22:51 +00:00
|
|
|
void qmp_block_job_cancel(const char *device,
|
|
|
|
bool has_force, bool force, Error **errp)
|
2012-01-18 14:40:48 +00:00
|
|
|
{
|
|
|
|
BlockJob *job = find_block_job(device);
|
|
|
|
|
2012-09-28 15:22:51 +00:00
|
|
|
if (!has_force) {
|
|
|
|
force = false;
|
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:48 +00:00
|
|
|
if (!job) {
|
2012-09-28 15:22:44 +00:00
|
|
|
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
2012-01-18 14:40:48 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-09-28 15:22:51 +00:00
|
|
|
if (job->paused && !force) {
|
2012-09-28 15:22:50 +00:00
|
|
|
error_set(errp, QERR_BLOCK_JOB_PAUSED, device);
|
|
|
|
return;
|
|
|
|
}
|
2012-01-18 14:40:48 +00:00
|
|
|
|
|
|
|
trace_qmp_block_job_cancel(job);
|
|
|
|
block_job_cancel(job);
|
|
|
|
}
|
2012-01-18 14:40:49 +00:00
|
|
|
|
2012-09-28 15:22:51 +00:00
|
|
|
void qmp_block_job_pause(const char *device, Error **errp)
|
|
|
|
{
|
|
|
|
BlockJob *job = find_block_job(device);
|
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_pause(job);
|
|
|
|
block_job_pause(job);
|
|
|
|
}
|
|
|
|
|
|
|
|
void qmp_block_job_resume(const char *device, Error **errp)
|
|
|
|
{
|
|
|
|
BlockJob *job = find_block_job(device);
|
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_resume(job);
|
|
|
|
block_job_resume(job);
|
|
|
|
}
|
|
|
|
|
2012-10-18 14:49:21 +00:00
|
|
|
void qmp_block_job_complete(const char *device, Error **errp)
|
|
|
|
{
|
|
|
|
BlockJob *job = find_block_job(device);
|
|
|
|
|
|
|
|
if (!job) {
|
|
|
|
error_set(errp, QERR_BLOCK_JOB_NOT_ACTIVE, device);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_qmp_block_job_complete(job);
|
|
|
|
block_job_complete(job, errp);
|
|
|
|
}
|
|
|
|
|
2012-01-18 14:40:49 +00:00
|
|
|
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
|
|
|
|
{
|
|
|
|
BlockJobInfoList **prev = opaque;
|
|
|
|
BlockJob *job = bs->job;
|
|
|
|
|
|
|
|
if (job) {
|
2012-09-28 15:22:48 +00:00
|
|
|
BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
|
|
|
|
elem->value = block_job_query(bs->job);
|
2012-01-18 14:40:49 +00:00
|
|
|
(*prev)->next = elem;
|
|
|
|
*prev = elem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
|
|
|
{
|
|
|
|
/* Dummy is a fake list element for holding the head pointer */
|
|
|
|
BlockJobInfoList dummy = {};
|
|
|
|
BlockJobInfoList *prev = &dummy;
|
|
|
|
bdrv_iterate(do_qmp_query_block_jobs_one, &prev);
|
|
|
|
return dummy.next;
|
|
|
|
}
|
2012-11-26 15:03:42 +00:00
|
|
|
|
2013-03-15 09:35:07 +00:00
|
|
|
QemuOptsList qemu_common_drive_opts = {
|
2012-11-26 15:03:42 +00:00
|
|
|
.name = "drive",
|
2013-03-15 09:35:07 +00:00
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
2012-11-26 15:03:42 +00:00
|
|
|
.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",
|
2013-02-08 13:06:12 +00:00
|
|
|
},{
|
|
|
|
.name = "discard",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "discard operation (ignore/off, unmap/on)",
|
2012-11-26 15:03:42 +00:00
|
|
|
},{
|
2013-07-18 14:31:25 +00:00
|
|
|
.name = "cache.writeback",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "enables writeback mode for any caches",
|
|
|
|
},{
|
|
|
|
.name = "cache.direct",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "enables use of O_DIRECT (bypass the host page cache)",
|
|
|
|
},{
|
|
|
|
.name = "cache.no-flush",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "ignore any flush requests for the device",
|
2012-11-26 15:03:42 +00:00
|
|
|
},{
|
|
|
|
.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)",
|
|
|
|
},{
|
2013-07-19 18:07:29 +00:00
|
|
|
.name = "read-only",
|
2012-11-26 15:03:42 +00:00
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "open drive file as read-only",
|
|
|
|
},{
|
2013-07-17 12:41:54 +00:00
|
|
|
.name = "throttling.iops-total",
|
2012-11-26 15:03:42 +00:00
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "limit total I/O operations per second",
|
|
|
|
},{
|
2013-07-17 12:41:54 +00:00
|
|
|
.name = "throttling.iops-read",
|
2012-11-26 15:03:42 +00:00
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "limit read operations per second",
|
|
|
|
},{
|
2013-07-17 12:41:54 +00:00
|
|
|
.name = "throttling.iops-write",
|
2012-11-26 15:03:42 +00:00
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "limit write operations per second",
|
|
|
|
},{
|
2013-07-17 12:41:54 +00:00
|
|
|
.name = "throttling.bps-total",
|
2012-11-26 15:03:42 +00:00
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "limit total bytes per second",
|
|
|
|
},{
|
2013-07-17 12:41:54 +00:00
|
|
|
.name = "throttling.bps-read",
|
2012-11-26 15:03:42 +00:00
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "limit read bytes per second",
|
|
|
|
},{
|
2013-07-17 12:41:54 +00:00
|
|
|
.name = "throttling.bps-write",
|
2012-11-26 15:03:42 +00:00
|
|
|
.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 */ }
|
|
|
|
},
|
|
|
|
};
|
2013-03-15 09:35:07 +00:00
|
|
|
|
2013-07-30 12:58:27 +00:00
|
|
|
QemuOptsList qemu_old_drive_opts = {
|
|
|
|
.name = "drive",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_old_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 */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-03-15 09:35:07 +00:00
|
|
|
QemuOptsList qemu_drive_opts = {
|
|
|
|
.name = "drive",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
|
|
|
.desc = {
|
2013-06-19 11:44:17 +00:00
|
|
|
/*
|
|
|
|
* no elements => accept any params
|
|
|
|
* validation will happen later
|
|
|
|
*/
|
2013-03-15 09:35:07 +00:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|